Hookify V2 for Pi.
This package turns Pi extension hooks into declarative YAML rules stored under .pi/.
Rule files are parsed from YAML and validated with Zod, so invalid on / when / do / respond structures fail fast with clear parse errors.
Development is Bun-first (bun test, bun run smoke, bun run check), while the shipped runtime stays Node-compatible for Pi portability.
Instead of the old event + action + message model, Hookify V2 uses a Pi-native rule DSL built around:
on- which Pi hook to subscribe towhen- predicates over normalized event fieldsdo- side effects such as notify, ask, approval, shell execution, and custom messagesrespond- event-specific return data used to transform or intercept the hook
Store rules in:
.pi/hookify.<name>.local.yaml
Example:
name: quick-rewrite
enabled: true
priority: 100
on:
event: input
when:
field: input.text
op: starts_with
value: "?quick "
do:
- kind: notify
level: info
text: Rewriting quick prompt
respond:
action: transform
textTemplate: "Respond briefly: {{input.text}}"inputtool_calltool_resultbefore_agent_startcontextuser_bash
Hookify also accepts observe-only Pi lifecycle hooks for side-effect rules, including:
session_startsession_shutdownagent_startagent_endturn_startturn_endmessage_startmessage_updatemessage_endtool_execution_starttool_execution_updatetool_execution_endmodel_selectbefore_provider_requestresources_discoversession_compactsession_treesession_before_switchsession_before_forksession_before_compactsession_before_tree
Some of these currently support side effects only. Event-specific respond handling is implemented first for the main transformation and interception hooks.
A rule can use a single leaf predicate:
when:
field: tool.input.path
op: contains
value: package.jsonOr nested groups:
when:
all:
- field: tool.name
op: eq
value: read
- any:
- field: tool.input.path
op: contains
value: package.json
- field: tool.input.path
op: contains
value: tsconfig.jsonSupported operators:
eqneqcontainsnot_containsstarts_withends_withregexexistsingtgteltlte
Current do effect kinds:
notifysend_messagesend_user_messagerun_shellaskrequest_approval
do:
- kind: notify
level: warning
text: Dangerous command detecteddo:
- kind: send_message
customType: hookify-audit
template: "Read triggered for {{tool.input.path}}"
deliverAs: steerdo:
- kind: run_shell
command: npm run lint -- --fix
blockOnFailure: truedo:
- kind: ask
questions:
- question: Which release type is this?
header: Release
options:
- label: Patch
description: Bug fixes only
- label: Minor
description: Backward-compatible features
messageOnAnswer: Thanks, I captured the release details.do:
- kind: request_approval
title: Approve dangerous action?
message: Dangerous command detected.
remember: truerespond:
action: handledOr:
respond:
action: transform
textTemplate: "Respond briefly: {{input.text}}"respond:
patchInput:
- path: command
template: "source ~/.profile\n{{tool.input.command}}"
block: falseOr:
respond:
block: true
reason: Dangerous commandStructured content blocks are supported, not just text shorthands:
respond:
replaceContent:
- type: text
text: reviewed output
appendContent:
- type: image
data: aGVsbG8=
mimeType: image/pngrespond:
appendSystemPrompt: "
Be extra careful about release safety."
injectMessage:
customType: hookify-release-guard
content: Release-related request detected.
display: trueYou can still filter messages, or replace them completely with typed message specs:
respond:
replaceMessages:
- role: user
contentText: Fresh user context
- role: custom
customType: hookify-context
contentText: Fresh custom context
display: true
- role: toolResult
toolCallId: call-1
toolName: read
isError: false
contentBlocks:
- type: text
text: tool outputReturn a full result:
respond:
result:
output: BLOCKED
exitCode: 1
cancelled: false
truncated: falseOr declaratively wrap Pi's local shell backend with rewritten commands:
respond:
operations:
kind: local_shell
prependCommand: source ~/.profilerespond:
compaction:
summary: compacted summary
firstKeptEntryIdTemplate: "{{meta.preparation.firstKeptEntryId}}"
tokensBeforePath: meta.preparation.tokensBeforerespond:
summary:
summary: branch summary
customInstructions: Focus on the recent refactor
replaceInstructions: true
label: reviewed/hookify <description>- create a starter YAML rule in.pi//hookify:list- list parsed Hookify V2 rules/hookify:configure- enable or disable a rule
pi install git:github.com/trotsky1997/pi-hookifyRun the Bun test suite:
bun testRun watch mode, coverage, or the Bun smoke/check workflows:
bun run test:watch
bun run test:coverage
bun run smoke
bun run check- Hookify V2 is a clean-slate redesign. Old markdown/frontmatter rule files are not part of the new format.
- Templated strings use
{{path.to.field}}with normalized event fields such asinput.text,tool.name,tool.input.path,result.content, andagent.prompt.