From 8ef543b9fc4fd81a113b27df72eb2bdb3ded203b Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Thu, 7 May 2026 18:03:06 -0700 Subject: [PATCH] feat(system): S135 maintenance: registry noise fix, doctor provider-hooks check, stop-hook CWD scoping, branch settings cleanup Co-Authored-By: @devpulse --- .claude/hooks/subagent_stop_gate.py | 25 ++++- pyproject.toml | 2 +- src/aipass/aipass/.claude/settings.json | 93 ------------------- src/aipass/aipass/apps/modules/doctor.py | 14 +-- .../aipass/status/.claude/settings.json | 93 ------------------- src/aipass/devpulse/.claude/settings.json | 93 ------------------- .../drone/apps/handlers/registry_handler.py | 5 +- 7 files changed, 34 insertions(+), 291 deletions(-) diff --git a/.claude/hooks/subagent_stop_gate.py b/.claude/hooks/subagent_stop_gate.py index f1d2cbd8..0b52b155 100644 --- a/.claude/hooks/subagent_stop_gate.py +++ b/.claude/hooks/subagent_stop_gate.py @@ -29,18 +29,41 @@ def _find_repo_root() -> Path | None: AIPASS_ROOT = _find_repo_root() +def _get_cwd_branch() -> str | None: + """Detect which branch directory (src/aipass/) the CWD is in.""" + cwd = Path.cwd().resolve() + if AIPASS_ROOT is None: + return None + src = AIPASS_ROOT / "src" / "aipass" + try: + rel = cwd.relative_to(src) + return rel.parts[0] if rel.parts else None + except ValueError: + return None + + def get_modified_py_files() -> list[str]: - """Get Python files modified in the working tree (unstaged + staged).""" + """Get Python files modified in the working tree, scoped to the CWD branch. + + Only returns files inside the current branch's directory (or repo-root files). + This prevents dispatched agents' changes from triggering violations on the + orchestrator or other agents sharing the worktree. + """ if AIPASS_ROOT is None: return [] try: result = subprocess.run( ["git", "diff", "--name-only", "HEAD"], capture_output=True, text=True, timeout=5, cwd=str(AIPASS_ROOT) ) + cwd_branch = _get_cwd_branch() files = [] for line in result.stdout.strip().split("\n"): line = line.strip() if line.endswith(".py") and not line.startswith(".claude/"): + if cwd_branch and line.startswith("src/aipass/"): + file_branch = line.split("/")[2] if len(line.split("/")) > 2 else None + if file_branch and file_branch != cwd_branch: + continue full = AIPASS_ROOT / line if full.exists(): files.append(str(full)) diff --git a/pyproject.toml b/pyproject.toml index 1f54955c..89870fe7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ trinity = [ memory = [ "numpy>=2.0", "chromadb>=1.0", - "sentence-transformers>=2.0", + "fastembed>=0.4", ] seedgo = [] dev = [ diff --git a/src/aipass/aipass/.claude/settings.json b/src/aipass/aipass/.claude/settings.json index 93277189..defb5cde 100644 --- a/src/aipass/aipass/.claude/settings.json +++ b/src/aipass/aipass/.claude/settings.json @@ -1,97 +1,4 @@ { - "hooks": { - "PostToolUse": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/auto_fix_diagnostics.py" - } - ] - } - ], - "PreToolUse": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/pre_edit_gate.py" - } - ] - } - ], - "Stop": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/subagent_stop_gate.py" - } - ] - } - ], - "PreCompact": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/pre_compact.py" - } - ] - } - ], - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "cat .aipass/aipass_global_prompt.md 2>/dev/null || true" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 -c \"from pathlib import Path; p=next((x/'.aipass'/'aipass_local_prompt.md' for x in [Path.cwd(),*Path.cwd().parents] if (x/'.aipass'/'aipass_local_prompt.md').exists()),None); p and print(p.read_text(encoding='utf-8'),end='')\"" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/branch_prompt_loader.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/email_notification.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/identity_injector.py" - } - ] - } - ] - }, "env": { "AIPASS_HOME": "/home/patrick/Projects/AIPass" } diff --git a/src/aipass/aipass/apps/modules/doctor.py b/src/aipass/aipass/apps/modules/doctor.py index 5152a7c8..64455629 100644 --- a/src/aipass/aipass/apps/modules/doctor.py +++ b/src/aipass/aipass/apps/modules/doctor.py @@ -271,17 +271,19 @@ def _check_services(verbose: bool = False) -> List[CheckResult]: logger.warning("[doctor] pytest collect timed out: %s", exc) results.append(CheckResult("pytest collect", GLYPH_WARN, "timed out", "")) - # hooks wired - auto_fix = Path("~/.claude/hooks/auto_fix_diagnostics.py").expanduser() - if auto_fix.exists(): - results.append(CheckResult("hooks", GLYPH_PASS, "auto_fix_diagnostics wired", "")) + # hooks wired — check provider-level enforcement hooks + provider_hooks_dir = Path("~/.claude/hooks").expanduser() + provider_hooks = ["auto_fix_diagnostics.py", "pre_edit_gate.py", "subagent_stop_gate.py"] + missing = [h for h in provider_hooks if not (provider_hooks_dir / h).exists()] + if not missing: + results.append(CheckResult("hooks", GLYPH_PASS, f"{len(provider_hooks)} provider hooks wired", "")) else: results.append( CheckResult( "hooks", GLYPH_WARN, - "auto_fix_diagnostics.py not found", - "Run setup to wire Claude Code hooks", + f"{len(missing)} provider hook(s) missing: {', '.join(missing)}", + "Copy from .claude/hooks/ to ~/.claude/hooks/ — see .claude/hooks/README.md", ) ) diff --git a/src/aipass/aipass/status/.claude/settings.json b/src/aipass/aipass/status/.claude/settings.json index 93277189..defb5cde 100644 --- a/src/aipass/aipass/status/.claude/settings.json +++ b/src/aipass/aipass/status/.claude/settings.json @@ -1,97 +1,4 @@ { - "hooks": { - "PostToolUse": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/auto_fix_diagnostics.py" - } - ] - } - ], - "PreToolUse": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/pre_edit_gate.py" - } - ] - } - ], - "Stop": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/subagent_stop_gate.py" - } - ] - } - ], - "PreCompact": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/pre_compact.py" - } - ] - } - ], - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "cat .aipass/aipass_global_prompt.md 2>/dev/null || true" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 -c \"from pathlib import Path; p=next((x/'.aipass'/'aipass_local_prompt.md' for x in [Path.cwd(),*Path.cwd().parents] if (x/'.aipass'/'aipass_local_prompt.md').exists()),None); p and print(p.read_text(encoding='utf-8'),end='')\"" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/branch_prompt_loader.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/email_notification.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/identity_injector.py" - } - ] - } - ] - }, "env": { "AIPASS_HOME": "/home/patrick/Projects/AIPass" } diff --git a/src/aipass/devpulse/.claude/settings.json b/src/aipass/devpulse/.claude/settings.json index 93277189..defb5cde 100644 --- a/src/aipass/devpulse/.claude/settings.json +++ b/src/aipass/devpulse/.claude/settings.json @@ -1,97 +1,4 @@ { - "hooks": { - "PostToolUse": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/auto_fix_diagnostics.py" - } - ] - } - ], - "PreToolUse": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/pre_edit_gate.py" - } - ] - } - ], - "Stop": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/subagent_stop_gate.py" - } - ] - } - ], - "PreCompact": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/pre_compact.py" - } - ] - } - ], - "UserPromptSubmit": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "cat .aipass/aipass_global_prompt.md 2>/dev/null || true" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 -c \"from pathlib import Path; p=next((x/'.aipass'/'aipass_local_prompt.md' for x in [Path.cwd(),*Path.cwd().parents] if (x/'.aipass'/'aipass_local_prompt.md').exists()),None); p and print(p.read_text(encoding='utf-8'),end='')\"" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/branch_prompt_loader.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/email_notification.py" - } - ] - }, - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "python3 .claude/hooks/identity_injector.py" - } - ] - } - ] - }, "env": { "AIPASS_HOME": "/home/patrick/Projects/AIPass" } diff --git a/src/aipass/drone/apps/handlers/registry_handler.py b/src/aipass/drone/apps/handlers/registry_handler.py index c1116cc3..382e59b9 100644 --- a/src/aipass/drone/apps/handlers/registry_handler.py +++ b/src/aipass/drone/apps/handlers/registry_handler.py @@ -128,10 +128,7 @@ def find_registry() -> Path: if hit is not None: if _registry_matches_credential(hit): return hit - logger.warning( - "Skipping mismatched registry at %s — continuing walk-up", - hit, - ) + continue # AIPASS_HOME fallback — for external projects where CWD walk finds nothing aipass_home = os.environ.get("AIPASS_HOME")