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
- `templates` - Project template management
- `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
- **Console script entry point** making kaizen-agentic command available globally after pip install
- **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
- **Complete test coverage** for all distribution system components (134 test files)
- **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
- **Comprehensive documentation suite**:

242
Makefile
View File

@@ -1,6 +1,6 @@
# 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
VENV = .venv
@@ -24,6 +24,7 @@ help:
@echo " setup-tests - Create testing infrastructure"
@echo " setup-verify - Verify complete setup functionality"
@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 ""
@echo "Standards Compliance:"
@@ -38,6 +39,14 @@ help:
@echo " agents-status - Show agent status and project info"
@echo " agents-install-cli - Install kaizen-agentic CLI tool"
@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 " test - Run unit tests only (fast)"
@echo " test-all - Run comprehensive test suite (tests + standards + quality)"
@@ -76,6 +85,49 @@ install-dev: $(VENV)/bin/activate
@$(VENV_PIP) install -e .
@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-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 -e .
@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
### Install the Package
**From Source (Development):**
```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)

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
**Option A: From Source (Current - Development Version)**
```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
```
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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -13,6 +13,7 @@ from .detection import AgentSystemDetector, DetectedAgent, AgentSystemType
class MigrationStrategy(Enum):
"""Strategies for migrating existing agents."""
REPLACE = "replace" # Replace with Kaizen equivalent
EXTEND = "extend" # Extend Kaizen agent with custom functionality
PRESERVE = "preserve" # Keep custom agent alongside Kaizen agents
@@ -22,6 +23,7 @@ class MigrationStrategy(Enum):
class ConflictResolution(Enum):
"""Ways to resolve conflicts between agents."""
RENAME = "rename" # Rename one of the conflicting agents
NAMESPACE = "namespace" # Put agents in different namespaces
MERGE_FUNCTIONALITY = "merge" # Combine functionality
@@ -33,6 +35,7 @@ class ConflictResolution(Enum):
@dataclass
class MigrationPlan:
"""Plan for migrating an existing agent."""
source_agent: DetectedAgent
strategy: MigrationStrategy
target_agent: Optional[str] = None # Kaizen agent name if applicable
@@ -48,6 +51,7 @@ class MigrationPlan:
@dataclass
class ConflictResolutionPlan:
"""Plan for resolving a conflict between agents."""
agent1: str
agent2: str
conflict_type: str
@@ -62,6 +66,7 @@ class ConflictResolutionPlan:
@dataclass
class IntegrationPlan:
"""Complete plan for integrating Kaizen agents into an existing project."""
project_path: Path
migration_plans: List[MigrationPlan]
conflict_resolutions: List[ConflictResolutionPlan]
@@ -104,10 +109,18 @@ class AgentMigrationPlanner:
}
self.functional_categories = {
"project_management": ["keepaTodofile", "project-management", "priority-evaluation"],
"project_management": [
"keepaTodofile",
"project-management",
"priority-evaluation",
],
"testing": ["testing-efficiency", "test-maintenance", "tdd-workflow"],
"documentation": ["claude-documentation", "keepaContributingfile"],
"code_quality": ["code-refactoring", "datamodel-optimization", "optimization"],
"code_quality": [
"code-refactoring",
"datamodel-optimization",
"optimization",
],
"infrastructure": ["setupRepository", "tooling-optimization"],
"version_control": ["keepaChangelog"],
}
@@ -118,7 +131,9 @@ class AgentMigrationPlanner:
detection_result = detector.detect_agent_systems(project_path)
# Create backup directory
backup_dir = project_path / f".kaizen-migration-backup-{int(Path().stat().st_mtime)}"
backup_dir = (
project_path / f".kaizen-migration-backup-{int(Path().stat().st_mtime)}"
)
# Create migration plans for each detected agent
migration_plans = []
@@ -145,7 +160,7 @@ class AgentMigrationPlanner:
conflict_resolutions=conflict_resolutions,
backup_directory=backup_dir,
integration_order=integration_order,
post_migration_tasks=post_migration_tasks
post_migration_tasks=post_migration_tasks,
)
def _create_migration_plan(
@@ -190,7 +205,7 @@ class AgentMigrationPlanner:
strategy=strategy,
target_agent=target_agent,
backup_path=backup_path,
migration_notes=notes
migration_notes=notes,
)
def _has_unique_functionality(self, agent: DetectedAgent) -> bool:
@@ -203,8 +218,18 @@ class AgentMigrationPlanner:
"""Check if a custom agent is essential to the project."""
# Heuristics for essential agents
essential_patterns = [
"deploy", "ci", "cd", "build", "release", "secret", "auth",
"database", "api", "server", "client", "integration"
"deploy",
"ci",
"cd",
"build",
"release",
"secret",
"auth",
"database",
"api",
"server",
"client",
"integration",
]
agent_name_lower = agent.name.lower()
@@ -230,7 +255,7 @@ class AgentMigrationPlanner:
resolution = ConflictResolution.RENAME
action_details = {
"rename_agent": agent2, # Rename the second agent
"new_name": f"{agent2}_custom"
"new_name": f"{agent2}_custom",
}
elif "Functional overlap" in reason:
# Check if one is a Kaizen agent
@@ -252,10 +277,12 @@ class AgentMigrationPlanner:
agent2=agent2,
conflict_type=reason,
resolution=resolution,
action_details=action_details
action_details=action_details,
)
def _determine_integration_order(self, migration_plans: List[MigrationPlan]) -> List[str]:
def _determine_integration_order(
self, migration_plans: List[MigrationPlan]
) -> List[str]:
"""Determine the order to perform migrations."""
# Order by dependency and risk
order = []
@@ -265,7 +292,11 @@ class AgentMigrationPlanner:
order.extend([p.source_agent.name for p in infra_agents])
# 2. Core functionality agents
core_agents = [p for p in migration_plans if self._is_core_agent(p) and p not in infra_agents]
core_agents = [
p
for p in migration_plans
if self._is_core_agent(p) and p not in infra_agents
]
order.extend([p.source_agent.name for p in core_agents])
# 3. Optional/enhancement agents last
@@ -287,7 +318,10 @@ class AgentMigrationPlanner:
return any(keyword in agent_name for keyword in core_keywords)
def _generate_migration_notes(
self, agent: DetectedAgent, strategy: MigrationStrategy, target_agent: Optional[str]
self,
agent: DetectedAgent,
strategy: MigrationStrategy,
target_agent: Optional[str],
) -> List[str]:
"""Generate helpful notes for the migration."""
notes = []
@@ -332,13 +366,15 @@ class AgentMigrationPlanner:
if detection_result.config_files:
tasks.append("Verify all configuration files are updated")
tasks.extend([
"Run 'kaizen-agentic validate' to verify installation",
"Test all agent functionality",
"Update project documentation",
"Train team on new agent workflows",
"Archive or remove backup files after verification"
])
tasks.extend(
[
"Run 'kaizen-agentic validate' to verify installation",
"Test all agent functionality",
"Update project documentation",
"Train team on new agent workflows",
"Archive or remove backup files after verification",
]
)
return tasks
@@ -349,7 +385,9 @@ class AgentMigrator:
def __init__(self):
self.planner = AgentMigrationPlanner()
def execute_migration(self, plan: IntegrationPlan, dry_run: bool = True) -> Dict[str, str]:
def execute_migration(
self, plan: IntegrationPlan, dry_run: bool = True
) -> Dict[str, str]:
"""Execute a migration plan."""
results = {}
@@ -361,7 +399,7 @@ class AgentMigrator:
for agent_name in plan.integration_order:
migration_plan = next(
(p for p in plan.migration_plans if p.source_agent.name == agent_name),
None
None,
)
if migration_plan:
result = self._execute_single_migration(migration_plan, dry_run)
@@ -413,11 +451,14 @@ class AgentMigrator:
extension_config = {
"base_agent": plan.target_agent,
"custom_source": str(plan.source_agent.file_path),
"extension_type": "functional_overlay"
"extension_type": "functional_overlay",
}
extension_path = plan.source_agent.file_path.parent / f"{plan.source_agent.name}_extension.json"
with open(extension_path, 'w') as f:
extension_path = (
plan.source_agent.file_path.parent
/ f"{plan.source_agent.name}_extension.json"
)
with open(extension_path, "w") as f:
json.dump(extension_config, f, indent=2)
return f"EXTENDED: {plan.target_agent} with {plan.source_agent.name}"
@@ -427,7 +468,10 @@ class AgentMigrator:
# Rename if necessary to avoid conflicts
if plan.source_agent.name in ["todo", "changelog", "test"]:
new_name = f"{plan.source_agent.name}_custom"
new_path = plan.source_agent.file_path.parent / f"{new_name}{plan.source_agent.file_path.suffix}"
new_path = (
plan.source_agent.file_path.parent
/ f"{new_name}{plan.source_agent.file_path.suffix}"
)
shutil.move(plan.source_agent.file_path, new_path)
return f"PRESERVED: {plan.source_agent.name} -> {new_name}"
@@ -458,4 +502,4 @@ class AgentMigrator:
elif plan.resolution == ConflictResolution.CHOOSE_KAIZEN:
return f"RESOLVED: Chose Kaizen agent {plan.action_details['keep']}"
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):
"""Categories of agents for organization."""
PROJECT_MANAGEMENT = "project-management"
DEVELOPMENT_PROCESS = "development-process"
CODE_QUALITY = "code-quality"
@@ -21,6 +22,7 @@ class AgentCategory(Enum):
@dataclass
class AgentDefinition:
"""Represents an agent definition with metadata."""
name: str
description: str
file_path: Path
@@ -31,11 +33,11 @@ class AgentDefinition:
@classmethod
def from_file(cls, file_path: Path) -> "AgentDefinition":
"""Create AgentDefinition from a markdown file."""
with open(file_path, 'r', encoding='utf-8') as f:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Extract YAML frontmatter
frontmatter_match = re.match(r'^---\n(.*?)\n---\n', content, re.DOTALL)
frontmatter_match = re.match(r"^---\n(.*?)\n---\n", content, re.DOTALL)
if not frontmatter_match:
raise ValueError(f"No YAML frontmatter found in {file_path}")
@@ -45,15 +47,15 @@ class AgentDefinition:
dependencies = cls._extract_dependencies(content, frontmatter)
# Determine category from name or content
category = cls._determine_category(frontmatter['name'], content)
category = cls._determine_category(frontmatter["name"], content)
return cls(
name=frontmatter['name'],
description=frontmatter['description'],
name=frontmatter["name"],
description=frontmatter["description"],
file_path=file_path,
category=category,
dependencies=dependencies,
model=frontmatter.get('model')
model=frontmatter.get("model"),
)
@staticmethod
@@ -62,42 +64,42 @@ class AgentDefinition:
dependencies = set()
# Check frontmatter for explicit dependencies
for key in ['dependencies', 'depends_on', 'requires']:
for key in ["dependencies", "depends_on", "requires"]:
if key in frontmatter:
deps = frontmatter[key]
if isinstance(deps, list):
dependencies.update(deps)
elif isinstance(deps, str):
# Handle comma-separated string
dependencies.update([d.strip() for d in deps.split(',')])
dependencies.update([d.strip() for d in deps.split(",")])
# Look for explicit dependencies in content
dep_patterns = [
r'depends_on:\s*\[(.*?)\]',
r'requires:\s*\[(.*?)\]',
r'dependencies:\s*\[(.*?)\]',
r"depends_on:\s*\[(.*?)\]",
r"requires:\s*\[(.*?)\]",
r"dependencies:\s*\[(.*?)\]",
]
for pattern in dep_patterns:
matches = re.findall(pattern, content, re.IGNORECASE)
for match in matches:
if isinstance(match, str):
deps = [d.strip().strip('"\'') for d in match.split(',')]
deps = [d.strip().strip("\"'") for d in match.split(",")]
dependencies.update(deps)
# Look for specific agent references in content (more precise)
# Only look for full agent names like "todo-keeper agent" or "uses changelog-keeper"
agent_patterns = [
r'uses?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))',
r'depends?\s+on\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))',
r'requires?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))',
r"uses?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
r"depends?\s+on\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
r"requires?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
]
for pattern in agent_patterns:
matches = re.findall(pattern, content.lower())
for match in matches:
if match not in ['optimization', 'agentic', 'driven', 'assisted']:
dependencies.add(match.replace('-', '_'))
if match not in ["optimization", "agentic", "driven", "assisted"]:
dependencies.add(match.replace("-", "_"))
return dependencies
@@ -107,28 +109,33 @@ class AgentDefinition:
name_lower = name.lower()
# Project management agents
project_keywords = ['todo', 'changelog', 'contributing', 'project']
project_keywords = ["todo", "changelog", "contributing", "project"]
if any(keyword in name_lower for keyword in project_keywords):
return AgentCategory.PROJECT_MANAGEMENT
# Testing agents
if any(keyword in name_lower for keyword in ['test', 'tdd']):
if any(keyword in name_lower for keyword in ["test", "tdd"]):
return AgentCategory.TESTING
# Code quality agents
if any(keyword in name_lower for keyword in ['refactor', 'optimization', 'code']):
if any(
keyword in name_lower for keyword in ["refactor", "optimization", "code"]
):
return AgentCategory.CODE_QUALITY
# Documentation agents
if any(keyword in name_lower for keyword in ['documentation', 'claude']):
if any(keyword in name_lower for keyword in ["documentation", "claude"]):
return AgentCategory.DOCUMENTATION
# Infrastructure agents
if any(keyword in name_lower for keyword in ['setup', 'repository', 'tooling']):
if any(keyword in name_lower for keyword in ["setup", "repository", "tooling"]):
return AgentCategory.INFRASTRUCTURE
# Development process agents
if any(keyword in name_lower for keyword in ['workflow', 'requirements', 'maintenance']):
if any(
keyword in name_lower
for keyword in ["workflow", "requirements", "maintenance"]
):
return AgentCategory.DEVELOPMENT_PROCESS
# Default fallback
@@ -159,7 +166,9 @@ class AgentRegistry:
"""Get agent definition by name."""
return self._agents.get(name)
def list_agents(self, category: Optional[AgentCategory] = None) -> List[AgentDefinition]:
def list_agents(
self, category: Optional[AgentCategory] = None
) -> List[AgentDefinition]:
"""List all agents, optionally filtered by category."""
agents = list(self._agents.values())
if category:
@@ -232,7 +241,9 @@ class AgentRegistry:
return errors
def _has_circular_dependency(self, agent_name: str, visited: Optional[Set[str]] = None) -> bool:
def _has_circular_dependency(
self, agent_name: str, visited: Optional[Set[str]] = None
) -> bool:
"""Check if an agent has circular dependencies."""
if visited is None:
visited = set()
@@ -255,18 +266,14 @@ class AgentRegistry:
def get_agent_templates(self) -> Dict[str, List[str]]:
"""Get predefined agent templates for different project types."""
return {
"python-basic": [
"setupRepository",
"keepaTodofile",
"keepaChangelog"
],
"python-basic": ["setupRepository", "keepaTodofile", "keepaChangelog"],
"python-web": [
"setupRepository",
"tdd-workflow",
"code-refactoring",
"keepaTodofile",
"keepaChangelog",
"keepaContributingfile"
"keepaContributingfile",
],
"python-cli": [
"setupRepository",
@@ -274,7 +281,7 @@ class AgentRegistry:
"testing-efficiency",
"claude-documentation",
"keepaTodofile",
"keepaChangelog"
"keepaChangelog",
],
"python-data": [
"setupRepository",
@@ -282,9 +289,7 @@ class AgentRegistry:
"testing-efficiency",
"requirements-engineering",
"keepaTodofile",
"keepaChangelog"
"keepaChangelog",
],
"comprehensive": [
agent.name for agent in self.list_agents()
]
"comprehensive": [agent.name for agent in self.list_agents()],
}

View File

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

View File

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