[FEATURE] Delayed and Debounced Hook Execution with Cancellation Events

Resolved 💬 2 comments Opened Feb 3, 2026 by sferich888 Closed Mar 5, 2026

[FEATURE] Delayed and Debounced Hook Execution with Cancellation Events

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

When Claude Code requires user attention (permission prompts, idle prompts, etc.), I want different notification behaviors based on my availability:

  • At my desk: Instant desktop notification is sufficient
  • Stepped away briefly: SMS/mobile notification after 2 minutes of no response
  • Away for extended period: Slack message after 10 minutes

Currently, all hooks fire immediately when their event triggers. There's no way to implement "notify me on my phone only if I haven't responded within 2 minutes" without complex external state management involving:

  • Writing marker files with timestamps
  • Running separate cron jobs or daemons
  • Coordinating state across multiple hook invocations

This pattern is common in on-call/alerting systems (PagerDuty, OpsGenie) where notifications escalate through channels based on response time.

Proposed Solution

Add two new optional fields to hook configuration:

delay (number, seconds)

Wait N seconds before executing the hook. If a cancelOn event fires before the delay expires, the hook is cancelled.

cancelOn (string array)

List of event names that cancel a pending delayed hook before it fires.

Example Configuration

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude needs attention'",
            "statusMessage": "Desktop notification"
          },
          {
            "type": "command",
            "command": "/scripts/send-sms.sh",
            "delay": 120,
            "cancelOn": ["PermissionRequest", "UserPromptSubmit"],
            "statusMessage": "SMS notification (2 min delay)"
          },
          {
            "type": "command",
            "command": "/scripts/send-slack.sh '#alerts'",
            "delay": 600,
            "cancelOn": ["PermissionRequest", "UserPromptSubmit"],
            "statusMessage": "Slack notification (10 min delay)"
          }
        ]
      }
    ]
  }
}

Behavior: When a permission prompt appears:

  1. Desktop notification fires immediately
  2. SMS is scheduled for 2 minutes later
  3. Slack is scheduled for 10 minutes later
  4. If user responds (triggering PermissionRequest), both delayed hooks are cancelled
  5. If user doesn't respond within 2 minutes, SMS fires; if still no response by 10 minutes, Slack fires

Optional: debounce field

For coalescing rapid-fire events (alternative to delay):

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "/scripts/notify-batch-complete.sh",
          "debounce": 30
        }
      ]
    }
  ]
}

This fires once, 30 seconds after the last file edit, rather than after every single edit.

Alternative Solutions

Current workaround: External state management

#!/bin/bash
# Hook 1: Schedule delayed notification
MARKER="/tmp/claude-pending-$(jq -r '.session_id')"
echo "$(date +%s)" > "$MARKER"
(sleep 120 && [ -f "$MARKER" ] && /scripts/send-sms.sh) &

# Hook 2: Cancel on action
rm -f "/tmp/claude-pending-*"

Problems:

  • Requires coordination between multiple hooks
  • Background processes can outlive Claude Code session
  • State files can get out of sync
  • No visibility into pending notifications
  • Error-prone race conditions

Alternative: External webhook service

Push to a service like Zapier that handles delays.

Problems:

  • Requires external infrastructure
  • Adds latency and complexity
  • Harder to configure per-project

Priority

Medium - Would be very helpful

This is particularly valuable for:

  • Remote/headless Claude Code sessions (SSH, CI/CD)
  • Long-running agentic tasks
  • Teams with different notification urgency tiers

Feature Category

Configuration and settings

(Hooks subsystem)

Use Case Example

Scenario: Working remotely with Claude Code over SSH

  1. I start a long-running Claude task on a remote server via SSH
  2. Claude needs permission to run a deployment script
  3. Immediate: Desktop notification fires (but I've stepped away for coffee)
  4. After 2 minutes: No response, so SMS fires to my phone
  5. I see the SMS, SSH back in, and approve the permission
  6. Cancelled: The 10-minute Slack escalation never fires because I responded

Without this feature: I either miss the notification entirely (if only desktop), or get spammed on all channels simultaneously (annoying and noisy).

Additional Context

Similar patterns in other tools

  • PagerDuty/OpsGenie: Escalation policies with time-based routing
  • GitHub Actions: timeout-minutes with conditional steps
  • Kubernetes: Probe delays with initialDelaySeconds

Implementation considerations

  • Pending delayed hooks could be tracked per-session
  • /hooks menu could show pending hooks with countdown
  • On session end, pending hooks could be cancelled or optionally executed
  • Verbose mode (Ctrl+O) should log scheduling, cancellation, and execution

Proposed UI in /hooks menu

Pending Delayed Hooks:
  - SMS notification (fires in 1m 42s) [Cancel]
  - Slack notification (fires in 8m 42s) [Cancel]

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