Skip to content

Conversation

@benkearsley
Copy link

@benkearsley benkearsley commented Feb 11, 2026

Summary

Adds support for discovering and loading JSON agents and skills from a project-level .code_puppy directory, enabling teams to version-control and share custom agents and skills within their repository.

Changes

  • Added get_project_agents_directory() to config.py
  • Extended discover_json_agents() to search both user and project directories
  • Project agents override user agents on name collision
  • Added .code_puppy/skills to skills discovery paths
  • Added comprehensive tests for project-level agent discovery

Testing

  • Added new test suite in tests/agents/test_project_agent_integration.py
  • Extended existing tests in tests/agents/test_json_agent_extended.py
  • 501 new lines of test coverage

Impact

This allows teams to:

  • Version-control custom agents and skills alongside their code
  • Share team-specific workflows and conventions
  • Override user-level agents with project-specific configurations

🤖 Generated with Code Puppy

Summary by CodeRabbit

  • New Features

    • Discover agents from both user and project config directories; project-scoped agents take precedence on name collisions.
    • Expanded skill discovery to include a project-specific skills location in addition to existing paths.
  • Chores

    • Improved command-history initialization with automatic migration and error handling for more reliable startup.
  • Tests

    • Added and updated tests covering project-scoped agent discovery, merging behavior, overrides, and invalid-file handling.

@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

discover_json_agents now searches both the user agents directory and a project-scoped ./.code_puppy/agents directory (project entries override user entries). Added get_project_agents_directory() and logging for invalid agent files. Skill-directory defaults now include a project-config path; tests updated for isolated CWDs and project-agent behavior.

Changes

Cohort / File(s) Summary
Agent Discovery Core
code_puppy/agents/json_agent.py, code_puppy/config.py
Added get_project_agents_directory(); discover_json_agents() scans user then project agent directories, merging with project agents overriding same-name user agents. Added module logger and debug logs for skipped/invalid files. initialize_command_history_file() ensures state dir, migrates legacy history, and normalizes history file.
Skill Directory Management
code_puppy/plugins/agent_skills/config.py, code_puppy/plugins/agent_skills/discovery.py
Expanded default skill directories to include Path.cwd() / ".code_puppy" / "skills". Discovery now prefers configured directories first while still merging in defaults not covered.
Tests — Agent Discovery & Skills
tests/agents/test_json_agent_extended.py, tests/agents/test_project_agent_integration.py, tests/test_json_agents.py, tests/plugins/test_agent_skills.py
Added/updated tests to use isolated temporary CWDs, added integration tests for project-scoped agents, verified project override semantics, and updated expectations for the expanded skill-directory defaults.

Sequence Diagram

sequenceDiagram
    participant Caller as Caller
    participant Discover as discover_json_agents()
    participant UserConfig as get_user_agents_directory()
    participant ProjectConfig as get_project_agents_directory()
    participant UserFS as User Agent Dir
    participant ProjectFS as Project Agent Dir

    Caller->>Discover: invoke discovery
    Discover->>UserConfig: request user agents path
    UserConfig-->>Discover: return path or None
    alt user path present
        Discover->>UserFS: scan files, validate JSON
        UserFS-->>Discover: user agents map
    else
        Discover-->>Discover: user agents map = {}
    end
    Discover->>ProjectConfig: request project agents path
    ProjectConfig-->>Discover: return path or None
    alt project path present
        Discover->>ProjectFS: scan files, validate JSON
        ProjectFS-->>Discover: project agents map
        Discover->>Discover: merge maps (project overrides user on name collisions)
    else
        Discover-->>Discover: keep user agents only
    end
    Discover-->>Caller: return merged agent-name → file-path map
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through folders, sniffed each file and name,
Found agents at home and ones inside the frame,
Project friends step forward, gentle and bold,
They took the lead where stories were retold,
A rabbit's joyful hop — new paths to tame.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main feature: adding project-level discovery for JSON agents and skills from the .code_puppy directory, which aligns with all major changes across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
tests/plugins/test_agent_skills.py (1)

614-620: Assertions are loosely matching path substrings.

