# How Ponytail Integrates with AI Agent Lifecycle Hooks: A Deep Dive into Hermes Framework Integration

> Discover how Ponytail integrates with AI agent lifecycle hooks using Hermes. Inject context pre-LLM and rewrite commands with pre_llm_call and pre_gateway_dispatch callbacks.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: deep-dive
- Published: 2026-09-06

---

**Ponytail integrates with AI agent lifecycle hooks by registering two callbacks with the Hermes framework—`pre_llm_call` to inject mode-specific context before LLM requests and `pre_gateway_dispatch` to rewrite slash commands into standard prompts.**

Ponytail is an AI coding assistant plugin that extends agent behavior through the **Hermes AI-agent framework's hook system**. By hooking into specific execution phases, Ponytail dynamically modifies prompts and command handling without altering core agent logic. This article examines how Ponytail implements lifecycle hooks based on its source code.

## The Two Lifecycle Hooks Ponytail Registers

In [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py), the `register()` function installs both hooks at lines [202-203](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L202-L203):

```python
ctx.register_hook("pre_llm_call", _pre_llm_call)                   # line 202

ctx.register_hook("pre_gateway_dispatch", rewrite_gateway_command) # line 203

```

These hooks operate at distinct phases of agent execution.

## pre_llm_call: Injecting Mode-Specific Context

The **`pre_llm_call`** hook fires **before every LLM request**, allowing Ponytail to prepend contextual guidance based on the active operating mode.

### Implementation Details

The handler `_pre_llm_call` (lines [125-128](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L125-L128)) determines the current mode and builds injected context:

```python
def _pre_llm_call(session_id: str = "", **_: Any) -> dict[str, str] | None:
    mode = _current_mode or _default_mode()
    context = build_injected_context(mode)  # ← builds injected prompt

    return {"context": context} if context else None

```

The **`build_injected_context()`** function (lines [105-122](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L105-L122)) loads the appropriate **SKILL.md** file for modes like `ponytail`, `review`, or others. When mode is `"off"`, no context is injected—allowing clean disabling without unregistering the hook.

## pre_gateway_dispatch: Rewriting Slash Commands

The **`pre_gateway_dispatch`** hook intercepts **slash commands from chat gateways** and transforms them into standard text prompts.

### Command Processing Pipeline

The `rewrite_gateway_command` handler (lines [153-164](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py#L153-L164)) performs four operations:

1. **Extract command text** from the gateway event
2. **Validate** against `SKILL_COMMANDS` registry
3. **Enforce permissions** via `_slash_access_denied`
4. **Rewrite** to standard prompt using `_skill_prompt`

```python
def rewrite_gateway_command(event: Any = None, gateway: Any = None, **_: Any):
    text = str(getattr(event, "text", "") or "").strip()
    if not text.startswith("/"):
        return None
    head, _, rest = text[1:].partition(" ")
    command = head.replace("_", "-").lower()
    if command not in SKILL_COMMANDS:
        return None
    if _slash_access_denied(event, gateway, command):
        return None
    return {"action": "rewrite", "text": _skill_prompt(command, rest)}

```

This transformation allows users to type `/ponytail-review` while the LLM receives a fully-formed natural language prompt.

## Hook Execution Comparison

| Hook | Trigger Point | Core Function | Returns |
|------|-------------|-------------|---------|
| `pre_llm_call` | Pre-LLM invocation | Inject mode context | `{"context": context}` or `None` |
| `pre_gateway_dispatch` | Gateway message receipt | Transform `/command` to prompt | `{"action": "rewrite", "text": ...}` or `None` |

Both hooks follow the **same architectural pattern**: inspect state, conditionally modify, return dict for action or `None` to pass through unchanged.

## Relation to SessionStart and Benchmarking

Ponytail's hooks are **independent of the SessionStart plugin** mentioned in [`benchmarks/agentic/run.py`](https://github.com/DietrichGebert/ponytail/blob/main/benchmarks/agentic/run.py), yet both implement the same Hermes callback pattern. The benchmark harness demonstrates this in [benchmarks/agentic/run.py](https://github.com/DietrichGebert/ponytail/blob/main/benchmarks/agentic/run.py), where Ponytail attaches as a session initialization plugin to Claude's CLI.

The [benchmarks/agentic/tasks.py](https://github.com/DietrichGebert/ponytail/blob/main/benchmarks/agentic/tasks.py) file defines evaluation scenarios that exercise hook-driven skill activation, validating that lifecycle integration works end-to-end.

## Summary

- **Two hooks enable full integration**: `pre_llm_call` for context injection, `pre_gateway_dispatch` for command rewriting
- **Hook registration occurs centrally** in [`__init__.py`](https://github.com/DietrichGebert/ponytail/blob/main/__init__.py) `register()` via `ctx.register_hook()` calls
- **Mode-driven behavior** allows runtime switching between `ponytail`, `review`, or `off` states without code changes
- **Clean pass-through semantics**—returning `None` leaves the execution flow undisturbed
- **Benchmark validation** in `benchmarks/agentic/` confirms production-ready hook reliability

## Frequently Asked Questions

### What AI agent framework does Ponytail use for lifecycle hooks?

Ponytail integrates with the **Hermes AI-agent framework**, a modular system that exposes lifecycle hooks for extending agent behavior without modifying core code.

### Can Ponytail's hooks be disabled without code changes?

Yes. Setting the Ponytail mode to `"off"` causes `build_injected_context()` to return no context, effectively disabling injection while keeping the hook registered. Gateway commands can also be blocked via `_slash_access_denied` permission checks.

### How does the pre_llm_call hook affect LLM performance?

The hook adds **minimal overhead**—it performs a dictionary lookup for the current mode, reads a cached SKILL.md file, and returns a small context string. No network calls or heavy computation occur in the hot path.

### What distinguishes pre_gateway_dispatch from standard message handlers?

Unlike handlers that process messages after dispatch, `pre_gateway_dispatch` **rewrites the message before routing**—converting `/ponytail-review my code` into a full prompt that the agent processes as ordinary user input, maintaining compatibility with existing agent logic.