diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 2f32b28..8eee1bd 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,13 +6,13 @@ }, "metadata": { "description": "AI-operated audio notification system for Claude Code. 26 hooks, native matcher routing, TTS, webhook fan-out, status line, focus flow, rate-limit alerts.", - "version": "5.0.3" + "version": "5.1.1" }, "plugins": [ { "name": "audio-hooks", "source": "./plugins/audio-hooks", - "version": "5.0.3", + "version": "5.1.1", "description": "Audio notifications + AI-controlled config for every Claude Code event", "author": { "name": "Chan Meng", diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml new file mode 100644 index 0000000..9b02fcb --- /dev/null +++ b/.github/workflows/smoke.yml @@ -0,0 +1,54 @@ +name: smoke + +on: + push: + branches: [master] + pull_request: + +jobs: + import-smoke: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ["3.9", "3.12", "3.13"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Linux audio player + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y mpg123 + + - name: Import canonical hook_runner + shell: bash + run: | + python -c "import sys; sys.path.insert(0,'hooks'); import hook_runner; print('canonical', hook_runner.HOOK_RUNNER_VERSION)" + + - name: Import plugin hook_runner copy + shell: bash + run: | + python -c "import sys; sys.path.insert(0,'plugins/audio-hooks/hooks'); import hook_runner; print('plugin', hook_runner.HOOK_RUNNER_VERSION)" + + - name: audio-hooks version / status / diagnose + shell: bash + run: | + python bin/audio-hooks.py version + python bin/audio-hooks.py status + python bin/audio-hooks.py diagnose + + - name: audio-hooks test all (every hook dispatches) + shell: bash + run: | + python bin/audio-hooks.py test all + + plugin-in-sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Verify plugin copy matches canonical sources + run: bash scripts/build-plugin.sh --check diff --git a/CHANGELOG.md b/CHANGELOG.md index cb677bb..0e192e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to Claude Code Audio Hooks will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.1.1] - 2026-04-18 + +Critical import-time crash fix plus regression-prevention CI. Everyone on v5.0.3 or v5.1.0 should upgrade. + +### Fixed + +- **`hook_runner.py` crashed on import with `NameError: name 'Tuple' is not defined`** ([#10](https://github.com/ChanMeng666/claude-code-audio-hooks/issues/10)). The file used `Tuple` in module-level type annotations (`SYNTHETIC_EVENT_MAP` and `_resolve_synthetic_event`) but did not import it from `typing`. Because the module has no `from __future__ import annotations`, CPython evaluated the annotations at import time and every `audio-hooks` subcommand (`diagnose`, `status`, `version`, `test`, …) crashed before dispatch. Users on v5.0.3 and v5.1.0 were fully blocked. One-line fix: add `Tuple` to the existing `typing` import. + +### Added + +- **CI import-smoke workflow** (`.github/workflows/smoke.yml`) to prevent regressions of this class of bug. Runs on every push to `master` and every PR across a 3×3 matrix (Ubuntu / Windows / macOS × Python 3.9 / 3.12 / 3.13) and exercises: + - `import hook_runner` from both the canonical `hooks/` and the synced `plugins/audio-hooks/hooks/` copy + - `audio-hooks version`, `status`, `diagnose` + - `audio-hooks test all` (all 26 hooks dispatch successfully) + - `scripts/build-plugin.sh --check` (the plugin copy is in sync with canonical sources) + +### Note on v5.1.0 + +The `v5.1.0` tag was cut from the "context window monitor" feature commit but the in-tree version strings, CHANGELOG, and release notes were never bumped, so v5.1.0 shipped with the broken import under the 5.0.3 version string. v5.1.1 corrects this: every version reference (`HOOK_RUNNER_VERSION`, `PROJECT_VERSION`, `marketplace.json`, `plugin.json`, `config/default_preferences.json`, `CLAUDE.md` header) is now consistently `5.1.1`. + ## [5.0.3] - 2026-04-11 Documentation correction. v5.0.2's README and release notes overclaimed that *"the human never types a command — Claude Code does everything"*. The user immediately caught the overclaim during real testing: the AI inside a Claude Code session **cannot** invoke `/reload-plugins` (or any other slash command) because slash commands are interactive REPL primitives with no CLI equivalent and no tool exposure. The user has to type `/reload-plugins` themselves once after install. diff --git a/CLAUDE.md b/CLAUDE.md index d6e30f5..55f2b6b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Claude Code Audio Hooks — AI Operator Guide -> **Version:** 5.0.3 | **Last Updated:** 2026-04-11 +> **Version:** 5.1.1 | **Last Updated:** 2026-04-18 This document is the canonical machine-facing guide for the claude-code-audio-hooks project. It is written for **Claude Code (and other AI agents) as the operator** — not for humans. A human user says "install audio hooks for me" or "snooze audio for an hour" and you do everything via the project's structured CLI without any human-in-the-loop confirmation, menu navigation, or log file reading. diff --git a/README.md b/README.md index d71c483..c573fff 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ You type one slash command at install time. Then natural language forever.
26 hook events, 2 audio themes, rate-limit alerts, webhooks, TTS, context monitor — all operated by Claude Code on your behalf. [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Version](https://img.shields.io/badge/version-5.0.3-blue.svg)](https://github.com/ChanMeng666/claude-code-audio-hooks) +[![Version](https://img.shields.io/badge/version-5.1.1-blue.svg)](https://github.com/ChanMeng666/claude-code-audio-hooks) [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-green.svg)](https://github.com/ChanMeng666/claude-code-audio-hooks) [![Claude Code](https://img.shields.io/badge/Claude_Code-v2.1.80%2B-brightgreen.svg)](https://claude.ai/download) [![Plugin](https://img.shields.io/badge/install-just_talk_to_Claude-purple.svg)](#-install-in-60-seconds) @@ -286,7 +286,7 @@ sequenceDiagram You->>CC: Show me the last 20 errors and clear the log. CC-->>You: 2 errors found (WEBHOOK_TIMEOUT). Log cleared. You->>CC: What version of audio-hooks am I running? - CC-->>You: v5.0.3, plugin install. + CC-->>You: v5.1.1, plugin install. You->>CC: Please uninstall audio-hooks completely. CC-->>You: Plugin uninstalled. All hooks removed. end @@ -505,7 +505,7 @@ Real-time context window and API quota bars — color-coded warnings before Clau

```text -[Opus] Audio Hooks v5.0.3 | 6/26 Sounds | Webhook: ntfy | Theme: Voice +[Opus] Audio Hooks v5.1.1 | 6/26 Sounds | Webhook: ntfy | Theme: Voice [MUTED 23m] feat/audio-v5 API Quota: 78% Context: 65% /compact ``` diff --git a/bin/audio-hooks.py b/bin/audio-hooks.py index 51973de..0f057e6 100644 --- a/bin/audio-hooks.py +++ b/bin/audio-hooks.py @@ -132,7 +132,7 @@ def require_project_root() -> int: # Project state — version, install detection, hook catalogue # --------------------------------------------------------------------------- -PROJECT_VERSION = "5.0.3" +PROJECT_VERSION = "5.1.1" # Canonical hook catalogue. Order matches CLAUDE.md and the install scripts. HOOK_CATALOG: List[Dict[str, Any]] = [ diff --git a/config/default_preferences.json b/config/default_preferences.json index 21a9e8d..86edfc4 100644 --- a/config/default_preferences.json +++ b/config/default_preferences.json @@ -1,10 +1,10 @@ { "$schema": "./user_preferences.schema.json", - "_comment": "Claude Code Audio Hooks - Default Configuration Template (v5.0.3)", - "_version": "5.0.3", + "_comment": "Claude Code Audio Hooks - Default Configuration Template (v5.1.1)", + "_version": "5.1.1", "_description": "This file is the default template for user_preferences.json. Plugin installs auto-copy this to ${CLAUDE_PLUGIN_DATA}/user_preferences.json on first read. AI agents should NOT edit this file directly — use 'audio-hooks set ' instead.", - "version": "5.0.3", + "version": "5.1.1", "_comment_audio_theme": "Audio theme: 'default' for voice recordings, 'custom' for non-voice chimes. Change this one line to switch all audio.", "audio_theme": "default", diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index fadf25a..2b719fc 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -207,7 +207,7 @@ Native matcher routing happens at the `settings.json` layer (Claude Code's match Two-line bottom bar registered in `~/.claude/settings.json` via `audio-hooks statusline install`. Reads stdin JSON Claude Code provides (model name, session_id, workspace.git_worktree, rate_limits, context_window) and emits two lines of plain text with ANSI colors: ```text -[Opus] 🔊 Audio Hooks v5.0.3 | 6/26 Sounds | Webhook: ntfy | Theme: Voice +[Opus] 🔊 Audio Hooks v5.1.1 | 6/26 Sounds | Webhook: ntfy | Theme: Voice [MUTED 23m] 🌿 feat/audio-v5 ████░░░░ API Quota: 78% █████░░░ Context: 65% ⚠️ /compact ``` diff --git a/hooks/hook_runner.py b/hooks/hook_runner.py index ea854df..c03aaf4 100644 --- a/hooks/hook_runner.py +++ b/hooks/hook_runner.py @@ -31,7 +31,7 @@ # Version used for auto-sync: when the installed copy in ~/.claude/hooks/ # detects a newer version in the project directory, it self-updates. -HOOK_RUNNER_VERSION = "5.0.3" +HOOK_RUNNER_VERSION = "5.1.1" # ============================================================================= # STRUCTURED LOGGING (NDJSON) diff --git a/plugins/audio-hooks/.claude-plugin/plugin.json b/plugins/audio-hooks/.claude-plugin/plugin.json index 15ce5ad..4a9e93e 100644 --- a/plugins/audio-hooks/.claude-plugin/plugin.json +++ b/plugins/audio-hooks/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "audio-hooks", - "version": "5.0.3", + "version": "5.1.1", "description": "Audio notifications + AI-controlled config for every Claude Code event. 26 hooks, native matcher routing, TTS, webhooks, status line, focus flow, rate-limit alerts.", "author": { "name": "Chan Meng", diff --git a/plugins/audio-hooks/bin/audio-hooks.py b/plugins/audio-hooks/bin/audio-hooks.py index 51973de..0f057e6 100644 --- a/plugins/audio-hooks/bin/audio-hooks.py +++ b/plugins/audio-hooks/bin/audio-hooks.py @@ -132,7 +132,7 @@ def require_project_root() -> int: # Project state — version, install detection, hook catalogue # --------------------------------------------------------------------------- -PROJECT_VERSION = "5.0.3" +PROJECT_VERSION = "5.1.1" # Canonical hook catalogue. Order matches CLAUDE.md and the install scripts. HOOK_CATALOG: List[Dict[str, Any]] = [ diff --git a/plugins/audio-hooks/config/default_preferences.json b/plugins/audio-hooks/config/default_preferences.json index 21a9e8d..86edfc4 100644 --- a/plugins/audio-hooks/config/default_preferences.json +++ b/plugins/audio-hooks/config/default_preferences.json @@ -1,10 +1,10 @@ { "$schema": "./user_preferences.schema.json", - "_comment": "Claude Code Audio Hooks - Default Configuration Template (v5.0.3)", - "_version": "5.0.3", + "_comment": "Claude Code Audio Hooks - Default Configuration Template (v5.1.1)", + "_version": "5.1.1", "_description": "This file is the default template for user_preferences.json. Plugin installs auto-copy this to ${CLAUDE_PLUGIN_DATA}/user_preferences.json on first read. AI agents should NOT edit this file directly — use 'audio-hooks set ' instead.", - "version": "5.0.3", + "version": "5.1.1", "_comment_audio_theme": "Audio theme: 'default' for voice recordings, 'custom' for non-voice chimes. Change this one line to switch all audio.", "audio_theme": "default", diff --git a/plugins/audio-hooks/hooks/hook_runner.py b/plugins/audio-hooks/hooks/hook_runner.py index ea854df..c03aaf4 100644 --- a/plugins/audio-hooks/hooks/hook_runner.py +++ b/plugins/audio-hooks/hooks/hook_runner.py @@ -31,7 +31,7 @@ # Version used for auto-sync: when the installed copy in ~/.claude/hooks/ # detects a newer version in the project directory, it self-updates. -HOOK_RUNNER_VERSION = "5.0.3" +HOOK_RUNNER_VERSION = "5.1.1" # ============================================================================= # STRUCTURED LOGGING (NDJSON) diff --git a/plugins/audio-hooks/skills/audio-hooks/SKILL.md b/plugins/audio-hooks/skills/audio-hooks/SKILL.md index af51cd8..b4377ce 100644 --- a/plugins/audio-hooks/skills/audio-hooks/SKILL.md +++ b/plugins/audio-hooks/skills/audio-hooks/SKILL.md @@ -110,7 +110,7 @@ The status line displays real-time audio-hooks state and context window usage at After installing, the status line updates every 60 seconds and shows two lines: ``` -[Opus] 🔊 Audio Hooks v5.0.3 | 6/26 Sounds | Webhook: off | Theme: Voice +[Opus] 🔊 Audio Hooks v5.1.1 | 6/26 Sounds | Webhook: off | Theme: Voice 🌿 main ████░░░░ API Quota: 60% █████░░░ Context: 65% ⚠️ /compact ```