HookStackGitHub
Back to catalogue
NotificationNotificationNotificationWhen Claude wants to notify the user· non-blocking

Slack notification on agent event

Relays agent notifications (permission request, awaiting input) to a Slack channel via webhook, useful for long or unattended sessions.

Use cases

  • Long sessions
  • Remote supervision
  • Team collaboration

Providers & tags

Claude Code
#notification#slack#webhook#monitoring

settings.json fragment

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/notify-slack.mjs",
            "type": "command"
          }
        ]
      }
    ]
  }
}

Script · .claude/hooks/notify-slack.mjs

#!/usr/bin/env node
// Envoie une notification Slack quand Claude veut notifier l'utilisateur (Notification)
import { readFileSync } from 'fs';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';

function defaultExec(cmd) {
  execSync(cmd, { stdio: 'ignore', timeout: 10_000 });
}

export function run(
  input,
  {
    exec = defaultExec,
    webhook = process.env.SLACK_WEBHOOK_URL ?? '',
    projectDir = process.env.CLAUDE_PROJECT_DIR,
  } = {},
) {
  if (!webhook) return null;

  const message = input.message ?? input.notification ?? '';
  if (!message) return null;

  const project = projectDir?.split('/').pop() ?? 'Claude';
  const payload = JSON.stringify({ text: `*[${project}]* ${message}` });

  try {
    exec(
      `curl -s -X POST -H 'Content-type: application/json' --data '${payload.replace(/'/g, "'\\''")}' '${webhook}'`,
    );
  } catch {
    // Échec réseau — non bloquant
  }
  return payload;
}

/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  const input = JSON.parse(readFileSync(0, 'utf8'));
  run(input);
}