Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c7173d7
Add claude-slack-test command and standardize paths to ~/.claude/slack/
BRBCoffeeDebuff Jan 17, 2026
5313976
Add interactive buttons, project channels, and fix session ID bug
BRBCoffeeDebuff Jan 18, 2026
a10b6d8
Add top-level messages for custom channels and deny feedback flow
BRBCoffeeDebuff Jan 18, 2026
4f0a364
Fix custom channel mode to allow null thread_ts
BRBCoffeeDebuff Jan 18, 2026
6c7424e
Add custom channel mode and UI/UX improvements
BRBCoffeeDebuff Jan 18, 2026
f6ebb21
Add rich session summaries, todo updates, and comprehensive docs
BRBCoffeeDebuff Jan 18, 2026
9da7ee3
Add systemd service for 24/7 listener availability
BRBCoffeeDebuff Jan 18, 2026
583fffb
Update README to recommend custom channel mode as baseline
BRBCoffeeDebuff Jan 18, 2026
fb1ed97
Clarify channel setup requirements in README
BRBCoffeeDebuff Jan 18, 2026
88ab05a
Update repository URL and channel name in README
BRBCoffeeDebuff Jan 18, 2026
21220d3
Add foreground/daemon modes to listener script
BRBCoffeeDebuff Jan 18, 2026
93a6eaf
Add auto-channel creation, notifications, and comprehensive test suite
BRBCoffeeDebuff Jan 18, 2026
a485913
Add DM Mode for subscribing to session output
BRBCoffeeDebuff Jan 18, 2026
48b7e58
Fix: defer sys.exit() in slack_listener to allow test imports
BRBCoffeeDebuff Jan 18, 2026
ac2548e
Add explicit permissions to workflow (security best practice)
BRBCoffeeDebuff Jan 18, 2026
94ed449
Merge pull request #2 from BRBCoffeeDebuff/feature/dm-mode
BRBCoffeeDebuff Jan 18, 2026
ada0f7b
Add user interaction modes, DM forwarding, and permission UX improvem…
BRBCoffeeDebuff Jan 18, 2026
17bc67e
docs: clarify DM Mode scope requirements in README
BRBCoffeeDebuff Jan 18, 2026
c08670c
Merge pull request #3 from BRBCoffeeDebuff/feature/dm-mode
BRBCoffeeDebuff Jan 18, 2026
3d9d0de
Add global shortcuts for session management and user modes
BRBCoffeeDebuff Jan 18, 2026
6ff1f15
Fix permission notification safety and add stale message cleanup
BRBCoffeeDebuff Jan 18, 2026
3b34f8c
Add hook updater for safe updates with backup
BRBCoffeeDebuff Jan 18, 2026
673db7b
Fix stale permission message cleanup when user responds via terminal
BRBCoffeeDebuff Jan 18, 2026
3c1ddda
Add stale permission cleanup to on_stop hook
BRBCoffeeDebuff Jan 18, 2026
e8530f6
SAFETY FIX: No emoji reactions when buffer parsing fails
BRBCoffeeDebuff Jan 18, 2026
eca40ce
Fix on_stop summary error: handle non-dict message data
BRBCoffeeDebuff Jan 18, 2026
b1a550e
Merge feature/dm-mode: DM Mode, shortcuts, permission safety fixes
BRBCoffeeDebuff Jan 18, 2026
450f39c
SAFETY FIX: Only show exact CLI options in permission prompts
BRBCoffeeDebuff Jan 18, 2026
5455a23
Fix: Require channel ID resolution, don't fall back to channel names
BRBCoffeeDebuff Jan 18, 2026
e1d38ac
Add install-hooks script for global Claude Code hooks setup
BRBCoffeeDebuff Jan 18, 2026
966448a
Add PermissionRequest hook for Slack-based permission handling
BRBCoffeeDebuff Jan 18, 2026
f47dbd1
Fix: Add missing shortcut and view methods to _DummyApp
BRBCoffeeDebuff Jan 19, 2026
ff83b59
Add AskUserQuestion Slack hook and CLI parsing improvements
BRBCoffeeDebuff Jan 19, 2026
169dc92
Fix: Add client attribute to _DummyApp for test compatibility
BRBCoffeeDebuff Jan 20, 2026
67437f3
Update README with comprehensive feature documentation
BRBCoffeeDebuff Jan 20, 2026
cdc28fa
Improve onboarding: filter join/leave messages, consolidate README setup
BRBCoffeeDebuff Jan 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
726 changes: 626 additions & 100 deletions .claude/hooks/on_notification.py

Large diffs are not rendered by default.

564 changes: 564 additions & 0 deletions .claude/hooks/on_permission_request.py

Large diffs are not rendered by default.

578 changes: 578 additions & 0 deletions .claude/hooks/on_posttooluse.py

Large diffs are not rendered by default.

