Compare commits

...

6 Commits

Author SHA1 Message Date
19b3c16cce Include releaseManager agent in package distribution
- Copy agent-releaseManager.md to src/kaizen_agentic/data/agents/
- Ensure releaseManager agent is packaged and distributed with v1.0.0
- Complete v1.0.0 package now includes all 17 agents including release management
- Final package ready for PyPI publication with full feature set

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 21:01:04 +02:00
712d85d1ab Update CHANGELOG.md to include release management and local installation features in v1.0.0
- Add release management system with agent-releaseManager and 6 release- targets
- Include install-local target for PyPI-equivalent local testing
- Document enhanced Makefile with structured release workflow
- Complete v1.0.0 feature documentation for production release

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 20:59:23 +02:00
63dacda163 Update documentation for local package installation option
- Add Option B: From Local Package installation instructions
- Include make install-local target in both README.md and GETTING_STARTED.md
- Provide clear path for testing PyPI-equivalent installation locally
- Maintains development, local package, and future PyPI installation options

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 20:55:09 +02:00
0f7c3a9a3c Add install-local target for testing local package installation
- Add make install-local target to install from locally built wheel package
- Provides equivalent to PyPI installation for testing before publication
- Includes comprehensive installation validation with import and CLI tests
- Shows package information and validates core functionality
- Perfect for testing the installation experience without PyPI dependency

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 20:54:42 +02:00
d68310793b 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>
2025-10-19 20:44:58 +02:00
30daabf12c Add release management system and prepare v1.0.0 publication
- Add agent-releaseManager.md with comprehensive publication workflow guidance
- Add 6 release- prefixed make targets for structured release process:
  - release-check: Validate release readiness
  - release-prepare: Build packages and prepare release
  - release-test: Test publication via TestPyPI
  - release-publish: Publish to production PyPI
  - release-finalize: Post-release tasks (tags, GitHub releases)
  - release-rollback: Emergency rollback procedures
- Update pyproject.toml version from 0.1.0 to 1.0.0 for consistency with CHANGELOG.md
- Update installation documentation in README.md and GETTING_STARTED.md
  - Add current "from source" installation instructions
  - Maintain "from PyPI" instructions for post-publication
- Framework now ready for v1.0.0 publication with complete release workflow

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:42:32 +02:00
15 changed files with 944 additions and 293 deletions

View File

@@ -43,6 +43,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `list/status/validate` - Discovery and maintenance operations - `list/status/validate` - Discovery and maintenance operations
- `templates` - Project template management - `templates` - Project template management
- `detect/migrate/extensions` - Existing project integration - `detect/migrate/extensions` - Existing project integration
- **Enhanced Makefile with release management and local testing**:
- `install-local` - Install from locally built package for PyPI-equivalent testing
- `release-*` targets - Complete structured release workflow (6 targets)
- Agent management targets with `agents-` prefix for consistency
- **Dependency resolution and validation** with safety measures and backup/rollback support - **Dependency resolution and validation** with safety measures and backup/rollback support
- **Console script entry point** making kaizen-agentic command available globally after pip install - **Console script entry point** making kaizen-agentic command available globally after pip install
- **Context manifest system** with external documentation references (ContextManifest.md) - **Context manifest system** with external documentation references (ContextManifest.md)
@@ -51,6 +55,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Multi-language build system integration** examples and patterns - **Multi-language build system integration** examples and patterns
- **Complete test coverage** for all distribution system components (134 test files) - **Complete test coverage** for all distribution system components (134 test files)
- **Package data distribution** with all agents included in pip-installable package - **Package data distribution** with all agents included in pip-installable package
- **Release management system** with agent-releaseManager and 6 structured make targets:
- `release-check` - Validate release readiness with comprehensive checklist
- `release-prepare` - Build packages and prepare for publication
- `release-test` - Test publication workflow using TestPyPI
- `release-publish` - Publish to production PyPI with safety checks
- `release-finalize` - Post-release tasks (tags, GitHub releases, documentation)
- `release-rollback` - Emergency rollback procedures and guidance
- **Local package installation** with `make install-local` target for PyPI-equivalent testing
#### Documentation and User Experience #### Documentation and User Experience
- **Comprehensive documentation suite**: - **Comprehensive documentation suite**:

242
Makefile
View File

