Ctrl K
ComunidadGuías…

tests/plugins/test_hooks_instructions_loaded.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""InstructionsLoaded: the hooks plugin fires Claude Code's event for each
instruction file the instructions plugin shows, read from its records."""

from __future__ import annotations

from pathlib import Path
from typing import Any

from noeta.plugins.compaction import Compaction
from noeta.plugins.compaction.extension import command_text
from noeta.plugins.instructions import Instructions
from noeta.testing import reply
from tests.plugins.test_hooks_events import Setup, hook


def _shown(payloads: list[dict[str, Any]]) -> list[tuple[str, str, str]]:
    return [(Path(p["file_path"]).name, p["memory_type"], p["load_reason"]) for p in payloads]


async def test_instructions_loaded_fires_for_every_file_shown(tmp_path: Path) -> None:
    home = tmp_path / "home"
    home.mkdir()
    (home / "NOETA.md").write_text("User rules.\n")
    s = Setup(tmp_path, {"InstructionsLoaded": hook("loaded")},
              [reply("", ("Read", {"file_path": "sub/x.py"})), reply("read"),
               reply("again"), reply("summary text"), reply("after")],
              extensions=(Compaction(auto=False),
                          Instructions(walk=False, user_dir=str(home))))
    (s.work / "NOETA.md").write_text("Project rules. See @extra.md\n")
    (s.work / "extra.md").write_text("Imported.\n")
    (s.work / "sub").mkdir()
    (s.work / "sub" / "NOETA.md").write_text("Sub rules.\n")
    (s.work / "sub" / "x.py").write_text("x = 1\n")
    rules = s.work / ".noeta" / "rules"
    rules.mkdir(parents=True)
    (rules / "py.md").write_text("---\npaths: ['**/*.py']\n---\nPython rules.\n")
    await s.start()
    loaded = s.runner.payloads("loaded")
    assert _shown(loaded) == [
        ("NOETA.md", "User", "session_start"), ("NOETA.md", "Project", "session_start"),
        ("extra.md", "Project", "include"),
        ("py.md", "Project", "path_glob_match"), ("NOETA.md", "Project", "nested_traversal")]
    assert loaded[0]["hook_event_name"] == "InstructionsLoaded"
    assert loaded[2]["parent_file_path"] == str(s.work / "NOETA.md")
    trigger = str((s.work / "sub" / "x.py").resolve())
    assert loaded[3]["globs"] == ["**/*.py"] and loaded[3]["trigger_file_path"] == trigger
    assert loaded[4]["trigger_file_path"] == trigger and "globs" not in loaded[4]
    assert "trigger_file_path" not in loaded[0]

    # A changed file is shown again with the next message: loaded again.
    (s.work / "sub" / "NOETA.md").write_text("Sub rules, changed.\n")
    await s.send("go on")
    assert _shown(s.runner.payloads("loaded")[5:]) == [("NOETA.md", "Project", "session_start")]

    # A compaction puts every file ahead of its summary.
    await s.send(command_text(""))
    compacted = s.runner.payloads("loaded")[6:]
    assert {p["load_reason"] for p in compacted} == {"compact"}
    assert len(compacted) == 5


async def test_instructions_loaded_matcher_is_the_load_reason(tmp_path: Path) -> None:
    s = Setup(tmp_path, {"InstructionsLoaded": hook("loaded", "nested_traversal")},
              [reply("", ("Read", {"file_path": "sub/x.txt"})), reply("read")],
              extensions=(Instructions(walk=False),))
    (s.work / "NOETA.md").write_text("Project rules.\n")
    (s.work / "sub").mkdir()
    (s.work / "sub" / "NOETA.md").write_text("Sub rules.\n")
    (s.work / "sub" / "x.txt").write_text("x\n")
    await s.start()
    assert _shown(s.runner.payloads("loaded")) == [("NOETA.md", "Project", "nested_traversal")]