"skills" in directories[2] will match any path containing "skills" (including the .code_puppy/skills entries). Consider tightening the assertion for the third entry to distinguish it from the second, e.g., verifying it ends with /skills but does not contain .code_puppy.

♻️ Suggested tightening
         assert ".code_puppy/skills" in directories[1]
         # The current directory path will contain the full path, ending with "skills"
-        assert "skills" in directories[2]
+        assert directories[2].endswith("/skills") or directories[2].endswith("\\skills")
+        assert ".code_puppy" not in directories[2]

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@benkearsley benkearsley marked this pull request as ready for review February 11, 2026 02:52
@benkearsley benkearsley force-pushed the feat/project-level-agents-skills branch from 776e195 to 3c530af Compare February 11, 2026 02:54
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
code_puppy/plugins/agent_skills/config.py (1)

13-48: ⚠️ Potential issue | 🟡 Minor

Stale docstring: default directory list is outdated.

The docstring on line 19 still says Default: ['~/.code_puppy/skills', './skills'] but the actual defaults now include 5 directories (adding ~/.claude/skills, ~/.wibey/skills, and <CWD>/.code_puppy/skills).

📝 Suggested fix
     """Get configured skill directories.

     Returns:
         List of skill directory paths from configuration.
         Reads from puppy.cfg [puppy] section under 'skill_directories' key.
-        Default: ['~/.code_puppy/skills', './skills']
+        Default: ['~/.code_puppy/skills', '~/.claude/skills', '~/.wibey/skills',
+                  '<CWD>/.code_puppy/skills', '<CWD>/skills']

     The directories are stored as a JSON list in the config.
     """
code_puppy/plugins/agent_skills/discovery.py (1)

26-40: ⚠️ Potential issue | 🟡 Minor

Docstring for get_default_skill_directories() is incomplete.

The docstring lists only ~/.code_puppy/skills, ./.code_puppy/skills, and ./skills, but the actual return value now also includes ~/.claude/skills and ~/.wibey/skills.

📝 Suggested fix
 def get_default_skill_directories() -> List[Path]:
     """Return default directories to scan for skills.
 
     Returns:
         - ~/.code_puppy/skills (user skills)
+        - ~/.claude/skills (Claude skills)
+        - ~/.wibey/skills (WIBey skills)
         - ./.code_puppy/skills (project config skills)
         - ./skills (project skills)
     """
🧹 Nitpick comments (3)
code_puppy/agents/json_agent.py (1)

170-185: Consider adding debug-level logging when skipping invalid agent files.

