diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 0000000..cadb9ac --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1,6 @@ +# Claude Code local settings +.claude/settings.local.json + +# Hooks +.claude/hooks/config/hooks-config.local.json +.claude/hooks/logs/ diff --git a/.claude/commands/weather.md b/.claude/commands/weather.md index e8a04c5..4872137 100644 --- a/.claude/commands/weather.md +++ b/.claude/commands/weather.md @@ -1,31 +1,20 @@ +--- +description: Fetch and transform weather data for a city +argument-hint: +model: haiku +--- + # Weather Command -You will invoke both the weather-fetcher and weather-transformer agents to complete the weather workflow. +Fetch the current temperature for ${1:-Karachi}, Pakistan and apply transformations. -## Task -Execute the complete weather workflow for Karachi, Pakistan by launching two specialized agents sequentially: +## Workflow -1. **weather-fetcher agent**: Retrieves the current temperature from wttr.in API -2. **weather-transformer agent**: Applies transformations from input/input.md to the temperature +1. Use the weather-fetcher agent to retrieve the current temperature from wttr.in API +2. Use the weather-transformer agent to read transformation rules from input/input.md and apply them to the temperature +3. Write the results to output/output.md -## Instructions - -Launch both agents sequentially, waiting for each to complete before starting the next: - -**Step 1:** Invoke weather-fetcher first: - - subagent_type: "weather-fetcher" - - description: "Fetch Karachi temperature" - - prompt: "Fetch the current temperature for Karachi, Pakistan in Celsius from the wttr.in API. Use the WebFetch tool to retrieve the temperature from wttr.in/Karachi?format=%t and return the numeric temperature value in Celsius in your final report." - - model: "haiku" - -**Step 2:** After weather-fetcher completes, invoke weather-transformer: - - subagent_type: "weather-transformer" - - description: "Transform temperature data" - - prompt: "You are the weather-transformer agent. The current temperature for Karachi, Pakistan is {temperature}°C (use the temperature value from the weather-fetcher agent). Read the transformation rules from input/input.md, apply those rules to the temperature value, and write the formatted results to output/output.md. Return a summary with the original temperature, transformation applied, and final result." - - model: "haiku" - -## Important -- Launch agents SEQUENTIALLY, not in parallel - weather fetching may take time -- Wait for weather-fetcher to complete before launching weather-transformer -- Pass the fetched temperature to the weather-transformer agent in the prompt -- Provide a clear final summary showing results from both agents +Launch the agents sequentially (not in parallel) and provide a clear summary showing: +- Original temperature +- Transformation applied +- Final result diff --git a/.claude/hooks/HOOKS-README.md b/.claude/hooks/HOOKS-README.md new file mode 100644 index 0000000..54aad9c --- /dev/null +++ b/.claude/hooks/HOOKS-README.md @@ -0,0 +1,94 @@ +# HOOKS-README +contains all the details, scripts, and instructions for the hooks + +## Hook Events Overview - [Official 9 Hooks](https://docs.claude.com/en/docs/claude-code/hooks-guide) +Claude Code provides several hook events that run at different points in the workflow: +1. PreToolUse: Runs before tool calls (can block them) +2. PostToolUse: Runs after tool calls complete +3. UserPromptSubmit: Runs when the user submits a prompt, before Claude processes it +4. Notification: Runs when Claude Code sends notifications +5. Stop: Runs when Claude Code finishes responding +6. SubagentStop: Runs when subagent tasks complete +7. PreCompact: Runs before Claude Code is about to run a compact operation +8. SessionStart: Runs when Claude Code starts a new session or resumes an existing session +9. SessionEnd: Runs when Claude Code session ends + +## Prerequisites + +Before using hooks, ensure you have the following prerequisites installed for your operating system: + +#### Windows +- **Python**: `python --version` + +#### macOS +- **Python 3**: `python3 --version` +- **Audio Player**: `afplay` (built-in, no installation needed) + +#### Linux +- **Python 3**: `python3 --version` +- **Audio Player**: `sudo apt install pulseaudio-utils` + +## Configuring Hooks (Enable/Disable) + +Hooks can be easily enabled or disabled at both the global and individual levels. + +### Disable All Hooks at Once + +Edit `.claude/settings.local.json` and set: +```json +{ + "disableAllHooks": true +} +``` + +**Note:** The `.claude/settings.local.json` file is git-ignored, so each user can configure their own hook preferences without affecting the team's shared settings in `.claude/settings.json`. + +### Disable Individual Hooks + +For granular control, you can disable specific hooks by editing the hooks configuration files. + +#### Configuration Files + +There are two configuration files for managing individual hooks: + +1. **`.claude/hooks/config/hooks-config.json`** - The shared/default configuration that is committed to git +2. **`.claude/hooks/config/hooks-config.local.json`** - Your personal overrides (git-ignored) + +The local config file (`.local.json`) takes precedence over the shared config, allowing each developer to customize their hook behavior without affecting the team. + +#### Shared Configuration + +Edit `.claude/hooks/config/hooks-config.json` for team-wide defaults: + +```json +{ + "disablePreToolUseHook": false, + "disablePostToolUseHook": false, + "disableUserPromptSubmitHook": false, + "disableNotificationHook": false, + "disableStopHook": false, + "disableSubagentStopHook": false, + "disablePreCompactHook": false, + "disableSessionStartHook": false, + "disableSessionEndHook": false +} +``` + +#### Local Configuration (Personal Overrides) + +Create or edit `.claude/hooks/config/hooks-config.local.json` for personal preferences: + +```json +{ + "disablePostToolUseHook": true, + "disableSessionStartHook": true +} +``` + +In this example, only the PostToolUse and SessionStart hooks are overridden locally. All other hooks will use the shared configuration values. + +**Note:** Individual hook toggles are checked by the hook script (`.claude/hooks/scripts/hooks.py`). Local settings override shared settings, and if a hook is disabled, the script exits silently without playing any sounds or executing hook logic. + +### Text to Speech (TTS) +website used to generate sounds: https://elevenlabs.io/ +voice used: Samara X \ No newline at end of file diff --git a/.claude/hooks/config/hooks-config.json b/.claude/hooks/config/hooks-config.json new file mode 100644 index 0000000..3832cda --- /dev/null +++ b/.claude/hooks/config/hooks-config.json @@ -0,0 +1,11 @@ +{ + "disablePreToolUseHook": false, + "disablePostToolUseHook": false, + "disableUserPromptSubmitHook": false, + "disableNotificationHook": false, + "disableStopHook": false, + "disableSubagentStopHook": false, + "disablePreCompactHook": false, + "disableSessionStartHook": false, + "disableSessionEndHook": false +} diff --git a/.claude/hooks/logs/hooks-log.jsonl b/.claude/hooks/logs/hooks-log.jsonl new file mode 100644 index 0000000..5430209 --- /dev/null +++ b/.claude/hooks/logs/hooks-log.jsonl @@ -0,0 +1,105 @@ +{ + "session_id": "f6991f4d-8b32-4ae0-a9c5-b9edbfc23060", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-claude-code-voice-hooks/f6991f4d-8b32-4ae0-a9c5-b9edbfc23060.jsonl", + "cwd": "/Users/shayanrais/Documents/claude-code-voice-hooks", + "hook_event_name": "SessionStart", + "source": "startup" +} +{ + "session_id": "f6991f4d-8b32-4ae0-a9c5-b9edbfc23060", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-claude-code-voice-hooks/f6991f4d-8b32-4ae0-a9c5-b9edbfc23060.jsonl", + "cwd": "/Users/shayanrais/Documents/claude-code-voice-hooks", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} +{ + "session_id": "f6991f4d-8b32-4ae0-a9c5-b9edbfc23060", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-claude-code-voice-hooks/f6991f4d-8b32-4ae0-a9c5-b9edbfc23060.jsonl", + "cwd": "/Users/shayanrais/Documents/claude-code-voice-hooks", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} +{ + "session_id": "f6991f4d-8b32-4ae0-a9c5-b9edbfc23060", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-claude-code-voice-hooks/f6991f4d-8b32-4ae0-a9c5-b9edbfc23060.jsonl", + "cwd": "/Users/shayanrais/Documents/claude-code-voice-hooks", + "hook_event_name": "SessionEnd", + "reason": "prompt_input_exit" +} +{ + "session_id": "5300e9bc-7040-4a1c-a397-4f8cfba2182b", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/5300e9bc-7040-4a1c-a397-4f8cfba2182b.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "hook_event_name": "SessionStart", + "source": "startup" +} +{ + "session_id": "5300e9bc-7040-4a1c-a397-4f8cfba2182b", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/5300e9bc-7040-4a1c-a397-4f8cfba2182b.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} +{ + "session_id": "5300e9bc-7040-4a1c-a397-4f8cfba2182b", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/5300e9bc-7040-4a1c-a397-4f8cfba2182b.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} +{ + "session_id": "5300e9bc-7040-4a1c-a397-4f8cfba2182b", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/5300e9bc-7040-4a1c-a397-4f8cfba2182b.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "hook_event_name": "SessionEnd", + "reason": "prompt_input_exit" +} +{ + "session_id": "ff165d78-4869-4e79-babe-7d923424024c", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/ff165d78-4869-4e79-babe-7d923424024c.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "hook_event_name": "SessionStart", + "source": "startup" +} +{ + "session_id": "ff165d78-4869-4e79-babe-7d923424024c", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/ff165d78-4869-4e79-babe-7d923424024c.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} +{ + "session_id": "ff165d78-4869-4e79-babe-7d923424024c", + "transcript_path": "/Users/shayanrais/.claude/projects/-Users-shayanrais-Documents-Github-claude-code-voice-hooks/ff165d78-4869-4e79-babe-7d923424024c.jsonl", + "cwd": "/Users/shayanrais/Documents/Github/claude-code-voice-hooks", + "hook_event_name": "SessionEnd", + "reason": "prompt_input_exit" +} +{ + "session_id": "9fc0d281-b095-4c23-97c8-2b06c6ed26a2", + "transcript_path": "/Users/shayanraees/.claude/projects/-Users-shayanraees-Documents-Github-claude-code-best-practice/9fc0d281-b095-4c23-97c8-2b06c6ed26a2.jsonl", + "cwd": "/Users/shayanraees/Documents/Github/claude-code-best-practice", + "hook_event_name": "SessionStart", + "source": "startup" +} +{ + "session_id": "9fc0d281-b095-4c23-97c8-2b06c6ed26a2", + "transcript_path": "/Users/shayanraees/.claude/projects/-Users-shayanraees-Documents-Github-claude-code-best-practice/9fc0d281-b095-4c23-97c8-2b06c6ed26a2.jsonl", + "cwd": "/Users/shayanraees/Documents/Github/claude-code-best-practice", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} +{ + "session_id": "9fc0d281-b095-4c23-97c8-2b06c6ed26a2", + "transcript_path": "/Users/shayanraees/.claude/projects/-Users-shayanraees-Documents-Github-claude-code-best-practice/9fc0d281-b095-4c23-97c8-2b06c6ed26a2.jsonl", + "cwd": "/Users/shayanraees/Documents/Github/claude-code-best-practice", + "permission_mode": "default", + "hook_event_name": "SubagentStop", + "stop_hook_active": false +} diff --git a/.claude/hooks/scripts/hooks.py b/.claude/hooks/scripts/hooks.py new file mode 100644 index 0000000..75bcee9 --- /dev/null +++ b/.claude/hooks/scripts/hooks.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +""" +Claude Code Hook Handler +============================================= +This script handles events from Claude Code and plays sounds for different hook events. +Supports all 9 Claude Code hooks: https://docs.claude.com/en/docs/claude-code/hooks-guide + +Special handling for git commits: plays pretooluse-git-committing.mp3 +""" + +import sys +import json +import subprocess +import re +import platform +from pathlib import Path + +# Windows-only module for playing WAV files +try: + import winsound +except ImportError: + winsound = None + +# ===== HOOK EVENT TO SOUND FOLDER MAPPING ===== +# Maps each hook event to its corresponding sound folder +HOOK_SOUND_MAP = { + "PreToolUse": "pretooluse", + "PostToolUse": "posttooluse", + "UserPromptSubmit": "userpromptsubmit", + "Notification": "notification", + "Stop": "stop", + "SubagentStop": "subagentstop", + "PreCompact": "precompact", + "SessionStart": "sessionstart", + "SessionEnd": "sessionend" +} + +# ===== BASH COMMAND PATTERNS ===== +# Regex patterns to detect specific bash commands and map to special sounds +BASH_PATTERNS = [ + (r'git commit', "pretooluse-git-committing"), # Git commits (anywhere in command) +] + +def get_audio_player(): + """ + Detect the appropriate audio player for the current platform. + + Returns: + List of command and args to use for playing audio, or None if no player found + """ + system = platform.system() + + if system == "Darwin": + # macOS: use afplay (built-in) + return ["afplay"] + elif system == "Linux": + # Linux: try different players in order of preference + # Try to find an available player + players = [ + ["paplay"], # PulseAudio (most common on modern Linux) + ["aplay"], # ALSA (fallback) + ["ffplay", "-nodisp", "-autoexit"], # FFmpeg (if installed) + ["mpg123", "-q"], # mpg123 (if installed) + ] + + for player in players: + try: + # Check if the player exists + subprocess.run( + ["which", player[0]], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=True + ) + return player + except (subprocess.CalledProcessError, FileNotFoundError): + continue + + # No player found + return None + elif system == "Windows": + # Windows: Use winsound for WAV, PowerShell for MP3 + return ["WINDOWS"] + else: + # Other OS - not supported yet + return None + + +def play_sound(sound_name): + """ + Play a sound file for the given sound name. + + Args: + sound_name: Name of the sound file (e.g., "pretooluse", "pretooluse-git-committing") + The file should be at .claude/hooks/sounds/{folder}/{sound_name}.{mp3|wav} + + Returns: + True if sound played successfully, False otherwise + """ + # Security check: Prevent directory traversal attacks + if "/" in sound_name or "\\" in sound_name or ".." in sound_name: + print(f"Invalid sound name: {sound_name}", file=sys.stderr) + return False + + # Get the appropriate audio player for this platform + audio_player = get_audio_player() + if not audio_player: + # No audio player available - fail silently + return False + + # Build the path to the sound folder + # Scripts are in .claude/hooks/scripts/, sounds are in .claude/hooks/sounds/ + script_dir = Path(__file__).parent # .claude/hooks/scripts/ + hooks_dir = script_dir.parent # .claude/hooks/ + + # Determine the folder based on the sound name prefix + # For special sounds like "pretooluse-git-committing", look in "pretooluse" folder + folder_name = sound_name.split('-')[0] + sounds_dir = hooks_dir / "sounds" / folder_name + + # Check if we're on Windows and need special handling + is_windows = audio_player[0] == "WINDOWS" + + # Try different audio formats + # Note: paplay (PulseAudio) doesn't support MP3, so try WAV first + # On Windows, only use WAV files to avoid PowerShell/COM issues + extensions = ['.wav'] if is_windows else ['.wav', '.mp3'] + + for extension in extensions: + file_path = sounds_dir / f"{sound_name}{extension}" + + if file_path.exists(): + try: + if is_windows: + # Windows: Use winsound for WAV files (built-in, reliable, fast) + if winsound: + # SND_FILENAME: file_path is a filename + # SND_SYNC: play sound synchronously (wait until complete) + # SND_NODEFAULT: don't play default sound if file not found + # Note: Using SND_SYNC instead of SND_ASYNC because the script exits immediately + # after this call, which would terminate async playback before it completes + winsound.PlaySound(str(file_path), + winsound.SND_FILENAME | winsound.SND_NODEFAULT) + return True + else: + # winsound not available, fail silently + return False + else: + # Unix/Linux/macOS: use subprocess with audio player + subprocess.Popen( + audio_player + [str(file_path)], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True + ) + return True + except (FileNotFoundError, OSError) as e: + print(f"Error playing sound {file_path.name}: {e}", file=sys.stderr) + return False + except Exception as e: + # Catch any other exceptions (e.g., winsound errors) + print(f"Error playing sound {file_path.name}: {e}", file=sys.stderr) + return False + + # Sound not found - fail silently to avoid disrupting Claude's work + return False + +def is_hook_disabled(event_name): + """ + Check if a specific hook is disabled in the config files. + Uses fallback logic: hooks-config.local.json -> hooks-config.json + + Priority: + 1. If hooks-config.local.json exists and has the setting, use it + 2. Otherwise, fall back to hooks-config.json + 3. If neither exists or the key is missing, assume hook is enabled (return False) + + Args: + event_name: The hook event name (e.g., "PreToolUse", "PostToolUse") + + Returns: + True if the hook is disabled, False otherwise + """ + try: + # Scripts are in .claude/hooks/scripts/, config is in .claude/hooks/config/ + script_dir = Path(__file__).parent # .claude/hooks/scripts/ + hooks_dir = script_dir.parent # .claude/hooks/ + config_dir = hooks_dir / "config" # .claude/hooks/config/ + + local_config_path = config_dir / "hooks-config.local.json" + default_config_path = config_dir / "hooks-config.json" + + # Map event names to config keys + config_key = f"disable{event_name}Hook" + + # Try to load local config first + local_config = None + if local_config_path.exists(): + try: + with open(local_config_path, "r", encoding="utf-8") as config_file: + local_config = json.load(config_file) + except Exception as e: + print(f"Error reading local config: {e}", file=sys.stderr) + + # Try to load default config + default_config = None + if default_config_path.exists(): + try: + with open(default_config_path, "r", encoding="utf-8") as config_file: + default_config = json.load(config_file) + except Exception as e: + print(f"Error reading default config: {e}", file=sys.stderr) + + # Apply fallback logic: local -> default -> False (enabled) + if local_config is not None and config_key in local_config: + return local_config[config_key] + elif default_config is not None and config_key in default_config: + return default_config[config_key] + else: + # If neither config has the key, assume hook is enabled + return False + + except Exception as e: + # If anything goes wrong, assume hook is enabled + print(f"Error in is_hook_disabled: {e}", file=sys.stderr) + return False + +def log_hook_data(hook_data): + """ + Log the full hook_data to hooks-log.jsonl for debugging/auditing. + Log file is stored at .claude/hooks/logs/hooks-log.jsonl + """ + try: + # Scripts are in .claude/hooks/scripts/, logs are in .claude/hooks/logs/ + script_dir = Path(__file__).parent # .claude/hooks/scripts/ + hooks_dir = script_dir.parent # .claude/hooks/ + logs_dir = hooks_dir / "logs" # .claude/hooks/logs/ + + # Ensure logs directory exists + logs_dir.mkdir(parents=True, exist_ok=True) + + log_path = logs_dir / "hooks-log.jsonl" + with open(log_path, "a", encoding="utf-8") as log_file: + log_file.write(json.dumps(hook_data, ensure_ascii=False, indent=2) + "\n") + except Exception as e: + # Fail silently, but print to stderr for visibility + print(f"Failed to log hook_data: {e}", file=sys.stderr) + + +def detect_bash_command_sound(command): + """ + Detect special bash commands and return the corresponding sound name. + + Args: + command: The bash command string + + Returns: + Sound name (string) if a pattern matches, None otherwise + """ + if not command: + return None + + for pattern, sound_name in BASH_PATTERNS: + if re.search(pattern, command.strip()): + return sound_name + + return None + + +def get_sound_name(hook_data): + """ + Determine which sound to play based on the hook event and context. + + Args: + hook_data: Dictionary containing event information from Claude + + Returns: + Sound name (string) or None if no sound should play + """ + event_name = hook_data.get("hook_event_name", "") + tool_name = hook_data.get("tool_name", "") + + # Check if this is a PreToolUse event with Bash tool + if event_name == "PreToolUse" and tool_name == "Bash": + tool_input = hook_data.get("tool_input", {}) + command = tool_input.get("command", "") + + # Check for special bash command patterns (e.g., git commit) + special_sound = detect_bash_command_sound(command) + if special_sound: + return special_sound + + # Return the default sound for this hook event + return HOOK_SOUND_MAP.get(event_name) + +def main(): + """ + Main program - this runs when Claude triggers a hook. + + How it works: + 1. Claude sends event data as JSON through stdin + 2. We check if this specific hook is disabled in hooks-config.json + 3. We parse the JSON to understand which hook event occurred + 4. We check for special bash commands (like git commit) + 5. We play the corresponding sound for that event + 6. We exit successfully + """ + try: + # Step 1: Read the event data from Claude + stdin_content = sys.stdin.read().strip() + + # If stdin is empty, exit gracefully (hook was called without data) + if not stdin_content: + sys.exit(0) + + input_data = json.loads(stdin_content) + log_hook_data(input_data) + + # Step 2: Check if this hook is disabled + event_name = input_data.get("hook_event_name", "") + if is_hook_disabled(event_name): + # Hook is disabled, exit silently without playing sound + sys.exit(0) + + # Step 3: Determine which sound to play (may be special or default) + sound_name = get_sound_name(input_data) + + # Step 4: Play the sound (if we found one) + if sound_name: + play_sound(sound_name) + + # Step 5: Exit successfully + # Always exit with code 0 so we don't interrupt Claude's work + sys.exit(0) + + except json.JSONDecodeError as e: + print(f"Error parsing JSON input: {e}", file=sys.stderr) + # Exit with 0 to avoid blocking Claude's work + sys.exit(0) + except Exception as e: + print(f"Unexpected error: {e}", file=sys.stderr) + # Exit with 0 to avoid blocking Claude's work + sys.exit(0) + + +# Entry point - Python calls main() when the script runs +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.claude/hooks/scripts/run-hooks-py-os-wise.sh b/.claude/hooks/scripts/run-hooks-py-os-wise.sh new file mode 100755 index 0000000..d4a970d --- /dev/null +++ b/.claude/hooks/scripts/run-hooks-py-os-wise.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Cross-platform Python wrapper for Claude Code hooks +# Automatically selects python or python3 based on OS and availability + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PYTHON_SCRIPT="$SCRIPT_DIR/hooks.py" + +# Function to detect and use the correct Python command +run_python() { + # Check if python3 is available (preferred on Unix-like systems) + if command -v python3 &> /dev/null; then + python3 "$PYTHON_SCRIPT" + # Fallback to python command (Windows and some systems) + elif command -v python &> /dev/null; then + python "$PYTHON_SCRIPT" + else + echo "Error: Neither python nor python3 command found" >&2 + exit 1 + fi +} + +# Run the Python script +run_python diff --git a/.claude/hooks/sounds/notification/notification.mp3 b/.claude/hooks/sounds/notification/notification.mp3 new file mode 100644 index 0000000..998f79b Binary files /dev/null and b/.claude/hooks/sounds/notification/notification.mp3 differ diff --git a/.claude/hooks/sounds/notification/notification.wav b/.claude/hooks/sounds/notification/notification.wav new file mode 100644 index 0000000..45e7af6 Binary files /dev/null and b/.claude/hooks/sounds/notification/notification.wav differ diff --git a/.claude/hooks/sounds/posttooluse/posttooluse.mp3 b/.claude/hooks/sounds/posttooluse/posttooluse.mp3 new file mode 100644 index 0000000..07c225b Binary files /dev/null and b/.claude/hooks/sounds/posttooluse/posttooluse.mp3 differ diff --git a/.claude/hooks/sounds/posttooluse/posttooluse.wav b/.claude/hooks/sounds/posttooluse/posttooluse.wav new file mode 100644 index 0000000..7fc103d Binary files /dev/null and b/.claude/hooks/sounds/posttooluse/posttooluse.wav differ diff --git a/.claude/hooks/sounds/precompact/precompact.mp3 b/.claude/hooks/sounds/precompact/precompact.mp3 new file mode 100644 index 0000000..9ab824c Binary files /dev/null and b/.claude/hooks/sounds/precompact/precompact.mp3 differ diff --git a/.claude/hooks/sounds/precompact/precompact.wav b/.claude/hooks/sounds/precompact/precompact.wav new file mode 100644 index 0000000..3c284a8 Binary files /dev/null and b/.claude/hooks/sounds/precompact/precompact.wav differ diff --git a/.claude/hooks/sounds/pretooluse/pretooluse-git-committing.mp3 b/.claude/hooks/sounds/pretooluse/pretooluse-git-committing.mp3 new file mode 100644 index 0000000..f0fbfa3 Binary files /dev/null and b/.claude/hooks/sounds/pretooluse/pretooluse-git-committing.mp3 differ diff --git a/.claude/hooks/sounds/pretooluse/pretooluse-git-committing.wav b/.claude/hooks/sounds/pretooluse/pretooluse-git-committing.wav new file mode 100644 index 0000000..67bea05 Binary files /dev/null and b/.claude/hooks/sounds/pretooluse/pretooluse-git-committing.wav differ diff --git a/.claude/hooks/sounds/pretooluse/pretooluse.mp3 b/.claude/hooks/sounds/pretooluse/pretooluse.mp3 new file mode 100644 index 0000000..b6447a5 Binary files /dev/null and b/.claude/hooks/sounds/pretooluse/pretooluse.mp3 differ diff --git a/.claude/hooks/sounds/pretooluse/pretooluse.wav b/.claude/hooks/sounds/pretooluse/pretooluse.wav new file mode 100644 index 0000000..4f993d8 Binary files /dev/null and b/.claude/hooks/sounds/pretooluse/pretooluse.wav differ diff --git a/.claude/hooks/sounds/sessionend/sessionend.mp3 b/.claude/hooks/sounds/sessionend/sessionend.mp3 new file mode 100644 index 0000000..70d21fa Binary files /dev/null and b/.claude/hooks/sounds/sessionend/sessionend.mp3 differ diff --git a/.claude/hooks/sounds/sessionend/sessionend.wav b/.claude/hooks/sounds/sessionend/sessionend.wav new file mode 100644 index 0000000..90bcc35 Binary files /dev/null and b/.claude/hooks/sounds/sessionend/sessionend.wav differ diff --git a/.claude/hooks/sounds/sessionstart/sessionstart.mp3 b/.claude/hooks/sounds/sessionstart/sessionstart.mp3 new file mode 100644 index 0000000..2cc6d45 Binary files /dev/null and b/.claude/hooks/sounds/sessionstart/sessionstart.mp3 differ diff --git a/.claude/hooks/sounds/sessionstart/sessionstart.wav b/.claude/hooks/sounds/sessionstart/sessionstart.wav new file mode 100644 index 0000000..5fc5d4f Binary files /dev/null and b/.claude/hooks/sounds/sessionstart/sessionstart.wav differ diff --git a/.claude/hooks/sounds/stop/stop.mp3 b/.claude/hooks/sounds/stop/stop.mp3 new file mode 100644 index 0000000..13e8c07 Binary files /dev/null and b/.claude/hooks/sounds/stop/stop.mp3 differ diff --git a/.claude/hooks/sounds/stop/stop.wav b/.claude/hooks/sounds/stop/stop.wav new file mode 100644 index 0000000..ce09702 Binary files /dev/null and b/.claude/hooks/sounds/stop/stop.wav differ diff --git a/.claude/hooks/sounds/subagentstop/subagentstop.mp3 b/.claude/hooks/sounds/subagentstop/subagentstop.mp3 new file mode 100644 index 0000000..e2e7ffa Binary files /dev/null and b/.claude/hooks/sounds/subagentstop/subagentstop.mp3 differ diff --git a/.claude/hooks/sounds/subagentstop/subagentstop.wav b/.claude/hooks/sounds/subagentstop/subagentstop.wav new file mode 100644 index 0000000..b1d97b4 Binary files /dev/null and b/.claude/hooks/sounds/subagentstop/subagentstop.wav differ diff --git a/.claude/hooks/sounds/userpromptsubmit/userpromptsubmit.mp3 b/.claude/hooks/sounds/userpromptsubmit/userpromptsubmit.mp3 new file mode 100644 index 0000000..307f803 Binary files /dev/null and b/.claude/hooks/sounds/userpromptsubmit/userpromptsubmit.mp3 differ diff --git a/.claude/hooks/sounds/userpromptsubmit/userpromptsubmit.wav b/.claude/hooks/sounds/userpromptsubmit/userpromptsubmit.wav new file mode 100644 index 0000000..1f134ae Binary files /dev/null and b/.claude/hooks/sounds/userpromptsubmit/userpromptsubmit.wav differ diff --git a/.claude/settings.json b/.claude/settings.json index 08c01d1..259aaef 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,9 +1,95 @@ { - "permissions": { - "allow": [ - "WebFetch(domain:wttr.in)" + "disableAllHooks": false, + "hooks": { + "PreToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } ], - "deny": [], - "ask": [] + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "PreCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/run-hooks-py-os-wise.sh" + } + ] + } + ] } -} +} \ No newline at end of file diff --git a/output/output.md b/output/output.md index d494f5a..6afa0b3 100644 --- a/output/output.md +++ b/output/output.md @@ -1,3 +1,3 @@ -Original Temperature: 28°C -Transformation Applied: Add +10 -Final Result: 38°C +Original Temperature: 26°C +Transformation Applied: add +10 in the result +Final Result: 36°C