feat(system): S90: local prompt hook injection + prax trigger dead code removal (Vera BUG-4 fix)
Co-Authored-By: @devpulse <devpulse@aipass>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
**Purpose:** Display and output formatting service for AIPass modules. Provides consistent terminal output — headers, success/error/warning messages, section breaks, and operation templates — so every module looks the same without duplicating Rich formatting code.
|
||||
**Module:** `aipass.cli`
|
||||
**Seedgo:** 100%
|
||||
**Tests:** 151 passing (6 files, 5/5 modules covered)
|
||||
**Tests:** 161 passing (6 files, 5/5 modules covered)
|
||||
**Last Updated:** 2026-04-11
|
||||
|
||||
## Usage
|
||||
@@ -66,7 +66,7 @@ cli/
|
||||
├── cli_json/ # Auto-created JSON output (three-file pattern)
|
||||
├── dropbox/ # Inbound file drop
|
||||
├── logs/ # Branch-level logs
|
||||
├── tests/ # 151 tests across 6 files
|
||||
├── tests/ # 161 tests across 6 files
|
||||
│ ├── test_bootstrap.py # bootstrap.py handler tests
|
||||
│ ├── test_json_handler.py # json_handler tests
|
||||
│ ├── test_display.py # display module tests
|
||||
|
||||
@@ -412,9 +412,23 @@ def _gitignore() -> str:
|
||||
def _claude_settings(aipass_home: str | None = None) -> str:
|
||||
"""Generate .claude/settings.json — minimal hooks for prompt injection.
|
||||
|
||||
Installs two UserPromptSubmit hooks:
|
||||
1. Global prompt — injects .aipass/aipass_global_prompt.md from CWD.
|
||||
2. Local prompt — walks up from CWD to find .aipass/aipass_local_prompt.md
|
||||
(branch-level prompt, e.g. inside src/<agent>/).
|
||||
|
||||
Args:
|
||||
aipass_home: Optional AIPass installation root to add as env.AIPASS_HOME.
|
||||
"""
|
||||
_local_prompt_cmd = (
|
||||
"dir=$(pwd); "
|
||||
"while [ \"$dir\" != \"/\" ]; do "
|
||||
"if [ -f \"$dir/.aipass/aipass_local_prompt.md\" ]; then "
|
||||
"cat \"$dir/.aipass/aipass_local_prompt.md\"; break; "
|
||||
"fi; "
|
||||
"dir=$(dirname \"$dir\"); "
|
||||
"done"
|
||||
)
|
||||
data: dict = {
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [
|
||||
@@ -426,7 +440,16 @@ def _claude_settings(aipass_home: str | None = None) -> str:
|
||||
"command": "cat .aipass/aipass_global_prompt.md 2>/dev/null || true",
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
"matcher": "",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": _local_prompt_cmd,
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,12 @@ def _handle_init(args: List[str]) -> bool:
|
||||
|
||||
success(f"Created {len(result['created_files'])} files")
|
||||
|
||||
if result.get("aipass_home"):
|
||||
console.print()
|
||||
console.print(f"[bold cyan]AIPASS_HOME:[/bold cyan] [yellow]{result['aipass_home']}[/yellow]")
|
||||
console.print(f"[dim]For terminal usage, add to your shell profile:[/dim]")
|
||||
console.print(f" [green]export AIPASS_HOME={result['aipass_home']}[/green]")
|
||||
|
||||
json_handler.log_operation("aipass_init", {
|
||||
"project_name": result["project_name"],
|
||||
"target": result["target"],
|
||||
@@ -462,7 +468,7 @@ def _print_init_help():
|
||||
console.print("[bold cyan]WHAT IT CREATES:[/bold cyan]")
|
||||
console.print()
|
||||
|
||||
files_text = """[bold]Project scaffold (11 items):[/bold]
|
||||
files_text = """[bold]Project scaffold (12 items):[/bold]
|
||||
|
||||
[green]1.[/green] [yellow]{NAME}_REGISTRY.json[/yellow] Project registry with UUID
|
||||
[green]2.[/green] [yellow].aipass/aipass_global_prompt.md[/yellow] Global prompt (injected every turn)
|
||||
@@ -472,9 +478,10 @@ def _print_init_help():
|
||||
[green]6.[/green] [yellow]README.md[/yellow] Getting started guide
|
||||
[green]7.[/green] [yellow]STATUS.local.md[/yellow] Project status
|
||||
[green]8.[/green] [yellow].gitignore[/yellow] Standard AIPass ignores
|
||||
[green]9.[/green] [yellow].claude/settings.json[/yellow] Claude Code hooks
|
||||
[green]9.[/green] [yellow].claude/settings.json[/yellow] Claude Code hooks + AIPASS_HOME
|
||||
[green]10.[/green] [yellow]hooks/[/yellow] User hooks directory
|
||||
[green]11.[/green] [yellow]src/[/yellow] Agent directories live here"""
|
||||
[green]11.[/green] [yellow]src/[/yellow] Agent directories live here
|
||||
[green]12.[/green] [yellow].ai_mail.local/inbox.json[/yellow] Empty project mailbox"""
|
||||
|
||||
console.print(Panel(files_text, border_style="green", padding=(1, 2), box=box.ROUNDED))
|
||||
console.print()
|
||||
|
||||
@@ -278,6 +278,28 @@ def test_init_project_claude_settings_content(tmp_path):
|
||||
assert "UserPromptSubmit" in data["hooks"]
|
||||
|
||||
|
||||
def test_init_project_settings_has_two_hooks(tmp_path):
|
||||
""".claude/settings.json has both global and local prompt hooks."""
|
||||
target = tmp_path / "proj"
|
||||
target.mkdir()
|
||||
|
||||
init_project(target, project_name="alpha")
|
||||
|
||||
settings_path = target / ".claude" / "settings.json"
|
||||
data = json.loads(settings_path.read_text(encoding="utf-8"))
|
||||
hooks = data["hooks"]["UserPromptSubmit"]
|
||||
assert len(hooks) == 2, f"Expected 2 UserPromptSubmit hooks, got {len(hooks)}"
|
||||
|
||||
# First hook: global prompt
|
||||
global_cmd = hooks[0]["hooks"][0]["command"]
|
||||
assert "aipass_global_prompt.md" in global_cmd
|
||||
|
||||
# Second hook: local prompt walk-up
|
||||
local_cmd = hooks[1]["hooks"][0]["command"]
|
||||
assert "aipass_local_prompt.md" in local_cmd
|
||||
assert "dirname" in local_cmd, "Local prompt hook should walk up with dirname"
|
||||
|
||||
|
||||
def test_init_project_global_prompt_content(tmp_path):
|
||||
"""Global prompt contains project name and AIPass terminology."""
|
||||
target = tmp_path / "proj"
|
||||
|
||||
Reference in New Issue
Block a user