Skip to content
Merged
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
23 changes: 16 additions & 7 deletions src/agent_learner/adapters/hermes.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,15 +296,20 @@ def _render_hooks_block(*, prompt_command: str, auto_command: str) -> str:

def _merge_hooks_section(existing_text: str, *, prompt_command: str, auto_command: str) -> str:
lines = existing_text.splitlines()
desired = {
"pre_llm_call": [f" - command: {prompt_command!r}", " timeout: 15"],
"on_session_end": [f" - command: {auto_command!r}", " timeout: 15"],
desired_commands = {
"pre_llm_call": prompt_command,
"on_session_end": auto_command,
}
if not lines:
return _render_hooks_block(prompt_command=prompt_command, auto_command=auto_command).rstrip("\n")
if len(lines) == 1 and lines[0].strip() == "hooks: {}":
return _render_hooks_block(prompt_command=prompt_command, auto_command=auto_command).rstrip("\n")

def _render_entry(command: str, *, compact: bool) -> list[str]:
if compact:
return [f" - command: {command!r}", " timeout: 15"]
return [f" - command: {command!r}", " timeout: 15"]

merged: list[str] = ["hooks:"]
index = 1
seen_events: set[str] = set()
Expand All @@ -323,9 +328,13 @@ def _merge_hooks_section(existing_text: str, *, prompt_command: str, auto_comman
index += 1
block = lines[start:index]
block_text = "\n".join(block)
addition = desired.get(event)
if addition and addition[0] not in block_text:
block = block + addition
command = desired_commands.get(event)
if command:
compact = any(existing_line.startswith(" - ") for existing_line in block[1:])
command_match = f"command: {command!r}"
command_match_alt = f"command: {command}"
if command_match not in block_text and command_match_alt not in block_text:
block = block + _render_entry(command, compact=compact)
merged.extend(block)
seen_events.add(event)
continue
Expand All @@ -335,7 +344,7 @@ def _merge_hooks_section(existing_text: str, *, prompt_command: str, auto_comman
if event in seen_events:
continue
merged.append(f" {event}:")
merged.extend(desired[event])
merged.extend(_render_entry(desired_commands[event], compact=False))
return "\n".join(merged)


Expand Down
26 changes: 26 additions & 0 deletions tests/test_installers.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,32 @@ def test_install_hermes_user_scope_merges_hooks_into_existing_config_without_dup
assert second_written


def test_install_hermes_user_scope_preserves_compact_yaml_hook_indentation(tmp_path: Path) -> None:
hermes_root = tmp_path / ".hermes"
hermes_root.mkdir(parents=True, exist_ok=True)
config_path = hermes_root / "config.yaml"
config_path.write_text(
"model:\n"
" provider: openai-codex\n"
"hooks:\n"
" pre_llm_call:\n"
" - command: /tmp/existing_prompt.py\n"
" timeout: 5\n"
"hooks_auto_accept: false\n",
encoding="utf-8",
)

install_hermes_adapter_with_scope(tmp_path, scope="user")
install_hermes_adapter_with_scope(tmp_path, scope="user")

config_text = config_path.read_text(encoding="utf-8")
assert config_text.count("hermes_prompt_context.py") == 1
assert config_text.count("auto_session_learning.py") == 1
assert " - command: /tmp/existing_prompt.py" in config_text
assert config_text.count(" - command: /tmp/existing_prompt.py") == 1
assert " - command: '/tmp/existing_prompt.py'" not in config_text


def test_installers_are_independent(tmp_path: Path) -> None:
install_codex_adapter(tmp_path)
assert not (tmp_path / ".claude").exists()
Expand Down
Loading