Fix linting violations for v1.0.0 release preparation

- Apply black formatting to all Python files
- Fix various flake8 violations in agent system code
- Clean up imports and whitespace issues

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-19 20:44:58 +02:00
parent 30daabf12c
commit d68310793b
9 changed files with 432 additions and 289 deletions

View File

@@ -730,7 +730,7 @@ release-check: $(VENV)/bin/activate
echo "📋 Release Readiness Checklist:"; \ echo "📋 Release Readiness Checklist:"; \
echo "==============================="; \ echo "==============================="; \
echo " • Version Consistency:"; \ echo " • Version Consistency:"; \
CHANGELOG_VERSION=$$(grep '^## \[' CHANGELOG.md | head -1 | sed 's/## \[\(.*\)\].*/\1/' | grep -v "Unreleased" || echo ""); \ CHANGELOG_VERSION=$$(grep '^## \[' CHANGELOG.md | grep -v "Unreleased" | head -1 | sed 's/## \[\(.*\)\].*/\1/' || echo ""); \
PYPROJECT_VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/' || echo ""); \ PYPROJECT_VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/' || echo ""); \
if [ "$$CHANGELOG_VERSION" = "$$PYPROJECT_VERSION" ] && [ -n "$$CHANGELOG_VERSION" ]; then \ if [ "$$CHANGELOG_VERSION" = "$$PYPROJECT_VERSION" ] && [ -n "$$CHANGELOG_VERSION" ]; then \
echo " ✅ Versions consistent: $$CHANGELOG_VERSION"; \ echo " ✅ Versions consistent: $$CHANGELOG_VERSION"; \

View File

