diff --git a/CLAUDE.md b/CLAUDE.md index 8194dc4..6bc44f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co **Current Status:** Scaffolding commands implemented: `create mcp-server`, `create agent`, `create gateway`, `create ui`, `create sandbox`, `create model-car`. Post-scaffolding commands: `generate` (tool, resource, prompt, middleware), `patch` (check, all + type-specific category subcommands — see below), `add` (code-executor, files, vision), `vendor`. Note: `create workflow` exists in code but is not yet working. The `patch` command is type-aware via `template.type` in `.template-info`: -- **MCP server** projects expose `patch generators | core | docs | build`. +- **MCP server** projects expose `patch generators | core | docs | build | claude`. - **Agent / workflow** projects expose `patch chart | docs | build | claude`. Running an MCP-only subcommand inside an agent project (or vice versa) exits with a clear "available categories" error. `patch check` and `patch all` work for any supported type. diff --git a/src/fips_agents_cli/commands/patch.py b/src/fips_agents_cli/commands/patch.py index d928f14..86bad7c 100644 --- a/src/fips_agents_cli/commands/patch.py +++ b/src/fips_agents_cli/commands/patch.py @@ -149,9 +149,10 @@ def chart(dry_run: bool): ) def claude(dry_run: bool): """ - Update Claude Code slash commands (agent / workflow projects only). + Update Claude Code slash commands and rules. Patches files under .claude/commands/ that ship with the template. + For agent / workflow projects also patches .claude/rules/. """ _patch_category("claude", dry_run) diff --git a/src/fips_agents_cli/tools/patching.py b/src/fips_agents_cli/tools/patching.py index 7c6c8c6..f7fd552 100644 --- a/src/fips_agents_cli/tools/patching.py +++ b/src/fips_agents_cli/tools/patching.py @@ -27,9 +27,10 @@ "core": { "description": "Core infrastructure (server, auth, etc)", "patterns": [ + "src/main.py", "src/core/server.py", "src/core/auth.py", - "src/*/__ init__.py", # Package __init__ files + "src/*/__init__.py", # Package __init__ files "conftest.py", ], "ask_before_patch": True, # May be customized @@ -39,8 +40,12 @@ "patterns": [ "docs/**/*", "*/examples/**/*", # Examples in any directory + "AGENTS.md", "CLAUDE.md", "ARCHITECTURE.md", + "CONTRIBUTING.md", + "DEVELOPMENT_PROCESS.md", + "OPENSHIFT_DEPLOYMENT.md", "TESTING.md", ], "ask_before_patch": False, # Usually safe to update @@ -53,9 +58,19 @@ "openshift.yaml", "deploy.sh", "remove_examples.sh", + ".dockerignore", + ".gitignore", + ".gitleaks.toml", ], "ask_before_patch": True, # May be customized }, + "claude": { + "description": "Claude Code slash commands shipped with the template", + "patterns": [ + ".claude/commands/**/*", + ], + "ask_before_patch": False, # Safe to overwrite + }, } # Files to NEVER patch in MCP server projects (user code) @@ -105,9 +120,10 @@ "ask_before_patch": True, # May be customized }, "claude": { - "description": "Claude Code slash commands shipped with the template", + "description": "Claude Code slash commands and rules shipped with the template", "patterns": [ ".claude/commands/**/*", + ".claude/rules/**/*", ], "ask_before_patch": False, # Safe to overwrite }, @@ -119,8 +135,14 @@ "agent.yaml", # User's agent config "chart/values.yaml", # User's deploy values "src/fipsagents/**", # Vendored — managed by `fips-agents vendor --update` + "tools/**", # User-authored tools (target of `fips-agents add code-executor`) + "examples/**", # User-authored examples (target of `fips-agents add vision`) + "prompts/**", # User-customized agent prompts + "rules/**", # User-customized agent rules + "skills/**", # User-customized agent skills "tests/**/*.py", ".env*", + ".memoryhub.yaml", # User-customized memory hub config "README.md", "pyproject.toml", # User may have added dependencies ] diff --git a/tests/test_patch.py b/tests/test_patch.py index bd7f786..d268fc1 100644 --- a/tests/test_patch.py +++ b/tests/test_patch.py @@ -56,6 +56,83 @@ def test_agent_categories_have_no_framework_language(self): assert "framework" not in config["description"].lower() +class TestMcpCategoryPatterns: + """Patterns for MCP categories must match the files the template ships. + + Regression tests for issue #42: the original `core` patterns had a literal + space in `src/*/__ init__.py`, and `claude` was missing entirely. + """ + + def test_core_init_pattern_has_no_space_typo(self): + # `src/*/__init__.py` must NOT have a space — that pattern matched nothing + patterns = MCP_FILE_CATEGORIES["core"]["patterns"] + assert "src/*/__init__.py" in patterns + assert not any(" " in p for p in patterns), patterns + + def test_core_init_pattern_matches_real_init_files(self): + # Path.match validates the fix — a real package __init__.py matches now + assert Path("src/core/__init__.py").match("src/*/__init__.py") + assert Path("src/middleware/__init__.py").match("src/*/__init__.py") + + def test_core_includes_main_entry_point(self): + # src/main.py is the MCP server entry point and ships with the template + assert "src/main.py" in MCP_FILE_CATEGORIES["core"]["patterns"] + + def test_claude_category_exists_for_mcp(self): + # MCP template ships .claude/commands/*.md — must be patchable + assert "claude" in MCP_FILE_CATEGORIES + patterns = MCP_FILE_CATEGORIES["claude"]["patterns"] + assert ".claude/commands/**/*" in patterns + assert MCP_FILE_CATEGORIES["claude"]["ask_before_patch"] is False + + def test_docs_includes_recently_added_files(self): + # AGENTS.md, CONTRIBUTING.md and friends ship in the MCP template root + patterns = MCP_FILE_CATEGORIES["docs"]["patterns"] + for expected in [ + "AGENTS.md", + "CONTRIBUTING.md", + "DEVELOPMENT_PROCESS.md", + "OPENSHIFT_DEPLOYMENT.md", + ]: + assert expected in patterns, f"{expected} missing from MCP docs patterns" + + def test_build_includes_root_dotfiles(self): + patterns = MCP_FILE_CATEGORIES["build"]["patterns"] + for expected in [".dockerignore", ".gitignore", ".gitleaks.toml"]: + assert expected in patterns, f"{expected} missing from MCP build patterns" + + +class TestAgentCategoryPatterns: + """Regression tests for issue #42: agent template gaps.""" + + def test_claude_category_includes_rules(self): + # Agent template ships .claude/rules/agent-development.md + patterns = AGENT_FILE_CATEGORIES["claude"]["patterns"] + assert ".claude/commands/**/*" in patterns + assert ".claude/rules/**/*" in patterns + + +class TestAgentNeverPatchExtensions: + """`add` writes user-customized files into well-known directories. + Those paths must be in NEVER_PATCH so a future pattern broadening + cannot clobber them. + """ + + @pytest.mark.parametrize( + "expected", + [ + "tools/**", + "examples/**", + "prompts/**", + "rules/**", + "skills/**", + ".memoryhub.yaml", + ], + ) + def test_user_directory_is_never_patched(self, expected): + assert expected in AGENT_NEVER_PATCH + + # --------------------------------------------------------------------------- # Unit tests — find_fips_project_root walks up to .template-info # ---------------------------------------------------------------------------