boldsoftware/shelley/shelley-hooks

Claude Code and agent skill by boldsoftware/shelley

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when the user wants to customize Shelley by injecting behavior at lifecycle events. It documents Shelley's hooks.

Install

npx vskill@latest install boldsoftware/shelley/shelley-hooks

SKILL.md

Executable files at ~/.config/shelley/hooks/<name>. Missing or non-executable files are ignored. 30s timeout. Any hook failure (non-zero exit, invalid output, etc.) aborts the operation it belongs to — except end-of-turn, where the operation is already finished, so failures are just logged, and status-bar, which shows its error in its row.

Auth-bearing headers (Cookie, Set-Cookie, Authorization, Proxy-Authorization) are stripped from the headers fields before hooks see them.

system-prompt

Runs on every system prompt (main, subagent).

  • stdin: prompt text
  • stdout: replacement prompt text (non-empty)

new-conversation

Runs once when a conversation is created: user-initiated or the first run of a new subagent.

stdin JSON:

json
{
"prompt": "...", "model": "...", "cwd": "...",
"readonly": {
"conversation_id": "cXXXXXX",
"is_subagent": false, "parent_id": "...",
"headers": [["X-Exedev-Email", "user@example.com"]]
}
}

parent_id is omitempty. headers is a sorted list of [name, value] pairs (multi-valued headers produce multiple pairs); omitted for subagent and other non-HTTP entry points.

stdout: same top-level shape. Only prompt/model/cwd/slug are read; empty fields mean no change; readonly is ignored. Empty stdout = no-op.

Applied when non-empty and changed:

  • cwd → conversation's working directory
  • model → re-resolves LLM service; falls back to original if unsupported
  • prompt → first user message
  • slug → sanitized to a slug-safe form; falls back to async slug on collision

chat-message

Fires when the user posts a follow-up chat message to an existing conversation (POST /api/conversation/<id>/chat). For the first message of a brand-new conversation, use new-conversation. Not fired for subagent conversations.

stdin JSON:

json
{
"message": "the user's chat message",
"readonly": {
"conversation_id": "cXXXXXX",
"model": "claude-sonnet-4.5",
"reasoning_level": "high",
"queued": false,
"headers": [["X-Exedev-Email", "user@example.com"]]
}
}

reasoning_level is the conversation's explicit reasoning level (off, minimal, low, medium, high, or xhigh), or the service's configured default when no override is set. It is empty only when the provider chooses the default dynamically and Shelley cannot know it in advance. queued is true when the message will be queued (client requested queue mode or the agent is distilling) rather than interrupting the current turn.

stdout: {"message": "..."}. Empty stdout, empty message, or an identical message means no change.

end-of-turn

Fires when an agent finishes a turn — the same signal that drives end-of-turn notifications (notification channels, push notifications, conversation-hook webhooks). Suppressed for subagent conversations. Stdout is ignored.

stdin JSON:

json
{
"type": "end_of_turn",
"conversation_id": "cXXXXXX",
"timestamp": "2024-01-02T03:04:05Z",
"hostname": "host.exe.xyz",
"model": "claude-sonnet-4.5",
"slug": "my-slug",
"conversation_url": "https://host.exe.xyz/c/my-slug",
"vm_name": "host",
"final_response": "agent's last text or tool-call summary"
}

Typical uses: play a sound, post a desktop notification, ping a local script.

status-bar

Adds a row above the conversation's status bar (above the composer on phones), e.g. with the git branch. Runs in the conversation's directory, for each browser tab showing the conversation: when it opens, after each new message (each tool result too), and when you come back to the tab or its window. Never for a background tab, a draft, an archived conversation, one no tab shows, or if the directory is gone. One run at a time per tab: triggers during a run make one more run after it, and switching conversations cancels the run. The row keeps the last output until the next run finishes. 2s timeout: keep it cheap (git branch --show-current, not git status of a big repo). It often runs alongside the agent's tools; Shelley sets GIT_OPTIONAL_LOCKS=0 so a git status in it can't make the agent's git commands fail on index.lock.

stdin JSON:

json
{
"conversation_id": "cXXXXXX",
"slug": "my-slug",
"cwd": "/home/user/project",
"model": "claude-sonnet-4.5"
}

stdout: one JSON object, all fields optional. Empty stdout shows nothing.

json
{ "body": "main", "bgcolor": "#204a87", "color": "white" }

body is one line of plain text, URLs linkified. bgcolor and color are CSS colors. Failures (non-zero exit, invalid JSON, unknown fields, over 4 KiB, a background process left holding its output) show as an error in the row.

Example: show the branch, red on main:

sh
#!/bin/sh
branch=$(git branch --show-current 2>/dev/null)
[ -n "$branch" ] || exit 0
if [ "$branch" = main ]; then
printf '{"body":"%s","bgcolor":"firebrick","color":"white"}\n' "$branch"
else
printf '{"body":"%s"}\n' "$branch"
fi

slash/<command>

Pluggable slash commands. When a user sends a message that starts with /<command> (where <command> matches [a-zA-Z0-9_][a-zA-Z0-9_-]*), Shelley looks for an executable at ~/.config/shelley/hooks/slash/<command>. If present, it is run synchronously before the message is recorded or sent to the LLM. Its stdout replaces the user-message body. Empty stdout leaves the original message unchanged. Applies to both new conversations and follow-up messages.

No matching executable → the message is treated as a normal user message (no special handling).

stdin JSON:

json
{
"command": "foo",
"args": "the rest of the message after /foo",
"raw_message": "/foo the rest of the message after /foo",
"conversation_id": "cXXXXXX",
"is_new_conversation": false,
"cwd": "/home/user/project",
"model": "claude-sonnet-4.5",
"user_email": "you@example.com"
}

The same context is also exposed via environment variables for shell-friendly hooks: SHELLEY_SLASH_COMMAND, SHELLEY_SLASH_ARGS, SHELLEY_CONVERSATION_ID, SHELLEY_CWD, SHELLEY_MODEL, SHELLEY_USER_EMAIL.

stdout: replacement user-message text. Empty stdout keeps the original message (useful for hooks that only have side effects). Failure (non-zero exit) surfaces as a 400 to the client and the message is not recorded.

Example: a ~/.config/shelley/hooks/slash/files hook that injects the contents of files matched by a glob:

sh
#!/bin/sh
set -e
printf 'Here are the files you asked about:\n\n'
for f in $SHELLEY_SLASH_ARGS; do
printf '=== %s ===\n' "$f"
cat "$f"
printf '\n'
done

Then /files src/main.go src/util.go please summarize becomes a normal user message with file contents inlined.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
710
Repository forks
113
7-day trend score
32

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/boldsoftware/shelley/shelley-hooks/badge)](https://verified-skill.com/skills/boldsoftware/shelley/shelley-hooks)

Something wrong with this skill or its page? Tell us.