Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Gradata/src/gradata/hooks/adapters/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,34 @@ def hook_command(brain_dir: Path) -> str:
)


def auto_correct_command(brain_dir: Path) -> str:
return (
f"BRAIN_DIR={shlex.quote(str(brain_dir))} "
f"{shlex.quote(sys.executable)} -m gradata.hooks.auto_correct"
)


def session_close_command(brain_dir: Path) -> str:
return (
f"BRAIN_DIR={shlex.quote(str(brain_dir))} "
f"{shlex.quote(sys.executable)} -m gradata.hooks.session_close"
)


def pre_compact_command(brain_dir: Path) -> str:
return (
f"BRAIN_DIR={shlex.quote(str(brain_dir))} "
f"{shlex.quote(sys.executable)} -m gradata.hooks.pre_compact"
)


def context_inject_command(brain_dir: Path) -> str:
return (
f"BRAIN_DIR={shlex.quote(str(brain_dir))} "
f"{shlex.quote(sys.executable)} -m gradata.hooks.context_inject"
)


def mcp_command(brain_dir: Path) -> list[str]:
return [sys.executable, "-m", "gradata.mcp_server", "--brain-dir", str(brain_dir)]

Expand Down
92 changes: 78 additions & 14 deletions Gradata/src/gradata/hooks/adapters/claude_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
WRITE_TOOL_ALIASES,
InstallResult,
_normalize_tool_name,
auto_correct_command,
context_inject_command,
extract_from_edit_args,
extract_from_write_args,
failure,
hook_command,
hook_signature,
pre_compact_command,
session_close_command,
read_json,
write_json,
)
Expand Down Expand Up @@ -58,24 +62,84 @@ def install(brain_dir: Path, agent_config_path: Path) -> InstallResult:
data = read_json(agent_config_path)
hooks = data.setdefault("hooks", {})
pre_tool = hooks.setdefault("PreToolUse", [])
if any(sig in str(item) for item in pre_tool):
post_tool = hooks.setdefault("PostToolUse", [])
stop = hooks.setdefault("Stop", [])
pre_compact = hooks.setdefault("PreCompact", [])
user_prompt = hooks.setdefault("UserPromptSubmit", [])
has_pre_tool = any(sig in str(item) for item in pre_tool)
has_post_tool = any(sig in str(item) for item in post_tool)
has_stop = any(sig in str(item) for item in stop)
has_pre_compact = any(sig in str(item) for item in pre_compact)
has_user_prompt = any(sig in str(item) for item in user_prompt)
if has_pre_tool and has_post_tool and has_stop and has_pre_compact and has_user_prompt:
return InstallResult(
AGENT, agent_config_path, "already_present", "hook already present"
)
pre_tool.append(
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": hook_command(brain_dir),
"id": sig,
}
],
}
)
if not has_pre_tool:
pre_tool.append(
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": hook_command(brain_dir),
"id": sig,
}
],
}
)
if not has_post_tool:
post_tool.append(
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": auto_correct_command(brain_dir),
"id": sig,
}
],
}
)
if not has_stop:
stop.append(
{
"hooks": [
{
"type": "command",
"command": session_close_command(brain_dir),
"id": sig,
}
],
}
)
if not has_pre_compact:
pre_compact.append(
{
"matcher": "manual|auto",
"hooks": [
{
"type": "command",
"command": pre_compact_command(brain_dir),
"id": sig,
}
],
}
)
if not has_user_prompt:
user_prompt.append(
{
"hooks": [
{
"type": "command",
"command": context_inject_command(brain_dir),
"id": sig,
}
],
}
)
write_json(agent_config_path, data)
return InstallResult(AGENT, agent_config_path, "added", "installed PreToolUse hook")
return InstallResult(AGENT, agent_config_path, "added", "installed Claude Code hooks")
except Exception as exc:
return failure(AGENT, agent_config_path, exc)

Expand Down
62 changes: 62 additions & 0 deletions Gradata/tests/snapshots/install_claude_code_settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"command": "BRAIN_DIR=__BRAIN_DIR__ /usr/bin/python3 -m gradata.hooks.auto_correct",
"id": "gradata:claude-code:__BRAIN_DIR__",
"type": "command"
}
],
"matcher": "Edit|Write"
}
],
"PreCompact": [
{
"hooks": [
{
"command": "BRAIN_DIR=__BRAIN_DIR__ /usr/bin/python3 -m gradata.hooks.pre_compact",
"id": "gradata:claude-code:__BRAIN_DIR__",
"type": "command"
}
],
"matcher": "manual|auto"
}
],
"PreToolUse": [
{
"hooks": [
{
"command": "BRAIN_DIR=__BRAIN_DIR__ /usr/bin/python3 -m gradata.hooks.inject_brain_rules",
"id": "gradata:claude-code:__BRAIN_DIR__",
"type": "command"
}
],
"matcher": "*"
}
],
"Stop": [
{
"hooks": [
{
"command": "BRAIN_DIR=__BRAIN_DIR__ /usr/bin/python3 -m gradata.hooks.session_close",
"id": "gradata:claude-code:__BRAIN_DIR__",
"type": "command"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"command": "BRAIN_DIR=__BRAIN_DIR__ /usr/bin/python3 -m gradata.hooks.context_inject",
"id": "gradata:claude-code:__BRAIN_DIR__",
"type": "command"
}
]
}
]
}
}
Loading
Loading