feat: Sunday release prep — hooks.json tracked, CHANGELOG.md, prax fix, gitignore cleanup

This commit is contained in:
AIOSAI
2026-05-22 16:52:03 -07:00
parent abf929fc1c
commit 40eb295e41
5 changed files with 209 additions and 1 deletions
+1
View File
@@ -1,4 +1,5 @@
*
!aipass_global_prompt.md
!hooks.json
!.gitignore
#Do not add other exceptions here without careful consideration. Developer permissions0ns needed.
+104
View File
@@ -0,0 +1,104 @@
{
"_comment": "Per-project hook configuration for the AIPass hook engine (DPLAN-0184)",
"hooks_enabled": true,
"UserPromptSubmit": {
"identity_injector": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.prompt.identity.handle",
"matcher": ""
},
"email_notification": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.notification.email.handle",
"matcher": ""
},
"branch_prompt": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.prompt.branch_loader.handle",
"matcher": ""
},
"global_prompt": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.prompt.global_loader.handle",
"matcher": ""
}
},
"PreToolUse": {
"tool_use_sound": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.notification.tool_sound.handle",
"matcher": "Bash|Edit|MultiEdit|Write|Read|Grep|Glob|WebSearch|WebFetch|Task"
},
"pre_edit_gate": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.security.edit_gate.handle",
"matcher": "Edit|MultiEdit|Write|NotebookEdit"
},
"git_gate": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.security.git_gate.handle",
"matcher": "Bash|Edit|MultiEdit|Write|NotebookEdit"
},
"engine_test_sound": {
"enabled": false,
"command": "python3 $AIPASS_HOME/.claude/hooks/engine_test_sound.py",
"matcher": "WebSearch"
}
},
"PostToolUse": {
"auto_fix_diagnostics": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.lifecycle.auto_fix.handle",
"matcher": "Edit|MultiEdit|Write|NotebookEdit",
"timeout": 45
},
"auto_watchdog": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.lifecycle.auto_watchdog.handle",
"matcher": "Bash"
}
},
"SubagentStop": {
"subagent_stop_gate": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.security.subagent_gate.handle",
"matcher": "",
"timeout": 60
}
},
"Stop": {
"stop_sound": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.notification.stop_sound.handle",
"matcher": ""
}
},
"Notification": {
"notification_sound": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.notification.announce.handle",
"matcher": ""
}
},
"PreCompact": {
"pre_compact": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.lifecycle.compact.handle",
"matcher": "",
"timeout": 60
},
"pre_compact_rollover": {
"enabled": true,
"handler": "aipass.hooks.apps.handlers.lifecycle.rollover.handle",
"matcher": "",
"timeout": 120
}
}
}
+3
View File
@@ -111,6 +111,9 @@ src/aipass/*/apps/integrations/**
!src/aipass/spawn/templates/builder/DASHBOARD.local.json
!src/aipass/spawn/templates/builder/STATUS.local.md
# CI artifacts
windows-pytest-results/
# Parked / one-off
HERALD.md
backup_data/
+92
View File
@@ -0,0 +1,92 @@
# Changelog
All notable changes to AIPass will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project uses [Calendar Versioning](https://calver.org/) in the format
`YYYY.WNN` (year and ISO week number).
---
## [2026.W21] - 2026-05-25
First weekly release. AIPass now follows a Sunday release cadence: changes
accumulate on `dev` throughout the week and merge to `main` as a single
versioned release with notes.
### Added
- **Hook engine** — a new centralized dispatch system for all hook
execution. A thin bridge receives events from the AI provider (Claude,
Codex, etc.) and routes them through a single Python engine that reads
per-project configuration, executes the appropriate handlers, and logs
every invocation. Replaces 14 standalone shell/Python scripts with native
handler modules organized by domain: prompt injection, security
enforcement, lifecycle management, and notifications.
- **Per-project hook configuration** via `.aipass/hooks.json`. Each project
can enable, disable, or customize individual hooks without touching
provider-level settings. Previously hooks fired globally with no
per-project control.
- **Audio feedback on hook events** using Piper TTS. Tool usage, response
completion, notifications, email alerts, and sub-agent activity each
produce distinct spoken audio cues so operators can monitor sessions
without watching the terminal.
- **Hooks agent** — the 13th citizen in the AIPass registry, owning all
hook infrastructure: the engine, bridge, handlers, and configuration
schema.
- **Dashboard plugin for devpulse** — aggregates git status, session
history, and dispatch state into a single startup view. Wired into the
session startup protocol so branch managers see current state
immediately.
- **External log routing** — prax now accepts structured log entries from
any branch, not just its own modules. Hook executions, dispatches, and
agent activity all flow into the central monitoring log.
### Changed
- **Provider settings fully migrated to bridge pattern.** All hook entries
in the Claude provider configuration now call the bridge dispatcher
instead of individual scripts. Each hook produces its own system-reminder
to the model, preserving prompt injection fidelity (a single merged
bridge was found to break prompt delivery due to Claude Code's output
persistence threshold).
- **setup.sh rewritten** to install hooks via the bridge pattern. The old
version hardcoded 14 script paths; the new version writes a single bridge
call per event type and validates that the bridge module exists.
- **Documentation sweep** across `.claude/README.md`, `SECURITY.md`, the
global prompt, and branch-level docs to reflect the new hook
architecture. References to legacy `.claude/hooks/` scripts replaced with
the native handler locations.
- **`aipass init update`** now correctly preserves user-customized hook
settings during project updates instead of overwriting them.
- **Seedgo snapshot tests rebuilt** — the provider hooks snapshot fixture
and extraction logic were structurally broken (silently passing with zero
results). Both the fixture format and the test assertions have been
corrected.
- **Test suite updated for hook migration** — `test_git_gate.py` imports
from the new handler module; `test_bootstrap.py` no longer asserts that
project initialization ships standalone hook scripts (it no longer does).
### Fixed
- **Settings merge on project update** — `aipass init update` was
clobbering user hook configurations. The merge logic now layers AIPass
defaults under existing user settings.
- **Python 3.10 test collision** — a module/function name collision caused
mock patch targets to fail on Python 3.10. Test targets corrected.
- **Dead code removal** — removed an unused CLI `__main__.py` entrypoint
and cleaned up `.gitignore` entries that were masking tracked files.
- **Codecov patch threshold** lowered to 50% to reflect the project's
current coverage baseline and stop false-negative CI failures.
### Removed
- **18 standalone hook scripts** in `.claude/hooks/` disabled (renamed with
`(disabled)` suffix). Their logic now lives in native handler modules
under `src/aipass/hooks/apps/handlers/`. The old files remain on disk for
reference but are no longer executed.
---
*This is the first CHANGELOG entry. Prior work is documented in the
repository's commit history and branch session logs.*
@@ -203,16 +203,21 @@ def _calculate_quick_status_standalone(sections: Dict) -> Dict:
"""
ai_mail = sections.get("ai_mail", {})
flow = sections.get("flow", {})
commons = sections.get("commons_activity", {})
new_mail_raw = ai_mail.get("new", ai_mail.get("unread", 0))
opened_raw = ai_mail.get("opened", 0)
active_plans_raw = flow.get("active_plans", 0)
commons_mentions_raw = commons.get("mentions", 0)
new_mail = len(new_mail_raw) if isinstance(new_mail_raw, list) else int(new_mail_raw or 0)
opened_mail = len(opened_raw) if isinstance(opened_raw, list) else int(opened_raw or 0)
active_plans = len(active_plans_raw) if isinstance(active_plans_raw, list) else int(active_plans_raw or 0)
commons_mentions = (
len(commons_mentions_raw) if isinstance(commons_mentions_raw, list) else int(commons_mentions_raw or 0)
)
action_required = new_mail > 0 or active_plans > 0
action_required = new_mail > 0 or active_plans > 0 or commons_mentions > 0
parts = []
if new_mail > 0:
@@ -221,11 +226,14 @@ def _calculate_quick_status_standalone(sections: Dict) -> Dict:
parts.append(f"{opened_mail} opened")
if active_plans > 0:
parts.append(f"{active_plans} active plans")
if commons_mentions > 0:
parts.append(f"{commons_mentions} mentions")
return {
"new_mail": new_mail,
"opened_mail": opened_mail,
"active_plans": active_plans,
"commons_mentions": commons_mentions,
"action_required": action_required,
"summary": ", ".join(parts) if parts else "All clear",
}