Merge pull request #658 from AIOSAI/dev
setup.sh merges user hooks instead of overwriting (DPLAN-0234 Strand C). AIPass bridge entries are refreshed on every install (bridges/claude.py marker); user-wired hooks and custom events now survive install/re-run. Fixture-verified: customs preserved, stale bridge entries replaced without duplicates, fresh-install output shape-identical (7 events, 6+6 entries). Background: full hook fire-test on fresh Linux Docker install passed 17/17.
This commit is contained in:
@@ -11,6 +11,37 @@ PyPI version — not the changelog header.
|
||||
|
||||
## [2026-07-05]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **HVTrust badge restored in the root README.** hvtracker corrected the
|
||||
methodology v4.1 grade-computation bug (issue #109); the badge shows the
|
||||
right grade again, so the temporary comment-out from earlier today is
|
||||
reverted.
|
||||
|
||||
- **Installer no longer destroys a user's custom Claude Code hooks (DPLAN-0234
|
||||
Strand C).** setup.sh used to write `settings["hooks"]` wholesale — anyone
|
||||
with their own hooks in `~/.claude/settings.json` lost them on install or
|
||||
re-run. Now it merges: every AIPass bridge entry (identified by the
|
||||
`bridges/claude.py` marker) is refreshed, while user-wired hooks and custom
|
||||
events are preserved. Verified against fixtures: custom hooks survive, stale
|
||||
AIPass entries are replaced without duplicates, and the fresh-install output
|
||||
is shape-identical to before (7 events, 6 UserPromptSubmit + 6 PreCompact
|
||||
entries). Found while fire-testing a fresh install's hooks in Docker — all
|
||||
17 wired hook entries pass on a cold Linux clone (real kernel/navmap/branch
|
||||
prompt bytes, git gate blocks, clean no-ops on empty state).
|
||||
|
||||
- **Windows fresh installs get a working hook bridge.** setup.sh wrote the
|
||||
Claude bridge command with `.venv/bin/python3` on every OS — but Windows
|
||||
venvs put the interpreter at `.venv/Scripts/python.exe` and have no `bin/`,
|
||||
so hooks on a fresh Windows install pointed at a nonexistent python and
|
||||
would never fire. The bridge string is now OS-aware (bash passes
|
||||
`IS_WINDOWS` into the hook-install step). @hooks assessed the rest of the
|
||||
chain: `$AIPASS_HOME` expansion works on Windows because Claude Code runs
|
||||
hooks via Git Bash (which must exist for setup.sh to have run), and the
|
||||
bridge itself has zero POSIX assumptions — the interpreter path was the
|
||||
only gap. Verified: both OS modes produce the right bridge string, merge
|
||||
marker unchanged, custom-hook preservation intact. (assessed by @hooks)
|
||||
|
||||
### Added
|
||||
|
||||
- **`./aipass` — repo-root cold-clone launcher (DPLAN-0234 Strand B).** The
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ PRs are welcome after an issue has been discussed. Keep changes focused -- one f
|
||||
- Python 3.10+
|
||||
- Tests: `pytest` (4,900+ tests across the project)
|
||||
- Quality: AIPass uses its own standards system ([seedgo](src/aipass/seedgo/README.md)) for automated audits
|
||||
- Run `./setup.sh` to bootstrap the full environment
|
||||
- Run `./aipass install --no-init` to bootstrap the full environment (contributors work in the engine repo itself — no first-project scaffold needed)
|
||||
|
||||
## Questions?
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
[](https://codecov.io/gh/AIOSAI/AIPass)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/AIOSAI/AIPass)
|
||||
[](https://www.bestpractices.dev/projects/13095)
|
||||
<!-- HVTrust badge temporarily hidden 2026-07-05: hvtracker methodology v4.1 recalibration bug is showing Grade D/~10 (dimensions on the detail page sum to ~78); correction filed as hvtracker issue #109. Restore when resolved. Was:
|
||||
[](https://hvtracker.net/agents/aipass) -->
|
||||
[](https://hvtracker.net/agents/aipass)
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/logo.png" alt="AIPass" width="400" />
|
||||
@@ -232,7 +231,7 @@ AIPass is built and tested with **Claude Code** on Linux/WSL.
|
||||
| [Claude Code](https://code.claude.com/docs) | `claude -p "prompt" --permission-mode bypassPermissions` | Fully tested |
|
||||
| [Codex](https://github.com/openai/codex) | `codex exec "prompt" --dangerously-bypass-approvals-and-sandbox` | Experimental |
|
||||
|
||||
setup.sh auto-detects which CLIs are installed and configures hooks for each.
|
||||
The installer (`./aipass install`, powered by setup.sh) auto-detects which CLIs are installed and configures hooks for each — merging with any hooks you've already wired, never overwriting them.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -633,7 +633,7 @@ if [ -f "$SCRIPT_DIR/src/aipass/hooks/apps/handlers/bridges/claude.py" ]; then
|
||||
echo "Installing Claude Code hooks ..."
|
||||
mkdir -p "$HOME/.claude"
|
||||
|
||||
"$PYTHON" - "$SCRIPT_DIR" "$CLAUDE_SETTINGS" << 'PYEOF'
|
||||
"$PYTHON" - "$SCRIPT_DIR" "$CLAUDE_SETTINGS" "$IS_WINDOWS" << 'PYEOF'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
@@ -641,11 +641,16 @@ from pathlib import Path
|
||||
|
||||
repo_root = sys.argv[1]
|
||||
settings_path = Path(sys.argv[2])
|
||||
is_windows = len(sys.argv) > 3 and sys.argv[3] == "1"
|
||||
|
||||
# Bridge entry point — all hooks route through the engine via this bridge.
|
||||
# Uses $AIPASS_HOME env var (injected into settings.env below) so the
|
||||
# settings file stays relocatable.
|
||||
bridge = "$AIPASS_HOME/.venv/bin/python3 $AIPASS_HOME/src/aipass/hooks/apps/handlers/bridges/claude.py"
|
||||
# settings file stays relocatable. CC on Windows runs hooks via Git Bash
|
||||
# (which must exist for setup.sh to have run), so $VAR expansion works —
|
||||
# but the venv interpreter lives at Scripts/python.exe there, not bin/python3
|
||||
# (@hooks assessment, DPLAN-0234 Strand C).
|
||||
venv_python = ".venv/Scripts/python.exe" if is_windows else ".venv/bin/python3"
|
||||
bridge = f"$AIPASS_HOME/{venv_python} $AIPASS_HOME/src/aipass/hooks/apps/handlers/bridges/claude.py"
|
||||
|
||||
# Load existing settings or start fresh
|
||||
if settings_path.exists():
|
||||
@@ -657,7 +662,7 @@ else:
|
||||
# UserPromptSubmit: 5 separate entries (EventType:hook_name) to avoid output merging
|
||||
# PreToolUse, PostToolUse, SubagentStop, Stop, Notification: single aggregate entries
|
||||
# PreCompact: 3 hooks x 2 matchers (manual + auto) = 6 entries
|
||||
settings["hooks"] = {
|
||||
aipass_hooks = {
|
||||
"UserPromptSubmit": [
|
||||
{"hooks": [{"type": "command", "command": f"{bridge} UserPromptSubmit:tier0_kernel"}]},
|
||||
{"hooks": [{"type": "command", "command": f"{bridge} UserPromptSubmit:navmap"}]},
|
||||
@@ -693,6 +698,19 @@ settings["hooks"] = {
|
||||
],
|
||||
}
|
||||
|
||||
# Merge, don't replace (DPLAN-0234 Strand C): refresh every AIPass bridge entry
|
||||
# (identified by the bridges/claude.py marker) but preserve any hooks the user
|
||||
# wired themselves. Re-runs stay idempotent; custom hooks survive reinstall.
|
||||
existing_hooks = settings.get("hooks", {})
|
||||
merged_hooks = {}
|
||||
for event in set(existing_hooks) | set(aipass_hooks):
|
||||
user_entries = [
|
||||
entry for entry in existing_hooks.get(event, [])
|
||||
if "bridges/claude.py" not in json.dumps(entry)
|
||||
]
|
||||
merged_hooks[event] = aipass_hooks.get(event, []) + user_entries
|
||||
settings["hooks"] = merged_hooks
|
||||
|
||||
# Inject AIPASS_HOME into env block so dispatched agents find AIPass
|
||||
env_block = settings.get("env", {})
|
||||
env_block["AIPASS_HOME"] = repo_root
|
||||
|
||||
Reference in New Issue
Block a user