feat(system): feat(hooks): DPLAN-0167 hook testing framework + bootstrap fix — 20-test harness with direct+integration layers, hook execution logger, CWD guard verification across projects, setup.sh provider wiring update (global_prompt_loader + env vars + git deny rules), bootstrap.py removes dead PreToolUse/PostToolUse from project settings, hook READMEs at provider+project level

Co-Authored-By: @devpulse <devpulse@aipass>
This commit is contained in:
AIOSAI
2026-05-07 12:58:47 -07:00
co-authored by @devpulse
parent 8da48ddd46
commit b971d2161e
29 changed files with 1584 additions and 174 deletions
+51 -8
View File
@@ -536,7 +536,7 @@ else:
# Build hooks config with absolute paths
settings["hooks"] = {
"UserPromptSubmit": [
{"hooks": [{"type": "command", "command": f"cat {repo_root}/.aipass/aipass_global_prompt.md 2>/dev/null || true"}]},
{"hooks": [{"type": "command", "command": f"{hook_python} {hooks_dir}/global_prompt_loader.py"}]},
{"hooks": [{"type": "command", "command": f"{hook_python} {hooks_dir}/branch_prompt_loader.py"}]},
{"hooks": [{"type": "command", "command": f"{hook_python} {hooks_dir}/identity_injector.py"}]},
{"hooks": [{"type": "command", "command": f"{hook_python} {hooks_dir}/email_notification.py"}]},
@@ -574,6 +574,8 @@ settings["hooks"] = {
import os
env_block = settings.get("env", {})
env_block["AIPASS_HOME"] = repo_root
env_block["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] = "1"
env_block["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"] = "1"
# Windows: force UTF-8 for Rich output in hook processes
msys = os.environ.get("MSYSTEM", "") + os.environ.get("OSTYPE", "")
if "MSYS" in msys or "msys" in msys or "MINGW" in msys:
@@ -585,17 +587,58 @@ permissions = settings.get("permissions", {})
deny = permissions.get("deny", [])
secrets_deny = [
"Read(~/.secrets/**)",
"Read(/home/*/.secrets/**)",
"Bash(cat *~/.secrets*)",
"Bash(less *~/.secrets*)",
"Bash(head *~/.secrets*)",
"Bash(tail *~/.secrets*)",
"Bash(*~/.secrets*)",
f"Read({os.path.expanduser('~')}/.secrets/**)",
"Bash(cat ~/.secrets/*)",
f"Bash(cat {os.path.expanduser('~')}/.secrets/*)",
"Bash(head ~/.secrets/*)",
f"Bash(head {os.path.expanduser('~')}/.secrets/*)",
"Bash(tail ~/.secrets/*)",
f"Bash(tail {os.path.expanduser('~')}/.secrets/*)",
"Bash(less ~/.secrets/*)",
f"Bash(less {os.path.expanduser('~')}/.secrets/*)",
]
for rule in secrets_deny:
git_deny = [
"Bash(git reset --hard*)",
"Bash(git push --force*)",
"Bash(git push -f *)",
"Bash(git rebase*)",
"Bash(git clean*)",
"Bash(rm -rf*)",
"Bash(git reset*)",
"Bash(git merge*)",
"Bash(git config*)",
"Bash(git checkout -- *)",
"Bash(git checkout .*)",
"Bash(git restore --staged*)",
"Bash(git restore .*)",
"Bash(git branch -D*)",
"Bash(git stash drop*)",
"Bash(git stash clear*)",
"Bash(rm -r *)",
"Bash(git checkout -b*)",
"Bash(git switch -c*)",
"Bash(git switch --create*)",
"Bash(git commit*)",
"Bash(git push*)",
]
for rule in secrets_deny + git_deny:
if rule not in deny:
deny.append(rule)
permissions["deny"] = deny
ask = permissions.get("ask", [])
home = os.path.expanduser("~")
ask_rules = [
f"Edit({home}/.claude/**)",
f"Write({home}/.claude/**)",
"Edit(~/.claude/**)",
"Write(~/.claude/**)",
]
for rule in ask_rules:
if rule not in ask:
ask.append(rule)
permissions["ask"] = ask
settings["permissions"] = permissions
settings_path.write_text(json.dumps(settings, indent=2) + "\n")