@@ -17,10 +17,12 @@ def cli():
@cli.command() @cli.command()
@click.option('--category', @click.option(
type=click.Choice([c.value for c in AgentCategory]), "--category",
help='Filter by category') type=click.Choice([c.value for c in AgentCategory]),
@click.option('--verbose', '-v', is_flag=True, help='Show detailed information') help="Filter by category",
)
@click.option("--verbose", "-v", is_flag=True, help="Show detailed information")
def list(category: Optional[str], verbose: bool): def list(category: Optional[str], verbose: bool):
"""List available agents.""" """List available agents."""
registry = _get_registry() registry = _get_registry()
@@ -34,7 +36,9 @@ def list(category: Optional[str], verbose: bool):
if verbose: if verbose:
categories = registry.get_categories() categories = registry.get_categories()
for cat, agents in categories.items(): for cat, agents in categories.items():
click.echo(f"\n{cat.value.replace('-', ' ').title()} ({len(agents)} agents):") click.echo(
f"\n{cat.value.replace('-', ' ').title()} ({len(agents)} agents):"
)
click.echo("=" * 50) click.echo("=" * 50)
for agent in agents: for agent in agents:
click.echo(f"{agent.name}: {agent.description}") click.echo(f"{agent.name}: {agent.description}")
@@ -56,10 +60,10 @@ def list(category: Optional[str], verbose: bool):
@cli.command() @cli.command()
@click.argument('agents', nargs=-1, required=True) @click.argument("agents", nargs=-1, required=True)
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.option('--no-backup', is_flag=True, help='Skip creating backup') @click.option("--no-backup", is_flag=True, help="Skip creating backup")
@click.option('--no-docs', is_flag=True, help='Skip updating documentation') @click.option("--no-docs", is_flag=True, help="Skip updating documentation")
def install(agents: List[str], target: str, no_backup: bool, no_docs: bool): def install(agents: List[str], target: str, no_backup: bool, no_docs: bool):
"""Install agents into a project.""" """Install agents into a project."""
registry = _get_registry() registry = _get_registry()
@@ -72,7 +76,7 @@ def install(agents: List[str], target: str, no_backup: bool, no_docs: bool):
claude_config_path=target_path / "CLAUDE.md", claude_config_path=target_path / "CLAUDE.md",
makefile_path=target_path / "Makefile", makefile_path=target_path / "Makefile",
update_docs=not no_docs, update_docs=not no_docs,
create_backup=not no_backup create_backup=not no_backup,
) )
click.echo(f"Installing agents to: {target_path}") click.echo(f"Installing agents to: {target_path}")
@@ -98,8 +102,8 @@ def install(agents: List[str], target: str, no_backup: bool, no_docs: bool):
@cli.command() @cli.command()
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.argument('agents', nargs=-1) @click.argument("agents", nargs=-1)
def update(target: str, agents: List[str]): def update(target: str, agents: List[str]):
"""Update installed agents.""" """Update installed agents."""
registry = _get_registry() registry = _get_registry()
@@ -131,8 +135,8 @@ def update(target: str, agents: List[str]):
@cli.command() @cli.command()
@click.argument('agents', nargs=-1, required=True) @click.argument("agents", nargs=-1, required=True)
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
def remove(agents: List[str], target: str): def remove(agents: List[str], target: str):
"""Remove agents from a project.""" """Remove agents from a project."""
registry = _get_registry() registry = _get_registry()
@@ -154,11 +158,17 @@ def remove(agents: List[str], target: str):
@cli.command() @cli.command()
@click.argument('project_name') @click.argument("project_name")
@click.option('--template', '-t', default='python-basic', @click.option(
help='Project template (python-basic, python-web, python-cli, python-data)') "--template",
@click.option('--agents', '-a', help='Comma-separated list of agents to install') "-t",
@click.option('--parent-dir', default='.', help='Parent directory for project (default: current)') default="python-basic",
help="Project template (python-basic, python-web, python-cli, python-data)",
)
@click.option("--agents", "-a", help="Comma-separated list of agents to install")
@click.option(
"--parent-dir", default=".", help="Parent directory for project (default: current)"
)
def init(project_name: str, template: str, agents: Optional[str], parent_dir: str): def init(project_name: str, template: str, agents: Optional[str], parent_dir: str):
"""Initialize a new project with agents.""" """Initialize a new project with agents."""
registry = _get_registry() registry = _get_registry()
@@ -173,7 +183,7 @@ def init(project_name: str, template: str, agents: Optional[str], parent_dir: st
# Parse agent list # Parse agent list
agent_list = None agent_list = None
if agents: if agents:
agent_list = [a.strip() for a in agents.split(',')] agent_list = [a.strip() for a in agents.split(",")]
click.echo(f"Initializing project: {project_name}") click.echo(f"Initializing project: {project_name}")
click.echo(f"Template: {template}") click.echo(f"Template: {template}")
@@ -204,7 +214,7 @@ def init(project_name: str, template: str, agents: Optional[str], parent_dir: st
@cli.command() @cli.command()
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
def validate(target: str): def validate(target: str):
"""Validate agents in a project.""" """Validate agents in a project."""
registry = _get_registry() registry = _get_registry()
@@ -263,7 +273,7 @@ def templates():
@cli.command() @cli.command()
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
def status(target: str): def status(target: str):
"""Show status of agents in a project.""" """Show status of agents in a project."""
registry = _get_registry() registry = _get_registry()
@@ -321,8 +331,8 @@ def status(target: str):
@cli.command() @cli.command()
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.option('--detailed', '-d', is_flag=True, help='Show detailed analysis') @click.option("--detailed", "-d", is_flag=True, help="Show detailed analysis")
def detect(target: str, detailed: bool): def detect(target: str, detailed: bool):
"""Detect existing agent systems in a project.""" """Detect existing agent systems in a project."""
from .detection import AgentSystemDetector from .detection import AgentSystemDetector
@@ -372,11 +382,13 @@ def detect(target: str, detailed: bool):
# Show integration strategy # Show integration strategy
if result.integration_strategy: if result.integration_strategy:
click.echo(f"\n💡 Recommended Integration Strategy: {result.integration_strategy}") click.echo(
f"\n💡 Recommended Integration Strategy: {result.integration_strategy}"
)
# Show migration recommendations # Show migration recommendations
if result.migration_recommendations: if result.migration_recommendations:
click.echo(f"\n📋 Migration Recommendations:") click.echo("\n📋 Migration Recommendations:")
for recommendation in result.migration_recommendations: for recommendation in result.migration_recommendations:
if recommendation.startswith(" "): if recommendation.startswith(" "):
click.echo(f" {recommendation}") click.echo(f" {recommendation}")
@@ -384,14 +396,18 @@ def detect(target: str, detailed: bool):
click.echo(f"{recommendation}") click.echo(f"{recommendation}")
if not result.detected_systems: if not result.detected_systems:
click.echo(f"\n✨ This project is ready for Kaizen Agentic installation!") click.echo("\n✨ This project is ready for Kaizen Agentic installation!")
click.echo(f" Run: kaizen-agentic install <agent-names>") click.echo(" Run: kaizen-agentic install <agent-names>")
@cli.command() @cli.command()
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.option('--dry-run', '-n', is_flag=True, help='Show what would be done without executing') @click.option(
@click.option('--auto-resolve', '-a', is_flag=True, help='Automatically resolve simple conflicts') "--dry-run", "-n", is_flag=True, help="Show what would be done without executing"
)
@click.option(
"--auto-resolve", "-a", is_flag=True, help="Automatically resolve simple conflicts"
)
def migrate(target: str, dry_run: bool, auto_resolve: bool): def migrate(target: str, dry_run: bool, auto_resolve: bool):
"""Create migration plan for integrating Kaizen agents into existing project.""" """Create migration plan for integrating Kaizen agents into existing project."""
from .migration import AgentMigrationPlanner, AgentMigrator from .migration import AgentMigrationPlanner, AgentMigrator
@@ -408,7 +424,10 @@ def migrate(target: str, dry_run: bool, auto_resolve: bool):
planner = AgentMigrationPlanner() planner = AgentMigrationPlanner()
integration_plan = planner.create_integration_plan(target_path) integration_plan = planner.create_integration_plan(target_path)
if not integration_plan.migration_plans and not integration_plan.conflict_resolutions: if (
not integration_plan.migration_plans
and not integration_plan.conflict_resolutions
):
click.echo("✨ No migration needed - project is ready for Kaizen agents!") click.echo("✨ No migration needed - project is ready for Kaizen agents!")
click.echo(" Run: kaizen-agentic install <agent-names>") click.echo(" Run: kaizen-agentic install <agent-names>")
return return
@@ -418,12 +437,17 @@ def migrate(target: str, dry_run: bool, auto_resolve: bool):
click.echo(f"\n🔄 Migration Plans ({len(integration_plan.migration_plans)}):") click.echo(f"\n🔄 Migration Plans ({len(integration_plan.migration_plans)}):")
for plan in integration_plan.migration_plans: for plan in integration_plan.migration_plans:
strategy_emoji = { strategy_emoji = {
"replace": "🔄", "extend": "🔗", "preserve": "💾", "replace": "🔄",
"merge": "🔀", "remove": "🗑️" "extend": "🔗",
"preserve": "💾",
"merge": "🔀",
"remove": "🗑️",
} }
emoji = strategy_emoji.get(plan.strategy.value, "") emoji = strategy_emoji.get(plan.strategy.value, "")
click.echo(f" {emoji} {plan.source_agent.name} ({plan.source_agent.type.value})") click.echo(
f" {emoji} {plan.source_agent.name} ({plan.source_agent.type.value})"
)
click.echo(f" Strategy: {plan.strategy.value}") click.echo(f" Strategy: {plan.strategy.value}")
if plan.target_agent: if plan.target_agent:
click.echo(f" Target: {plan.target_agent}") click.echo(f" Target: {plan.target_agent}")
@@ -433,7 +457,9 @@ def migrate(target: str, dry_run: bool, auto_resolve: bool):
# Show conflict resolutions # Show conflict resolutions
if integration_plan.conflict_resolutions: if integration_plan.conflict_resolutions:
click.echo(f"\n⚠️ Conflict Resolutions ({len(integration_plan.conflict_resolutions)}):") click.echo(
f"\n⚠️ Conflict Resolutions ({len(integration_plan.conflict_resolutions)}):"
)
for resolution in integration_plan.conflict_resolutions: for resolution in integration_plan.conflict_resolutions:
click.echo(f"{resolution.agent1} vs {resolution.agent2}") click.echo(f"{resolution.agent1} vs {resolution.agent2}")
click.echo(f" Resolution: {resolution.resolution.value}") click.echo(f" Resolution: {resolution.resolution.value}")
@@ -443,31 +469,35 @@ def migrate(target: str, dry_run: bool, auto_resolve: bool):
# Show integration order # Show integration order
if integration_plan.integration_order: if integration_plan.integration_order:
click.echo(f"\n📋 Integration Order:") click.echo("\n📋 Integration Order:")
for i, agent_name in enumerate(integration_plan.integration_order, 1): for i, agent_name in enumerate(integration_plan.integration_order, 1):
click.echo(f" {i}. {agent_name}") click.echo(f" {i}. {agent_name}")
# Show post-migration tasks # Show post-migration tasks
if integration_plan.post_migration_tasks: if integration_plan.post_migration_tasks:
click.echo(f"\n✅ Post-Migration Tasks:") click.echo("\n✅ Post-Migration Tasks:")
for task in integration_plan.post_migration_tasks: for task in integration_plan.post_migration_tasks:
click.echo(f"{task}") click.echo(f"{task}")
# Execute migration if requested # Execute migration if requested
if not dry_run: if not dry_run:
click.echo(f"\n🚀 Executing migration...") click.echo("\n🚀 Executing migration...")
migrator = AgentMigrator() migrator = AgentMigrator()
results = migrator.execute_migration(integration_plan, dry_run=False) results = migrator.execute_migration(integration_plan, dry_run=False)
click.echo(f"\n📊 Migration Results:") click.echo("\n📊 Migration Results:")
for agent, result in results.items(): for agent, result in results.items():
status_emoji = "" if "ERROR" not in result else "" status_emoji = "" if "ERROR" not in result else ""
click.echo(f" {status_emoji} {agent}: {result}") click.echo(f" {status_emoji} {agent}: {result}")
click.echo(f"\n💾 Backup created at: {integration_plan.backup_directory}") click.echo(f"\n💾 Backup created at: {integration_plan.backup_directory}")
else: else:
click.echo(f"\n🔍 This was a dry run. Use --no-dry-run to execute the migration.") click.echo(
click.echo(f" Backup would be created at: {integration_plan.backup_directory}") "\n🔍 This was a dry run. Use --no-dry-run to execute the migration."
)
click.echo(
f" Backup would be created at: {integration_plan.backup_directory}"
)
@cli.group() @cli.group()
@@ -477,8 +507,8 @@ def extensions():
@extensions.command() @extensions.command()
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.option('--base-agent', '-b', help='Filter by base agent') @click.option("--base-agent", "-b", help="Filter by base agent")
def list_extensions(target: str, base_agent: Optional[str]): def list_extensions(target: str, base_agent: Optional[str]):
"""List installed extensions.""" """List installed extensions."""
from .extensions import ExtensionManager from .extensions import ExtensionManager
@@ -510,12 +540,14 @@ def list_extensions(target: str, base_agent: Optional[str]):
@extensions.command() @extensions.command()
@click.argument('name') @click.argument("name")
@click.argument('base_agent') @click.argument("base_agent")
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.option('--description', '-d', help='Extension description') @click.option("--description", "-d", help="Extension description")
@click.option('--template', default='basic', help='Template type (basic, advanced)') @click.option("--template", default="basic", help="Template type (basic, advanced)")
def create(name: str, base_agent: str, target: str, description: Optional[str], template: str): def create(
name: str, base_agent: str, target: str, description: Optional[str], template: str
):
"""Create a new agent extension.""" """Create a new agent extension."""
from .extensions import ExtensionManager, ExtensionType, create_extension_template from .extensions import ExtensionManager, ExtensionType, create_extension_template
@@ -523,7 +555,9 @@ def create(name: str, base_agent: str, target: str, description: Optional[str],
manager = ExtensionManager(target_path) manager = ExtensionManager(target_path)
# Generate template # Generate template
template_content = create_extension_template(name, base_agent, target_path, template) template_content = create_extension_template(
name, base_agent, target_path, template
)
# Save template to file # Save template to file
template_dir = target_path / ".kaizen" / "extensions" / name template_dir = target_path / ".kaizen" / "extensions" / name
@@ -537,18 +571,20 @@ def create(name: str, base_agent: str, target: str, description: Optional[str],
name=name, name=name,
base_agent=base_agent, base_agent=base_agent,
extension_type=ExtensionType.FUNCTIONAL_EXTENSION, extension_type=ExtensionType.FUNCTIONAL_EXTENSION,
description=description or f"Custom extension for {base_agent}" description=description or f"Custom extension for {base_agent}",
) )
click.echo(f"✅ Created extension: {name}") click.echo(f"✅ Created extension: {name}")
click.echo(f" Base agent: {base_agent}") click.echo(f" Base agent: {base_agent}")
click.echo(f" Template saved to: {template_file}") click.echo(f" Template saved to: {template_file}")
click.echo(f" Edit the configuration and run: kaizen-agentic extensions enable {name}") click.echo(
f" Edit the configuration and run: kaizen-agentic extensions enable {name}"
)
@extensions.command() @extensions.command()
@click.argument('name') @click.argument("name")
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
def enable(name: str, target: str): def enable(name: str, target: str):
"""Enable an extension.""" """Enable an extension."""
from .extensions import ExtensionManager from .extensions import ExtensionManager
@@ -563,8 +599,8 @@ def enable(name: str, target: str):
@extensions.command() @extensions.command()
@click.argument('name') @click.argument("name")
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
def disable(name: str, target: str): def disable(name: str, target: str):
"""Disable an extension.""" """Disable an extension."""
from .extensions import ExtensionManager from .extensions import ExtensionManager
@@ -579,9 +615,9 @@ def disable(name: str, target: str):
@extensions.command() @extensions.command()
@click.argument('name') @click.argument("name")
@click.option('--target', '-t', default='.', help='Target directory (default: current)') @click.option("--target", "-t", default=".", help="Target directory (default: current)")
@click.confirmation_option(prompt='Are you sure you want to remove this extension?') @click.confirmation_option(prompt="Are you sure you want to remove this extension?")
def remove(name: str, target: str): def remove(name: str, target: str):
"""Remove an extension.""" """Remove an extension."""
from .extensions import ExtensionManager from .extensions import ExtensionManager
@@ -610,6 +646,7 @@ def _get_registry() -> AgentRegistry:
# Try to find installed package # Try to find installed package
try: try:
import kaizen_agentic import kaizen_agentic
package_dir = Path(kaizen_agentic.__file__).parent.parent.parent package_dir = Path(kaizen_agentic.__file__).parent.parent.parent
agents_dir = package_dir / "agents" agents_dir = package_dir / "agents"
if not agents_dir.exists(): if not agents_dir.exists():
@@ -617,7 +654,9 @@ def _get_registry() -> AgentRegistry:
agents_dir = Path(kaizen_agentic.__file__).parent / "data" / "agents" agents_dir = Path(kaizen_agentic.__file__).parent / "data" / "agents"
except ImportError: except ImportError:
click.echo("Error: Could not find agents directory") click.echo("Error: Could not find agents directory")
click.echo("Make sure you're in a kaizen-agentic project or have the package installed") click.echo(
"Make sure you're in a kaizen-agentic project or have the package installed"
)
sys.exit(1) sys.exit(1)
if not agents_dir.exists(): if not agents_dir.exists():
@@ -627,5 +666,5 @@ def _get_registry() -> AgentRegistry:
return AgentRegistry(agents_dir) return AgentRegistry(agents_dir)
if __name__ == '__main__': if __name__ == "__main__":
cli() cli()

View File

@@ -1,15 +1,15 @@
"""Detection and analysis of existing agent systems in projects.""" """Detection and analysis of existing agent systems in projects."""
import json
import yaml import yaml
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Set, Tuple from typing import List, Optional, Set, Tuple
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
class AgentSystemType(Enum): class AgentSystemType(Enum):
"""Types of existing agent systems that might be found.""" """Types of existing agent systems that might be found."""
KAIZEN_AGENTIC = "kaizen-agentic" KAIZEN_AGENTIC = "kaizen-agentic"
CLAUDE_CODE = "claude-code" CLAUDE_CODE = "claude-code"
GITHUB_COPILOT = "github-copilot" GITHUB_COPILOT = "github-copilot"
@@ -25,6 +25,7 @@ class AgentSystemType(Enum):
@dataclass @dataclass
class DetectedAgent: class DetectedAgent:
"""Information about a detected agent.""" """Information about a detected agent."""
name: str name: str
type: AgentSystemType type: AgentSystemType
file_path: Path file_path: Path
@@ -44,6 +45,7 @@ class DetectedAgent:
@dataclass @dataclass
class AgentSystemDetectionResult: class AgentSystemDetectionResult:
"""Result of agent system detection in a project.""" """Result of agent system detection in a project."""
project_path: Path project_path: Path
detected_systems: List[AgentSystemType] detected_systems: List[AgentSystemType]
agents: List[DetectedAgent] agents: List[DetectedAgent]
@@ -197,33 +199,37 @@ class AgentSystemDetector:
agents.append(agent) agents.append(agent)
except Exception as e: except Exception as e:
# Create a detected agent with error info # Create a detected agent with error info
agents.append(DetectedAgent( agents.append(
name=agent_file.stem.replace("agent-", ""), DetectedAgent(
type=AgentSystemType.KAIZEN_AGENTIC, name=agent_file.stem.replace("agent-", ""),
file_path=agent_file, type=AgentSystemType.KAIZEN_AGENTIC,
can_migrate=False, file_path=agent_file,
migration_notes=f"Parse error: {e}" can_migrate=False,
)) migration_notes=f"Parse error: {e}",
)
)
return agents return agents
def _parse_kaizen_agent_file(self, agent_file: Path) -> Optional[DetectedAgent]: def _parse_kaizen_agent_file(self, agent_file: Path) -> Optional[DetectedAgent]:
"""Parse a Kaizen Agentic agent file.""" """Parse a Kaizen Agentic agent file."""
try: try:
content = agent_file.read_text(encoding='utf-8') content = agent_file.read_text(encoding="utf-8")
# Extract YAML frontmatter # Extract YAML frontmatter
if content.startswith('---'): if content.startswith("---"):
parts = content.split('---', 2) parts = content.split("---", 2)
if len(parts) >= 3: if len(parts) >= 3:
frontmatter = yaml.safe_load(parts[1]) frontmatter = yaml.safe_load(parts[1])
return DetectedAgent( return DetectedAgent(
name=frontmatter.get('name', agent_file.stem.replace("agent-", "")), name=frontmatter.get(
"name", agent_file.stem.replace("agent-", "")
),
type=AgentSystemType.KAIZEN_AGENTIC, type=AgentSystemType.KAIZEN_AGENTIC,
file_path=agent_file, file_path=agent_file,
description=frontmatter.get('description'), description=frontmatter.get("description"),
dependencies=set(frontmatter.get('dependencies', [])) dependencies=set(frontmatter.get("dependencies", [])),
) )
except Exception: except Exception:
pass pass
@@ -238,12 +244,14 @@ class AgentSystemDetector:
if claude_file.exists(): if claude_file.exists():
# Claude Code typically doesn't have separate agent files # Claude Code typically doesn't have separate agent files
# but might reference agent usage in CLAUDE.md # but might reference agent usage in CLAUDE.md
agents.append(DetectedAgent( agents.append(
name="claude-integration", DetectedAgent(
type=AgentSystemType.CLAUDE_CODE, name="claude-integration",
file_path=claude_file, type=AgentSystemType.CLAUDE_CODE,
description="Claude Code integration configuration" file_path=claude_file,
)) description="Claude Code integration configuration",
)
)
return agents return agents
@@ -261,15 +269,20 @@ class AgentSystemDetector:
for pattern in ["*.py", "*.yml", "*.yaml", "*.json", "*.md"]: for pattern in ["*.py", "*.yml", "*.yaml", "*.json", "*.md"]:
for agent_file in agent_dir.glob(pattern): for agent_file in agent_dir.glob(pattern):
# Skip kaizen-agentic files # Skip kaizen-agentic files
if agent_file.name.startswith("agent-") and agent_file.suffix == ".md": if (
agent_file.name.startswith("agent-")
and agent_file.suffix == ".md"
):
continue continue
agents.append(DetectedAgent( agents.append(
name=agent_file.stem, DetectedAgent(
type=AgentSystemType.CUSTOM_AGENTS, name=agent_file.stem,
file_path=agent_file, type=AgentSystemType.CUSTOM_AGENTS,
description=f"Custom agent in {dir_name}/" file_path=agent_file,
)) description=f"Custom agent in {dir_name}/",
)
)
return agents return agents
@@ -286,7 +299,9 @@ class AgentSystemDetector:
return config_files return config_files
def _analyze_conflicts(self, agents: List[DetectedAgent]) -> List[Tuple[str, str, str]]: def _analyze_conflicts(
self, agents: List[DetectedAgent]
) -> List[Tuple[str, str, str]]:
"""Analyze potential conflicts between agents.""" """Analyze potential conflicts between agents."""
conflicts = [] conflicts = []
@@ -298,15 +313,16 @@ class AgentSystemDetector:
agents_by_type[agent.type].append(agent) agents_by_type[agent.type].append(agent)
# Check for naming conflicts # Check for naming conflicts
all_names = [agent.name for agent in agents]
for i, agent1 in enumerate(agents): for i, agent1 in enumerate(agents):
for j, agent2 in enumerate(agents[i+1:], i+1): for j, agent2 in enumerate(agents[i + 1 :], i + 1):
if agent1.name == agent2.name and agent1.type != agent2.type: if agent1.name == agent2.name and agent1.type != agent2.type:
conflicts.append(( conflicts.append(
agent1.name, (
agent2.name, agent1.name,
f"Name conflict between {agent1.type.value} and {agent2.type.value}" agent2.name,
)) f"Name conflict between {agent1.type.value} and {agent2.type.value}",
)
)
# Check for functional overlaps # Check for functional overlaps
functional_conflicts = { functional_conflicts = {
@@ -324,12 +340,14 @@ class AgentSystemDetector:
if len(matching_agents) > 1: if len(matching_agents) > 1:
for i, agent1 in enumerate(matching_agents): for i, agent1 in enumerate(matching_agents):
for agent2 in matching_agents[i+1:]: for agent2 in matching_agents[i + 1 :]:
conflicts.append(( conflicts.append(
agent1.name, (
agent2.name, agent1.name,
f"Functional overlap: {conflict_type}" agent2.name,
)) f"Functional overlap: {conflict_type}",
)
)
return conflicts return conflicts
@@ -343,7 +361,10 @@ class AgentSystemDetector:
if AgentSystemType.KAIZEN_AGENTIC in detected_systems: if AgentSystemType.KAIZEN_AGENTIC in detected_systems:
return "update_existing" return "update_existing"
if len(detected_systems) == 1 and detected_systems[0] == AgentSystemType.CLAUDE_CODE: if (
len(detected_systems) == 1
and detected_systems[0] == AgentSystemType.CLAUDE_CODE
):
return "claude_compatible" return "claude_compatible"
if len([a for a in agents if a.type == AgentSystemType.CUSTOM_AGENTS]) > 5: if len([a for a in agents if a.type == AgentSystemType.CUSTOM_AGENTS]) > 5:
@@ -355,13 +376,15 @@ class AgentSystemDetector:
self, self,
detected_systems: List[AgentSystemType], detected_systems: List[AgentSystemType],
agents: List[DetectedAgent], agents: List[DetectedAgent],
conflicts: List[Tuple[str, str, str]] conflicts: List[Tuple[str, str, str]],
) -> List[str]: ) -> List[str]:
"""Generate migration recommendations.""" """Generate migration recommendations."""
recommendations = [] recommendations = []
if not detected_systems: if not detected_systems:
recommendations.append("Clean installation - no existing agent systems detected") recommendations.append(
"Clean installation - no existing agent systems detected"
)
return recommendations return recommendations
if AgentSystemType.KAIZEN_AGENTIC in detected_systems: if AgentSystemType.KAIZEN_AGENTIC in detected_systems:
@@ -369,18 +392,26 @@ class AgentSystemDetector:
recommendations.append("Run 'kaizen-agentic update' to get latest agents") recommendations.append("Run 'kaizen-agentic update' to get latest agents")
if conflicts: if conflicts:
recommendations.append(f"Resolve {len(conflicts)} naming/functional conflicts") recommendations.append(
f"Resolve {len(conflicts)} naming/functional conflicts"
)
for agent1, agent2, reason in conflicts: for agent1, agent2, reason in conflicts:
recommendations.append(f" - Conflict: {agent1} vs {agent2} ({reason})") recommendations.append(f" - Conflict: {agent1} vs {agent2} ({reason})")
custom_agents = [a for a in agents if a.type == AgentSystemType.CUSTOM_AGENTS] custom_agents = [a for a in agents if a.type == AgentSystemType.CUSTOM_AGENTS]
if custom_agents: if custom_agents:
recommendations.append(f"Consider migrating {len(custom_agents)} custom agents") recommendations.append(
recommendations.append(" - Review custom agents for Kaizen Agentic equivalents") f"Consider migrating {len(custom_agents)} custom agents"
recommendations.append(" - Create project-specific extensions for unique functionality") )
recommendations.append(
" - Review custom agents for Kaizen Agentic equivalents"
)
recommendations.append(
" - Create project-specific extensions for unique functionality"
)
if AgentSystemType.CLAUDE_CODE in detected_systems: if AgentSystemType.CLAUDE_CODE in detected_systems:
recommendations.append("Maintain Claude Code compatibility") recommendations.append("Maintain Claude Code compatibility")
recommendations.append(" - Update CLAUDE.md with new agent references") recommendations.append(" - Update CLAUDE.md with new agent references")
return recommendations return recommendations

View File

@@ -3,13 +3,14 @@
import json import json
import yaml import yaml
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Any, Union from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
class ExtensionType(Enum): class ExtensionType(Enum):
"""Types of agent extensions.""" """Types of agent extensions."""
CONFIGURATION_OVERLAY = "config_overlay" # Override default configurations CONFIGURATION_OVERLAY = "config_overlay" # Override default configurations
FUNCTIONAL_EXTENSION = "functional_extension" # Add new functionality FUNCTIONAL_EXTENSION = "functional_extension" # Add new functionality
WORKFLOW_INTEGRATION = "workflow_integration" # Integrate with project workflows WORKFLOW_INTEGRATION = "workflow_integration" # Integrate with project workflows
@@ -21,6 +22,7 @@ class ExtensionType(Enum):
@dataclass @dataclass
class AgentExtension: class AgentExtension:
"""Defines an extension to a Kaizen agent.""" """Defines an extension to a Kaizen agent."""
name: str name: str
base_agent: str # The Kaizen agent this extends base_agent: str # The Kaizen agent this extends
extension_type: ExtensionType extension_type: ExtensionType
@@ -44,6 +46,7 @@ class AgentExtension:
@dataclass @dataclass
class ProjectExtensionRegistry: class ProjectExtensionRegistry:
"""Registry of extensions for a project.""" """Registry of extensions for a project."""
project_path: Path project_path: Path
extensions: List[AgentExtension] = field(default_factory=list) extensions: List[AgentExtension] = field(default_factory=list)
global_config: Dict[str, Any] = field(default_factory=dict) global_config: Dict[str, Any] = field(default_factory=dict)
@@ -63,7 +66,7 @@ class ExtensionManager:
base_agent: str, base_agent: str,
extension_type: ExtensionType, extension_type: ExtensionType,
description: str, description: str,
**kwargs **kwargs,
) -> AgentExtension: ) -> AgentExtension:
"""Create a new agent extension.""" """Create a new agent extension."""
extension = AgentExtension( extension = AgentExtension(
@@ -71,7 +74,7 @@ class ExtensionManager:
base_agent=base_agent, base_agent=base_agent,
extension_type=extension_type, extension_type=extension_type,
description=description, description=description,
**kwargs **kwargs,
) )
self._save_extension(extension) self._save_extension(extension)
@@ -79,14 +82,16 @@ class ExtensionManager:
def install_extension(self, extension_path: Path) -> AgentExtension: def install_extension(self, extension_path: Path) -> AgentExtension:
"""Install an extension from a file.""" """Install an extension from a file."""
if extension_path.suffix == '.json': if extension_path.suffix == ".json":
with open(extension_path) as f: with open(extension_path) as f:
data = json.load(f) data = json.load(f)
elif extension_path.suffix in ['.yml', '.yaml']: elif extension_path.suffix in [".yml", ".yaml"]:
with open(extension_path) as f: with open(extension_path) as f:
data = yaml.safe_load(f) data = yaml.safe_load(f)
else: else:
raise ValueError(f"Unsupported extension file format: {extension_path.suffix}") raise ValueError(
f"Unsupported extension file format: {extension_path.suffix}"
)
extension = AgentExtension(**data) extension = AgentExtension(**data)
self._save_extension(extension) self._save_extension(extension)
@@ -127,6 +132,7 @@ class ExtensionManager:
extension_dir = self.extensions_dir / name extension_dir = self.extensions_dir / name
if extension_dir.exists(): if extension_dir.exists():
import shutil import shutil
shutil.rmtree(extension_dir) shutil.rmtree(extension_dir)
return True return True
@@ -136,8 +142,11 @@ class ExtensionManager:
def get_effective_config(self, base_agent: str) -> Dict[str, Any]: def get_effective_config(self, base_agent: str) -> Dict[str, Any]:
"""Get the effective configuration for an agent with all extensions applied.""" """Get the effective configuration for an agent with all extensions applied."""
base_config = self._get_base_agent_config(base_agent) base_config = self._get_base_agent_config(base_agent)
extensions = [ext for ext in self._load_extensions() extensions = [
if ext.base_agent == base_agent and ext.enabled] ext
for ext in self._load_extensions()
if ext.base_agent == base_agent and ext.enabled
]
# Apply extensions in order # Apply extensions in order
for extension in extensions: for extension in extensions:
@@ -151,7 +160,7 @@ class ExtensionManager:
base_agent: str, base_agent: str,
custom_instructions: str, custom_instructions: str,
custom_commands: Optional[Dict[str, str]] = None, custom_commands: Optional[Dict[str, str]] = None,
environment_config: Optional[Dict[str, Any]] = None environment_config: Optional[Dict[str, Any]] = None,
) -> AgentExtension: ) -> AgentExtension:
"""Create a project-specific agent based on a Kaizen agent.""" """Create a project-specific agent based on a Kaizen agent."""
@@ -161,8 +170,8 @@ class ExtensionManager:
"project_context": { "project_context": {
"name": self.project_path.name, "name": self.project_path.name,
"path": str(self.project_path), "path": str(self.project_path),
"type": self._detect_project_type() "type": self._detect_project_type(),
} },
} }
if environment_config: if environment_config:
@@ -175,7 +184,7 @@ class ExtensionManager:
description=f"Project-specific extension of {base_agent} for {self.project_path.name}", description=f"Project-specific extension of {base_agent} for {self.project_path.name}",
configuration=config, configuration=config,
custom_commands=custom_commands or {}, custom_commands=custom_commands or {},
environment_overrides=environment_config or {} environment_overrides=environment_config or {},
) )
# Create agent file # Create agent file
@@ -187,7 +196,7 @@ class ExtensionManager:
self, self,
legacy_agent_path: Path, legacy_agent_path: Path,
target_kaizen_agent: str, target_kaizen_agent: str,
migration_strategy: str = "preserve_functionality" migration_strategy: str = "preserve_functionality",
) -> AgentExtension: ) -> AgentExtension:
"""Integrate a legacy agent as an extension to a Kaizen agent.""" """Integrate a legacy agent as an extension to a Kaizen agent."""
@@ -201,7 +210,7 @@ class ExtensionManager:
"legacy_source": str(legacy_agent_path), "legacy_source": str(legacy_agent_path),
"migration_strategy": migration_strategy, "migration_strategy": migration_strategy,
"preserved_functionality": legacy_analysis.get("functionality", []), "preserved_functionality": legacy_analysis.get("functionality", []),
"custom_config": legacy_analysis.get("config", {}) "custom_config": legacy_analysis.get("config", {}),
} }
extension = self.create_extension( extension = self.create_extension(
@@ -209,7 +218,7 @@ class ExtensionManager:
base_agent=target_kaizen_agent, base_agent=target_kaizen_agent,
extension_type=ExtensionType.WORKFLOW_INTEGRATION, extension_type=ExtensionType.WORKFLOW_INTEGRATION,
description=f"Legacy integration of {legacy_agent_path.name}", description=f"Legacy integration of {legacy_agent_path.name}",
configuration=config configuration=config,
) )
# Create migration wrapper # Create migration wrapper
@@ -232,23 +241,23 @@ class ExtensionManager:
extension_dir.mkdir(parents=True, exist_ok=True) extension_dir.mkdir(parents=True, exist_ok=True)
# Save extension definition # Save extension definition
with open(extension_dir / "extension.yml", 'w') as f: with open(extension_dir / "extension.yml", "w") as f:
# Convert dataclass to dict for YAML serialization # Convert dataclass to dict for YAML serialization
data = { data = {
'name': extension.name, "name": extension.name,
'base_agent': extension.base_agent, "base_agent": extension.base_agent,
'extension_type': extension.extension_type.value, "extension_type": extension.extension_type.value,
'description': extension.description, "description": extension.description,
'version': extension.version, "version": extension.version,
'author': extension.author, "author": extension.author,
'configuration': extension.configuration, "configuration": extension.configuration,
'custom_commands': extension.custom_commands, "custom_commands": extension.custom_commands,
'workflow_hooks': extension.workflow_hooks, "workflow_hooks": extension.workflow_hooks,
'data_transformations': extension.data_transformations, "data_transformations": extension.data_transformations,
'environment_overrides': extension.environment_overrides, "environment_overrides": extension.environment_overrides,
'dependencies': extension.dependencies, "dependencies": extension.dependencies,
'compatibility': extension.compatibility, "compatibility": extension.compatibility,
'enabled': extension.enabled "enabled": extension.enabled,
} }
yaml.dump(data, f, default_flow_style=False) yaml.dump(data, f, default_flow_style=False)
@@ -262,9 +271,9 @@ class ExtensionManager:
data = yaml.safe_load(f) or {} data = yaml.safe_load(f) or {}
extensions = [] extensions = []
for ext_data in data.get('extensions', []): for ext_data in data.get("extensions", []):
# Convert string back to enum # Convert string back to enum
ext_data['extension_type'] = ExtensionType(ext_data['extension_type']) ext_data["extension_type"] = ExtensionType(ext_data["extension_type"])
extensions.append(AgentExtension(**ext_data)) extensions.append(AgentExtension(**ext_data))
return extensions return extensions
@@ -277,28 +286,28 @@ class ExtensionManager:
# Convert to serializable format # Convert to serializable format
data = { data = {
'extensions': [ "extensions": [
{ {
'name': ext.name, "name": ext.name,
'base_agent': ext.base_agent, "base_agent": ext.base_agent,
'extension_type': ext.extension_type.value, "extension_type": ext.extension_type.value,
'description': ext.description, "description": ext.description,
'version': ext.version, "version": ext.version,
'author': ext.author, "author": ext.author,
'configuration': ext.configuration, "configuration": ext.configuration,
'custom_commands': ext.custom_commands, "custom_commands": ext.custom_commands,
'workflow_hooks': ext.workflow_hooks, "workflow_hooks": ext.workflow_hooks,
'data_transformations': ext.data_transformations, "data_transformations": ext.data_transformations,
'environment_overrides': ext.environment_overrides, "environment_overrides": ext.environment_overrides,
'dependencies': ext.dependencies, "dependencies": ext.dependencies,
'compatibility': ext.compatibility, "compatibility": ext.compatibility,
'enabled': ext.enabled "enabled": ext.enabled,
} }
for ext in extensions for ext in extensions
] ]
} }
with open(self.config_file, 'w') as f: with open(self.config_file, "w") as f:
yaml.dump(data, f, default_flow_style=False) yaml.dump(data, f, default_flow_style=False)
def _toggle_extension(self, name: str, enabled: bool) -> bool: def _toggle_extension(self, name: str, enabled: bool) -> bool:
@@ -321,7 +330,7 @@ class ExtensionManager:
"name": base_agent, "name": base_agent,
"type": "kaizen_agent", "type": "kaizen_agent",
"enabled": True, "enabled": True,
"config": {} "config": {},
} }
def _apply_extension_config( def _apply_extension_config(
@@ -343,11 +352,13 @@ class ExtensionManager:
config.setdefault("environment", {}).update(extension.environment_overrides) config.setdefault("environment", {}).update(extension.environment_overrides)
# Add extension metadata # Add extension metadata
config.setdefault("extensions", []).append({ config.setdefault("extensions", []).append(
"name": extension.name, {
"type": extension.extension_type.value, "name": extension.name,
"version": extension.version "type": extension.extension_type.value,
}) "version": extension.version,
}
)
return config return config
@@ -370,7 +381,7 @@ class ExtensionManager:
"functionality": [], "functionality": [],
"config": {}, "config": {},
"commands": [], "commands": [],
"dependencies": [] "dependencies": [],
} }
if agent_path.suffix == ".py": if agent_path.suffix == ".py":
@@ -379,8 +390,9 @@ class ExtensionManager:
# Simple analysis - look for class and function definitions # Simple analysis - look for class and function definitions
import re import re
classes = re.findall(r'class\s+(\w+)', content)
functions = re.findall(r'def\s+(\w+)', content) classes = re.findall(r"class\s+(\w+)", content)
functions = re.findall(r"def\s+(\w+)", content)
analysis["functionality"] = classes + functions analysis["functionality"] = classes + functions
@@ -487,10 +499,7 @@ class LegacyWrapper:
def create_extension_template( def create_extension_template(
name: str, name: str, base_agent: str, project_path: Path, template_type: str = "basic"
base_agent: str,
project_path: Path,
template_type: str = "basic"
) -> str: ) -> str:
"""Create a template for a new agent extension.""" """Create a template for a new agent extension."""
@@ -535,7 +544,6 @@ Save this as `.kaizen/extensions/{name}/extension.yml` and run:
kaizen-agentic extensions install {name} kaizen-agentic extensions install {name}
``` ```
""", """,
"advanced": f"""# Advanced Extension Template: {name} "advanced": f"""# Advanced Extension Template: {name}
This template provides advanced customization options for the {base_agent} agent. This template provides advanced customization options for the {base_agent} agent.
@@ -610,7 +618,7 @@ Create these files in `.kaizen/extensions/{name}/scripts/`:
1. Save the configuration as `.kaizen/extensions/{name}/extension.yml` 1. Save the configuration as `.kaizen/extensions/{name}/extension.yml`
2. Add any custom scripts to the scripts directory 2. Add any custom scripts to the scripts directory
3. Install with: `kaizen-agentic extensions install {name}` 3. Install with: `kaizen-agentic extensions install {name}`
""" """,
} }
return templates.get(template_type, templates["basic"]) return templates.get(template_type, templates["basic"])

