Initial commit
This commit is contained in:
234
hooks/handlers/stop_handler.rb
Executable file
234
hooks/handlers/stop_handler.rb
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'claude_hooks'
|
||||
|
||||
# Stop Handler
|
||||
#
|
||||
# PURPOSE: Control and customize Claude Code's stopping behavior
|
||||
# TRIGGERS: When Claude Code is about to stop execution or end a session
|
||||
#
|
||||
# COMMON USE CASES:
|
||||
# - Save session state and cleanup resources
|
||||
# - Generate session summaries or reports
|
||||
# - Backup important work or temporary files
|
||||
# - Log session metrics and analytics
|
||||
# - Send completion notifications
|
||||
# - Trigger follow-up actions or workflows
|
||||
#
|
||||
# SETTINGS.JSON CONFIGURATION:
|
||||
# {
|
||||
# "hooks": {
|
||||
# "Stop": [{
|
||||
# "matcher": "",
|
||||
# "hooks": [{
|
||||
# "type": "command",
|
||||
# "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/entrypoints/stop.rb"
|
||||
# }]
|
||||
# }]
|
||||
# }
|
||||
# }
|
||||
|
||||
class StopHandler < ClaudeHooks::Stop
|
||||
def call
|
||||
log 'Processing session stop request'
|
||||
|
||||
# Example: Save session state
|
||||
# save_session_state
|
||||
|
||||
# Example: Generate session summary
|
||||
# generate_session_summary
|
||||
|
||||
# Example: Cleanup temporary resources
|
||||
# cleanup_resources
|
||||
|
||||
# Example: Send completion notifications
|
||||
# send_completion_notifications
|
||||
|
||||
# Allow stopping to proceed
|
||||
allow_continue!
|
||||
|
||||
output_data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def save_session_state
|
||||
log 'Saving session state'
|
||||
|
||||
# Example: Save important session data
|
||||
{
|
||||
session_id: session_id,
|
||||
end_time: Time.now,
|
||||
working_directory: cwd || Dir.pwd,
|
||||
transcript_path: transcript_path
|
||||
}
|
||||
|
||||
# Example: Write to session cache
|
||||
# write_session_cache(session_data)
|
||||
|
||||
# Example: Update project metadata
|
||||
# update_project_metadata(session_data)
|
||||
end
|
||||
|
||||
def generate_session_summary
|
||||
log 'Generating session summary'
|
||||
|
||||
# Example: Read transcript and generate summary
|
||||
return unless transcript_path && File.exist?(transcript_path)
|
||||
|
||||
transcript = read_transcript
|
||||
summary = analyze_transcript(transcript)
|
||||
|
||||
log "Session summary: #{summary[:total_interactions]} interactions, #{summary[:tools_used].length} tools used"
|
||||
|
||||
# Example: Save summary to file
|
||||
# save_session_summary(summary)
|
||||
end
|
||||
|
||||
def cleanup_resources
|
||||
log 'Cleaning up session resources'
|
||||
|
||||
# Example: Remove temporary files
|
||||
# cleanup_temp_files
|
||||
|
||||
# Example: Close database connections
|
||||
# close_database_connections
|
||||
|
||||
# Example: Clear caches
|
||||
# clear_session_caches
|
||||
|
||||
# Example: Stop background processes
|
||||
# stop_background_processes
|
||||
end
|
||||
|
||||
def send_completion_notifications
|
||||
log 'Sending session completion notifications'
|
||||
|
||||
# Example: Notify team about session completion
|
||||
# notify_team_completion
|
||||
|
||||
# Example: Send analytics data
|
||||
# send_analytics_data
|
||||
|
||||
# Example: Update project dashboard
|
||||
# update_project_dashboard
|
||||
end
|
||||
|
||||
def analyze_transcript(transcript)
|
||||
# Simple transcript analysis
|
||||
lines = transcript.split("\n")
|
||||
|
||||
{
|
||||
total_lines: lines.length,
|
||||
total_interactions: count_user_interactions(lines),
|
||||
tools_used: extract_tools_used(lines),
|
||||
errors_encountered: count_errors(lines),
|
||||
session_duration: calculate_session_duration(lines)
|
||||
}
|
||||
end
|
||||
|
||||
def count_user_interactions(lines)
|
||||
# Count lines that look like user messages
|
||||
lines.count { |line| line.match?(/^(user:|User:)/i) }
|
||||
end
|
||||
|
||||
def extract_tools_used(lines)
|
||||
# Extract tool names from transcript
|
||||
tools = []
|
||||
|
||||
lines.each do |line|
|
||||
tools << ::Regexp.last_match(1) if line.match?(/Using tool: (\w+)/) || line.match?(/Tool call: (\w+)/)
|
||||
end
|
||||
|
||||
tools.uniq
|
||||
end
|
||||
|
||||
def count_errors(lines)
|
||||
# Count error messages in transcript
|
||||
error_patterns = [
|
||||
/error:/i,
|
||||
/failed:/i,
|
||||
/exception:/i,
|
||||
/cannot/i
|
||||
]
|
||||
|
||||
lines.count do |line|
|
||||
error_patterns.any? { |pattern| line.match?(pattern) }
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_session_duration(lines)
|
||||
# Simple duration calculation based on first and last timestamps
|
||||
first_timestamp = extract_first_timestamp(lines)
|
||||
last_timestamp = extract_last_timestamp(lines)
|
||||
|
||||
if first_timestamp && last_timestamp
|
||||
(last_timestamp - first_timestamp).to_i
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def extract_first_timestamp(_lines)
|
||||
# Extract timestamp from first line (implementation depends on transcript format)
|
||||
# This is a placeholder - actual implementation would depend on transcript format
|
||||
nil
|
||||
end
|
||||
|
||||
def extract_last_timestamp(_lines)
|
||||
# Extract timestamp from last line (implementation depends on transcript format)
|
||||
# This is a placeholder - actual implementation would depend on transcript format
|
||||
nil
|
||||
end
|
||||
|
||||
def write_session_cache(data)
|
||||
# Example: Write session data to cache file
|
||||
project_path_for('cache/sessions')
|
||||
# FileUtils.mkdir_p(cache_dir)
|
||||
# File.write("#{cache_dir}/#{session_id}.json", JSON.pretty_generate(data))
|
||||
log "Would write session cache: #{data.keys.join(', ')}"
|
||||
end
|
||||
|
||||
def update_project_metadata(_data)
|
||||
# Example: Update project-level metadata
|
||||
log 'Would update project metadata with session data'
|
||||
end
|
||||
|
||||
def save_session_summary(summary)
|
||||
# Example: Save session summary to reports directory
|
||||
project_path_for('reports/sessions')
|
||||
# FileUtils.mkdir_p(reports_dir)
|
||||
# File.write("#{reports_dir}/#{session_id}_summary.json", JSON.pretty_generate(summary))
|
||||
log "Would save session summary: #{summary.keys.join(', ')}"
|
||||
end
|
||||
|
||||
def cleanup_temp_files
|
||||
# Example: Remove temporary files created during session
|
||||
temp_patterns = [
|
||||
project_path_for('tmp/**/*'),
|
||||
project_path_for('.temp/**/*'),
|
||||
project_path_for('*.tmp')
|
||||
]
|
||||
|
||||
log "Would cleanup temporary files matching patterns: #{temp_patterns.join(', ')}"
|
||||
end
|
||||
|
||||
def notify_team_completion
|
||||
# Example: Send Slack/Teams notification about session completion
|
||||
log 'Would notify team about session completion'
|
||||
end
|
||||
|
||||
def send_analytics_data
|
||||
# Example: Send session metrics to analytics service
|
||||
log 'Would send analytics data for session'
|
||||
end
|
||||
end
|
||||
|
||||
# Testing support - run this file directly to test with sample data
|
||||
if __FILE__ == $PROGRAM_NAME
|
||||
ClaudeHooks::CLI.test_runner(StopHandler) do |input_data|
|
||||
input_data['session_id'] = 'test-session-01'
|
||||
input_data['reason'] = 'user_requested'
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user