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:
2
Makefile
2
Makefile
@@ -730,7 +730,7 @@ release-check: $(VENV)/bin/activate
|
||||
echo "📋 Release Readiness Checklist:"; \
|
||||
echo "==============================="; \
|
||||
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 ""); \
|
||||
if [ "$$CHANGELOG_VERSION" = "$$PYPROJECT_VERSION" ] && [ -n "$$CHANGELOG_VERSION" ]; then \
|
||||
echo " ✅ Versions consistent: $$CHANGELOG_VERSION"; \
|
||||
|
||||
@@ -17,10 +17,12 @@ def cli():
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--category',
|
||||
@click.option(
|
||||
"--category",
|
||||
type=click.Choice([c.value for c in AgentCategory]),
|
||||
help='Filter by category')
|
||||
@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):
|
||||
"""List available agents."""
|
||||
registry = _get_registry()
|
||||
@@ -34,7 +36,9 @@ def list(category: Optional[str], verbose: bool):
|
||||
if verbose:
|
||||
categories = registry.get_categories()
|
||||
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)
|
||||
for agent in agents:
|
||||
click.echo(f" • {agent.name}: {agent.description}")
|
||||
@@ -56,10 +60,10 @@ def list(category: Optional[str], verbose: bool):
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument('agents', nargs=-1, required=True)
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.option('--no-backup', is_flag=True, help='Skip creating backup')
|
||||
@click.option('--no-docs', is_flag=True, help='Skip updating documentation')
|
||||
@click.argument("agents", nargs=-1, required=True)
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
@click.option("--no-backup", is_flag=True, help="Skip creating backup")
|
||||
@click.option("--no-docs", is_flag=True, help="Skip updating documentation")
|
||||
def install(agents: List[str], target: str, no_backup: bool, no_docs: bool):
|
||||
"""Install agents into a project."""
|
||||
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",
|
||||
makefile_path=target_path / "Makefile",
|
||||
update_docs=not no_docs,
|
||||
create_backup=not no_backup
|
||||
create_backup=not no_backup,
|
||||
)
|
||||
|
||||
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()
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.argument('agents', nargs=-1)
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
@click.argument("agents", nargs=-1)
|
||||
def update(target: str, agents: List[str]):
|
||||
"""Update installed agents."""
|
||||
registry = _get_registry()
|
||||
@@ -131,8 +135,8 @@ def update(target: str, agents: List[str]):
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument('agents', nargs=-1, required=True)
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.argument("agents", nargs=-1, required=True)
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
def remove(agents: List[str], target: str):
|
||||
"""Remove agents from a project."""
|
||||
registry = _get_registry()
|
||||
@@ -154,11 +158,17 @@ def remove(agents: List[str], target: str):
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument('project_name')
|
||||
@click.option('--template', '-t', 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)')
|
||||
@click.argument("project_name")
|
||||
@click.option(
|
||||
"--template",
|
||||
"-t",
|
||||
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):
|
||||
"""Initialize a new project with agents."""
|
||||
registry = _get_registry()
|
||||
@@ -173,7 +183,7 @@ def init(project_name: str, template: str, agents: Optional[str], parent_dir: st
|
||||
# Parse agent list
|
||||
agent_list = None
|
||||
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"Template: {template}")
|
||||
@@ -204,7 +214,7 @@ def init(project_name: str, template: str, agents: Optional[str], parent_dir: st
|
||||
|
||||
|
||||
@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):
|
||||
"""Validate agents in a project."""
|
||||
registry = _get_registry()
|
||||
@@ -263,7 +273,7 @@ def templates():
|
||||
|
||||
|
||||
@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):
|
||||
"""Show status of agents in a project."""
|
||||
registry = _get_registry()
|
||||
@@ -321,8 +331,8 @@ def status(target: str):
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.option('--detailed', '-d', is_flag=True, help='Show detailed analysis')
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
@click.option("--detailed", "-d", is_flag=True, help="Show detailed analysis")
|
||||
def detect(target: str, detailed: bool):
|
||||
"""Detect existing agent systems in a project."""
|
||||
from .detection import AgentSystemDetector
|
||||
@@ -372,11 +382,13 @@ def detect(target: str, detailed: bool):
|
||||
|
||||
# Show 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
|
||||
if result.migration_recommendations:
|
||||
click.echo(f"\n📋 Migration Recommendations:")
|
||||
click.echo("\n📋 Migration Recommendations:")
|
||||
for recommendation in result.migration_recommendations:
|
||||
if recommendation.startswith(" "):
|
||||
click.echo(f" {recommendation}")
|
||||
@@ -384,14 +396,18 @@ def detect(target: str, detailed: bool):
|
||||
click.echo(f" • {recommendation}")
|
||||
|
||||
if not result.detected_systems:
|
||||
click.echo(f"\n✨ This project is ready for Kaizen Agentic installation!")
|
||||
click.echo(f" Run: kaizen-agentic install <agent-names>")
|
||||
click.echo("\n✨ This project is ready for Kaizen Agentic installation!")
|
||||
click.echo(" Run: kaizen-agentic install <agent-names>")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@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('--auto-resolve', '-a', is_flag=True, help='Automatically resolve simple conflicts')
|
||||
@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(
|
||||
"--auto-resolve", "-a", is_flag=True, help="Automatically resolve simple conflicts"
|
||||
)
|
||||
def migrate(target: str, dry_run: bool, auto_resolve: bool):
|
||||
"""Create migration plan for integrating Kaizen agents into existing project."""
|
||||
from .migration import AgentMigrationPlanner, AgentMigrator
|
||||
@@ -408,7 +424,10 @@ def migrate(target: str, dry_run: bool, auto_resolve: bool):
|
||||
planner = AgentMigrationPlanner()
|
||||
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(" Run: kaizen-agentic install <agent-names>")
|
||||
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)}):")
|
||||
for plan in integration_plan.migration_plans:
|
||||
strategy_emoji = {
|
||||
"replace": "🔄", "extend": "🔗", "preserve": "💾",
|
||||
"merge": "🔀", "remove": "🗑️"
|
||||
"replace": "🔄",
|
||||
"extend": "🔗",
|
||||
"preserve": "💾",
|
||||
"merge": "🔀",
|
||||
"remove": "🗑️",
|
||||
}
|
||||
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}")
|
||||
if 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
|
||||
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:
|
||||
click.echo(f" • {resolution.agent1} vs {resolution.agent2}")
|
||||
click.echo(f" Resolution: {resolution.resolution.value}")
|
||||
@@ -443,31 +469,35 @@ def migrate(target: str, dry_run: bool, auto_resolve: bool):
|
||||
|
||||
# Show 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):
|
||||
click.echo(f" {i}. {agent_name}")
|
||||
|
||||
# Show 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:
|
||||
click.echo(f" • {task}")
|
||||
|
||||
# Execute migration if requested
|
||||
if not dry_run:
|
||||
click.echo(f"\n🚀 Executing migration...")
|
||||
click.echo("\n🚀 Executing migration...")
|
||||
migrator = AgentMigrator()
|
||||
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():
|
||||
status_emoji = "✅" if "ERROR" not in result else "❌"
|
||||
click.echo(f" {status_emoji} {agent}: {result}")
|
||||
|
||||
click.echo(f"\n💾 Backup created at: {integration_plan.backup_directory}")
|
||||
else:
|
||||
click.echo(f"\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}")
|
||||
click.echo(
|
||||
"\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()
|
||||
@@ -477,8 +507,8 @@ def extensions():
|
||||
|
||||
|
||||
@extensions.command()
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.option('--base-agent', '-b', help='Filter by base agent')
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
@click.option("--base-agent", "-b", help="Filter by base agent")
|
||||
def list_extensions(target: str, base_agent: Optional[str]):
|
||||
"""List installed extensions."""
|
||||
from .extensions import ExtensionManager
|
||||
@@ -510,12 +540,14 @@ def list_extensions(target: str, base_agent: Optional[str]):
|
||||
|
||||
|
||||
@extensions.command()
|
||||
@click.argument('name')
|
||||
@click.argument('base_agent')
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.option('--description', '-d', help='Extension description')
|
||||
@click.option('--template', default='basic', help='Template type (basic, advanced)')
|
||||
def create(name: str, base_agent: str, target: str, description: Optional[str], template: str):
|
||||
@click.argument("name")
|
||||
@click.argument("base_agent")
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
@click.option("--description", "-d", help="Extension description")
|
||||
@click.option("--template", default="basic", help="Template type (basic, advanced)")
|
||||
def create(
|
||||
name: str, base_agent: str, target: str, description: Optional[str], template: str
|
||||
):
|
||||
"""Create a new agent extension."""
|
||||
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)
|
||||
|
||||
# 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
|
||||
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,
|
||||
base_agent=base_agent,
|
||||
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" Base agent: {base_agent}")
|
||||
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()
|
||||
@click.argument('name')
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.argument("name")
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
def enable(name: str, target: str):
|
||||
"""Enable an extension."""
|
||||
from .extensions import ExtensionManager
|
||||
@@ -563,8 +599,8 @@ def enable(name: str, target: str):
|
||||
|
||||
|
||||
@extensions.command()
|
||||
@click.argument('name')
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.argument("name")
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
def disable(name: str, target: str):
|
||||
"""Disable an extension."""
|
||||
from .extensions import ExtensionManager
|
||||
@@ -579,9 +615,9 @@ def disable(name: str, target: str):
|
||||
|
||||
|
||||
@extensions.command()
|
||||
@click.argument('name')
|
||||
@click.option('--target', '-t', default='.', help='Target directory (default: current)')
|
||||
@click.confirmation_option(prompt='Are you sure you want to remove this extension?')
|
||||
@click.argument("name")
|
||||
@click.option("--target", "-t", default=".", help="Target directory (default: current)")
|
||||
@click.confirmation_option(prompt="Are you sure you want to remove this extension?")
|
||||
def remove(name: str, target: str):
|
||||
"""Remove an extension."""
|
||||
from .extensions import ExtensionManager
|
||||
@@ -610,6 +646,7 @@ def _get_registry() -> AgentRegistry:
|
||||
# Try to find installed package
|
||||
try:
|
||||
import kaizen_agentic
|
||||
|
||||
package_dir = Path(kaizen_agentic.__file__).parent.parent.parent
|
||||
agents_dir = package_dir / "agents"
|
||||
if not agents_dir.exists():
|
||||
@@ -617,7 +654,9 @@ def _get_registry() -> AgentRegistry:
|
||||
agents_dir = Path(kaizen_agentic.__file__).parent / "data" / "agents"
|
||||
except ImportError:
|
||||
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)
|
||||
|
||||
if not agents_dir.exists():
|
||||
@@ -627,5 +666,5 @@ def _get_registry() -> AgentRegistry:
|
||||
return AgentRegistry(agents_dir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"""Detection and analysis of existing agent systems in projects."""
|
||||
|
||||
import json
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Set, Tuple
|
||||
from typing import List, Optional, Set, Tuple
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class AgentSystemType(Enum):
|
||||
"""Types of existing agent systems that might be found."""
|
||||
|
||||
KAIZEN_AGENTIC = "kaizen-agentic"
|
||||
CLAUDE_CODE = "claude-code"
|
||||
GITHUB_COPILOT = "github-copilot"
|
||||
@@ -25,6 +25,7 @@ class AgentSystemType(Enum):
|
||||
@dataclass
|
||||
class DetectedAgent:
|
||||
"""Information about a detected agent."""
|
||||
|
||||
name: str
|
||||
type: AgentSystemType
|
||||
file_path: Path
|
||||
@@ -44,6 +45,7 @@ class DetectedAgent:
|
||||
@dataclass
|
||||
class AgentSystemDetectionResult:
|
||||
"""Result of agent system detection in a project."""
|
||||
|
||||
project_path: Path
|
||||
detected_systems: List[AgentSystemType]
|
||||
agents: List[DetectedAgent]
|
||||
@@ -197,33 +199,37 @@ class AgentSystemDetector:
|
||||
agents.append(agent)
|
||||
except Exception as e:
|
||||
# Create a detected agent with error info
|
||||
agents.append(DetectedAgent(
|
||||
agents.append(
|
||||
DetectedAgent(
|
||||
name=agent_file.stem.replace("agent-", ""),
|
||||
type=AgentSystemType.KAIZEN_AGENTIC,
|
||||
file_path=agent_file,
|
||||
can_migrate=False,
|
||||
migration_notes=f"Parse error: {e}"
|
||||
))
|
||||
migration_notes=f"Parse error: {e}",
|
||||
)
|
||||
)
|
||||
|
||||
return agents
|
||||
|
||||
def _parse_kaizen_agent_file(self, agent_file: Path) -> Optional[DetectedAgent]:
|
||||
"""Parse a Kaizen Agentic agent file."""
|
||||
try:
|
||||
content = agent_file.read_text(encoding='utf-8')
|
||||
content = agent_file.read_text(encoding="utf-8")
|
||||
|
||||
# Extract YAML frontmatter
|
||||
if content.startswith('---'):
|
||||
parts = content.split('---', 2)
|
||||
if content.startswith("---"):
|
||||
parts = content.split("---", 2)
|
||||
if len(parts) >= 3:
|
||||
frontmatter = yaml.safe_load(parts[1])
|
||||
|
||||
return DetectedAgent(
|
||||
name=frontmatter.get('name', agent_file.stem.replace("agent-", "")),
|
||||
name=frontmatter.get(
|
||||
"name", agent_file.stem.replace("agent-", "")
|
||||
),
|
||||
type=AgentSystemType.KAIZEN_AGENTIC,
|
||||
file_path=agent_file,
|
||||
description=frontmatter.get('description'),
|
||||
dependencies=set(frontmatter.get('dependencies', []))
|
||||
description=frontmatter.get("description"),
|
||||
dependencies=set(frontmatter.get("dependencies", [])),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -238,12 +244,14 @@ class AgentSystemDetector:
|
||||
if claude_file.exists():
|
||||
# Claude Code typically doesn't have separate agent files
|
||||
# but might reference agent usage in CLAUDE.md
|
||||
agents.append(DetectedAgent(
|
||||
agents.append(
|
||||
DetectedAgent(
|
||||
name="claude-integration",
|
||||
type=AgentSystemType.CLAUDE_CODE,
|
||||
file_path=claude_file,
|
||||
description="Claude Code integration configuration"
|
||||
))
|
||||
description="Claude Code integration configuration",
|
||||
)
|
||||
)
|
||||
|
||||
return agents
|
||||
|
||||
@@ -261,15 +269,20 @@ class AgentSystemDetector:
|
||||
for pattern in ["*.py", "*.yml", "*.yaml", "*.json", "*.md"]:
|
||||
for agent_file in agent_dir.glob(pattern):
|
||||
# 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
|
||||
|
||||
agents.append(DetectedAgent(
|
||||
agents.append(
|
||||
DetectedAgent(
|
||||
name=agent_file.stem,
|
||||
type=AgentSystemType.CUSTOM_AGENTS,
|
||||
file_path=agent_file,
|
||||
description=f"Custom agent in {dir_name}/"
|
||||
))
|
||||
description=f"Custom agent in {dir_name}/",
|
||||
)
|
||||
)
|
||||
|
||||
return agents
|
||||
|
||||
@@ -286,7 +299,9 @@ class AgentSystemDetector:
|
||||
|
||||
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."""
|
||||
conflicts = []
|
||||
|
||||
@@ -298,15 +313,16 @@ class AgentSystemDetector:
|
||||
agents_by_type[agent.type].append(agent)
|
||||
|
||||
# Check for naming conflicts
|
||||
all_names = [agent.name for agent in agents]
|
||||
for i, agent1 in enumerate(agents):
|
||||
for j, agent2 in enumerate(agents[i + 1 :], i + 1):
|
||||
if agent1.name == agent2.name and agent1.type != agent2.type:
|
||||
conflicts.append((
|
||||
conflicts.append(
|
||||
(
|
||||
agent1.name,
|
||||
agent2.name,
|
||||
f"Name conflict between {agent1.type.value} and {agent2.type.value}"
|
||||
))
|
||||
f"Name conflict between {agent1.type.value} and {agent2.type.value}",
|
||||
)
|
||||
)
|
||||
|
||||
# Check for functional overlaps
|
||||
functional_conflicts = {
|
||||
@@ -325,11 +341,13 @@ class AgentSystemDetector:
|
||||
if len(matching_agents) > 1:
|
||||
for i, agent1 in enumerate(matching_agents):
|
||||
for agent2 in matching_agents[i + 1 :]:
|
||||
conflicts.append((
|
||||
conflicts.append(
|
||||
(
|
||||
agent1.name,
|
||||
agent2.name,
|
||||
f"Functional overlap: {conflict_type}"
|
||||
))
|
||||
f"Functional overlap: {conflict_type}",
|
||||
)
|
||||
)
|
||||
|
||||
return conflicts
|
||||
|
||||
@@ -343,7 +361,10 @@ class AgentSystemDetector:
|
||||
if AgentSystemType.KAIZEN_AGENTIC in detected_systems:
|
||||
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"
|
||||
|
||||
if len([a for a in agents if a.type == AgentSystemType.CUSTOM_AGENTS]) > 5:
|
||||
@@ -355,13 +376,15 @@ class AgentSystemDetector:
|
||||
self,
|
||||
detected_systems: List[AgentSystemType],
|
||||
agents: List[DetectedAgent],
|
||||
conflicts: List[Tuple[str, str, str]]
|
||||
conflicts: List[Tuple[str, str, str]],
|
||||
) -> List[str]:
|
||||
"""Generate migration recommendations."""
|
||||
recommendations = []
|
||||
|
||||
if not detected_systems:
|
||||
recommendations.append("Clean installation - no existing agent systems detected")
|
||||
recommendations.append(
|
||||
"Clean installation - no existing agent systems detected"
|
||||
)
|
||||
return recommendations
|
||||
|
||||
if AgentSystemType.KAIZEN_AGENTIC in detected_systems:
|
||||
@@ -369,15 +392,23 @@ class AgentSystemDetector:
|
||||
recommendations.append("Run 'kaizen-agentic update' to get latest agents")
|
||||
|
||||
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:
|
||||
recommendations.append(f" - Conflict: {agent1} vs {agent2} ({reason})")
|
||||
|
||||
custom_agents = [a for a in agents if a.type == AgentSystemType.CUSTOM_AGENTS]
|
||||
if custom_agents:
|
||||
recommendations.append(f"Consider migrating {len(custom_agents)} custom agents")
|
||||
recommendations.append(" - Review custom agents for Kaizen Agentic equivalents")
|
||||
recommendations.append(" - Create project-specific extensions for unique functionality")
|
||||
recommendations.append(
|
||||
f"Consider migrating {len(custom_agents)} custom agents"
|
||||
)
|
||||
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:
|
||||
recommendations.append("Maintain Claude Code compatibility")
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
import json
|
||||
import yaml
|
||||
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 enum import Enum
|
||||
|
||||
|
||||
class ExtensionType(Enum):
|
||||
"""Types of agent extensions."""
|
||||
|
||||
CONFIGURATION_OVERLAY = "config_overlay" # Override default configurations
|
||||
FUNCTIONAL_EXTENSION = "functional_extension" # Add new functionality
|
||||
WORKFLOW_INTEGRATION = "workflow_integration" # Integrate with project workflows
|
||||
@@ -21,6 +22,7 @@ class ExtensionType(Enum):
|
||||
@dataclass
|
||||
class AgentExtension:
|
||||
"""Defines an extension to a Kaizen agent."""
|
||||
|
||||
name: str
|
||||
base_agent: str # The Kaizen agent this extends
|
||||
extension_type: ExtensionType
|
||||
@@ -44,6 +46,7 @@ class AgentExtension:
|
||||
@dataclass
|
||||
class ProjectExtensionRegistry:
|
||||
"""Registry of extensions for a project."""
|
||||
|
||||
project_path: Path
|
||||
extensions: List[AgentExtension] = field(default_factory=list)
|
||||
global_config: Dict[str, Any] = field(default_factory=dict)
|
||||
@@ -63,7 +66,7 @@ class ExtensionManager:
|
||||
base_agent: str,
|
||||
extension_type: ExtensionType,
|
||||
description: str,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> AgentExtension:
|
||||
"""Create a new agent extension."""
|
||||
extension = AgentExtension(
|
||||
@@ -71,7 +74,7 @@ class ExtensionManager:
|
||||
base_agent=base_agent,
|
||||
extension_type=extension_type,
|
||||
description=description,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self._save_extension(extension)
|
||||
@@ -79,14 +82,16 @@ class ExtensionManager:
|
||||
|
||||
def install_extension(self, extension_path: Path) -> AgentExtension:
|
||||
"""Install an extension from a file."""
|
||||
if extension_path.suffix == '.json':
|
||||
if extension_path.suffix == ".json":
|
||||
with open(extension_path) as 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:
|
||||
data = yaml.safe_load(f)
|
||||
else:
|
||||
raise ValueError(f"Unsupported extension file format: {extension_path.suffix}")
|
||||
raise ValueError(
|
||||
f"Unsupported extension file format: {extension_path.suffix}"
|
||||
)
|
||||
|
||||
extension = AgentExtension(**data)
|
||||
self._save_extension(extension)
|
||||
@@ -127,6 +132,7 @@ class ExtensionManager:
|
||||
extension_dir = self.extensions_dir / name
|
||||
if extension_dir.exists():
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(extension_dir)
|
||||
|
||||
return True
|
||||
@@ -136,8 +142,11 @@ class ExtensionManager:
|
||||
def get_effective_config(self, base_agent: str) -> Dict[str, Any]:
|
||||
"""Get the effective configuration for an agent with all extensions applied."""
|
||||
base_config = self._get_base_agent_config(base_agent)
|
||||
extensions = [ext for ext in self._load_extensions()
|
||||
if ext.base_agent == base_agent and ext.enabled]
|
||||
extensions = [
|
||||
ext
|
||||
for ext in self._load_extensions()
|
||||
if ext.base_agent == base_agent and ext.enabled
|
||||
]
|
||||
|
||||
# Apply extensions in order
|
||||
for extension in extensions:
|
||||
@@ -151,7 +160,7 @@ class ExtensionManager:
|
||||
base_agent: str,
|
||||
custom_instructions: str,
|
||||
custom_commands: Optional[Dict[str, str]] = None,
|
||||
environment_config: Optional[Dict[str, Any]] = None
|
||||
environment_config: Optional[Dict[str, Any]] = None,
|
||||
) -> AgentExtension:
|
||||
"""Create a project-specific agent based on a Kaizen agent."""
|
||||
|
||||
@@ -161,8 +170,8 @@ class ExtensionManager:
|
||||
"project_context": {
|
||||
"name": self.project_path.name,
|
||||
"path": str(self.project_path),
|
||||
"type": self._detect_project_type()
|
||||
}
|
||||
"type": self._detect_project_type(),
|
||||
},
|
||||
}
|
||||
|
||||
if environment_config:
|
||||
@@ -175,7 +184,7 @@ class ExtensionManager:
|
||||
description=f"Project-specific extension of {base_agent} for {self.project_path.name}",
|
||||
configuration=config,
|
||||
custom_commands=custom_commands or {},
|
||||
environment_overrides=environment_config or {}
|
||||
environment_overrides=environment_config or {},
|
||||
)
|
||||
|
||||
# Create agent file
|
||||
@@ -187,7 +196,7 @@ class ExtensionManager:
|
||||
self,
|
||||
legacy_agent_path: Path,
|
||||
target_kaizen_agent: str,
|
||||
migration_strategy: str = "preserve_functionality"
|
||||
migration_strategy: str = "preserve_functionality",
|
||||
) -> AgentExtension:
|
||||
"""Integrate a legacy agent as an extension to a Kaizen agent."""
|
||||
|
||||
@@ -201,7 +210,7 @@ class ExtensionManager:
|
||||
"legacy_source": str(legacy_agent_path),
|
||||
"migration_strategy": migration_strategy,
|
||||
"preserved_functionality": legacy_analysis.get("functionality", []),
|
||||
"custom_config": legacy_analysis.get("config", {})
|
||||
"custom_config": legacy_analysis.get("config", {}),
|
||||
}
|
||||
|
||||
extension = self.create_extension(
|
||||
@@ -209,7 +218,7 @@ class ExtensionManager:
|
||||
base_agent=target_kaizen_agent,
|
||||
extension_type=ExtensionType.WORKFLOW_INTEGRATION,
|
||||
description=f"Legacy integration of {legacy_agent_path.name}",
|
||||
configuration=config
|
||||
configuration=config,
|
||||
)
|
||||
|
||||
# Create migration wrapper
|
||||
@@ -232,23 +241,23 @@ class ExtensionManager:
|
||||
extension_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 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
|
||||
data = {
|
||||
'name': extension.name,
|
||||
'base_agent': extension.base_agent,
|
||||
'extension_type': extension.extension_type.value,
|
||||
'description': extension.description,
|
||||
'version': extension.version,
|
||||
'author': extension.author,
|
||||
'configuration': extension.configuration,
|
||||
'custom_commands': extension.custom_commands,
|
||||
'workflow_hooks': extension.workflow_hooks,
|
||||
'data_transformations': extension.data_transformations,
|
||||
'environment_overrides': extension.environment_overrides,
|
||||
'dependencies': extension.dependencies,
|
||||
'compatibility': extension.compatibility,
|
||||
'enabled': extension.enabled
|
||||
"name": extension.name,
|
||||
"base_agent": extension.base_agent,
|
||||
"extension_type": extension.extension_type.value,
|
||||
"description": extension.description,
|
||||
"version": extension.version,
|
||||
"author": extension.author,
|
||||
"configuration": extension.configuration,
|
||||
"custom_commands": extension.custom_commands,
|
||||
"workflow_hooks": extension.workflow_hooks,
|
||||
"data_transformations": extension.data_transformations,
|
||||
"environment_overrides": extension.environment_overrides,
|
||||
"dependencies": extension.dependencies,
|
||||
"compatibility": extension.compatibility,
|
||||
"enabled": extension.enabled,
|
||||
}
|
||||
yaml.dump(data, f, default_flow_style=False)
|
||||
|
||||
@@ -262,9 +271,9 @@ class ExtensionManager:
|
||||
data = yaml.safe_load(f) or {}
|
||||
|
||||
extensions = []
|
||||
for ext_data in data.get('extensions', []):
|
||||
for ext_data in data.get("extensions", []):
|
||||
# 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))
|
||||
|
||||
return extensions
|
||||
@@ -277,28 +286,28 @@ class ExtensionManager:
|
||||
|
||||
# Convert to serializable format
|
||||
data = {
|
||||
'extensions': [
|
||||
"extensions": [
|
||||
{
|
||||
'name': ext.name,
|
||||
'base_agent': ext.base_agent,
|
||||
'extension_type': ext.extension_type.value,
|
||||
'description': ext.description,
|
||||
'version': ext.version,
|
||||
'author': ext.author,
|
||||
'configuration': ext.configuration,
|
||||
'custom_commands': ext.custom_commands,
|
||||
'workflow_hooks': ext.workflow_hooks,
|
||||
'data_transformations': ext.data_transformations,
|
||||
'environment_overrides': ext.environment_overrides,
|
||||
'dependencies': ext.dependencies,
|
||||
'compatibility': ext.compatibility,
|
||||
'enabled': ext.enabled
|
||||
"name": ext.name,
|
||||
"base_agent": ext.base_agent,
|
||||
"extension_type": ext.extension_type.value,
|
||||
"description": ext.description,
|
||||
"version": ext.version,
|
||||
"author": ext.author,
|
||||
"configuration": ext.configuration,
|
||||
"custom_commands": ext.custom_commands,
|
||||
"workflow_hooks": ext.workflow_hooks,
|
||||
"data_transformations": ext.data_transformations,
|
||||
"environment_overrides": ext.environment_overrides,
|
||||
"dependencies": ext.dependencies,
|
||||
"compatibility": ext.compatibility,
|
||||
"enabled": ext.enabled,
|
||||
}
|
||||
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)
|
||||
|
||||
def _toggle_extension(self, name: str, enabled: bool) -> bool:
|
||||
@@ -321,7 +330,7 @@ class ExtensionManager:
|
||||
"name": base_agent,
|
||||
"type": "kaizen_agent",
|
||||
"enabled": True,
|
||||
"config": {}
|
||||
"config": {},
|
||||
}
|
||||
|
||||
def _apply_extension_config(
|
||||
@@ -343,11 +352,13 @@ class ExtensionManager:
|
||||
config.setdefault("environment", {}).update(extension.environment_overrides)
|
||||
|
||||
# Add extension metadata
|
||||
config.setdefault("extensions", []).append({
|
||||
config.setdefault("extensions", []).append(
|
||||
{
|
||||
"name": extension.name,
|
||||
"type": extension.extension_type.value,
|
||||
"version": extension.version
|
||||
})
|
||||
"version": extension.version,
|
||||
}
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
@@ -370,7 +381,7 @@ class ExtensionManager:
|
||||
"functionality": [],
|
||||
"config": {},
|
||||
"commands": [],
|
||||
"dependencies": []
|
||||
"dependencies": [],
|
||||
}
|
||||
|
||||
if agent_path.suffix == ".py":
|
||||
@@ -379,8 +390,9 @@ class ExtensionManager:
|
||||
|
||||
# Simple analysis - look for class and function definitions
|
||||
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
|
||||
|
||||
@@ -487,10 +499,7 @@ class LegacyWrapper:
|
||||
|
||||
|
||||
def create_extension_template(
|
||||
name: str,
|
||||
base_agent: str,
|
||||
project_path: Path,
|
||||
template_type: str = "basic"
|
||||
name: str, base_agent: str, project_path: Path, template_type: str = "basic"
|
||||
) -> str:
|
||||
"""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}
|
||||
```
|
||||
""",
|
||||
|
||||
"advanced": f"""# Advanced Extension Template: {name}
|
||||
|
||||
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`
|
||||
2. Add any custom scripts to the scripts directory
|
||||
3. Install with: `kaizen-agentic extensions install {name}`
|
||||
"""
|
||||
""",
|
||||
}
|
||||
|
||||
return templates.get(template_type, templates["basic"])
|
||||
@@ -12,6 +12,7 @@ from .registry import AgentRegistry
|
||||
@dataclass
|
||||
class InstallationConfig:
|
||||
"""Configuration for agent installation."""
|
||||
|
||||
target_dir: Path
|
||||
claude_config_path: Optional[Path] = None
|
||||
makefile_path: Optional[Path] = None
|
||||
@@ -26,9 +27,7 @@ class AgentInstaller:
|
||||
self.registry = registry
|
||||
|
||||
def install_agents(
|
||||
self,
|
||||
agent_names: List[str],
|
||||
config: InstallationConfig
|
||||
self, agent_names: List[str], config: InstallationConfig
|
||||
) -> Dict[str, str]:
|
||||
"""Install agents into a project.
|
||||
|
||||
@@ -89,9 +88,7 @@ class AgentInstaller:
|
||||
return sorted(installed)
|
||||
|
||||
def update_agents(
|
||||
self,
|
||||
project_dir: Path,
|
||||
agent_names: Optional[List[str]] = None
|
||||
self, project_dir: Path, agent_names: Optional[List[str]] = None
|
||||
) -> Dict[str, str]:
|
||||
"""Update installed agents to latest versions."""
|
||||
if agent_names is None:
|
||||
@@ -101,9 +98,7 @@ class AgentInstaller:
|
||||
return self.install_agents(agent_names, config)
|
||||
|
||||
def remove_agents(
|
||||
self,
|
||||
agent_names: List[str],
|
||||
project_dir: Path
|
||||
self, agent_names: List[str], project_dir: Path
|
||||
) -> Dict[str, str]:
|
||||
"""Remove agents from a project."""
|
||||
results = {}
|
||||
@@ -162,7 +157,10 @@ class AgentInstaller:
|
||||
counter = 0
|
||||
while backup_dir.exists():
|
||||
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)
|
||||
print(f"Created backup at: {backup_dir}")
|
||||
@@ -173,22 +171,22 @@ class AgentInstaller:
|
||||
# Read existing config
|
||||
config = {}
|
||||
if config_path.exists():
|
||||
with open(config_path, 'r') as f:
|
||||
with open(config_path, "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
# Ensure agents section exists
|
||||
if 'agents' not in config:
|
||||
config['agents'] = {}
|
||||
if "agents" not in config:
|
||||
config["agents"] = {}
|
||||
|
||||
# Add agent references
|
||||
for agent_name in agent_names:
|
||||
config['agents'][agent_name] = {
|
||||
config["agents"][agent_name] = {
|
||||
"path": f"agents/agent-{agent_name}.md",
|
||||
"enabled": True
|
||||
"enabled": True,
|
||||
}
|
||||
|
||||
# Write updated config
|
||||
with open(config_path, 'w') as f:
|
||||
with open(config_path, "w") as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
print(f"Updated Claude configuration: {config_path}")
|
||||
@@ -200,7 +198,7 @@ class AgentInstaller:
|
||||
"""Update Makefile with agent-specific targets."""
|
||||
try:
|
||||
# Read existing Makefile
|
||||
with open(makefile_path, 'r') as f:
|
||||
with open(makefile_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Add agent management targets if not present
|
||||
@@ -224,7 +222,7 @@ agents-validate:
|
||||
content += agent_targets
|
||||
|
||||
# Write updated Makefile
|
||||
with open(makefile_path, 'w') as f:
|
||||
with open(makefile_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"Updated Makefile: {makefile_path}")
|
||||
@@ -238,7 +236,9 @@ agents-validate:
|
||||
claude_md = project_dir / "CLAUDE.md"
|
||||
|
||||
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
|
||||
categories = {}
|
||||
@@ -257,34 +257,37 @@ agents-validate:
|
||||
agent_section += f"- **{agent.name}**: {agent.description}\n"
|
||||
agent_section += "\n"
|
||||
|
||||
agent_section += ("Use these agents by referencing them in your "
|
||||
"Claude Code interactions.\n\n")
|
||||
agent_section += (
|
||||
"Use these agents by referencing them in your "
|
||||
"Claude Code interactions.\n\n"
|
||||
)
|
||||
|
||||
# Update or create CLAUDE.md
|
||||
if claude_md.exists():
|
||||
with open(claude_md, 'r') as f:
|
||||
with open(claude_md, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace existing agent section or append
|
||||
if "## Installed Agents" in content:
|
||||
import re
|
||||
|
||||
content = re.sub(
|
||||
r'## Installed Agents.*?(?=##|\Z)',
|
||||
r"## Installed Agents.*?(?=##|\Z)",
|
||||
agent_section,
|
||||
content,
|
||||
flags=re.DOTALL
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
else:
|
||||
content += "\n" + agent_section
|
||||
|
||||
with open(claude_md, 'w') as f:
|
||||
with open(claude_md, "w") as f:
|
||||
f.write(content)
|
||||
else:
|
||||
# Create new CLAUDE.md
|
||||
header = "# Claude Code Configuration\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)
|
||||
|
||||
print(f"Updated documentation: {claude_md}")
|
||||
@@ -304,7 +307,7 @@ class ProjectInitializer:
|
||||
project_dir: Path,
|
||||
template: str = "python-basic",
|
||||
agent_names: Optional[List[str]] = None,
|
||||
project_name: Optional[str] = None
|
||||
project_name: Optional[str] = None,
|
||||
) -> Dict[str, str]:
|
||||
"""Initialize a new project with agents and structure."""
|
||||
results = {}
|
||||
@@ -325,7 +328,7 @@ class ProjectInitializer:
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir,
|
||||
claude_config_path=project_dir / "CLAUDE.md",
|
||||
makefile_path=project_dir / "Makefile"
|
||||
makefile_path=project_dir / "Makefile",
|
||||
)
|
||||
|
||||
installer = AgentInstaller(self.registry)
|
||||
@@ -337,12 +340,16 @@ class ProjectInitializer:
|
||||
|
||||
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 directories
|
||||
dirs_to_create = ["src", "tests", "docs"]
|
||||
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:
|
||||
(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):
|
||||
"""Create package __init__.py file."""
|
||||
package_name = project_name.replace('-', '_')
|
||||
package_name = project_name.replace("-", "_")
|
||||
init_content = f'''"""
|
||||
{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):
|
||||
"""Create Makefile with standard targets."""
|
||||
package_name = project_name.replace('-', '_')
|
||||
package_name = project_name.replace("-", "_")
|
||||
makefile_content = f"""# {project_name} - Makefile for development workflow
|
||||
# Generated by Kaizen Agentic
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from .detection import AgentSystemDetector, DetectedAgent, AgentSystemType
|
||||
|
||||
class MigrationStrategy(Enum):
|
||||
"""Strategies for migrating existing agents."""
|
||||
|
||||
REPLACE = "replace" # Replace with Kaizen equivalent
|
||||
EXTEND = "extend" # Extend Kaizen agent with custom functionality
|
||||
PRESERVE = "preserve" # Keep custom agent alongside Kaizen agents
|
||||
@@ -22,6 +23,7 @@ class MigrationStrategy(Enum):
|
||||
|
||||
class ConflictResolution(Enum):
|
||||
"""Ways to resolve conflicts between agents."""
|
||||
|
||||
RENAME = "rename" # Rename one of the conflicting agents
|
||||
NAMESPACE = "namespace" # Put agents in different namespaces
|
||||
MERGE_FUNCTIONALITY = "merge" # Combine functionality
|
||||
@@ -33,6 +35,7 @@ class ConflictResolution(Enum):
|
||||
@dataclass
|
||||
class MigrationPlan:
|
||||
"""Plan for migrating an existing agent."""
|
||||
|
||||
source_agent: DetectedAgent
|
||||
strategy: MigrationStrategy
|
||||
target_agent: Optional[str] = None # Kaizen agent name if applicable
|
||||
@@ -48,6 +51,7 @@ class MigrationPlan:
|
||||
@dataclass
|
||||
class ConflictResolutionPlan:
|
||||
"""Plan for resolving a conflict between agents."""
|
||||
|
||||
agent1: str
|
||||
agent2: str
|
||||
conflict_type: str
|
||||
@@ -62,6 +66,7 @@ class ConflictResolutionPlan:
|
||||
@dataclass
|
||||
class IntegrationPlan:
|
||||
"""Complete plan for integrating Kaizen agents into an existing project."""
|
||||
|
||||
project_path: Path
|
||||
migration_plans: List[MigrationPlan]
|
||||
conflict_resolutions: List[ConflictResolutionPlan]
|
||||
@@ -104,10 +109,18 @@ class AgentMigrationPlanner:
|
||||
}
|
||||
|
||||
self.functional_categories = {
|
||||
"project_management": ["keepaTodofile", "project-management", "priority-evaluation"],
|
||||
"project_management": [
|
||||
"keepaTodofile",
|
||||
"project-management",
|
||||
"priority-evaluation",
|
||||
],
|
||||
"testing": ["testing-efficiency", "test-maintenance", "tdd-workflow"],
|
||||
"documentation": ["claude-documentation", "keepaContributingfile"],
|
||||
"code_quality": ["code-refactoring", "datamodel-optimization", "optimization"],
|
||||
"code_quality": [
|
||||
"code-refactoring",
|
||||
"datamodel-optimization",
|
||||
"optimization",
|
||||
],
|
||||
"infrastructure": ["setupRepository", "tooling-optimization"],
|
||||
"version_control": ["keepaChangelog"],
|
||||
}
|
||||
@@ -118,7 +131,9 @@ class AgentMigrationPlanner:
|
||||
detection_result = detector.detect_agent_systems(project_path)
|
||||
|
||||
# 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
|
||||
migration_plans = []
|
||||
@@ -145,7 +160,7 @@ class AgentMigrationPlanner:
|
||||
conflict_resolutions=conflict_resolutions,
|
||||
backup_directory=backup_dir,
|
||||
integration_order=integration_order,
|
||||
post_migration_tasks=post_migration_tasks
|
||||
post_migration_tasks=post_migration_tasks,
|
||||
)
|
||||
|
||||
def _create_migration_plan(
|
||||
@@ -190,7 +205,7 @@ class AgentMigrationPlanner:
|
||||
strategy=strategy,
|
||||
target_agent=target_agent,
|
||||
backup_path=backup_path,
|
||||
migration_notes=notes
|
||||
migration_notes=notes,
|
||||
)
|
||||
|
||||
def _has_unique_functionality(self, agent: DetectedAgent) -> bool:
|
||||
@@ -203,8 +218,18 @@ class AgentMigrationPlanner:
|
||||
"""Check if a custom agent is essential to the project."""
|
||||
# Heuristics for essential agents
|
||||
essential_patterns = [
|
||||
"deploy", "ci", "cd", "build", "release", "secret", "auth",
|
||||
"database", "api", "server", "client", "integration"
|
||||
"deploy",
|
||||
"ci",
|
||||
"cd",
|
||||
"build",
|
||||
"release",
|
||||
"secret",
|
||||
"auth",
|
||||
"database",
|
||||
"api",
|
||||
"server",
|
||||
"client",
|
||||
"integration",
|
||||
]
|
||||
|
||||
agent_name_lower = agent.name.lower()
|
||||
@@ -230,7 +255,7 @@ class AgentMigrationPlanner:
|
||||
resolution = ConflictResolution.RENAME
|
||||
action_details = {
|
||||
"rename_agent": agent2, # Rename the second agent
|
||||
"new_name": f"{agent2}_custom"
|
||||
"new_name": f"{agent2}_custom",
|
||||
}
|
||||
elif "Functional overlap" in reason:
|
||||
# Check if one is a Kaizen agent
|
||||
@@ -252,10 +277,12 @@ class AgentMigrationPlanner:
|
||||
agent2=agent2,
|
||||
conflict_type=reason,
|
||||
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."""
|
||||
# Order by dependency and risk
|
||||
order = []
|
||||
@@ -265,7 +292,11 @@ class AgentMigrationPlanner:
|
||||
order.extend([p.source_agent.name for p in infra_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])
|
||||
|
||||
# 3. Optional/enhancement agents last
|
||||
@@ -287,7 +318,10 @@ class AgentMigrationPlanner:
|
||||
return any(keyword in agent_name for keyword in core_keywords)
|
||||
|
||||
def _generate_migration_notes(
|
||||
self, agent: DetectedAgent, strategy: MigrationStrategy, target_agent: Optional[str]
|
||||
self,
|
||||
agent: DetectedAgent,
|
||||
strategy: MigrationStrategy,
|
||||
target_agent: Optional[str],
|
||||
) -> List[str]:
|
||||
"""Generate helpful notes for the migration."""
|
||||
notes = []
|
||||
@@ -332,13 +366,15 @@ class AgentMigrationPlanner:
|
||||
if detection_result.config_files:
|
||||
tasks.append("Verify all configuration files are updated")
|
||||
|
||||
tasks.extend([
|
||||
tasks.extend(
|
||||
[
|
||||
"Run 'kaizen-agentic validate' to verify installation",
|
||||
"Test all agent functionality",
|
||||
"Update project documentation",
|
||||
"Train team on new agent workflows",
|
||||
"Archive or remove backup files after verification"
|
||||
])
|
||||
"Archive or remove backup files after verification",
|
||||
]
|
||||
)
|
||||
|
||||
return tasks
|
||||
|
||||
@@ -349,7 +385,9 @@ class AgentMigrator:
|
||||
def __init__(self):
|
||||
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."""
|
||||
results = {}
|
||||
|
||||
@@ -361,7 +399,7 @@ class AgentMigrator:
|
||||
for agent_name in plan.integration_order:
|
||||
migration_plan = next(
|
||||
(p for p in plan.migration_plans if p.source_agent.name == agent_name),
|
||||
None
|
||||
None,
|
||||
)
|
||||
if migration_plan:
|
||||
result = self._execute_single_migration(migration_plan, dry_run)
|
||||
@@ -413,11 +451,14 @@ class AgentMigrator:
|
||||
extension_config = {
|
||||
"base_agent": plan.target_agent,
|
||||
"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"
|
||||
with open(extension_path, 'w') as f:
|
||||
extension_path = (
|
||||
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)
|
||||
|
||||
return f"EXTENDED: {plan.target_agent} with {plan.source_agent.name}"
|
||||
@@ -427,7 +468,10 @@ class AgentMigrator:
|
||||
# Rename if necessary to avoid conflicts
|
||||
if plan.source_agent.name in ["todo", "changelog", "test"]:
|
||||
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)
|
||||
return f"PRESERVED: {plan.source_agent.name} -> {new_name}"
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from enum import Enum
|
||||
|
||||
class AgentCategory(Enum):
|
||||
"""Categories of agents for organization."""
|
||||
|
||||
PROJECT_MANAGEMENT = "project-management"
|
||||
DEVELOPMENT_PROCESS = "development-process"
|
||||
CODE_QUALITY = "code-quality"
|
||||
@@ -21,6 +22,7 @@ class AgentCategory(Enum):
|
||||
@dataclass
|
||||
class AgentDefinition:
|
||||
"""Represents an agent definition with metadata."""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
file_path: Path
|
||||
@@ -31,11 +33,11 @@ class AgentDefinition:
|
||||
@classmethod
|
||||
def from_file(cls, file_path: Path) -> "AgentDefinition":
|
||||
"""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()
|
||||
|
||||
# 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:
|
||||
raise ValueError(f"No YAML frontmatter found in {file_path}")
|
||||
|
||||
@@ -45,15 +47,15 @@ class AgentDefinition:
|
||||
dependencies = cls._extract_dependencies(content, frontmatter)
|
||||
|
||||
# Determine category from name or content
|
||||
category = cls._determine_category(frontmatter['name'], content)
|
||||
category = cls._determine_category(frontmatter["name"], content)
|
||||
|
||||
return cls(
|
||||
name=frontmatter['name'],
|
||||
description=frontmatter['description'],
|
||||
name=frontmatter["name"],
|
||||
description=frontmatter["description"],
|
||||
file_path=file_path,
|
||||
category=category,
|
||||
dependencies=dependencies,
|
||||
model=frontmatter.get('model')
|
||||
model=frontmatter.get("model"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -62,42 +64,42 @@ class AgentDefinition:
|
||||
dependencies = set()
|
||||
|
||||
# Check frontmatter for explicit dependencies
|
||||
for key in ['dependencies', 'depends_on', 'requires']:
|
||||
for key in ["dependencies", "depends_on", "requires"]:
|
||||
if key in frontmatter:
|
||||
deps = frontmatter[key]
|
||||
if isinstance(deps, list):
|
||||
dependencies.update(deps)
|
||||
elif isinstance(deps, str):
|
||||
# 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
|
||||
dep_patterns = [
|
||||
r'depends_on:\s*\[(.*?)\]',
|
||||
r'requires:\s*\[(.*?)\]',
|
||||
r'dependencies:\s*\[(.*?)\]',
|
||||
r"depends_on:\s*\[(.*?)\]",
|
||||
r"requires:\s*\[(.*?)\]",
|
||||
r"dependencies:\s*\[(.*?)\]",
|
||||
]
|
||||
|
||||
for pattern in dep_patterns:
|
||||
matches = re.findall(pattern, content, re.IGNORECASE)
|
||||
for match in matches:
|
||||
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)
|
||||
|
||||
# Look for specific agent references in content (more precise)
|
||||
# Only look for full agent names like "todo-keeper agent" or "uses changelog-keeper"
|
||||
agent_patterns = [
|
||||
r'uses?\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"uses?\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))",
|
||||
]
|
||||
|
||||
for pattern in agent_patterns:
|
||||
matches = re.findall(pattern, content.lower())
|
||||
for match in matches:
|
||||
if match not in ['optimization', 'agentic', 'driven', 'assisted']:
|
||||
dependencies.add(match.replace('-', '_'))
|
||||
if match not in ["optimization", "agentic", "driven", "assisted"]:
|
||||
dependencies.add(match.replace("-", "_"))
|
||||
|
||||
return dependencies
|
||||
|
||||
@@ -107,28 +109,33 @@ class AgentDefinition:
|
||||
name_lower = name.lower()
|
||||
|
||||
# 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):
|
||||
return AgentCategory.PROJECT_MANAGEMENT
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# Default fallback
|
||||
@@ -159,7 +166,9 @@ class AgentRegistry:
|
||||
"""Get agent definition by 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."""
|
||||
agents = list(self._agents.values())
|
||||
if category:
|
||||
@@ -232,7 +241,9 @@ class AgentRegistry:
|
||||
|
||||
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."""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
@@ -255,18 +266,14 @@ class AgentRegistry:
|
||||
def get_agent_templates(self) -> Dict[str, List[str]]:
|
||||
"""Get predefined agent templates for different project types."""
|
||||
return {
|
||||
"python-basic": [
|
||||
"setupRepository",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog"
|
||||
],
|
||||
"python-basic": ["setupRepository", "keepaTodofile", "keepaChangelog"],
|
||||
"python-web": [
|
||||
"setupRepository",
|
||||
"tdd-workflow",
|
||||
"code-refactoring",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog",
|
||||
"keepaContributingfile"
|
||||
"keepaContributingfile",
|
||||
],
|
||||
"python-cli": [
|
||||
"setupRepository",
|
||||
@@ -274,7 +281,7 @@ class AgentRegistry:
|
||||
"testing-efficiency",
|
||||
"claude-documentation",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog"
|
||||
"keepaChangelog",
|
||||
],
|
||||
"python-data": [
|
||||
"setupRepository",
|
||||
@@ -282,9 +289,7 @@ class AgentRegistry:
|
||||
"testing-efficiency",
|
||||
"requirements-engineering",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog"
|
||||
"keepaChangelog",
|
||||
],
|
||||
"comprehensive": [
|
||||
agent.name for agent in self.list_agents()
|
||||
]
|
||||
"comprehensive": [agent.name for agent in self.list_agents()],
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
import json
|
||||
import pytest
|
||||
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
|
||||
|
||||
|
||||
@@ -72,9 +76,7 @@ def test_install_agents(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir,
|
||||
create_backup=False,
|
||||
update_docs=False
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir,
|
||||
create_backup=False,
|
||||
update_docs=False
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
|
||||
# Install an agent that depends on others
|
||||
@@ -111,7 +111,9 @@ def test_list_installed_agents(test_registry, tmp_path):
|
||||
assert installed == []
|
||||
|
||||
# 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)
|
||||
|
||||
# Check installed agents
|
||||
@@ -127,7 +129,9 @@ def test_update_agents(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
# 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)
|
||||
|
||||
# Update all agents
|
||||
@@ -146,7 +150,9 @@ def test_remove_agents(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
# 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)
|
||||
|
||||
# Remove an agent
|
||||
@@ -170,7 +176,9 @@ def test_validate_installation(test_registry, tmp_path):
|
||||
assert "No agents directory found" in errors["project"]
|
||||
|
||||
# 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)
|
||||
|
||||
errors = installer.validate_installation(project_dir)
|
||||
@@ -187,7 +195,7 @@ def test_update_claude_config(test_registry, tmp_path):
|
||||
target_dir=project_dir,
|
||||
claude_config_path=claude_config,
|
||||
create_backup=False,
|
||||
update_docs=False
|
||||
update_docs=False,
|
||||
)
|
||||
|
||||
installer.install_agents(["base-agent"], config)
|
||||
@@ -208,9 +216,7 @@ def test_project_initializer(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "new_project"
|
||||
|
||||
initializer.init_project(
|
||||
project_dir,
|
||||
template="python-basic",
|
||||
project_name="new_project"
|
||||
project_dir, template="python-basic", project_name="new_project"
|
||||
)
|
||||
|
||||
# Check that project structure was created
|
||||
@@ -236,7 +242,7 @@ def test_project_initializer_custom_agents(test_registry, tmp_path):
|
||||
project_dir,
|
||||
template="python-basic",
|
||||
agent_names=["base-agent", "keepaTodofile"],
|
||||
project_name="custom_project"
|
||||
project_name="custom_project",
|
||||
)
|
||||
|
||||
# Check that specific agents were installed
|
||||
@@ -251,7 +257,7 @@ def test_installation_config():
|
||||
claude_config_path=Path("/tmp/test/claude.json"),
|
||||
makefile_path=Path("/tmp/test/Makefile"),
|
||||
update_docs=True,
|
||||
create_backup=False
|
||||
create_backup=False,
|
||||
)
|
||||
|
||||
assert config.target_dir == Path("/tmp/test")
|
||||
|
||||
@@ -158,7 +158,10 @@ This agent uses both base-agent and dependent-agent.
|
||||
|
||||
# Test complex dependency resolution
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user