Both scan loops silently swallow all exceptions. While this is appropriate for discovery (one bad file shouldn't break everything), having debug logging would aid troubleshooting when an agent file is unexpectedly ignored.

♻️ Suggested improvement
+        import logging
+        logger = logging.getLogger(__name__)
+
         for json_file in user_agents_dir.glob("*.json"):
             try:
                 agent = JSONAgent(str(json_file))
                 agents[agent.name] = str(json_file)
-            except Exception:
+            except Exception as e:
+                logger.debug("Skipping invalid agent file %s: %s", json_file, e)
                 continue

Apply the same pattern to the project-level loop.

tests/agents/test_project_agent_integration.py (1)

38-53: Prefer monkeypatch.chdir() over manual CWD save/restore.

The manual os.getcwd() / os.chdir() in try/finally works but monkeypatch.chdir() is safer (auto-restores even on unexpected failures) and more concise. This pattern is already used in the other test files in this PR (e.g., tests/test_json_agents.py).

♻️ Example for test_discover_project_agent_via_cwd
-    def test_discover_project_agent_via_cwd(self, tmp_path):
+    def test_discover_project_agent_via_cwd(self, tmp_path, monkeypatch):
         """Test that changing to a directory with .code_puppy discovers agents."""
         # ... setup ...
 
-        original_cwd = os.getcwd()
-        try:
-            os.chdir(str(project_dir))
-
-            with patch(
-                "code_puppy.config.get_user_agents_directory",
-                return_value=str(empty_user_dir),
-            ):
-                agents = discover_json_agents()
-
-            assert "project-agent" in agents
-            assert agents["project-agent"] == str(agent_file)
-        finally:
-            os.chdir(original_cwd)
+        monkeypatch.chdir(str(project_dir))
+
+        with patch(
+            "code_puppy.config.get_user_agents_directory",
+            return_value=str(empty_user_dir),
+        ):
+            agents = discover_json_agents()
+
+        assert "project-agent" in agents
+        assert agents["project-agent"] == str(agent_file)

Apply the same pattern to the other three test methods.

tests/agents/test_json_agent_extended.py (1)

377-398: Repeated inline import os across multiple test methods.

import os appears inside 5 different test methods (lines 383, 428, 450, 466, 503) instead of at the module level. Move it to the top-level imports for consistency.

♻️ Suggested fix

Add at the top of the file alongside other imports:

 import json
+import os
 from unittest.mock import patch

Then remove the inline import os from each test method.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@code_puppy/plugins/agent_skills/discovery.py`:
- Around line 34-38: get_default_skill_directories currently returns only three
paths; update it to include the two missing home-dir skill paths so tests
expecting five directories pass. Specifically, add Path.home() / ".claude" /
"skills" and Path.home() / ".wibey" / "skills" to the list returned by
get_default_skill_directories (alongside the existing Path.home() /
".code_puppy" / "skills", Path.cwd() / ".code_puppy" / "skills", and Path.cwd()
/ "skills") so the order matches the test expectations.

In `@tests/plugins/test_agent_skills.py`:
- Around line 148-153: The test expects get_default_skill_directories() to
return five paths but the implementation currently returns only three; update
the get_default_skill_directories function to include the two missing home
subdirectories (Path.home() / ".claude" / "skills" and Path.home() / ".wibey" /
"skills") and ensure the returned list ordering matches the test: Path.home() /
".code_puppy" / "skills", Path.home() / ".claude" / "skills", Path.home() /
".wibey" / "skills", Path.cwd() / ".code_puppy" / "skills", Path.cwd() /
"skills".
🧹 Nitpick comments (1)
tests/agents/test_json_agent_extended.py (1)

377-398: Prefer monkeypatch.chdir over manual os.chdir with try/finally.

Several tests in TestDiscoverJsonAgents use manual os.chdir / os.getcwd() with try/finally for CWD isolation. pytest's monkeypatch.chdir() automatically restores the original directory and is already used in tests/test_json_agents.py for the same purpose. This would also eliminate the repeated import os inside test methods.

Example refactor for test_discover_valid_agents
-    def test_discover_valid_agents(self, tmp_path):
+    def test_discover_valid_agents(self, tmp_path, monkeypatch):
         """Test discovering valid JSON agents."""
         # ... setup ...
 
         with patch("code_puppy.config.get_user_agents_directory") as mock_get_user_dir:
             mock_get_user_dir.return_value = str(tmp_path)
 
-            import os
-            original_cwd = os.getcwd()
             isolated_dir = tmp_path / "isolated"
             isolated_dir.mkdir()
-            os.chdir(str(isolated_dir))
+            monkeypatch.chdir(isolated_dir)
 
-            try:
-                agents = discover_json_agents()
+            agents = discover_json_agents()
 
-                assert len(agents) == 2
-                assert "agent1" in agents
-                assert "agent2" in agents
-                assert agents["agent1"] == str(agent1_file)
-                assert agents["agent2"] == str(agent2_file)
-            finally:
-                os.chdir(original_cwd)
+            assert len(agents) == 2
+            assert "agent1" in agents
+            assert "agent2" in agents
+            assert agents["agent1"] == str(agent1_file)
+            assert agents["agent2"] == str(agent2_file)

Apply the same pattern to test_discover_skip_invalid_agents, test_discover_no_agents_directory, test_discover_empty_directory, and test_discover_duplicate_names.

…rectory

- Add get_project_agents_directory() to config.py
- Extend discover_json_agents() to search both user and project directories
- Project agents override user agents on name collision
- Add .code_puppy/skills to skills discovery paths
- Add comprehensive tests for project-level agent discovery

This allows teams to version-control and share custom agents and skills
by placing them in .code_puppy/agents/ and .code_puppy/skills/ within
their repository.
@benkearsley benkearsley force-pushed the feat/project-level-agents-skills branch from 3c530af to 13a8330 Compare February 11, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant