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
23 changes: 12 additions & 11 deletions code_puppy/command_line/core_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def handle_cd_command(command: str) -> bool:
# Use shlex.split to handle quoted paths properly
import shlex

from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
from code_puppy.messaging import emit_error, emit_info, emit_success

try:
tokens = shlex.split(command)
Expand All @@ -77,20 +77,21 @@ def handle_cd_command(command: str) -> bool:
if os.path.isdir(target):
os.chdir(target)
emit_success(f"Changed directory to: {target}")
# Reload the agent so the system prompt and project-local
# AGENT.md rules reflect the new working directory. Without
# this, the LLM keeps receiving stale path information for the
# remainder of the session (the PydanticAgent instructions are
# baked in at construction time and never refreshed otherwise).

# Reload the agent to pick up new working directory context
# This ensures AGENTS.md is re-read and system prompt is updated
try:
from code_puppy.agents.agent_manager import get_current_agent

get_current_agent().reload_code_generation_agent()
current_agent = get_current_agent()
if current_agent:
# reload_code_generation_agent() invalidates cached rules
# and rebuilds prompt/context from the new cwd
current_agent.reload_code_generation_agent()
emit_info("Agent context updated for new directory")
except Exception as e:
emit_warning(
f"Directory changed, but agent reload failed: {e}. "
"You may need to run /agent or /model to force a refresh."
)
# Non-fatal: directory change succeeded even if reload failed
emit_error(f"Could not reload agent context: {e}")
else:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
emit_error(f"Not a directory: {dirname}")
return True
Expand Down
8 changes: 5 additions & 3 deletions tests/agents/test_base_agent_full_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2129,9 +2129,11 @@ def test_loads_from_project_dir(self, agent, tmp_path):
patch("code_puppy.config.CONFIG_DIR", str(tmp_path / "nonexistent")),
patch(
"pathlib.Path.exists",
side_effect=lambda self: str(self) == str(rules_file)
or str(self).endswith("AGENTS.md")
and "nonexistent" not in str(self),
side_effect=lambda self: (
str(self) == str(rules_file)
or str(self).endswith("AGENTS.md")
and "nonexistent" not in str(self)
),
),
):
# Complex to test due to pathlib patching, just test cached path
Expand Down
55 changes: 55 additions & 0 deletions tests/command_line/test_core_commands_extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,61 @@ def test_cd_listing_with_permission_error(self):
args, kwargs = mock_error.call_args
assert "Access denied" in args[0]

def test_cd_reloads_agent_context(self):
"""Test that /cd reloads agent to pick up new directory context (AGENTS.md, etc)."""
mock_agent = MagicMock()

with patch("code_puppy.messaging.emit_success"):
with patch("code_puppy.messaging.emit_info") as mock_emit_info:
with patch("os.path.expanduser", side_effect=lambda x: x):
with patch("os.path.isabs", return_value=True):
with patch("os.path.isdir", return_value=True):
with patch("os.chdir"):
with patch(
"code_puppy.agents.agent_manager.get_current_agent",
return_value=mock_agent,
):
result = handle_cd_command("/cd /new/dir")

# Verify directory changed
assert result is True

# Verify agent was reloaded (public behavior)
mock_agent.reload_code_generation_agent.assert_called_once()

# Verify context refresh message was emitted
mock_emit_info.assert_called_once_with(
"Agent context updated for new directory"
)

def test_cd_handles_agent_reload_failure_gracefully(self):
"""Test that /cd continues even if agent reload fails."""
mock_agent = MagicMock()
mock_agent.reload_code_generation_agent.side_effect = Exception("Reload failed")

with patch("code_puppy.messaging.emit_success"):
with patch("code_puppy.messaging.emit_error") as mock_error:
with patch("os.path.expanduser", side_effect=lambda x: x):
with patch("os.path.isabs", return_value=True):
with patch("os.path.isdir", return_value=True):
with patch("os.chdir"):
with patch(
"code_puppy.agents.agent_manager.get_current_agent",
return_value=mock_agent,
):
result = handle_cd_command("/cd /new/dir")

# Directory change should still succeed
assert result is True

# Error should be emitted about reload failure
mock_error.assert_called_once()
error_msg = mock_error.call_args[0][0]
assert error_msg.startswith(
"Could not reload agent context:"
)
assert "Reload failed" in error_msg

def test_cd_with_nonexistent_parent(self):
"""Test cd command with path containing nonexistent parent directories."""
with patch("code_puppy.messaging.emit_error") as mock_error:
Expand Down
11 changes: 7 additions & 4 deletions tests/command_line/test_model_settings_menu_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,13 @@ def test_get_supported_settings(self, mock_supports):
def test_load_model_settings_with_openai(
self, mock_supports, mock_get_all, mock_effort, mock_verb
):
mock_supports.side_effect = lambda m, s: s in (
"temperature",
"reasoning_effort",
"verbosity",
mock_supports.side_effect = lambda m, s: (
s
in (
"temperature",
"reasoning_effort",
"verbosity",
)
)
menu = _make_menu()
menu._load_model_settings("gpt-5")
Expand Down
14 changes: 7 additions & 7 deletions tests/test_command_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_cd_valid_change_reload_failure_is_nonfatal():
"""A reload failure after /cd must not abort the directory change."""
mocks = setup_messaging_mocks()
mock_emit_success = mocks["emit_success"].start()
mock_emit_warning = mocks["emit_warning"].start()
mock_emit_error = mocks["emit_error"].start()

try:
mock_agent = MagicMock()
Expand All @@ -111,14 +111,14 @@ def test_cd_valid_change_reload_failure_is_nonfatal():
mock_chdir.assert_called_once_with("/some/dir")
mock_emit_success.assert_called_once_with("Changed directory to: /some/dir")
mock_agent.reload_code_generation_agent.assert_called_once()
# Reload failure should emit a warning, not silently pass
mock_emit_warning.assert_called_once()
warning_msg = str(mock_emit_warning.call_args)
assert "agent reload failed" in warning_msg
assert "boom" in warning_msg
# Reload failure should emit an error, not silently pass
mock_emit_error.assert_called_once()
error_msg = mock_emit_error.call_args[0][0]
assert error_msg.startswith("Could not reload agent context:")
assert "boom" in error_msg
finally:
mocks["emit_success"].stop()
mocks["emit_warning"].stop()
mocks["emit_error"].stop()


def test_cd_invalid_directory():
Expand Down
Loading