@@ -1,6 +1,6 @@
# Makefile for Kaizen Agentic development tasks # Makefile for Kaizen Agentic development tasks
.PHONY: help setup-complete setup-structure setup-python setup-tools setup-docs setup-tests setup-verify ensure-project-structure install-dev standards-check standards-fix standards-test test test-all build clean lint format venv-status agents-list agents-update agents-validate agents-status agents-install-cli .PHONY: help setup-complete setup-structure setup-python setup-tools setup-docs setup-tests setup-verify ensure-project-structure install-dev install-local standards-check standards-fix standards-test test test-all build clean lint format venv-status agents-list agents-update agents-validate agents-status agents-install-cli release-check release-prepare release-test release-publish release-finalize release-rollback
# Variables # Variables
VENV = .venv VENV = .venv
@@ -24,6 +24,7 @@ help:
@echo " setup-tests - Create testing infrastructure" @echo " setup-tests - Create testing infrastructure"
@echo " setup-verify - Verify complete setup functionality" @echo " setup-verify - Verify complete setup functionality"
@echo " install-dev - Install package in development mode" @echo " install-dev - Install package in development mode"
@echo " install-local - Install from locally built package (test PyPI installation)"
@echo " venv-status - Check if venv is active" @echo " venv-status - Check if venv is active"
@echo "" @echo ""
@echo "Standards Compliance:" @echo "Standards Compliance:"
@@ -38,6 +39,14 @@ help:
@echo " agents-status - Show agent status and project info" @echo " agents-status - Show agent status and project info"
@echo " agents-install-cli - Install kaizen-agentic CLI tool" @echo " agents-install-cli - Install kaizen-agentic CLI tool"
@echo "" @echo ""
@echo "Release Management:"
@echo " release-check - Validate release readiness (tests, linting, version consistency)"
@echo " release-prepare - Prepare release (update versions, build packages)"
@echo " release-test - Test publication workflow using TestPyPI"
@echo " release-publish - Publish to production PyPI"
@echo " release-finalize - Post-release tasks (tags, GitHub release, documentation)"
@echo " release-rollback - Emergency rollback procedures"
@echo ""
@echo "Development:" @echo "Development:"
@echo " test - Run unit tests only (fast)" @echo " test - Run unit tests only (fast)"
@echo " test-all - Run comprehensive test suite (tests + standards + quality)" @echo " test-all - Run comprehensive test suite (tests + standards + quality)"
@@ -76,6 +85,49 @@ install-dev: $(VENV)/bin/activate
@$(VENV_PIP) install -e . @$(VENV_PIP) install -e .
@echo "✅ Package installed in development mode" @echo "✅ Package installed in development mode"
# Install from locally built package (test PyPI installation)
install-local: $(VENV)/bin/activate
@echo "📦 Installing from locally built package..."
@if [ ! -d "dist" ] || [ -z "$$(ls dist/*.whl 2>/dev/null)" ]; then \
echo "❌ No wheel package found in dist/"; \
echo " Run 'make build' or 'make release-prepare' first"; \
exit 1; \
fi; \
WHEEL_FILE=$$(ls dist/*.whl | head -1); \
VERSION=$$(basename "$$WHEEL_FILE" | sed 's/kaizen_agentic-\(.*\)-py3.*/\1/'); \
echo " Installing kaizen-agentic v$$VERSION from local wheel..."; \
$(VENV_PIP) install --upgrade pip; \
$(VENV_PIP) uninstall -y kaizen-agentic 2>/dev/null || true; \
$(VENV_PIP) install "$$WHEEL_FILE" --force-reinstall; \
echo ""; \
echo "✅ Local package installed successfully!"; \
echo " Version: $$VERSION"; \
echo " Package contents:"; \
$(VENV_PIP) show kaizen-agentic; \
echo ""; \
echo "🧪 Testing installation..."; \
if $(VENV_PYTHON) -c "import kaizen_agentic; print(f'✅ Import successful: kaizen_agentic v{kaizen_agentic.__version__}')" 2>/dev/null; then \
echo " ✅ Package import test passed"; \
else \
echo " ❌ Package import test failed"; \
$(VENV_PYTHON) -c "import kaizen_agentic" 2>&1 || true; \
fi; \
if $(VENV)/bin/kaizen-agentic --version 2>/dev/null; then \
echo " ✅ CLI command test passed"; \
else \
echo " ❌ CLI command test failed"; \
$(VENV)/bin/kaizen-agentic --version 2>&1 || true; \
fi; \
if $(VENV)/bin/kaizen-agentic list 2>/dev/null | head -5; then \
echo " ✅ CLI functionality test passed"; \
else \
echo " ❌ CLI functionality test failed"; \
fi; \
echo ""; \
echo "💡 Usage:"; \
echo " source $(VENV)/bin/activate"; \
echo " kaizen-agentic --help"
# Ensure proper Python project structure exists # Ensure proper Python project structure exists
ensure-project-structure: ensure-project-structure:
@echo "🔍 Ensuring proper Python project structure..." @echo "🔍 Ensuring proper Python project structure..."
@@ -708,4 +760,190 @@ agents-install-cli: $(VENV)/bin/activate
@$(VENV_PIP) install --upgrade pip @$(VENV_PIP) install --upgrade pip
@$(VENV_PIP) install -e . @$(VENV_PIP) install -e .
@echo "✅ CLI installed. Use 'kaizen-agentic --help' for usage." @echo "✅ CLI installed. Use 'kaizen-agentic --help' for usage."
@echo "💡 Activate virtual environment with: source $(VENV)/bin/activate" @echo "💡 Activate virtual environment with: source $(VENV)/bin/activate"
# ============================================================================
# Release Management Targets
# ============================================================================
# Validate release readiness (tests, linting, version consistency)
release-check: $(VENV)/bin/activate
@echo "🔍 Validating release readiness..."
@echo ""
@ISSUES=0; \
echo "📋 Release Readiness Checklist:"; \
echo "==============================="; \
echo " • Version Consistency:"; \
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"; \
else \
echo " ❌ Version mismatch: CHANGELOG.md='$$CHANGELOG_VERSION', pyproject.toml='$$PYPROJECT_VERSION'"; \
ISSUES=$$((ISSUES + 1)); \
fi; \
echo " • Code Quality:"; \
if $(VENV_PYTHON) -m flake8 src/ --max-line-length=100 >/dev/null 2>&1; then \
echo " ✅ Code passes linting"; \
else \
echo " ❌ Code has linting violations"; \
ISSUES=$$((ISSUES + 1)); \
fi; \
echo " • Test Suite:"; \
if $(VENV_PYTHON) -m pytest tests/ --tb=no -q >/dev/null 2>&1; then \
echo " ✅ All tests pass"; \
else \
echo " ❌ Tests failing"; \
ISSUES=$$((ISSUES + 1)); \
fi; \
echo " • Documentation:"; \
if [ -f "README.md" ] && [ -f "CHANGELOG.md" ] && [ -f "docs/GETTING_STARTED.md" ]; then \
echo " ✅ Core documentation exists"; \
else \
echo " ❌ Missing core documentation files"; \
ISSUES=$$((ISSUES + 1)); \
fi; \
echo " • Build System:"; \
if [ -f "pyproject.toml" ] && grep -q '^\[build-system\]' pyproject.toml; then \
echo " ✅ Build configuration present"; \
else \
echo " ❌ Build system not configured"; \
ISSUES=$$((ISSUES + 1)); \
fi; \
echo ""; \
if [ $$ISSUES -eq 0 ]; then \
echo "✅ Release readiness: PASSED"; \
echo " Ready for release preparation."; \
else \
echo "❌ Release readiness: FAILED ($$ISSUES issues)"; \
echo " Fix issues before proceeding with release."; \
exit 1; \
fi
# Prepare release (update versions, build packages)
release-prepare: release-check clean
@echo "🏗️ Preparing release..."
@echo ""
@VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/'); \
echo "📦 Building packages for version $$VERSION..."; \
$(VENV_PYTHON) -c "import build" 2>/dev/null || $(VENV_PIP) install build; \
$(VENV_PYTHON) -m build; \
echo ""; \
echo "✅ Release preparation completed:"; \
echo " • Version: $$VERSION"; \
echo " • Packages built in dist/"; \
ls -la dist/ | grep "$$VERSION" || echo " • Package files:"; ls -la dist/; \
echo ""; \
echo "💡 Next steps:"; \
echo " • Run 'make release-test' to test publication"; \
echo " • Run 'make release-publish' for production release"
# Test publication workflow using TestPyPI
release-test: release-prepare
@echo "🧪 Testing publication workflow with TestPyPI..."
@echo ""
@VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/'); \
echo "🚀 Uploading to TestPyPI (version $$VERSION)..."; \
$(VENV_PYTHON) -c "import twine" 2>/dev/null || $(VENV_PIP) install twine; \
echo ""; \
echo "⚠️ Manual Step Required:"; \
echo " Run the following command to upload to TestPyPI:"; \
echo " $(VENV_PYTHON) -m twine upload --repository testpypi dist/*"; \
echo ""; \
echo " Then test installation:"; \
echo " pip install --index-url https://test.pypi.org/simple/ kaizen-agentic==$$VERSION"; \
echo ""; \
echo "💡 Configure TestPyPI credentials:"; \
echo " • Create account at https://test.pypi.org/"; \
echo " • Generate API token"; \
echo " • Store in ~/.pypirc or use keyring"
# Publish to production PyPI
release-publish: release-check
@echo "🚀 Publishing to production PyPI..."
@echo ""
@VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/'); \
echo "⚠️ PRODUCTION RELEASE WARNING ⚠️"; \
echo " About to publish version $$VERSION to PyPI"; \
echo " This action cannot be undone!"; \
echo ""; \
echo "📋 Pre-flight checklist:"; \
echo " ✅ All tests pass"; \
echo " ✅ Documentation updated"; \
echo " ✅ Version tagged in git"; \
echo " ✅ TestPyPI upload tested"; \
echo ""; \
$(VENV_PYTHON) -c "import twine" 2>/dev/null || $(VENV_PIP) install twine; \
echo "⚠️ Manual Step Required:"; \
echo " Run the following command to publish:"; \
echo " $(VENV_PYTHON) -m twine upload dist/*"; \
echo ""; \
echo "💡 Configure PyPI credentials:"; \
echo " • Create account at https://pypi.org/"; \
echo " • Generate API token"; \
echo " • Store in ~/.pypirc or use keyring"
# Post-release tasks (tags, GitHub release, documentation)
release-finalize: $(VENV)/bin/activate
@echo "🏁 Finalizing release..."
@echo ""
@VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/'); \
echo "📝 Post-release tasks for version $$VERSION:"; \
echo ""; \
echo " • Git Tagging:"; \
if git tag -l | grep -q "^v$$VERSION$$"; then \
echo " ✅ Tag v$$VERSION already exists"; \
else \
echo " 📌 Creating git tag v$$VERSION..."; \
git tag -a "v$$VERSION" -m "Release version $$VERSION"; \
echo " ✅ Tag created"; \
fi; \
echo ""; \
echo " • GitHub Release:"; \
echo " 💡 Manual steps required:"; \
echo " 1. Push tags: git push origin v$$VERSION"; \
echo " 2. Create GitHub release at:"; \
echo " https://github.com/kaizen-agentic/kaizen-agentic/releases/new"; \
echo " 3. Upload dist/ files as release assets"; \
echo ""; \
echo " • Documentation:"; \
echo " 💡 Verify installation instructions work:"; \
echo " pip install kaizen-agentic==$$VERSION"; \
echo ""; \
echo "✅ Release finalization checklist provided"; \
echo " Complete manual steps above to finish release process"
# Emergency rollback procedures
release-rollback: $(VENV)/bin/activate
@echo "🚨 Emergency release rollback procedures..."
@echo ""
@echo "⚠️ ROLLBACK PROCEDURES ⚠️"; \
echo "================================"; \
echo ""; \
echo "📋 Available rollback options:"; \
echo ""; \
echo " 1. PyPI Package Yanking:"; \
echo " • Cannot delete published packages"; \
echo " • Can 'yank' versions to prevent new installs"; \
echo " • Use PyPI web interface or twine"; \
echo ""; \
echo " 2. Git Rollback:"; \
echo " • Revert commits: git revert <commit-hash>"; \
echo " • Delete tags: git tag -d v<version>"; \
echo " • Force push (if no external users)"; \
echo ""; \
echo " 3. Documentation Updates:"; \
echo " • Update CHANGELOG.md with rollback notice"; \
echo " • Add deprecation warnings"; \
echo " • Publish corrected documentation"; \
echo ""; \
echo " 4. Emergency Release:"; \
echo " • Increment patch version"; \
echo " • Fix critical issues"; \
echo " • Fast-track through release process"; \
echo ""; \
echo "💡 Prevention for next time:"; \
echo " • Always test with TestPyPI first"; \
echo " • Use staging/preview environments"; \
echo " • Implement automated quality gates"; \
echo " • Consider pre-release versions for testing"

View File

@@ -7,8 +7,28 @@ This project embraces the Japanese concept of "kaizen" (continuous improvement)
## Quick Start ## Quick Start
### Install the Package ### Install the Package
**From Source (Development):**
```bash ```bash
pip install kaizen-agentic git clone https://github.com/kaizen-agentic/kaizen-agentic.git
cd kaizen-agentic
make setup-complete
make agents-install-cli
source .venv/bin/activate
```
**From Local Package (Test Installation):**
```bash
git clone https://github.com/kaizen-agentic/kaizen-agentic.git
cd kaizen-agentic
make setup-complete
python3 -m build && make install-local
source .venv/bin/activate
```
**From PyPI (Coming Soon):**
```bash
pip install kaizen-agentic # Available after v1.0.0 publication
``` ```
### Your First Project (New Users) ### Your First Project (New Users)

View File

@@ -0,0 +1,101 @@
---
name: releaseManager
category: project-management
description: Manages software releases, version control, and publication workflows for Python packages
dependencies: []
---
# Release Manager Agent
You are a specialized release management agent focused on Python package publication workflows, version control, and release automation.
## Core Responsibilities
### Version Management
- **Semantic Versioning**: Ensure proper semantic versioning (MAJOR.MINOR.PATCH) compliance
- **Version Synchronization**: Keep versions consistent across pyproject.toml, CHANGELOG.md, and documentation
- **Release Notes**: Generate comprehensive release notes from CHANGELOG.md entries
- **Tag Management**: Create and manage git tags for releases
### Publication Workflow
- **Package Building**: Build distribution packages (sdist and wheel) using modern Python tools
- **Quality Assurance**: Run comprehensive tests and validation before publication
- **PyPI Publication**: Handle TestPyPI and production PyPI uploads with proper authentication
- **Post-Release Tasks**: Update documentation, create GitHub releases, and notify stakeholders
### Documentation Updates
- **Installation Instructions**: Update installation guides to reflect publication status
- **Version References**: Ensure all documentation references correct versions
- **Migration Guides**: Create migration guides for breaking changes
- **Release Communication**: Draft release announcements and update project status
## Release Types
### Pre-Release (Alpha/Beta/RC)
- Use for testing publication workflow
- Publish to TestPyPI first
- Version format: 1.0.0a1, 1.0.0b1, 1.0.0rc1
### Production Release
- Full validation and testing required
- Publish to production PyPI
- Create GitHub releases with assets
- Update all documentation
### Patch Releases
- Hotfixes and critical bug fixes
- Minimal documentation updates
- Fast-track publication process
## Make Target Structure
Provide these release- prefixed make targets:
- `release-check`: Validate release readiness (tests, linting, version consistency)
- `release-prepare`: Prepare release (update versions, build packages)
- `release-test`: Test publication workflow using TestPyPI
- `release-publish`: Publish to production PyPI
- `release-finalize`: Post-release tasks (tags, GitHub release, documentation)
- `release-rollback`: Emergency rollback procedures
## Best Practices
### Pre-Release Checklist
1. All tests passing
2. Documentation updated
3. CHANGELOG.md entries complete
4. Version numbers synchronized
5. Dependencies validated
6. Security scan clean
### Publication Security
- Use API tokens, never passwords
- Separate TestPyPI and production credentials
- Validate package contents before upload
- Monitor for supply chain attacks
### Communication
- Clear release notes
- Breaking change notifications
- Deprecation warnings with timelines
- Community update posts
## Integration Points
### CI/CD Systems
- GitHub Actions workflow integration
- Automated testing on multiple Python versions
- Security scanning and dependency checking
- Automated documentation deployment
### Monitoring
- Download statistics tracking
- Error rate monitoring
- User feedback collection
- Dependency vulnerability scanning
When managing releases, always prioritize:
1. **Security**: Never compromise on security practices
2. **Reliability**: Thorough testing before publication
3. **Communication**: Clear documentation and announcements
4. **Reproducibility**: Consistent and documented processes

View File

@@ -8,11 +8,47 @@ This guide walks you through using Kaizen Agentic agents in any project, from in
### 1. Install the Package ### 1. Install the Package
**Option A: From Source (Current - Development Version)**
```bash ```bash
# Clone the repository
git clone https://github.com/kaizen-agentic/kaizen-agentic.git
cd kaizen-agentic
# Set up development environment
make setup-complete
# Install CLI tool
make agents-install-cli
# Activate virtual environment
source .venv/bin/activate
```
**Option B: From Local Package (Test PyPI Installation)**
```bash
# Clone the repository and build package
git clone https://github.com/kaizen-agentic/kaizen-agentic.git
cd kaizen-agentic
make setup-complete
# Build and install from local package
python3 -m build
make install-local
# Activate virtual environment
source .venv/bin/activate
```
**Option C: From PyPI (Coming Soon)**
```bash
# Will be available once v1.0.0 is published
pip install kaizen-agentic pip install kaizen-agentic
``` ```
This gives you the `kaizen-agentic` command globally. > **📦 Release Status**: v1.0.0 is ready for publication. Test local installation with `make install-local` before PyPI publication.
### 2. Verify Installation ### 2. Verify Installation

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "kaizen-agentic" name = "kaizen-agentic"
version = "0.1.0" version = "1.0.0"
description = "AI agent development framework embracing continuous improvement (kaizen)" description = "AI agent development framework embracing continuous improvement (kaizen)"
readme = "README.md" readme = "README.md"
license = {file = "LICENSE"} license = {file = "LICENSE"}

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

@@ -0,0 +1,101 @@
---
name: releaseManager
category: project-management
description: Manages software releases, version control, and publication workflows for Python packages
dependencies: []
---
# Release Manager Agent
You are a specialized release management agent focused on Python package publication workflows, version control, and release automation.
## Core Responsibilities
### Version Management
- **Semantic Versioning**: Ensure proper semantic versioning (MAJOR.MINOR.PATCH) compliance
- **Version Synchronization**: Keep versions consistent across pyproject.toml, CHANGELOG.md, and documentation
- **Release Notes**: Generate comprehensive release notes from CHANGELOG.md entries
- **Tag Management**: Create and manage git tags for releases
### Publication Workflow
- **Package Building**: Build distribution packages (sdist and wheel) using modern Python tools
- **Quality Assurance**: Run comprehensive tests and validation before publication
- **PyPI Publication**: Handle TestPyPI and production PyPI uploads with proper authentication
- **Post-Release Tasks**: Update documentation, create GitHub releases, and notify stakeholders
### Documentation Updates
- **Installation Instructions**: Update installation guides to reflect publication status
- **Version References**: Ensure all documentation references correct versions
- **Migration Guides**: Create migration guides for breaking changes
- **Release Communication**: Draft release announcements and update project status
## Release Types
### Pre-Release (Alpha/Beta/RC)
- Use for testing publication workflow
- Publish to TestPyPI first
- Version format: 1.0.0a1, 1.0.0b1, 1.0.0rc1
### Production Release
- Full validation and testing required
- Publish to production PyPI
- Create GitHub releases with assets
- Update all documentation
### Patch Releases
- Hotfixes and critical bug fixes
- Minimal documentation updates
- Fast-track publication process
## Make Target Structure
Provide these release- prefixed make targets:
- `release-check`: Validate release readiness (tests, linting, version consistency)
- `release-prepare`: Prepare release (update versions, build packages)
- `release-test`: Test publication workflow using TestPyPI
- `release-publish`: Publish to production PyPI
- `release-finalize`: Post-release tasks (tags, GitHub release, documentation)
- `release-rollback`: Emergency rollback procedures
## Best Practices
### Pre-Release Checklist
1. All tests passing
2. Documentation updated
3. CHANGELOG.md entries complete
4. Version numbers synchronized
5. Dependencies validated
6. Security scan clean
### Publication Security
- Use API tokens, never passwords
- Separate TestPyPI and production credentials
- Validate package contents before upload
- Monitor for supply chain attacks
### Communication
- Clear release notes
- Breaking change notifications
- Deprecation warnings with timelines
- Community update posts
## Integration Points
### CI/CD Systems
- GitHub Actions workflow integration
- Automated testing on multiple Python versions
- Security scanning and dependency checking
- Automated documentation deployment
### Monitoring
- Download statistics tracking
- Error rate monitoring
- User feedback collection
- Dependency vulnerability scanning
When managing releases, always prioritize:
1. **Security**: Never compromise on security practices
2. **Reliability**: Thorough testing before publication
3. **Communication**: Clear documentation and announcements
4. **Reproducibility**: Consistent and documented processes

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):