Add interactive Slack integration with permissions, AskUserQuestion, and session tracking#14
Open
BRBCoffeeDebuff wants to merge 36 commits intodbenn8:mainfrom
Open
Add interactive Slack integration with permissions, AskUserQuestion, and session tracking#14BRBCoffeeDebuff wants to merge 36 commits intodbenn8:mainfrom
BRBCoffeeDebuff wants to merge 36 commits intodbenn8:mainfrom
Conversation
New Features: - Add claude-slack-test command for testing Slack connection - Add --local flag to test directory-level hook configuration - Add optional --description flag to claude-slack for meaningful thread descriptions Path Standardization: - Consolidate all paths to ~/.claude/slack/ directory structure: - ~/.claude/slack/logs/ for all log files - ~/.claude/slack/sockets/ for Unix sockets - ~/.claude/slack/registry.db for session database - Remove hardcoded /tmp/ paths that were inconsistent with config.py defaults - Update .env.example to show correct default paths (commented out) - Fix .env to not override defaults with /tmp/ paths Files Changed: - bin/claude-slack-test (new): Test Slack connection and directory setup - bin/claude-slack: Add --description and --help flags - bin/claude-slack-listener: Fix monitor log path - bin/claude-slack-monitor: Add env loading, fix log paths - bin/claude-slack-debug: Fix log/socket directory paths - core/slack_listener.py: Use get_socket_dir() for legacy socket - core/claude_wrapper_hybrid.py: Use LOG_DIR for buffer files - core/session_registry.py: Add description support in Slack threads - hooks/on_*.py: Add LOG_DIR, fix debug log paths - README.md, SECURITY.md: Update path references Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Key changes: 1. Fix session ID detection bug - hooks now use project_dir fallback when session_id lookup fails, resolving the mismatch between wrapper session IDs (8-char) and Claude's internal project session IDs (36-char UUID) 2. Replace emoji reactions with interactive Block Kit buttons for permission prompts - buttons show colored options (green approve, red deny) and update in-place when clicked to show the selection 3. Add project-specific channel support - use `--channel #my-channel` flag to post session threads to a specific channel instead of the default 4. Database schema migration - added project_dir column and expanded session_id column to support UUIDs, with automatic migration for existing databases Technical details: - registry_db.py: Expanded session_id column from String(8) to String(50), added project_dir column with index, added get_by_project_dir() method - on_notification.py: Added post_permission_card() for Block Kit cards, enhanced enhance_notification_message() to return permission options - slack_listener.py: Added handle_permission_button() for button clicks - session_registry.py: Support custom_channel in _create_slack_thread() - claude_wrapper_hybrid.py: Added --channel flag, pass project_dir in registration calls Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Custom channels: - Custom channel mode now uses top-level messages instead of threading - Helps keep project-specific channels organized without long threads Permissions channel: - New --permissions-channel flag for dedicated permission prompts channel - Permissions can go to a separate channel from main session messages Message cleanup: - Permission messages are deleted after approval to keep channels clean - Falls back to updating message if delete permissions not available Deny feedback: - Clicking deny button prompts user to type feedback for Claude - User's reply in thread will be routed as the deny response - Improves communication flow when rejecting proposed actions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Wrapper now waits for channel (not thread_ts) since custom channels use top-level messages without a parent thread - REGISTER_EXISTING command now only requires session_id and channel (thread_ts can be None for custom channel mode) - Added file-based logging to session registry for debugging - Updated log messages to clarify top-level vs threaded mode Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UI/UX Enhancements: - Custom channel mode (--channel/-c): Post messages as top-level instead of threads - Interactive buttons for permission prompts (Block Kit cards) - Proper handling of 2-option vs 3-option permission prompts - Channel-based routing for custom channel sessions Bug Fixes: - Fix stale sessions: Sessions now marked inactive on cleanup - Fix socket lookup: Skip sessions with non-existent sockets - Fix deny button: Detect via button style (danger) not just value Technical Changes: - hooks/on_notification.py v2.2.0: Custom channel support, interactive buttons - core/slack_listener.py: get_socket_for_channel(), channel ID resolution - core/session_registry.py: deactivate_session() method - core/claude_wrapper_hybrid.py: Cleanup marks sessions inactive - README.md: Documentation for custom channel mode Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Features: - Real-time todo updates in Slack via PostToolUse hook - Rich session summaries with progress bars, modified files, and stats - Reply threading (responses thread to triggering message) - Safe 2-option fallback for permission prompts when buffer parsing fails Fixes: - Fixed 2-option vs 3-option permission detection for dangerous commands - Fixed buffer file lookup across session ID mismatches (session resume) - Fixed duplicate message processing for @mentions - Store buffer_file_path in registry for reliable lookup Database: - Added todo_message_ts column for tracking todo update messages - Added buffer_file_path column for terminal output buffer lookup Documentation: - Comprehensive README rewrite with architecture diagram - Added requirements.txt with Python dependencies - Installation instructions with virtual environment setup - Troubleshooting guide and project structure Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add claude-slack-service command to manage systemd user service - Add systemd service unit with security hardening: - NoNewPrivileges, ProtectSystem, ProtectHome - Auto-restart on crash/disconnect - Lingering enabled for post-logout operation - Update README with systemd installation instructions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Reorder features to emphasize dedicated project channels - Make `-c` flag the primary recommended approach in Quick Start - Reorder Basic Usage examples to show channel mode first - Update Interacting via Slack to list Custom Channel Mode first - Add tip explaining benefits of channel-based setup - Clarify one-session-per-channel limitation with workaround Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add explicit steps to create channel and invite bot before starting - Use clearer channel naming convention (claude-myproject) - Note that channel must exist and have bot invited Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Author
|
This also includes major updates to the UI/UX with ability to maintain standard threaded mode or custom channels per project. Also adds deamon support for 24/7 availability where needed. |
- Default to foreground mode (attached to terminal, Ctrl+C to stop) - Add --daemon/-d flag for background operation using setsid - Resolve symlinks so script works when symlinked to PATH - Update README with new usage instructions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This PR adds automatic Slack channel management capabilities and a full test
suite for the claude-slack integration.
## Auto-Channel Creation
- Added `_ensure_channel_exists()` method to SessionRegistry that:
- Searches for existing channels via pagination
- Creates new channels if not found (requires `channels:manage` scope)
- Auto-joins channels the bot isn't a member of
- Posts a notification to the default channel with a join link
- Fixed infinite loop bug in channel pagination:
- Added `max_pages=50` safety limit
- Changed `isinstance(response, dict)` to `hasattr(response, 'get')`
to properly handle SlackResponse objects (dict-like but not dict)
- Added explicit cursor validation
## New Channel Notification
When a new channel is created via `claude-slack -c channel-name`:
- Bot creates the channel
- Posts notification to default channel: "📢 New Claude session channel
created - Click to join: #channel-name"
- User clicks link to join and interact
## Slack App Manifest
- Added `app-manifest.yaml` with documented permission tiers
- Minimum scopes for basic operation
- Recommended scopes for full functionality (auto-create, auto-join)
## Comprehensive Test Suite (220+ tests)
### Unit Tests
- `test_config.py` - Configuration and path handling
- `test_registry_db.py` - SQLite database operations
- `test_session_registry.py` - Session management and socket protocol
- `test_transcript_parser.py` - JSONL transcript parsing
- `test_slack_listener.py` - Message routing and reactions
- `tests/unit/hooks/` - All hook tests (notification, stop, pre/post tooluse)
### Integration Tests
- `test_registry_listener.py` - Registry <-> Listener communication
- `test_wrapper_registry.py` - Wrapper <-> Registry communication
- `test_hooks_registry.py` - Hooks <-> Registry communication
### E2E Tests
- `test_session_lifecycle.py` - Full session workflow
- `test_permission_flow.py` - Permission prompts and responses
- `test_multi_session.py` - Concurrent session handling
- `test_failure_recovery.py` - Error recovery scenarios
- `test_daemon_lifecycle.py` - Daemon start/stop and auto-channel creation
- `test_live_slack.py` - Live Slack API integration tests
## README Updates
- Added "Default Channel Setup" section explaining `SLACK_CHANNEL` requirement
- Added "Custom Channel Workflow" section explaining notification behavior
- Added troubleshooting for "New channel created but not visible"
- Updated permission tiers documentation
- Clarified channel requirements throughout
## DevContainer & CI
- Added `.devcontainer/` for GitHub Codespaces / VS Code
- Added `.github/workflows/test.yml` for CI testing
- Added `requirements-dev.txt` for test dependencies
- Added `pytest.ini` for test configuration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Features: - /sessions - List active sessions - /attach <session_id> [N] - Subscribe to session output, optionally fetch last N messages - /detach - Unsubscribe from current session DM subscribers receive FULL Claude output while channel continues to receive only curated messages (permission prompts, todos, summaries). Implementation: - DMSubscription model in registry_db.py with CRUD methods - get_last_n_messages() in transcript_parser.py for history retrieval - dm_mode.py with command parsing, session listing, attach/detach logic - Hook integration to forward output to DM subscribers - Session end cleanup notifies subscribers and removes subscriptions Tests: 51 new tests (294 total, all passing) Also updates CI to: - Skip live_slack tests by default (using pytest.ini) - Add separate job for live tests using GitHub environment secrets Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The module was calling sys.exit(1) at import time when SLACK_BOT_TOKEN was not set, which caused pytest to fail during test collection. Now uses a _DummyApp class when token is missing, allowing decorators to work during imports. The actual error check is deferred to main(). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sets minimal 'contents: read' permission following the principle of least privilege, addressing CodeQL actions/missing-workflow-permissions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add DM Mode for subscribing to session output
…ents Bug fixes: - Fix multi-line messages not submitting (replace \n with \r for PTY input) - Enable DM message forwarding to attached sessions (non-commands now work) - Fix permission notifications showing JSON instead of formatted text - Fix reactions requiring channel ID instead of channel name Features: - Add /mode command for interaction modes (plan, research, execute) - Mode prompts appended to DM messages based on user preference - Add UserPreference model and database table for storing user modes - Add number emoji reactions (1️⃣-5️⃣) for non-button permission prompts - Only show buttons for Yes/No or Yes/Yes,allow.../No patterns Test utilities: - Add bin/test-permission-formats for testing notification formats - Add bin/test-4-option-notification for testing multi-option prompts Documentation: - Add DM Mode section to README with command reference - Document interaction modes (research, plan, execute) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Split im:* into individual scopes (im:history, im:read, im:write) - Mark DM scopes as required for DM Mode feature - Add note about message.im event subscription requirement - Add troubleshooting hint about reinstalling app after adding scopes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add DM Mode for mobile session interaction
- Add shortcut handlers: get_sessions, attach_to_session, research_mode, plan_mode, execute_mode - Get Sessions: Opens modal with list of active Claude sessions - Attach to Session: Opens modal with session picker and history options - Mode shortcuts: Set user interaction mode via ⚡ menu from anywhere - Update app-manifest.yaml with shortcut definitions - Update README with Global Shortcuts documentation - Update listener startup message to mention shortcuts Shortcuts provide a better UX than DM commands - accessible from the ⚡ lightning menu anywhere in Slack without having to DM the bot. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Safety improvements: - Only show buttons when exact options parsed from terminal buffer - Fallback to text-only + emoji reactions when parsing fails - Always include full text with numbered options in permission cards - Add emoji reactions to both button and non-button permission messages Stale permission cleanup: - Add permission_message_ts field to track pending permission messages - Clean up stale messages in on_posttooluse when user responds via terminal - Clear permission_message_ts after button click to prevent double-delete - Add get_by_channel helper to registry_db Tests: - Add TestPermissionNotificationBehavior with safety tests - Add TestStalePermissionCleanup for cleanup logic - Document button mismatch danger in test comments Fixes critical bug where clicking "No" button could send "2" which maps to "Yes, allow all" when CLI has 3 options but Slack shows 2 buttons. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add claude-slack-update-hooks script that safely updates hooks: - Version checking: only updates if source version is newer - Backup: customized hooks backed up to .claude/hooks/backup/ - Interactive: prompts before overwriting customizations - Options: --check (dry run), --force (update all with backup) - Bump on_notification.py version to 2.3.0 - Sync hooks to hooks/ directory (source of truth for git) - Add on_posttooluse.py to tracked hooks - Update README with hook update instructions Usage: claude-slack-update-hooks # Interactive update claude-slack-update-hooks --check # Check without applying claude-slack-update-hooks --force # Force update all Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a user responds to a permission prompt via the terminal (not Slack), the Slack message with buttons remained visible. This fix adds cleanup logic to on_notification.py that deletes stale permission messages before posting new notifications. Changes: - Add cleanup_stale_permission_message() function to on_notification.py - Call cleanup before posting any new notification (v2.3.0) - Add tests for cleanup-before-posting behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When Claude responds (on_stop fires), clean up any stale permission message that was left over from a terminal response. This ensures permission cards are removed when the user responds via CLI. Changes: - Add cleanup_stale_permission_message() to on_stop.py (v1.4.0) - Call cleanup after getting bot token, before posting response - Sync both hooks/ and .claude/hooks/ directories Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When buffer parsing fails to detect exact permission options, we were falling back to hardcoded options with 3 reactions. This is dangerous because the CLI might only show 2 options - clicking reaction 3 would send "3" to CLI which doesn't have that option. Fix: - Set permission_options = None when buffer parsing fails - No emoji reactions added for fallback cases - User sees text saying "Reply with a number from the terminal prompt" - Buttons/reactions only shown when we have EXACT match from buffer This prevents users from accidentally approving/denying wrong actions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The transcript parser was failing with "'str' object has no attribute 'get'" when message.content was not a dict. Added defensive type checking: - Add _get_message_content() helper method for safe content extraction - Handle cases where 'message' field might be string instead of dict - Add isinstance checks for content items before accessing .get() - Add debug logging and traceback to on_stop for future debugging Also added debug logging for permission_message_ts cleanup tracking. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When buffer parsing fails: - Don't show interpreted/hardcoded option text - Show "Reply with a number from the terminal prompt" - Default to 3 emoji reactions (standard permission prompt count) This ensures users only see 100% exact CLI text or are told to check the terminal - no misleading interpreted options. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- session_registry.py: Raise error if channel can't be resolved to ID (previously fell back to using channel name which doesn't work) - on_notification.py: Add validation to detect invalid channel format early and provide helpful error message Slack API requires channel IDs (starting with C, G, or D), not names. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Creates bin/install-hooks that: - Merges claude-slack hooks into ~/.claude/settings.json - Applies hooks globally to all Claude Code sessions - Preserves existing settings (permissions, mcpServers, etc.) - Creates backups before modifying settings - Is idempotent (safe to run multiple times) - Supports --remove to uninstall hooks - Supports --status to check installation Usage: ./bin/install-hooks # Install ./bin/install-hooks --remove # Uninstall ./bin/install-hooks --status # Check status Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New on_permission_request.py hook intercepts Claude Code permission prompts - Posts Allow/Deny/Allow Always buttons to Slack instead of terminal - Waits for user response via file polling, returns decision to Claude Code - Supports both 2-option (Yes/No) and 3-option (Yes/Yes always/No) prompts Supporting changes: - Add UPDATE command to session_registry.py for buffer path storage - Add update_session() method to RegistryClient in wrapper - Add resume session detection to set buffer file path immediately - Don't delete buffer file on wrapper cleanup (needed for resumed sessions) - Add button handlers in slack_listener.py for permission_allow/deny/allow_always - Disable notification hook for permission_prompt (PermissionRequest handles it) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The _DummyApp fallback class (used during tests when SLACK_BOT_TOKEN is not set) was missing these decorator methods, causing test imports to fail with AttributeError. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AskUserQuestion Implementation: - Emoji-based option selection (1️⃣ 2️⃣ 3️⃣ 4️⃣) in Slack messages - Thread reply support for "Other" custom text responses - Multi-question support (2-4 questions per prompt) - Multi-select support with reaction accumulation - File-based response protocol with atomic locking - Reaction and reply handlers in slack_listener.py CLI Options Parsing Improvements: - Line-based terminal logger (500 lines vs 4KB buffer) - Noise filtering for spinners, status messages, token counts - Cursor prefix handling (❯ and >) for permission prompts - Backward-scanning permission parser with keyword validation - Fallback chain: line_log → byte_buffer → generic options - Metrics logging for parse source tracking Session Change Handling: - Detect /compact and /resume commands in terminal output - Session discovery by buffer file modification time - Registry update preserving Slack thread routing - E2E tests for full session change flow New modules: - core/line_logger.py - Thread-safe line-based terminal logger - core/permission_parser.py - Permission prompt parser - core/session_discovery.py - Active session discovery Test coverage: 117 new tests across unit and E2E suites Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The _DummyApp class was missing a client attribute, causing test_handle_message_threaded to fail with AttributeError when handle_message tried to access app.client. Added _DummyClient class that returns empty dicts for any method call, allowing tests to run without SLACK_BOT_TOKEN set. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Features at a Glance summary table - Document AskUserQuestion support (emoji selection, thread replies) - Add Interactive Features section with permission handling details - Document session commands (/compact, /resume auto-detection) - Add Database Migrations section (automatic migrations) - Update component tables with new modules (line_logger, permission_parser, session_discovery) - Document all hooks including on_permission_request.py - Update test count to 400+ - Add troubleshooting for AskUserQuestion and session changes - Document data storage paths (askuser_responses, permission_responses) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Filter out channel_join/leave and group_join/leave messages in listener - Combine Quick Start and Setup sections into unified Getting Started - Add clear callout about creating default channel before use - Add collapsible details for Slack app requirements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds comprehensive Slack integration features for Claude Code, including interactive permission handling, real-time updates, and robust session management.
🎯 Major Features
Interactive Permission Handling via Slack
AskUserQuestion Slack Integration (NEW)
Real-Time Session Updates
Global Shortcuts (via Slack's
/menu)Get SessionsAttach to SessionResearch ModePlan ModeExecute ModeDM Commands (message the bot directly)
/sessions/attach <session_id>/attach <session_id> --history 10/detach/mode/mode set <research|plan|execute>DM Mode
CLI Parsing Improvements (NEW)
Session Change Handling (NEW)
/compactand/resume: Automatically tracks session ID changes🔧 Infrastructure
New Core Modules
core/line_logger.py- Thread-safe line-based terminal loggercore/permission_parser.py- Backward-scanning permission prompt parsercore/session_discovery.py- Active session discovery by file mtimecore/registry_db.py- SQLAlchemy-based session registry with new columnscore/dm_mode.py- DM subscription and mode managementHook System
on_permission_request.py- Slack-based permission handling with emoji reactionson_pretooluse.py- AskUserQuestion interactive promptson_notification.py- Enhanced with line-log parsing fallbackon_stop.py- Rich session summaries and cleanupon_posttooluse.py- Post-tool notificationsDatabase Schema Updates
todo_message_ts- Track todo update messagesbuffer_file_path- Terminal output buffer lookuppermissions_channel- Separate channel for permission promptsaskuser_questionstable - Track AskUserQuestion statedm_subscriptionstable - Track user DM subscriptionsuser_preferencestable - Store user mode settings📊 Test Coverage
117+ new unit tests covering:
E2E tests for:
📁 Files Changed
Test Plan
/compactpreserves Slack thread routing/sessions,/attach,/detach,/mode)Breaking Changes
None - all changes are additive and backward compatible.
Migration Notes
install-hooksscriptslack_listener.py🤖 Generated with Claude Code