From 2e08f6a4b8cd1ce68913eaffb28cc4bac21102c9 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 4 Mar 2026 00:34:21 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20add=20SubagentStop=20=E2=86=92=20Discor?= =?UTF-8?q?d=20notification=20via=20n8n=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add SubagentStop HTTP hook to .claude/settings.json pointing at n8n - Add importable n8n workflow (claude-agent-done.json): webhook trigger → extract agent name → POST to Discord - Add setup guide (claude-agent-notifications.md) with payload reference, test curl command, and future extension notes - Update n8n CONTEXT.md and workflows/README.md with new workflow entry Discord webhook URL is stored as n8n variable DISCORD_CLAUDE_ALERTS_WEBHOOK to keep it out of local config files. Co-Authored-By: Claude Sonnet 4.6 --- .claude/settings.json | 11 +- productivity/n8n/CONTEXT.md | 26 +++++ productivity/n8n/workflows/README.md | 21 ++++ .../n8n/workflows/claude-agent-done.json | 84 ++++++++++++++ .../workflows/claude-agent-notifications.md | 105 ++++++++++++++++++ 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 productivity/n8n/workflows/claude-agent-done.json create mode 100644 productivity/n8n/workflows/claude-agent-notifications.md diff --git a/.claude/settings.json b/.claude/settings.json index 6265ea6..d0fc0f2 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -6,5 +6,14 @@ "allowed_working_directories": [ "/mnt/NV2/Development/claude-home", "/mnt/media" - ] + ], + "hooks": { + "SubagentStop": [ + { + "type": "http", + "url": "http://localhost:5678/webhook/claude-agent-done", + "async": true + } + ] + } } diff --git a/productivity/n8n/CONTEXT.md b/productivity/n8n/CONTEXT.md index f4172db..aeaf1a8 100644 --- a/productivity/n8n/CONTEXT.md +++ b/productivity/n8n/CONTEXT.md @@ -275,6 +275,32 @@ ssh root@10.10.0.210 "docker stats --no-stream n8n n8n-postgres" ## Active Workflows +### Claude Agent Done → Discord + +**Purpose:** Discord notification when a Claude Code subagent finishes (for long-running pipelines) + +**Documentation:** `workflows/claude-agent-notifications.md` + +**Workflow Features:** +- Receives `SubagentStop` HTTP hook from Claude Code +- Extracts agent name from payload (`subagent_name` or `session_id` prefix) +- Posts `🏁 Claude agent **{name}** has finished.` to Discord +- Discord webhook URL stored as n8n variable (not in local config) + +**Webhook URL:** +- Production: `http://10.10.0.210:5678/webhook/claude-agent-done` +- Test: `http://10.10.0.210:5678/webhook-test/claude-agent-done` + +**Related Files:** +- `workflows/claude-agent-notifications.md` - Setup guide +- `workflows/claude-agent-done.json` - Importable n8n workflow +- `.claude/settings.json` (repo root) - Claude Code hook config + +**Custom Variables:** +- `DISCORD_CLAUDE_ALERTS_WEBHOOK` - Discord webhook URL for the target channel + +--- + ### Ko-fi → Paper Dynasty Integration **Purpose:** Automated pack distribution when users purchase on Ko-fi diff --git a/productivity/n8n/workflows/README.md b/productivity/n8n/workflows/README.md index 438f2c4..1c78b91 100644 --- a/productivity/n8n/workflows/README.md +++ b/productivity/n8n/workflows/README.md @@ -4,6 +4,27 @@ Collection of production n8n workflows and integration guides. ## Available Workflows +### Claude Agent Done → Discord + +**Status:** Production Ready +**Purpose:** Discord notification when a Claude Code subagent finishes + +**Quick Links:** +- 📖 **[claude-agent-notifications.md](claude-agent-notifications.md)** - Setup guide and payload reference +- 📦 **[claude-agent-done.json](claude-agent-done.json)** - Importable n8n workflow + +**Features:** +- Triggered by Claude Code's `SubagentStop` HTTP hook +- Extracts agent name from the event payload +- Posts a completion message to Discord +- Discord webhook URL kept in n8n variables (not local config) + +**Webhook URL:** +- Test: `http://10.10.0.210:5678/webhook-test/claude-agent-done` +- Production: `http://10.10.0.210:5678/webhook/claude-agent-done` + +--- + ### Ko-fi → Paper Dynasty Integration **Status:** Production Ready diff --git a/productivity/n8n/workflows/claude-agent-done.json b/productivity/n8n/workflows/claude-agent-done.json new file mode 100644 index 0000000..e0ebd99 --- /dev/null +++ b/productivity/n8n/workflows/claude-agent-done.json @@ -0,0 +1,84 @@ +{ + "name": "Claude Agent Done → Discord", + "nodes": [ + { + "parameters": { + "path": "claude-agent-done", + "responseMode": "immediatelyReturnWith200", + "options": {} + }, + "id": "webhook-trigger", + "name": "Claude Agent Done", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [0, 0], + "webhookId": "claude-agent-done" + }, + { + "parameters": { + "jsCode": "const data = $input.first().json;\nconst sessionId = data.session_id || '';\nconst agentName = data.subagent_name || (sessionId ? sessionId.substring(0, 8) : 'a subagent');\nreturn [{\n json: {\n content: `🏁 Claude agent **${agentName}** has finished.`\n }\n}];" + }, + "id": "build-message", + "name": "Build Discord Message", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [220, 0] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $vars.DISCORD_CLAUDE_ALERTS_WEBHOOK }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ JSON.stringify({ content: $json.content }) }}", + "options": {} + }, + "id": "discord-post", + "name": "Post to Discord", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [440, 0] + } + ], + "connections": { + "Claude Agent Done": { + "main": [ + [ + { + "node": "Build Discord Message", + "type": "main", + "index": 0 + } + ] + ] + }, + "Build Discord Message": { + "main": [ + [ + { + "node": "Post to Discord", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "settings": { + "executionOrder": "v1" + }, + "staticData": null, + "tags": [ + { + "name": "claude-code" + }, + { + "name": "notifications" + }, + { + "name": "discord" + } + ], + "triggerCount": 1, + "pinData": {} +} diff --git a/productivity/n8n/workflows/claude-agent-notifications.md b/productivity/n8n/workflows/claude-agent-notifications.md new file mode 100644 index 0000000..b9775df --- /dev/null +++ b/productivity/n8n/workflows/claude-agent-notifications.md @@ -0,0 +1,105 @@ +# Claude Agent Done → Discord Notifications + +Notifies a Discord channel via webhook when a Claude Code subagent finishes. Useful for long-running pipelines (10+ minutes) where you want a heads-up when work completes. + +## Architecture + +``` +Claude Code (SubagentStop hook) + │ + │ POST raw event JSON + ▼ +n8n Webhook: /webhook/claude-agent-done + │ + │ Extract agent name, build message + ▼ +Discord Webhook (stored as n8n variable) + │ + ▼ +"🏁 Claude agent **{name}** has finished." +``` + +### Why n8n as the middle layer? + +Claude Code HTTP hooks send raw event JSON — you can't customize the body. Discord requires `{"content": "..."}`. n8n does the transformation and keeps the Discord webhook URL out of local config files. + +## Setup + +### 1. n8n workflow + +Import `claude-agent-done.json` into n8n: + +1. n8n → Workflows → Import from File → select `claude-agent-done.json` +2. Set the workflow to **Active** +3. Note the production webhook URL: `http://10.10.0.210:5678/webhook/claude-agent-done` + +### 2. Discord webhook + +1. Open the target Discord channel → Edit Channel → Integrations → Webhooks → New Webhook +2. Copy the webhook URL + +### 3. n8n variable + +Store the Discord URL so it stays out of config files: + +1. n8n → Settings → Variables → Add Variable +2. **Key:** `DISCORD_CLAUDE_ALERTS_WEBHOOK` +3. **Value:** your Discord webhook URL + +### 4. Claude Code hook config + +The hook is configured in `.claude/settings.json` at the repo root: + +```json +{ + "hooks": { + "SubagentStop": [ + { + "type": "http", + "url": "http://localhost:5678/webhook/claude-agent-done", + "async": true + } + ] + } +} +``` + +> **Note:** Update `localhost:5678` to match your n8n host if running remotely (e.g. `http://10.10.0.210:5678`). + +## Testing + +Send a test payload to verify the pipeline end-to-end: + +```bash +curl -X POST http://10.10.0.210:5678/webhook-test/claude-agent-done \ + -H "Content-Type: application/json" \ + -d '{"hook_event_name": "SubagentStop", "session_id": "abc12345", "subagent_name": "test-agent"}' +``` + +Switch to `/webhook/` (not `/webhook-test/`) once confirmed working. + +## Payload reference + +Claude Code sends a JSON payload on `SubagentStop`. Known fields: + +| Field | Description | +|-------|-------------| +| `hook_event_name` | Always `"SubagentStop"` | +| `session_id` | Agent session identifier | +| `subagent_name` | Name of the subagent (if set) | + +The n8n workflow uses `subagent_name` if present, falling back to the first 8 chars of `session_id`. + +## Future extensions + +- Route different agent names to different Discord channels +- Add rich embeds with duration and success/failure status +- Filter by agent name (only notify for specific long-running pipelines) +- Route to local TTS/voice server for audio alerts + +## Change Log + +### 2026-03-04 - Initial setup +- Created n8n webhook workflow (`claude-agent-done.json`) +- Added `SubagentStop` HTTP hook to `.claude/settings.json` +- Discord webhook URL stored as n8n variable `DISCORD_CLAUDE_ALERTS_WEBHOOK`