View File

@@ -12,6 +12,7 @@ from .registry import AgentRegistry
@dataclass @dataclass
class InstallationConfig: class InstallationConfig:
"""Configuration for agent installation.""" """Configuration for agent installation."""
target_dir: Path target_dir: Path
claude_config_path: Optional[Path] = None claude_config_path: Optional[Path] = None
makefile_path: Optional[Path] = None makefile_path: Optional[Path] = None
@@ -26,9 +27,7 @@ class AgentInstaller:
self.registry = registry self.registry = registry
def install_agents( def install_agents(
self, self, agent_names: List[str], config: InstallationConfig
agent_names: List[str],
config: InstallationConfig
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Install agents into a project. """Install agents into a project.
@@ -89,9 +88,7 @@ class AgentInstaller:
return sorted(installed) return sorted(installed)
def update_agents( def update_agents(
self, self, project_dir: Path, agent_names: Optional[List[str]] = None
project_dir: Path,
agent_names: Optional[List[str]] = None
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Update installed agents to latest versions.""" """Update installed agents to latest versions."""
if agent_names is None: if agent_names is None:
@@ -101,9 +98,7 @@ class AgentInstaller:
return self.install_agents(agent_names, config) return self.install_agents(agent_names, config)
def remove_agents( def remove_agents(
self, self, agent_names: List[str], project_dir: Path
agent_names: List[str],
project_dir: Path
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Remove agents from a project.""" """Remove agents from a project."""
results = {} results = {}
@@ -162,7 +157,10 @@ class AgentInstaller:
counter = 0 counter = 0
while backup_dir.exists(): while backup_dir.exists():
counter += 1 counter += 1
backup_dir = agents_dir.parent / f"agents_backup_{timestamp}_{microseconds}_{counter}" backup_dir = (
agents_dir.parent
/ f"agents_backup_{timestamp}_{microseconds}_{counter}"
)
shutil.copytree(agents_dir, backup_dir) shutil.copytree(agents_dir, backup_dir)
print(f"Created backup at: {backup_dir}") print(f"Created backup at: {backup_dir}")
@@ -173,22 +171,22 @@ class AgentInstaller:
# Read existing config # Read existing config
config = {} config = {}
if config_path.exists(): if config_path.exists():
with open(config_path, 'r') as f: with open(config_path, "r") as f:
config = json.load(f) config = json.load(f)
# Ensure agents section exists # Ensure agents section exists
if 'agents' not in config: if "agents" not in config:
config['agents'] = {} config["agents"] = {}
# Add agent references # Add agent references
for agent_name in agent_names: for agent_name in agent_names:
config['agents'][agent_name] = { config["agents"][agent_name] = {
"path": f"agents/agent-{agent_name}.md", "path": f"agents/agent-{agent_name}.md",
"enabled": True "enabled": True,
} }
# Write updated config # Write updated config
with open(config_path, 'w') as f: with open(config_path, "w") as f:
json.dump(config, f, indent=2) json.dump(config, f, indent=2)
print(f"Updated Claude configuration: {config_path}") print(f"Updated Claude configuration: {config_path}")
@@ -200,7 +198,7 @@ class AgentInstaller:
"""Update Makefile with agent-specific targets.""" """Update Makefile with agent-specific targets."""
try: try:
# Read existing Makefile # Read existing Makefile
with open(makefile_path, 'r') as f: with open(makefile_path, "r") as f:
content = f.read() content = f.read()
# Add agent management targets if not present # Add agent management targets if not present
@@ -224,7 +222,7 @@ agents-validate:
content += agent_targets content += agent_targets
# Write updated Makefile # Write updated Makefile
with open(makefile_path, 'w') as f: with open(makefile_path, "w") as f:
f.write(content) f.write(content)
print(f"Updated Makefile: {makefile_path}") print(f"Updated Makefile: {makefile_path}")
@@ -238,7 +236,9 @@ agents-validate:
claude_md = project_dir / "CLAUDE.md" claude_md = project_dir / "CLAUDE.md"
agent_section = "## Installed Agents\n\n" agent_section = "## Installed Agents\n\n"
agent_section += "This project includes the following specialized agents:\n\n" agent_section += (
"This project includes the following specialized agents:\n\n"
)
# Group agents by category # Group agents by category
categories = {} categories = {}
@@ -257,34 +257,37 @@ agents-validate:
agent_section += f"- **{agent.name}**: {agent.description}\n" agent_section += f"- **{agent.name}**: {agent.description}\n"
agent_section += "\n" agent_section += "\n"
agent_section += ("Use these agents by referencing them in your " agent_section += (
"Claude Code interactions.\n\n") "Use these agents by referencing them in your "
"Claude Code interactions.\n\n"
)
# Update or create CLAUDE.md # Update or create CLAUDE.md
if claude_md.exists(): if claude_md.exists():
with open(claude_md, 'r') as f: with open(claude_md, "r") as f:
content = f.read() content = f.read()
# Replace existing agent section or append # Replace existing agent section or append
if "## Installed Agents" in content: if "## Installed Agents" in content:
import re import re
content = re.sub( content = re.sub(
r'## Installed Agents.*?(?=##|\Z)', r"## Installed Agents.*?(?=##|\Z)",
agent_section, agent_section,
content, content,
flags=re.DOTALL flags=re.DOTALL,
) )
else: else:
content += "\n" + agent_section content += "\n" + agent_section
with open(claude_md, 'w') as f: with open(claude_md, "w") as f:
f.write(content) f.write(content)
else: else:
# Create new CLAUDE.md # Create new CLAUDE.md
header = "# Claude Code Configuration\n\n" header = "# Claude Code Configuration\n\n"
header += "This file contains Claude Code configuration and agent information.\n\n" header += "This file contains Claude Code configuration and agent information.\n\n"
with open(claude_md, 'w') as f: with open(claude_md, "w") as f:
f.write(header + agent_section) f.write(header + agent_section)
print(f"Updated documentation: {claude_md}") print(f"Updated documentation: {claude_md}")
@@ -304,7 +307,7 @@ class ProjectInitializer:
project_dir: Path, project_dir: Path,
template: str = "python-basic", template: str = "python-basic",
agent_names: Optional[List[str]] = None, agent_names: Optional[List[str]] = None,
project_name: Optional[str] = None project_name: Optional[str] = None,
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Initialize a new project with agents and structure.""" """Initialize a new project with agents and structure."""
results = {} results = {}
@@ -325,7 +328,7 @@ class ProjectInitializer:
config = InstallationConfig( config = InstallationConfig(
target_dir=project_dir, target_dir=project_dir,
claude_config_path=project_dir / "CLAUDE.md", claude_config_path=project_dir / "CLAUDE.md",
makefile_path=project_dir / "Makefile" makefile_path=project_dir / "Makefile",
) )
installer = AgentInstaller(self.registry) installer = AgentInstaller(self.registry)
@@ -337,12 +340,16 @@ class ProjectInitializer:
return results return results
def _create_project_structure(self, project_dir: Path, project_name: str, template: str): def _create_project_structure(
self, project_dir: Path, project_name: str, template: str
):
"""Create basic project structure based on template.""" """Create basic project structure based on template."""
# Create directories # Create directories
dirs_to_create = ["src", "tests", "docs"] dirs_to_create = ["src", "tests", "docs"]
if template.startswith("python"): if template.startswith("python"):
dirs_to_create.extend([f"src/{project_name.replace('-', '_')}", ".github/workflows"]) dirs_to_create.extend(
[f"src/{project_name.replace('-', '_')}", ".github/workflows"]
)
for dir_name in dirs_to_create: for dir_name in dirs_to_create:
(project_dir / dir_name).mkdir(parents=True, exist_ok=True) (project_dir / dir_name).mkdir(parents=True, exist_ok=True)
@@ -501,7 +508,7 @@ python_functions = ["test_*"]
def _create_init_py(self, project_dir: Path, project_name: str): def _create_init_py(self, project_dir: Path, project_name: str):
"""Create package __init__.py file.""" """Create package __init__.py file."""
package_name = project_name.replace('-', '_') package_name = project_name.replace("-", "_")
init_content = f'''""" init_content = f'''"""
{project_name} - A Python project with Kaizen Agentic agents. {project_name} - A Python project with Kaizen Agentic agents.
""" """
@@ -512,7 +519,7 @@ __version__ = "0.1.0"
def _create_makefile(self, project_dir: Path, project_name: str): def _create_makefile(self, project_dir: Path, project_name: str):
"""Create Makefile with standard targets.""" """Create Makefile with standard targets."""
package_name = project_name.replace('-', '_') package_name = project_name.replace("-", "_")
makefile_content = f"""# {project_name} - Makefile for development workflow makefile_content = f"""# {project_name} - Makefile for development workflow
# Generated by Kaizen Agentic # Generated by Kaizen Agentic

View File

@@ -13,6 +13,7 @@ from .detection import AgentSystemDetector, DetectedAgent, AgentSystemType
class MigrationStrategy(Enum): class MigrationStrategy(Enum):
"""Strategies for migrating existing agents.""" """Strategies for migrating existing agents."""
REPLACE = "replace" # Replace with Kaizen equivalent REPLACE = "replace" # Replace with Kaizen equivalent
EXTEND = "extend" # Extend Kaizen agent with custom functionality EXTEND = "extend" # Extend Kaizen agent with custom functionality
PRESERVE = "preserve" # Keep custom agent alongside Kaizen agents PRESERVE = "preserve" # Keep custom agent alongside Kaizen agents
@@ -22,6 +23,7 @@ class MigrationStrategy(Enum):
class ConflictResolution(Enum): class ConflictResolution(Enum):
"""Ways to resolve conflicts between agents.""" """Ways to resolve conflicts between agents."""
RENAME = "rename" # Rename one of the conflicting agents RENAME = "rename" # Rename one of the conflicting agents
NAMESPACE = "namespace" # Put agents in different namespaces NAMESPACE = "namespace" # Put agents in different namespaces
MERGE_FUNCTIONALITY = "merge" # Combine functionality MERGE_FUNCTIONALITY = "merge" # Combine functionality
@@ -33,6 +35,7 @@ class ConflictResolution(Enum):
@dataclass @dataclass
class MigrationPlan: class MigrationPlan:
"""Plan for migrating an existing agent.""" """Plan for migrating an existing agent."""
source_agent: DetectedAgent source_agent: DetectedAgent
strategy: MigrationStrategy strategy: MigrationStrategy
target_agent: Optional[str] = None # Kaizen agent name if applicable target_agent: Optional[str] = None # Kaizen agent name if applicable
@@ -48,6 +51,7 @@ class MigrationPlan:
@dataclass @dataclass
class ConflictResolutionPlan: class ConflictResolutionPlan:
"""Plan for resolving a conflict between agents.""" """Plan for resolving a conflict between agents."""
agent1: str agent1: str
agent2: str agent2: str
conflict_type: str conflict_type: str
@@ -62,6 +66,7 @@ class ConflictResolutionPlan:
@dataclass @dataclass
class IntegrationPlan: class IntegrationPlan:
"""Complete plan for integrating Kaizen agents into an existing project.""" """Complete plan for integrating Kaizen agents into an existing project."""
project_path: Path project_path: Path
migration_plans: List[MigrationPlan] migration_plans: List[MigrationPlan]
conflict_resolutions: List[ConflictResolutionPlan] conflict_resolutions: List[ConflictResolutionPlan]
@@ -104,10 +109,18 @@ class AgentMigrationPlanner:
} }
self.functional_categories = { self.functional_categories = {
"project_management": ["keepaTodofile", "project-management", "priority-evaluation"], "project_management": [
"keepaTodofile",
"project-management",
"priority-evaluation",
],
"testing": ["testing-efficiency", "test-maintenance", "tdd-workflow"], "testing": ["testing-efficiency", "test-maintenance", "tdd-workflow"],
"documentation": ["claude-documentation", "keepaContributingfile"], "documentation": ["claude-documentation", "keepaContributingfile"],
"code_quality": ["code-refactoring", "datamodel-optimization", "optimization"], "code_quality": [
"code-refactoring",
"datamodel-optimization",
"optimization",
],
"infrastructure": ["setupRepository", "tooling-optimization"], "infrastructure": ["setupRepository", "tooling-optimization"],
"version_control": ["keepaChangelog"], "version_control": ["keepaChangelog"],
} }
@@ -118,7 +131,9 @@ class AgentMigrationPlanner:
detection_result = detector.detect_agent_systems(project_path) detection_result = detector.detect_agent_systems(project_path)
# Create backup directory # Create backup directory
backup_dir = project_path / f".kaizen-migration-backup-{int(Path().stat().st_mtime)}" backup_dir = (
project_path / f".kaizen-migration-backup-{int(Path().stat().st_mtime)}"
)
# Create migration plans for each detected agent # Create migration plans for each detected agent
migration_plans = [] migration_plans = []
@@ -145,7 +160,7 @@ class AgentMigrationPlanner:
conflict_resolutions=conflict_resolutions, conflict_resolutions=conflict_resolutions,
backup_directory=backup_dir, backup_directory=backup_dir,
integration_order=integration_order, integration_order=integration_order,
post_migration_tasks=post_migration_tasks post_migration_tasks=post_migration_tasks,
) )
def _create_migration_plan( def _create_migration_plan(
@@ -190,7 +205,7 @@ class AgentMigrationPlanner:
strategy=strategy, strategy=strategy,
target_agent=target_agent, target_agent=target_agent,
backup_path=backup_path, backup_path=backup_path,
migration_notes=notes migration_notes=notes,
) )
def _has_unique_functionality(self, agent: DetectedAgent) -> bool: def _has_unique_functionality(self, agent: DetectedAgent) -> bool:
@@ -203,8 +218,18 @@ class AgentMigrationPlanner:
"""Check if a custom agent is essential to the project.""" """Check if a custom agent is essential to the project."""
# Heuristics for essential agents # Heuristics for essential agents
essential_patterns = [ essential_patterns = [
"deploy", "ci", "cd", "build", "release", "secret", "auth", "deploy",
"database", "api", "server", "client", "integration" "ci",
"cd",
"build",
"release",
"secret",
"auth",
"database",
"api",
"server",
"client",
"integration",
] ]
agent_name_lower = agent.name.lower() agent_name_lower = agent.name.lower()
@@ -230,7 +255,7 @@ class AgentMigrationPlanner:
resolution = ConflictResolution.RENAME resolution = ConflictResolution.RENAME
action_details = { action_details = {
"rename_agent": agent2, # Rename the second agent "rename_agent": agent2, # Rename the second agent
"new_name": f"{agent2}_custom" "new_name": f"{agent2}_custom",
} }
elif "Functional overlap" in reason: elif "Functional overlap" in reason:
# Check if one is a Kaizen agent # Check if one is a Kaizen agent
@@ -252,10 +277,12 @@ class AgentMigrationPlanner:
agent2=agent2, agent2=agent2,
conflict_type=reason, conflict_type=reason,
resolution=resolution, resolution=resolution,
action_details=action_details action_details=action_details,
) )
def _determine_integration_order(self, migration_plans: List[MigrationPlan]) -> List[str]: def _determine_integration_order(
self, migration_plans: List[MigrationPlan]
) -> List[str]:
"""Determine the order to perform migrations.""" """Determine the order to perform migrations."""
# Order by dependency and risk # Order by dependency and risk
order = [] order = []
@@ -265,7 +292,11 @@ class AgentMigrationPlanner:
order.extend([p.source_agent.name for p in infra_agents]) order.extend([p.source_agent.name for p in infra_agents])
# 2. Core functionality agents # 2. Core functionality agents
core_agents = [p for p in migration_plans if self._is_core_agent(p) and p not in infra_agents] core_agents = [
p
for p in migration_plans
if self._is_core_agent(p) and p not in infra_agents
]
order.extend([p.source_agent.name for p in core_agents]) order.extend([p.source_agent.name for p in core_agents])
# 3. Optional/enhancement agents last # 3. Optional/enhancement agents last
@@ -287,7 +318,10 @@ class AgentMigrationPlanner:
return any(keyword in agent_name for keyword in core_keywords) return any(keyword in agent_name for keyword in core_keywords)
def _generate_migration_notes( def _generate_migration_notes(
self, agent: DetectedAgent, strategy: MigrationStrategy, target_agent: Optional[str] self,
agent: DetectedAgent,
strategy: MigrationStrategy,
target_agent: Optional[str],
) -> List[str]: ) -> List[str]:
"""Generate helpful notes for the migration.""" """Generate helpful notes for the migration."""
notes = [] notes = []
@@ -332,13 +366,15 @@ class AgentMigrationPlanner:
if detection_result.config_files: if detection_result.config_files:
tasks.append("Verify all configuration files are updated") tasks.append("Verify all configuration files are updated")
tasks.extend([ tasks.extend(
"Run 'kaizen-agentic validate' to verify installation", [
"Test all agent functionality", "Run 'kaizen-agentic validate' to verify installation",
"Update project documentation", "Test all agent functionality",
"Train team on new agent workflows", "Update project documentation",
"Archive or remove backup files after verification" "Train team on new agent workflows",
]) "Archive or remove backup files after verification",
]
)
return tasks return tasks
@@ -349,7 +385,9 @@ class AgentMigrator:
def __init__(self): def __init__(self):
self.planner = AgentMigrationPlanner() self.planner = AgentMigrationPlanner()
def execute_migration(self, plan: IntegrationPlan, dry_run: bool = True) -> Dict[str, str]: def execute_migration(
self, plan: IntegrationPlan, dry_run: bool = True
) -> Dict[str, str]:
"""Execute a migration plan.""" """Execute a migration plan."""
results = {} results = {}
@@ -361,7 +399,7 @@ class AgentMigrator:
for agent_name in plan.integration_order: for agent_name in plan.integration_order:
migration_plan = next( migration_plan = next(
(p for p in plan.migration_plans if p.source_agent.name == agent_name), (p for p in plan.migration_plans if p.source_agent.name == agent_name),
None None,
) )
if migration_plan: if migration_plan:
result = self._execute_single_migration(migration_plan, dry_run) result = self._execute_single_migration(migration_plan, dry_run)
@@ -413,11 +451,14 @@ class AgentMigrator:
extension_config = { extension_config = {
"base_agent": plan.target_agent, "base_agent": plan.target_agent,
"custom_source": str(plan.source_agent.file_path), "custom_source": str(plan.source_agent.file_path),
"extension_type": "functional_overlay" "extension_type": "functional_overlay",
} }
extension_path = plan.source_agent.file_path.parent / f"{plan.source_agent.name}_extension.json" extension_path = (
with open(extension_path, 'w') as f: plan.source_agent.file_path.parent
/ f"{plan.source_agent.name}_extension.json"
)
with open(extension_path, "w") as f:
json.dump(extension_config, f, indent=2) json.dump(extension_config, f, indent=2)
return f"EXTENDED: {plan.target_agent} with {plan.source_agent.name}" return f"EXTENDED: {plan.target_agent} with {plan.source_agent.name}"
@@ -427,7 +468,10 @@ class AgentMigrator:
# Rename if necessary to avoid conflicts # Rename if necessary to avoid conflicts
if plan.source_agent.name in ["todo", "changelog", "test"]: if plan.source_agent.name in ["todo", "changelog", "test"]:
new_name = f"{plan.source_agent.name}_custom" new_name = f"{plan.source_agent.name}_custom"
new_path = plan.source_agent.file_path.parent / f"{new_name}{plan.source_agent.file_path.suffix}" new_path = (
plan.source_agent.file_path.parent
/ f"{new_name}{plan.source_agent.file_path.suffix}"
)
shutil.move(plan.source_agent.file_path, new_path) shutil.move(plan.source_agent.file_path, new_path)
return f"PRESERVED: {plan.source_agent.name} -> {new_name}" return f"PRESERVED: {plan.source_agent.name} -> {new_name}"
@@ -458,4 +502,4 @@ class AgentMigrator:
elif plan.resolution == ConflictResolution.CHOOSE_KAIZEN: elif plan.resolution == ConflictResolution.CHOOSE_KAIZEN:
return f"RESOLVED: Chose Kaizen agent {plan.action_details['keep']}" return f"RESOLVED: Chose Kaizen agent {plan.action_details['keep']}"
else: else:
return f"RESOLUTION_PLANNED: {plan.resolution.value}" return f"RESOLUTION_PLANNED: {plan.resolution.value}"

