235 lines
6.1 KiB
Ruby
Executable File
235 lines
6.1 KiB
Ruby
Executable File
#!/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
|