Middleware hub for Claude Code plugins. Register once, dispatch to many.
claude-lifecycle-core is a Claude Code plugin that hooks into all lifecycle events and dispatches them to lightweight subscriber plugins. Subscribers are npm packages that export plain JavaScript callbacks — no hooks.json boilerplate, no stdin parsing, no response formatting.
npm install -g claude-lifecycle-coreThe hub registers itself as a Claude Code plugin automatically via postinstall. It starts dispatching events in your next Claude Code session.
const { definePlugin } = require('claude-lifecycle-core');
module.exports = definePlugin({
name: 'my-plugin',
version: '1.0.0',
subscriptions: {
onPostToolBatch: async (context) => {
return { additionalContext: 'Hello from my plugin!' };
},
},
});Publish as an npm package with claude-lifecycle-core as a peerDependency.
| Hook | Callback | Can Block? |
|---|---|---|
| SessionStart | onSessionStart(ctx) |
No |
| UserPromptSubmit | onUserPromptSubmit(ctx) |
Yes |
| PreToolUse | onPreToolUse(ctx) |
Yes |
| PostToolBatch | onPostToolBatch(ctx) |
No |
| SubagentStart | onSubagentStart(ctx) |
No |
| SessionEnd | onSessionEnd(ctx) |
No |
{
hookEventName: 'PostToolBatch',
toolCalls: [...],
prompt: '...',
raw: { ... },
store: {
get(key) { ... },
set(key, value) { ... },
},
}Non-blocking hooks: Return { additionalContext: 'string' } to inject a system reminder, or null for no-op.
Blocking hooks: Return { status: 'approved' } or { status: 'blocked', message: 'reason' }. Null/undefined = approved.
The context.store persists key-value data across hook events within a session. Store is reset on each SessionStart. Namespace your keys: 'my-plugin:counter'.
lifecycle install <path> # Install a subscriber plugin
lifecycle uninstall <name> # Remove a plugin from registry
lifecycle enable <name> # Enable a plugin
lifecycle disable <name> # Disable a plugin
lifecycle list # Show all registered plugins
lifecycle config # Show hub configuration- The hub is the only Claude Code plugin — subscribers are NOT separate plugins
- On each hook event, the hub reads stdin, loads enabled subscribers, and calls matching callbacks
- Each plugin gets a 2s timeout (configurable) with a 4s total timeout across all plugins
- Results are aggregated:
additionalContextstrings concatenated with---separators - For blocking hooks (
UserPromptSubmit,PreToolUse), any plugin returningblockedhalts the action - Errors are caught per-plugin — one plugin crashing never affects others
- Store is cleared on
SessionStartto keep data session-scoped
Hub config at ~/.config/claude-lifecycle/config.json:
{
"defaultTimeout": 2000,
"totalTimeout": 4000,
"logLevel": "error"
}npm uninstall -g claude-lifecycle-coreThe preuninstall script automatically deregisters the plugin from Claude Code.
MIT