118 changes: 98 additions & 20 deletions .claude/hooks/on_pretooluse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"""
Claude Code PreToolUse Hook - Capture AskUserQuestion calls to Slack

Version: 1.1.0

Changelog:
- v1.1.0 (2025/11/18): Fixed early termination bug - continue posting remaining chunks on failure
- v1.0.0 (2025/11/18): Initial versioned release

Triggered before Claude executes any tool, allowing us to capture AskUserQuestion
calls with their full question text and options, which are not available in the
Notification hook.
Expand Down Expand Up @@ -31,7 +37,7 @@

Environment Variables:
SLACK_BOT_TOKEN - Bot User OAuth Token (required)
REGISTRY_DATA_DIR - Registry database directory (default: /tmp/claude_sessions)
REGISTRY_DB_PATH - Registry database path (default: ~/.claude/slack/registry.db)

Architecture:
1. Read hook data from stdin
Expand All @@ -42,7 +48,7 @@
6. Exit 0 (success or failure)

Debug Logging:
- All execution logged to /tmp/pretooluse_hook_debug.log
- All execution logged to ~/.claude/slack/logs/pretooluse_hook_debug.log
"""

import sys
Expand All @@ -51,8 +57,15 @@
from pathlib import Path
from datetime import datetime

# Hook version for auto-update detection
HOOK_VERSION = "1.1.0"

# Log directory - use ~/.claude/slack/logs as default
LOG_DIR = os.environ.get("SLACK_LOG_DIR", os.path.expanduser("~/.claude/slack/logs"))
os.makedirs(LOG_DIR, exist_ok=True)

# Debug log file path
DEBUG_LOG = "/tmp/pretooluse_hook_debug.log"
DEBUG_LOG = os.path.join(LOG_DIR, "pretooluse_hook_debug.log")

# Find claude-slack directory dynamically
def find_claude_slack_dir():
Expand Down Expand Up @@ -213,8 +226,49 @@ def format_askuserquestion_for_slack(tool_input: dict) -> str:
return "\n".join(lines)


def split_message(text: str, max_length: int = 39000) -> list:
"""
Split long message into chunks that fit in Slack's 40K char limit.

Args:
text: Message text to split
max_length: Max chars per chunk (default: 39000, leaves room for part indicators)

Returns:
List of text chunks
"""
if len(text) <= max_length:
return [text]

chunks = []
while text:
# Find a good breaking point (newline near max_length)
if len(text) <= max_length:
chunks.append(text)
break

# Look for newline near the max length
break_point = text.rfind('\n', max_length - 500, max_length)
if break_point == -1:
# No newline found, just split at max_length
break_point = max_length

chunks.append(text[:break_point])
text = text[break_point:].lstrip('\n')

return chunks


def post_to_slack(channel: str, thread_ts: str, text: str, bot_token: str):
"""Post message to Slack thread."""
"""
Post message to Slack thread, handling long messages.

Args:
channel: Slack channel ID
thread_ts: Thread timestamp
text: Message text
bot_token: Slack bot token
"""
try:
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
Expand All @@ -224,22 +278,47 @@ def post_to_slack(channel: str, thread_ts: str, text: str, bot_token: str):

client = WebClient(token=bot_token)

try:
client.chat_postMessage(
channel=channel,
thread_ts=thread_ts,
text=text
)
log_info("Posted to Slack")
return True

except SlackApiError as e:
log_error(f"Slack API error: {e.response['error']}")
return False
except Exception as e:
log_error(f"Error posting to Slack: {e}")
# Split message if too long
chunks = split_message(text)

if len(chunks) > 5:
# Too many chunks, truncate
log_info(f"Message too long ({len(chunks)} chunks), truncating to 5 chunks")
chunks = chunks[:5]

# Post each chunk
failed_chunks = []
for i, chunk in enumerate(chunks):
try:
# Add part indicator for multi-part messages
if len(chunks) > 1:
message_text = f"{chunk}\n\n_(Part {i+1}/{len(chunks)})_"
else:
message_text = chunk

client.chat_postMessage(
channel=channel,
thread_ts=thread_ts,
text=message_text
)

log_info(f"Posted to Slack (part {i+1}/{len(chunks)})")

except SlackApiError as e:
log_error(f"Slack API error on chunk {i+1}: {e.response['error']}")
failed_chunks.append(i+1)
continue
except Exception as e:
log_error(f"Error posting chunk {i+1} to Slack: {e}")
failed_chunks.append(i+1)
continue

if failed_chunks:
log_error(f"Failed to post chunks: {failed_chunks}")
return False

return True


def main():
"""Main hook entry point"""
Expand Down Expand Up @@ -286,8 +365,7 @@ def main():
log_error(f"registry_db module not found: {e}")
sys.exit(0)

registry_dir = os.environ.get("REGISTRY_DATA_DIR", "/tmp/claude_sessions")
db_path = os.path.join(registry_dir, "registry.db")
db_path = os.environ.get("REGISTRY_DB_PATH", os.path.expanduser("~/.claude/slack/registry.db"))
debug_log(f"Registry database path: {db_path}", "REGISTRY")

if not os.path.exists(db_path):
Expand Down
Loading