feat: Hooks (#9093)

This commit is contained in:
Alex Hancock
2026-05-11 12:02:44 -04:00
committed by GitHub
parent f5eda3527c
commit 40d4b118bb
12 changed files with 1258 additions and 18 deletions
+48
View File
@@ -0,0 +1,48 @@
# hello-hooks
A tiny [Open Plugins](https://open-plugins.com) plugin that demonstrates goose's
hook system. It registers four event handlers — `SessionStart`,
`UserPromptSubmit`, `PreToolUse`, and `PostToolUse` (matched on
`developer__shell|developer__text_editor`) — that each shell out to
`scripts/announce.sh` to print a noticeable line to stderr and append the full
event payload to `last-event.log` next to the plugin.
## Layout
```
hello-hooks/
├── plugin.json
├── hooks/
│ └── hooks.json
└── scripts/
└── announce.sh
```
## Try it
Goose discovers plugins under `~/.agents/plugins/<name>/` (user scope) and
`<project-root>/.agents/plugins/<name>/` (project scope) per the Open Plugins
[installation spec](https://open-plugins.com/plugin-builders/installation#recommended-storage-paths).
```bash
mkdir -p ~/.agents/plugins
cp -R examples/plugins/hello-hooks ~/.agents/plugins/hello-hooks
chmod +x ~/.agents/plugins/hello-hooks/scripts/announce.sh
# Then run goose normally; you should see lines like
# 🚀 [hello-hooks] SessionStart
# 💬 [hello-hooks] UserPromptSubmit
# ⚡ [hello-hooks] PreToolUse tool=developer__shell
# ✅ [hello-hooks] PostToolUse tool=developer__shell
goose session
# Inspect the full payloads goose passed to the hook:
tail ~/.agents/plugins/hello-hooks/last-event.log
```
To turn the plugin off, add it to `disabledPlugins` in
`~/.config/goose/settings.json`:
```json
{ "disabledPlugins": ["hello-hooks"] }
```
@@ -0,0 +1,45 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "${PLUGIN_ROOT}/scripts/announce.sh start"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "${PLUGIN_ROOT}/scripts/announce.sh prompt"
}
]
}
],
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "${PLUGIN_ROOT}/scripts/announce.sh pre-tool"
}
]
}
],
"PostToolUse": [
{
"matcher": "developer__shell|developer__text_editor",
"hooks": [
{
"type": "command",
"command": "${PLUGIN_ROOT}/scripts/announce.sh post-tool"
}
]
}
]
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "hello-hooks",
"version": "0.1.0",
"description": "A tiny demo plugin that prints messages so you can see goose's hook system firing in real time."
}
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Tiny hook handler that loudly announces every event goose fires at it.
#
# Goose pipes the event payload as JSON on stdin and sets PLUGIN_ROOT in the
# environment. We tee a copy of the payload into a log file inside the plugin
# directory so you can see the full structure later.
set -euo pipefail
label="${1:-event}"
log="${PLUGIN_ROOT:-.}/last-event.log"
payload="$(cat)"
event_name="$(printf '%s' "$payload" | sed -n 's/.*"event":"\([^"]*\)".*/\1/p')"
tool_name="$(printf '%s' "$payload" | sed -n 's/.*"tool_name":"\([^"]*\)".*/\1/p')"
emoji() {
case "$1" in
start) printf '\xf0\x9f\x9a\x80' ;; # rocket
prompt) printf '\xf0\x9f\x92\xac' ;; # speech balloon
pre-tool) printf '\xe2\x9a\xa1' ;; # zap
post-tool) printf '\xe2\x9c\x85' ;; # check
*) printf '\xf0\x9f\x94\x94' ;; # bell
esac
}
icon="$(emoji "$label")"
suffix=""
[ -n "$tool_name" ] && suffix=" tool=$tool_name"
# Both stderr (so it shows up alongside goose tracing) and a log file.
printf '%s [hello-hooks] %s%s\n' "$icon" "${event_name:-$label}" "$suffix" 1>&2
{
printf -- '---- %s @ %s ----\n' "${event_name:-$label}" "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
printf '%s\n' "$payload"
} >> "$log"