80 lines
2.2 KiB
Python
80 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""SessionStart hook — verify AirContext/ presence and surface a friendly notice.
|
|
|
|
Stage-2 implementation. For now we:
|
|
- look for AirContext/ in cwd
|
|
- if absent or invalid, set state.config_missing=true so UserPromptSubmit can block
|
|
- emit a SessionStart additionalContext message describing AirContext status
|
|
"""
|
|
from __future__ import annotations
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
_HERE = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(_HERE))
|
|
|
|
from core.config_loader import validate_config # noqa: E402
|
|
from core.state import StateFile # noqa: E402
|
|
|
|
|
|
def _find_project_root(stdin_payload: dict) -> Path:
|
|
cwd = stdin_payload.get("cwd") or os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()
|
|
return Path(cwd)
|
|
|
|
|
|
def main() -> int:
|
|
raw = sys.stdin.read() or "{}"
|
|
try:
|
|
payload = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
payload = {}
|
|
|
|
project = _find_project_root(payload)
|
|
air = project / "AirContext"
|
|
|
|
# Project hasn't opted in — stay completely silent so AirContext doesn't
|
|
# pollute every Claude Code session globally.
|
|
if not air.exists():
|
|
return 0
|
|
|
|
config = air / "config.yaml"
|
|
state = StateFile(air / "state.json")
|
|
|
|
sid = payload.get("session_id")
|
|
if sid:
|
|
state.update(last_session_id=sid)
|
|
|
|
if not config.exists():
|
|
state.update(config_missing=True)
|
|
print(json.dumps({
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "SessionStart",
|
|
"additionalContext": (
|
|
"AirContext: AirContext/ exists but config.yaml is missing. "
|
|
"Run `/aircontext-init` to (re)install templates."
|
|
)
|
|
}
|
|
}))
|
|
return 0
|
|
|
|
err = validate_config(config)
|
|
if err:
|
|
state.update(config_missing=True)
|
|
print(json.dumps({
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "SessionStart",
|
|
"additionalContext": f"AirContext config invalid: {err}"
|
|
}
|
|
}))
|
|
return 0
|
|
|
|
state.update(config_missing=False)
|
|
# Healthy — stay silent to keep prompt clean.
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|