From 18dea21726e8a19d39aa69b6c7a001938da42929 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Sun, 5 Jul 2026 17:15:38 -0700 Subject: [PATCH 1/4] setup.sh: merge user hooks instead of overwriting (DPLAN-0234 Strand C fix) - AIPass bridge entries (bridges/claude.py marker) refreshed on every install; user-wired hooks and custom events preserved - Fixture-verified: customs survive, stale bridge entries replaced no-dupe, fresh-install output shape-identical - Context: full 17/17 hook fire-test passed on fresh Linux Docker install --- CHANGELOG.md | 14 ++++++++++++++ setup.sh | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 658d640e..a8e98d8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ PyPI version — not the changelog header. ## [2026-07-05] +### Fixed + +- **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). + ### Added - **`./aipass` — repo-root cold-clone launcher (DPLAN-0234 Strand B).** The diff --git a/setup.sh b/setup.sh index d58a5267..b288bf19 100755 --- a/setup.sh +++ b/setup.sh @@ -657,7 +657,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 +693,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 From 6200e015228d001f83b791e20147f8cc4335f2e4 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Sun, 5 Jul 2026 17:36:19 -0700 Subject: [PATCH 2/4] =?UTF-8?q?setup.sh:=20OS-aware=20hook=20bridge=20?= =?UTF-8?q?=E2=80=94=20Windows=20venv=20python=20is=20Scripts/python.exe?= =?UTF-8?q?=20(DPLAN-0234=20Strand=20C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bash passes IS_WINDOWS into the hook-install heredoc; bridge string picks .venv/Scripts/python.exe vs .venv/bin/python3 - @hooks assessment: $AIPASS_HOME expansion fine (CC runs hooks via Git Bash on Windows), bridge has zero POSIX assumptions — interpreter path was the only gap - Verified both OS modes + merge-marker/custom-hook regression --- CHANGELOG.md | 12 ++++++++++++ setup.sh | 11 ++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8e98d8d..ac68791b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,18 @@ PyPI version — not the changelog header. 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 diff --git a/setup.sh b/setup.sh index b288bf19..a4a90d93 100755 --- a/setup.sh +++ b/setup.sh @@ -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(): From cccfe5fb3ab6712b3c215069af2e1b09628e57bb Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Sun, 5 Jul 2026 21:43:43 -0700 Subject: [PATCH 3/4] docs: README CLI-support + CONTRIBUTING reflect ./aipass install flow - README: setup.sh mention tied to the ./aipass install command + notes hook merge-not-overwrite - CONTRIBUTING: contributor bootstrap is ./aipass install --no-init (no first-project scaffold in the engine repo) --- CONTRIBUTING.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ccf041b3..0739712d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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? diff --git a/README.md b/README.md index e29f8694..a95b2cfb 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,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. --- From ce1a138cdfbb4799bdef0b7c6dcd674b74185ab3 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Sun, 5 Jul 2026 23:02:17 -0700 Subject: [PATCH 4/4] =?UTF-8?q?README:=20restore=20HVTrust=20badge=20?= =?UTF-8?q?=E2=80=94=20hvtracker=20issue=20#109=20fixed=20upstream?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++++ README.md | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac68791b..b2b3b76a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ PyPI version — not the changelog header. ### 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 diff --git a/README.md b/README.md index a95b2cfb..13a07998 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ [![codecov](https://codecov.io/gh/AIOSAI/AIPass/graph/badge.svg)](https://codecov.io/gh/AIOSAI/AIPass) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/AIOSAI/AIPass/badge)](https://scorecard.dev/viewer/?uri=github.com/AIOSAI/AIPass) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13095/badge)](https://www.bestpractices.dev/projects/13095) - +[![HVTrust](https://hvtracker.net/badge/aipass.svg)](https://hvtracker.net/agents/aipass)

AIPass