View File

@@ -10,6 +10,7 @@ from enum import Enum
class AgentCategory(Enum): class AgentCategory(Enum):
"""Categories of agents for organization.""" """Categories of agents for organization."""
PROJECT_MANAGEMENT = "project-management" PROJECT_MANAGEMENT = "project-management"
DEVELOPMENT_PROCESS = "development-process" DEVELOPMENT_PROCESS = "development-process"
CODE_QUALITY = "code-quality" CODE_QUALITY = "code-quality"
@@ -21,6 +22,7 @@ class AgentCategory(Enum):
@dataclass @dataclass
class AgentDefinition: class AgentDefinition:
"""Represents an agent definition with metadata.""" """Represents an agent definition with metadata."""
name: str name: str
description: str description: str
file_path: Path file_path: Path
@@ -31,11 +33,11 @@ class AgentDefinition:
@classmethod @classmethod
def from_file(cls, file_path: Path) -> "AgentDefinition": def from_file(cls, file_path: Path) -> "AgentDefinition":
"""Create AgentDefinition from a markdown file.""" """Create AgentDefinition from a markdown file."""
with open(file_path, 'r', encoding='utf-8') as f: with open(file_path, "r", encoding="utf-8") as f:
content = f.read() content = f.read()
# Extract YAML frontmatter # Extract YAML frontmatter
frontmatter_match = re.match(r'^---\n(.*?)\n---\n', content, re.DOTALL) frontmatter_match = re.match(r"^---\n(.*?)\n---\n", content, re.DOTALL)
if not frontmatter_match: if not frontmatter_match:
raise ValueError(f"No YAML frontmatter found in {file_path}") raise ValueError(f"No YAML frontmatter found in {file_path}")
@@ -45,15 +47,15 @@ class AgentDefinition:
dependencies = cls._extract_dependencies(content, frontmatter) dependencies = cls._extract_dependencies(content, frontmatter)
# Determine category from name or content # Determine category from name or content
category = cls._determine_category(frontmatter['name'], content) category = cls._determine_category(frontmatter["name"], content)
return cls( return cls(
name=frontmatter['name'], name=frontmatter["name"],
description=frontmatter['description'], description=frontmatter["description"],
file_path=file_path, file_path=file_path,
category=category, category=category,
dependencies=dependencies, dependencies=dependencies,
model=frontmatter.get('model') model=frontmatter.get("model"),
) )
@staticmethod @staticmethod
@@ -62,42 +64,42 @@ class AgentDefinition:
dependencies = set() dependencies = set()
# Check frontmatter for explicit dependencies # Check frontmatter for explicit dependencies
for key in ['dependencies', 'depends_on', 'requires']: for key in ["dependencies", "depends_on", "requires"]:
if key in frontmatter: if key in frontmatter:
deps = frontmatter[key] deps = frontmatter[key]
if isinstance(deps, list): if isinstance(deps, list):
dependencies.update(deps) dependencies.update(deps)
elif isinstance(deps, str): elif isinstance(deps, str):
# Handle comma-separated string # Handle comma-separated string
dependencies.update([d.strip() for d in deps.split(',')]) dependencies.update([d.strip() for d in deps.split(",")])
# Look for explicit dependencies in content # Look for explicit dependencies in content
dep_patterns = [ dep_patterns = [
r'depends_on:\s*\[(.*?)\]', r"depends_on:\s*\[(.*?)\]",
r'requires:\s*\[(.*?)\]', r"requires:\s*\[(.*?)\]",
r'dependencies:\s*\[(.*?)\]', r"dependencies:\s*\[(.*?)\]",
] ]
for pattern in dep_patterns: for pattern in dep_patterns:
matches = re.findall(pattern, content, re.IGNORECASE) matches = re.findall(pattern, content, re.IGNORECASE)
for match in matches: for match in matches:
if isinstance(match, str): if isinstance(match, str):
deps = [d.strip().strip('"\'') for d in match.split(',')] deps = [d.strip().strip("\"'") for d in match.split(",")]
dependencies.update(deps) dependencies.update(deps)
# Look for specific agent references in content (more precise) # Look for specific agent references in content (more precise)
# Only look for full agent names like "todo-keeper agent" or "uses changelog-keeper" # Only look for full agent names like "todo-keeper agent" or "uses changelog-keeper"
agent_patterns = [ agent_patterns = [
r'uses?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))', r"uses?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
r'depends?\s+on\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))', r"depends?\s+on\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
r'requires?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))', r"requires?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
] ]
for pattern in agent_patterns: for pattern in agent_patterns:
matches = re.findall(pattern, content.lower()) matches = re.findall(pattern, content.lower())
for match in matches: for match in matches:
if match not in ['optimization', 'agentic', 'driven', 'assisted']: if match not in ["optimization", "agentic", "driven", "assisted"]:
dependencies.add(match.replace('-', '_')) dependencies.add(match.replace("-", "_"))
return dependencies return dependencies
@@ -107,28 +109,33 @@ class AgentDefinition:
name_lower = name.lower() name_lower = name.lower()
# Project management agents # Project management agents
project_keywords = ['todo', 'changelog', 'contributing', 'project'] project_keywords = ["todo", "changelog", "contributing", "project"]
if any(keyword in name_lower for keyword in project_keywords): if any(keyword in name_lower for keyword in project_keywords):
return AgentCategory.PROJECT_MANAGEMENT return AgentCategory.PROJECT_MANAGEMENT
# Testing agents # Testing agents
if any(keyword in name_lower for keyword in ['test', 'tdd']): if any(keyword in name_lower for keyword in ["test", "tdd"]):
return AgentCategory.TESTING return AgentCategory.TESTING
# Code quality agents # Code quality agents
if any(keyword in name_lower for keyword in ['refactor', 'optimization', 'code']): if any(
keyword in name_lower for keyword in ["refactor", "optimization", "code"]
):
return AgentCategory.CODE_QUALITY return AgentCategory.CODE_QUALITY
# Documentation agents # Documentation agents
if any(keyword in name_lower for keyword in ['documentation', 'claude']): if any(keyword in name_lower for keyword in ["documentation", "claude"]):
return AgentCategory.DOCUMENTATION return AgentCategory.DOCUMENTATION
# Infrastructure agents # Infrastructure agents
if any(keyword in name_lower for keyword in ['setup', 'repository', 'tooling']): if any(keyword in name_lower for keyword in ["setup", "repository", "tooling"]):
return AgentCategory.INFRASTRUCTURE return AgentCategory.INFRASTRUCTURE
# Development process agents # Development process agents
if any(keyword in name_lower for keyword in ['workflow', 'requirements', 'maintenance']): if any(
keyword in name_lower
for keyword in ["workflow", "requirements", "maintenance"]
):
return AgentCategory.DEVELOPMENT_PROCESS return AgentCategory.DEVELOPMENT_PROCESS
# Default fallback # Default fallback
@@ -159,7 +166,9 @@ class AgentRegistry:
"""Get agent definition by name.""" """Get agent definition by name."""
return self._agents.get(name) return self._agents.get(name)
def list_agents(self, category: Optional[AgentCategory] = None) -> List[AgentDefinition]: def list_agents(
self, category: Optional[AgentCategory] = None
) -> List[AgentDefinition]:
"""List all agents, optionally filtered by category.""" """List all agents, optionally filtered by category."""
agents = list(self._agents.values()) agents = list(self._agents.values())
if category: if category:
@@ -232,7 +241,9 @@ class AgentRegistry:
return errors return errors
def _has_circular_dependency(self, agent_name: str, visited: Optional[Set[str]] = None) -> bool: def _has_circular_dependency(
self, agent_name: str, visited: Optional[Set[str]] = None
) -> bool:
"""Check if an agent has circular dependencies.""" """Check if an agent has circular dependencies."""
if visited is None: if visited is None:
visited = set() visited = set()
@@ -255,18 +266,14 @@ class AgentRegistry:
def get_agent_templates(self) -> Dict[str, List[str]]: def get_agent_templates(self) -> Dict[str, List[str]]:
"""Get predefined agent templates for different project types.""" """Get predefined agent templates for different project types."""
return { return {
"python-basic": [ "python-basic": ["setupRepository", "keepaTodofile", "keepaChangelog"],
"setupRepository",
"keepaTodofile",
"keepaChangelog"
],
"python-web": [ "python-web": [
"setupRepository", "setupRepository",
"tdd-workflow", "tdd-workflow",
"code-refactoring", "code-refactoring",
"keepaTodofile", "keepaTodofile",
"keepaChangelog", "keepaChangelog",
"keepaContributingfile" "keepaContributingfile",
], ],
"python-cli": [ "python-cli": [
"setupRepository", "setupRepository",
@@ -274,7 +281,7 @@ class AgentRegistry:
"testing-efficiency", "testing-efficiency",
"claude-documentation", "claude-documentation",
"keepaTodofile", "keepaTodofile",
"keepaChangelog" "keepaChangelog",
], ],
"python-data": [ "python-data": [
"setupRepository", "setupRepository",
@@ -282,9 +289,7 @@ class AgentRegistry:
"testing-efficiency", "testing-efficiency",
"requirements-engineering", "requirements-engineering",
"keepaTodofile", "keepaTodofile",
"keepaChangelog" "keepaChangelog",
], ],
"comprehensive": [ "comprehensive": [agent.name for agent in self.list_agents()],
agent.name for agent in self.list_agents()
]
} }

View File

@@ -3,7 +3,11 @@
import json import json
import pytest import pytest
from pathlib import Path from pathlib import Path
from kaizen_agentic.installer import AgentInstaller, ProjectInitializer, InstallationConfig from kaizen_agentic.installer import (
AgentInstaller,
ProjectInitializer,
InstallationConfig,
)
from kaizen_agentic.registry import AgentRegistry from kaizen_agentic.registry import AgentRegistry
@@ -72,9 +76,7 @@ def test_install_agents(test_registry, tmp_path):
project_dir = tmp_path / "test_project" project_dir = tmp_path / "test_project"
config = InstallationConfig( config = InstallationConfig(
target_dir=project_dir, target_dir=project_dir, create_backup=False, update_docs=False
create_backup=False,
update_docs=False
) )
results = installer.install_agents(["base-agent"], config) results = installer.install_agents(["base-agent"], config)
@@ -89,9 +91,7 @@ def test_install_agents_with_dependencies(test_registry, tmp_path):
project_dir = tmp_path / "test_project" project_dir = tmp_path / "test_project"
config = InstallationConfig( config = InstallationConfig(
target_dir=project_dir, target_dir=project_dir, create_backup=False, update_docs=False
create_backup=False,
update_docs=False
) )
# Install an agent that depends on others # Install an agent that depends on others
@@ -111,7 +111,9 @@ def test_list_installed_agents(test_registry, tmp_path):
assert installed == [] assert installed == []
# Install some agents # Install some agents
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False) config = InstallationConfig(
target_dir=project_dir, create_backup=False, update_docs=False
)
installer.install_agents(["base-agent", "keepaTodofile"], config) installer.install_agents(["base-agent", "keepaTodofile"], config)
# Check installed agents # Check installed agents
@@ -127,7 +129,9 @@ def test_update_agents(test_registry, tmp_path):
project_dir = tmp_path / "test_project" project_dir = tmp_path / "test_project"
# Install initial agents # Install initial agents
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False) config = InstallationConfig(
target_dir=project_dir, create_backup=False, update_docs=False
)
installer.install_agents(["base-agent"], config) installer.install_agents(["base-agent"], config)
# Update all agents # Update all agents
@@ -146,7 +150,9 @@ def test_remove_agents(test_registry, tmp_path):
project_dir = tmp_path / "test_project" project_dir = tmp_path / "test_project"
# Install agents first # Install agents first
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False) config = InstallationConfig(
target_dir=project_dir, create_backup=False, update_docs=False
)
installer.install_agents(["base-agent", "keepaTodofile"], config) installer.install_agents(["base-agent", "keepaTodofile"], config)
# Remove an agent # Remove an agent
@@ -170,7 +176,9 @@ def test_validate_installation(test_registry, tmp_path):
assert "No agents directory found" in errors["project"] assert "No agents directory found" in errors["project"]
# Install agents and validate # Install agents and validate
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False) config = InstallationConfig(
target_dir=project_dir, create_backup=False, update_docs=False
)
installer.install_agents(["base-agent"], config) installer.install_agents(["base-agent"], config)
errors = installer.validate_installation(project_dir) errors = installer.validate_installation(project_dir)
@@ -187,7 +195,7 @@ def test_update_claude_config(test_registry, tmp_path):
target_dir=project_dir, target_dir=project_dir,
claude_config_path=claude_config, claude_config_path=claude_config,
create_backup=False, create_backup=False,
update_docs=False update_docs=False,
) )
installer.install_agents(["base-agent"], config) installer.install_agents(["base-agent"], config)
@@ -208,9 +216,7 @@ def test_project_initializer(test_registry, tmp_path):
project_dir = tmp_path / "new_project" project_dir = tmp_path / "new_project"
initializer.init_project( initializer.init_project(
project_dir, project_dir, template="python-basic", project_name="new_project"
template="python-basic",
project_name="new_project"
) )
# Check that project structure was created # Check that project structure was created
@@ -236,7 +242,7 @@ def test_project_initializer_custom_agents(test_registry, tmp_path):
project_dir, project_dir,
template="python-basic", template="python-basic",
agent_names=["base-agent", "keepaTodofile"], agent_names=["base-agent", "keepaTodofile"],
project_name="custom_project" project_name="custom_project",
) )
# Check that specific agents were installed # Check that specific agents were installed
@@ -251,7 +257,7 @@ def test_installation_config():
claude_config_path=Path("/tmp/test/claude.json"), claude_config_path=Path("/tmp/test/claude.json"),
makefile_path=Path("/tmp/test/Makefile"), makefile_path=Path("/tmp/test/Makefile"),
update_docs=True, update_docs=True,
create_backup=False create_backup=False,
) )
assert config.target_dir == Path("/tmp/test") assert config.target_dir == Path("/tmp/test")

View File

@@ -158,7 +158,10 @@ This agent uses both base-agent and dependent-agent.
# Test complex dependency resolution # Test complex dependency resolution
resolved = registry.resolve_dependencies(["complex-agent"]) resolved = registry.resolve_dependencies(["complex-agent"])
assert all(agent in resolved for agent in ["base-agent", "dependent-agent", "complex-agent"]) assert all(
agent in resolved
for agent in ["base-agent", "dependent-agent", "complex-agent"]
)
def test_agent_registry_get_templates(tmp_path): def test_agent_registry_get_templates(tmp_path):