Compare commits
6 Commits
de94036e35
...
19b3c16cce
| Author | SHA1 | Date | |
|---|---|---|---|
| 19b3c16cce | |||
| 712d85d1ab | |||
| 63dacda163 | |||
| 0f7c3a9a3c | |||
| d68310793b | |||
| 30daabf12c |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -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
242
Makefile
@@ -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"
|
||||
22
README.md
22
README.md
@@ -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)
|
||||
|
||||
101
agents/agent-releaseManager.md
Normal file
101
agents/agent-releaseManager.md
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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()
|
||||
|
||||
101
src/kaizen_agentic/data/agents/agent-releaseManager.md
Normal file
101
src/kaizen_agentic/data/agents/agent-releaseManager.md
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -12,6 +12,7 @@ from .registry import AgentRegistry
|
||||
@dataclass
|
||||
class InstallationConfig:
|
||||
"""Configuration for agent installation."""
|
||||
|
||||
target_dir: Path
|
||||
claude_config_path: Optional[Path] = None
|
||||
makefile_path: Optional[Path] = None
|
||||
@@ -26,9 +27,7 @@ class AgentInstaller:
|
||||
self.registry = registry
|
||||
|
||||
def install_agents(
|
||||
self,
|
||||
agent_names: List[str],
|
||||
config: InstallationConfig
|
||||
self, agent_names: List[str], config: InstallationConfig
|
||||
) -> Dict[str, str]:
|
||||
"""Install agents into a project.
|
||||
|
||||
@@ -89,9 +88,7 @@ class AgentInstaller:
|
||||
return sorted(installed)
|
||||
|
||||
def update_agents(
|
||||
self,
|
||||
project_dir: Path,
|
||||
agent_names: Optional[List[str]] = None
|
||||
self, project_dir: Path, agent_names: Optional[List[str]] = None
|
||||
) -> Dict[str, str]:
|
||||
"""Update installed agents to latest versions."""
|
||||
if agent_names is None:
|
||||
@@ -101,9 +98,7 @@ class AgentInstaller:
|
||||
return self.install_agents(agent_names, config)
|
||||
|
||||
def remove_agents(
|
||||
self,
|
||||
agent_names: List[str],
|
||||
project_dir: Path
|
||||
self, agent_names: List[str], project_dir: Path
|
||||
) -> Dict[str, str]:
|
||||
"""Remove agents from a project."""
|
||||
results = {}
|
||||
@@ -162,7 +157,10 @@ class AgentInstaller:
|
||||
counter = 0
|
||||
while backup_dir.exists():
|
||||
counter += 1
|
||||
backup_dir = agents_dir.parent / f"agents_backup_{timestamp}_{microseconds}_{counter}"
|
||||
backup_dir = (
|
||||
agents_dir.parent
|
||||
/ f"agents_backup_{timestamp}_{microseconds}_{counter}"
|
||||
)
|
||||
|
||||
shutil.copytree(agents_dir, backup_dir)
|
||||
print(f"Created backup at: {backup_dir}")
|
||||
@@ -173,22 +171,22 @@ class AgentInstaller:
|
||||
# Read existing config
|
||||
config = {}
|
||||
if config_path.exists():
|
||||
with open(config_path, 'r') as f:
|
||||
with open(config_path, "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
# Ensure agents section exists
|
||||
if 'agents' not in config:
|
||||
config['agents'] = {}
|
||||
if "agents" not in config:
|
||||
config["agents"] = {}
|
||||
|
||||
# Add agent references
|
||||
for agent_name in agent_names:
|
||||
config['agents'][agent_name] = {
|
||||
config["agents"][agent_name] = {
|
||||
"path": f"agents/agent-{agent_name}.md",
|
||||
"enabled": True
|
||||
"enabled": True,
|
||||
}
|
||||
|
||||
# Write updated config
|
||||
with open(config_path, 'w') as f:
|
||||
with open(config_path, "w") as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
print(f"Updated Claude configuration: {config_path}")
|
||||
@@ -200,7 +198,7 @@ class AgentInstaller:
|
||||
"""Update Makefile with agent-specific targets."""
|
||||
try:
|
||||
# Read existing Makefile
|
||||
with open(makefile_path, 'r') as f:
|
||||
with open(makefile_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Add agent management targets if not present
|
||||
@@ -224,7 +222,7 @@ agents-validate:
|
||||
content += agent_targets
|
||||
|
||||
# Write updated Makefile
|
||||
with open(makefile_path, 'w') as f:
|
||||
with open(makefile_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"Updated Makefile: {makefile_path}")
|
||||
@@ -238,7 +236,9 @@ agents-validate:
|
||||
claude_md = project_dir / "CLAUDE.md"
|
||||
|
||||
agent_section = "## Installed Agents\n\n"
|
||||
agent_section += "This project includes the following specialized agents:\n\n"
|
||||
agent_section += (
|
||||
"This project includes the following specialized agents:\n\n"
|
||||
)
|
||||
|
||||
# Group agents by category
|
||||
categories = {}
|
||||
@@ -257,34 +257,37 @@ agents-validate:
|
||||
agent_section += f"- **{agent.name}**: {agent.description}\n"
|
||||
agent_section += "\n"
|
||||
|
||||
agent_section += ("Use these agents by referencing them in your "
|
||||
"Claude Code interactions.\n\n")
|
||||
agent_section += (
|
||||
"Use these agents by referencing them in your "
|
||||
"Claude Code interactions.\n\n"
|
||||
)
|
||||
|
||||
# Update or create CLAUDE.md
|
||||
if claude_md.exists():
|
||||
with open(claude_md, 'r') as f:
|
||||
with open(claude_md, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace existing agent section or append
|
||||
if "## Installed Agents" in content:
|
||||
import re
|
||||
|
||||
content = re.sub(
|
||||
r'## Installed Agents.*?(?=##|\Z)',
|
||||
r"## Installed Agents.*?(?=##|\Z)",
|
||||
agent_section,
|
||||
content,
|
||||
flags=re.DOTALL
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
else:
|
||||
content += "\n" + agent_section
|
||||
|
||||
with open(claude_md, 'w') as f:
|
||||
with open(claude_md, "w") as f:
|
||||
f.write(content)
|
||||
else:
|
||||
# Create new CLAUDE.md
|
||||
header = "# Claude Code Configuration\n\n"
|
||||
header += "This file contains Claude Code configuration and agent information.\n\n"
|
||||
|
||||
with open(claude_md, 'w') as f:
|
||||
with open(claude_md, "w") as f:
|
||||
f.write(header + agent_section)
|
||||
|
||||
print(f"Updated documentation: {claude_md}")
|
||||
@@ -304,7 +307,7 @@ class ProjectInitializer:
|
||||
project_dir: Path,
|
||||
template: str = "python-basic",
|
||||
agent_names: Optional[List[str]] = None,
|
||||
project_name: Optional[str] = None
|
||||
project_name: Optional[str] = None,
|
||||
) -> Dict[str, str]:
|
||||
"""Initialize a new project with agents and structure."""
|
||||
results = {}
|
||||
@@ -325,7 +328,7 @@ class ProjectInitializer:
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir,
|
||||
claude_config_path=project_dir / "CLAUDE.md",
|
||||
makefile_path=project_dir / "Makefile"
|
||||
makefile_path=project_dir / "Makefile",
|
||||
)
|
||||
|
||||
installer = AgentInstaller(self.registry)
|
||||
@@ -337,12 +340,16 @@ class ProjectInitializer:
|
||||
|
||||
return results
|
||||
|
||||
def _create_project_structure(self, project_dir: Path, project_name: str, template: str):
|
||||
def _create_project_structure(
|
||||
self, project_dir: Path, project_name: str, template: str
|
||||
):
|
||||
"""Create basic project structure based on template."""
|
||||
# Create directories
|
||||
dirs_to_create = ["src", "tests", "docs"]
|
||||
if template.startswith("python"):
|
||||
dirs_to_create.extend([f"src/{project_name.replace('-', '_')}", ".github/workflows"])
|
||||
dirs_to_create.extend(
|
||||
[f"src/{project_name.replace('-', '_')}", ".github/workflows"]
|
||||
)
|
||||
|
||||
for dir_name in dirs_to_create:
|
||||
(project_dir / dir_name).mkdir(parents=True, exist_ok=True)
|
||||
@@ -501,7 +508,7 @@ python_functions = ["test_*"]
|
||||
|
||||
def _create_init_py(self, project_dir: Path, project_name: str):
|
||||
"""Create package __init__.py file."""
|
||||
package_name = project_name.replace('-', '_')
|
||||
package_name = project_name.replace("-", "_")
|
||||
init_content = f'''"""
|
||||
{project_name} - A Python project with Kaizen Agentic agents.
|
||||
"""
|
||||
@@ -512,7 +519,7 @@ __version__ = "0.1.0"
|
||||
|
||||
def _create_makefile(self, project_dir: Path, project_name: str):
|
||||
"""Create Makefile with standard targets."""
|
||||
package_name = project_name.replace('-', '_')
|
||||
package_name = project_name.replace("-", "_")
|
||||
makefile_content = f"""# {project_name} - Makefile for development workflow
|
||||
# Generated by Kaizen Agentic
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from .detection import AgentSystemDetector, DetectedAgent, AgentSystemType
|
||||
|
||||
class MigrationStrategy(Enum):
|
||||
"""Strategies for migrating existing agents."""
|
||||
|
||||
REPLACE = "replace" # Replace with Kaizen equivalent
|
||||
EXTEND = "extend" # Extend Kaizen agent with custom functionality
|
||||
PRESERVE = "preserve" # Keep custom agent alongside Kaizen agents
|
||||
@@ -22,6 +23,7 @@ class MigrationStrategy(Enum):
|
||||
|
||||
class ConflictResolution(Enum):
|
||||
"""Ways to resolve conflicts between agents."""
|
||||
|
||||
RENAME = "rename" # Rename one of the conflicting agents
|
||||
NAMESPACE = "namespace" # Put agents in different namespaces
|
||||
MERGE_FUNCTIONALITY = "merge" # Combine functionality
|
||||
@@ -33,6 +35,7 @@ class ConflictResolution(Enum):
|
||||
@dataclass
|
||||
class MigrationPlan:
|
||||
"""Plan for migrating an existing agent."""
|
||||
|
||||
source_agent: DetectedAgent
|
||||
strategy: MigrationStrategy
|
||||
target_agent: Optional[str] = None # Kaizen agent name if applicable
|
||||
@@ -48,6 +51,7 @@ class MigrationPlan:
|
||||
@dataclass
|
||||
class ConflictResolutionPlan:
|
||||
"""Plan for resolving a conflict between agents."""
|
||||
|
||||
agent1: str
|
||||
agent2: str
|
||||
conflict_type: str
|
||||
@@ -62,6 +66,7 @@ class ConflictResolutionPlan:
|
||||
@dataclass
|
||||
class IntegrationPlan:
|
||||
"""Complete plan for integrating Kaizen agents into an existing project."""
|
||||
|
||||
project_path: Path
|
||||
migration_plans: List[MigrationPlan]
|
||||
conflict_resolutions: List[ConflictResolutionPlan]
|
||||
@@ -104,10 +109,18 @@ class AgentMigrationPlanner:
|
||||
}
|
||||
|
||||
self.functional_categories = {
|
||||
"project_management": ["keepaTodofile", "project-management", "priority-evaluation"],
|
||||
"project_management": [
|
||||
"keepaTodofile",
|
||||
"project-management",
|
||||
"priority-evaluation",
|
||||
],
|
||||
"testing": ["testing-efficiency", "test-maintenance", "tdd-workflow"],
|
||||
"documentation": ["claude-documentation", "keepaContributingfile"],
|
||||
"code_quality": ["code-refactoring", "datamodel-optimization", "optimization"],
|
||||
"code_quality": [
|
||||
"code-refactoring",
|
||||
"datamodel-optimization",
|
||||
"optimization",
|
||||
],
|
||||
"infrastructure": ["setupRepository", "tooling-optimization"],
|
||||
"version_control": ["keepaChangelog"],
|
||||
}
|
||||
@@ -118,7 +131,9 @@ class AgentMigrationPlanner:
|
||||
detection_result = detector.detect_agent_systems(project_path)
|
||||
|
||||
# Create backup directory
|
||||
backup_dir = project_path / f".kaizen-migration-backup-{int(Path().stat().st_mtime)}"
|
||||
backup_dir = (
|
||||
project_path / f".kaizen-migration-backup-{int(Path().stat().st_mtime)}"
|
||||
)
|
||||
|
||||
# Create migration plans for each detected agent
|
||||
migration_plans = []
|
||||
@@ -145,7 +160,7 @@ class AgentMigrationPlanner:
|
||||
conflict_resolutions=conflict_resolutions,
|
||||
backup_directory=backup_dir,
|
||||
integration_order=integration_order,
|
||||
post_migration_tasks=post_migration_tasks
|
||||
post_migration_tasks=post_migration_tasks,
|
||||
)
|
||||
|
||||
def _create_migration_plan(
|
||||
@@ -190,7 +205,7 @@ class AgentMigrationPlanner:
|
||||
strategy=strategy,
|
||||
target_agent=target_agent,
|
||||
backup_path=backup_path,
|
||||
migration_notes=notes
|
||||
migration_notes=notes,
|
||||
)
|
||||
|
||||
def _has_unique_functionality(self, agent: DetectedAgent) -> bool:
|
||||
@@ -203,8 +218,18 @@ class AgentMigrationPlanner:
|
||||
"""Check if a custom agent is essential to the project."""
|
||||
# Heuristics for essential agents
|
||||
essential_patterns = [
|
||||
"deploy", "ci", "cd", "build", "release", "secret", "auth",
|
||||
"database", "api", "server", "client", "integration"
|
||||
"deploy",
|
||||
"ci",
|
||||
"cd",
|
||||
"build",
|
||||
"release",
|
||||
"secret",
|
||||
"auth",
|
||||
"database",
|
||||
"api",
|
||||
"server",
|
||||
"client",
|
||||
"integration",
|
||||
]
|
||||
|
||||
agent_name_lower = agent.name.lower()
|
||||
@@ -230,7 +255,7 @@ class AgentMigrationPlanner:
|
||||
resolution = ConflictResolution.RENAME
|
||||
action_details = {
|
||||
"rename_agent": agent2, # Rename the second agent
|
||||
"new_name": f"{agent2}_custom"
|
||||
"new_name": f"{agent2}_custom",
|
||||
}
|
||||
elif "Functional overlap" in reason:
|
||||
# Check if one is a Kaizen agent
|
||||
@@ -252,10 +277,12 @@ class AgentMigrationPlanner:
|
||||
agent2=agent2,
|
||||
conflict_type=reason,
|
||||
resolution=resolution,
|
||||
action_details=action_details
|
||||
action_details=action_details,
|
||||
)
|
||||
|
||||
def _determine_integration_order(self, migration_plans: List[MigrationPlan]) -> List[str]:
|
||||
def _determine_integration_order(
|
||||
self, migration_plans: List[MigrationPlan]
|
||||
) -> List[str]:
|
||||
"""Determine the order to perform migrations."""
|
||||
# Order by dependency and risk
|
||||
order = []
|
||||
@@ -265,7 +292,11 @@ class AgentMigrationPlanner:
|
||||
order.extend([p.source_agent.name for p in infra_agents])
|
||||
|
||||
# 2. Core functionality agents
|
||||
core_agents = [p for p in migration_plans if self._is_core_agent(p) and p not in infra_agents]
|
||||
core_agents = [
|
||||
p
|
||||
for p in migration_plans
|
||||
if self._is_core_agent(p) and p not in infra_agents
|
||||
]
|
||||
order.extend([p.source_agent.name for p in core_agents])
|
||||
|
||||
# 3. Optional/enhancement agents last
|
||||
@@ -287,7 +318,10 @@ class AgentMigrationPlanner:
|
||||
return any(keyword in agent_name for keyword in core_keywords)
|
||||
|
||||
def _generate_migration_notes(
|
||||
self, agent: DetectedAgent, strategy: MigrationStrategy, target_agent: Optional[str]
|
||||
self,
|
||||
agent: DetectedAgent,
|
||||
strategy: MigrationStrategy,
|
||||
target_agent: Optional[str],
|
||||
) -> List[str]:
|
||||
"""Generate helpful notes for the migration."""
|
||||
notes = []
|
||||
@@ -332,13 +366,15 @@ class AgentMigrationPlanner:
|
||||
if detection_result.config_files:
|
||||
tasks.append("Verify all configuration files are updated")
|
||||
|
||||
tasks.extend([
|
||||
"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}"
|
||||
|
||||
@@ -10,6 +10,7 @@ from enum import Enum
|
||||
|
||||
class AgentCategory(Enum):
|
||||
"""Categories of agents for organization."""
|
||||
|
||||
PROJECT_MANAGEMENT = "project-management"
|
||||
DEVELOPMENT_PROCESS = "development-process"
|
||||
CODE_QUALITY = "code-quality"
|
||||
@@ -21,6 +22,7 @@ class AgentCategory(Enum):
|
||||
@dataclass
|
||||
class AgentDefinition:
|
||||
"""Represents an agent definition with metadata."""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
file_path: Path
|
||||
@@ -31,11 +33,11 @@ class AgentDefinition:
|
||||
@classmethod
|
||||
def from_file(cls, file_path: Path) -> "AgentDefinition":
|
||||
"""Create AgentDefinition from a markdown file."""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Extract YAML frontmatter
|
||||
frontmatter_match = re.match(r'^---\n(.*?)\n---\n', content, re.DOTALL)
|
||||
frontmatter_match = re.match(r"^---\n(.*?)\n---\n", content, re.DOTALL)
|
||||
if not frontmatter_match:
|
||||
raise ValueError(f"No YAML frontmatter found in {file_path}")
|
||||
|
||||
@@ -45,15 +47,15 @@ class AgentDefinition:
|
||||
dependencies = cls._extract_dependencies(content, frontmatter)
|
||||
|
||||
# Determine category from name or content
|
||||
category = cls._determine_category(frontmatter['name'], content)
|
||||
category = cls._determine_category(frontmatter["name"], content)
|
||||
|
||||
return cls(
|
||||
name=frontmatter['name'],
|
||||
description=frontmatter['description'],
|
||||
name=frontmatter["name"],
|
||||
description=frontmatter["description"],
|
||||
file_path=file_path,
|
||||
category=category,
|
||||
dependencies=dependencies,
|
||||
model=frontmatter.get('model')
|
||||
model=frontmatter.get("model"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -62,42 +64,42 @@ class AgentDefinition:
|
||||
dependencies = set()
|
||||
|
||||
# Check frontmatter for explicit dependencies
|
||||
for key in ['dependencies', 'depends_on', 'requires']:
|
||||
for key in ["dependencies", "depends_on", "requires"]:
|
||||
if key in frontmatter:
|
||||
deps = frontmatter[key]
|
||||
if isinstance(deps, list):
|
||||
dependencies.update(deps)
|
||||
elif isinstance(deps, str):
|
||||
# Handle comma-separated string
|
||||
dependencies.update([d.strip() for d in deps.split(',')])
|
||||
dependencies.update([d.strip() for d in deps.split(",")])
|
||||
|
||||
# Look for explicit dependencies in content
|
||||
dep_patterns = [
|
||||
r'depends_on:\s*\[(.*?)\]',
|
||||
r'requires:\s*\[(.*?)\]',
|
||||
r'dependencies:\s*\[(.*?)\]',
|
||||
r"depends_on:\s*\[(.*?)\]",
|
||||
r"requires:\s*\[(.*?)\]",
|
||||
r"dependencies:\s*\[(.*?)\]",
|
||||
]
|
||||
|
||||
for pattern in dep_patterns:
|
||||
matches = re.findall(pattern, content, re.IGNORECASE)
|
||||
for match in matches:
|
||||
if isinstance(match, str):
|
||||
deps = [d.strip().strip('"\'') for d in match.split(',')]
|
||||
deps = [d.strip().strip("\"'") for d in match.split(",")]
|
||||
dependencies.update(deps)
|
||||
|
||||
# Look for specific agent references in content (more precise)
|
||||
# Only look for full agent names like "todo-keeper agent" or "uses changelog-keeper"
|
||||
agent_patterns = [
|
||||
r'uses?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))',
|
||||
r'depends?\s+on\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))',
|
||||
r'requires?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))',
|
||||
r"uses?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
|
||||
r"depends?\s+on\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
|
||||
r"requires?\s+(\w+(?:-\w+)*-(?:keeper|agent|workflow|helper|manager))",
|
||||
]
|
||||
|
||||
for pattern in agent_patterns:
|
||||
matches = re.findall(pattern, content.lower())
|
||||
for match in matches:
|
||||
if match not in ['optimization', 'agentic', 'driven', 'assisted']:
|
||||
dependencies.add(match.replace('-', '_'))
|
||||
if match not in ["optimization", "agentic", "driven", "assisted"]:
|
||||
dependencies.add(match.replace("-", "_"))
|
||||
|
||||
return dependencies
|
||||
|
||||
@@ -107,28 +109,33 @@ class AgentDefinition:
|
||||
name_lower = name.lower()
|
||||
|
||||
# Project management agents
|
||||
project_keywords = ['todo', 'changelog', 'contributing', 'project']
|
||||
project_keywords = ["todo", "changelog", "contributing", "project"]
|
||||
if any(keyword in name_lower for keyword in project_keywords):
|
||||
return AgentCategory.PROJECT_MANAGEMENT
|
||||
|
||||
# Testing agents
|
||||
if any(keyword in name_lower for keyword in ['test', 'tdd']):
|
||||
if any(keyword in name_lower for keyword in ["test", "tdd"]):
|
||||
return AgentCategory.TESTING
|
||||
|
||||
# Code quality agents
|
||||
if any(keyword in name_lower for keyword in ['refactor', 'optimization', 'code']):
|
||||
if any(
|
||||
keyword in name_lower for keyword in ["refactor", "optimization", "code"]
|
||||
):
|
||||
return AgentCategory.CODE_QUALITY
|
||||
|
||||
# Documentation agents
|
||||
if any(keyword in name_lower for keyword in ['documentation', 'claude']):
|
||||
if any(keyword in name_lower for keyword in ["documentation", "claude"]):
|
||||
return AgentCategory.DOCUMENTATION
|
||||
|
||||
# Infrastructure agents
|
||||
if any(keyword in name_lower for keyword in ['setup', 'repository', 'tooling']):
|
||||
if any(keyword in name_lower for keyword in ["setup", "repository", "tooling"]):
|
||||
return AgentCategory.INFRASTRUCTURE
|
||||
|
||||
# Development process agents
|
||||
if any(keyword in name_lower for keyword in ['workflow', 'requirements', 'maintenance']):
|
||||
if any(
|
||||
keyword in name_lower
|
||||
for keyword in ["workflow", "requirements", "maintenance"]
|
||||
):
|
||||
return AgentCategory.DEVELOPMENT_PROCESS
|
||||
|
||||
# Default fallback
|
||||
@@ -159,7 +166,9 @@ class AgentRegistry:
|
||||
"""Get agent definition by name."""
|
||||
return self._agents.get(name)
|
||||
|
||||
def list_agents(self, category: Optional[AgentCategory] = None) -> List[AgentDefinition]:
|
||||
def list_agents(
|
||||
self, category: Optional[AgentCategory] = None
|
||||
) -> List[AgentDefinition]:
|
||||
"""List all agents, optionally filtered by category."""
|
||||
agents = list(self._agents.values())
|
||||
if category:
|
||||
@@ -232,7 +241,9 @@ class AgentRegistry:
|
||||
|
||||
return errors
|
||||
|
||||
def _has_circular_dependency(self, agent_name: str, visited: Optional[Set[str]] = None) -> bool:
|
||||
def _has_circular_dependency(
|
||||
self, agent_name: str, visited: Optional[Set[str]] = None
|
||||
) -> bool:
|
||||
"""Check if an agent has circular dependencies."""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
@@ -255,18 +266,14 @@ class AgentRegistry:
|
||||
def get_agent_templates(self) -> Dict[str, List[str]]:
|
||||
"""Get predefined agent templates for different project types."""
|
||||
return {
|
||||
"python-basic": [
|
||||
"setupRepository",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog"
|
||||
],
|
||||
"python-basic": ["setupRepository", "keepaTodofile", "keepaChangelog"],
|
||||
"python-web": [
|
||||
"setupRepository",
|
||||
"tdd-workflow",
|
||||
"code-refactoring",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog",
|
||||
"keepaContributingfile"
|
||||
"keepaContributingfile",
|
||||
],
|
||||
"python-cli": [
|
||||
"setupRepository",
|
||||
@@ -274,7 +281,7 @@ class AgentRegistry:
|
||||
"testing-efficiency",
|
||||
"claude-documentation",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog"
|
||||
"keepaChangelog",
|
||||
],
|
||||
"python-data": [
|
||||
"setupRepository",
|
||||
@@ -282,9 +289,7 @@ class AgentRegistry:
|
||||
"testing-efficiency",
|
||||
"requirements-engineering",
|
||||
"keepaTodofile",
|
||||
"keepaChangelog"
|
||||
"keepaChangelog",
|
||||
],
|
||||
"comprehensive": [
|
||||
agent.name for agent in self.list_agents()
|
||||
]
|
||||
"comprehensive": [agent.name for agent in self.list_agents()],
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
import json
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from kaizen_agentic.installer import AgentInstaller, ProjectInitializer, InstallationConfig
|
||||
from kaizen_agentic.installer import (
|
||||
AgentInstaller,
|
||||
ProjectInitializer,
|
||||
InstallationConfig,
|
||||
)
|
||||
from kaizen_agentic.registry import AgentRegistry
|
||||
|
||||
|
||||
@@ -72,9 +76,7 @@ def test_install_agents(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir,
|
||||
create_backup=False,
|
||||
update_docs=False
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
|
||||
results = installer.install_agents(["base-agent"], config)
|
||||
@@ -89,9 +91,7 @@ def test_install_agents_with_dependencies(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir,
|
||||
create_backup=False,
|
||||
update_docs=False
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
|
||||
# Install an agent that depends on others
|
||||
@@ -111,7 +111,9 @@ def test_list_installed_agents(test_registry, tmp_path):
|
||||
assert installed == []
|
||||
|
||||
# Install some agents
|
||||
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False)
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
installer.install_agents(["base-agent", "keepaTodofile"], config)
|
||||
|
||||
# Check installed agents
|
||||
@@ -127,7 +129,9 @@ def test_update_agents(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
# Install initial agents
|
||||
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False)
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
installer.install_agents(["base-agent"], config)
|
||||
|
||||
# Update all agents
|
||||
@@ -146,7 +150,9 @@ def test_remove_agents(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "test_project"
|
||||
|
||||
# Install agents first
|
||||
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False)
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
installer.install_agents(["base-agent", "keepaTodofile"], config)
|
||||
|
||||
# Remove an agent
|
||||
@@ -170,7 +176,9 @@ def test_validate_installation(test_registry, tmp_path):
|
||||
assert "No agents directory found" in errors["project"]
|
||||
|
||||
# Install agents and validate
|
||||
config = InstallationConfig(target_dir=project_dir, create_backup=False, update_docs=False)
|
||||
config = InstallationConfig(
|
||||
target_dir=project_dir, create_backup=False, update_docs=False
|
||||
)
|
||||
installer.install_agents(["base-agent"], config)
|
||||
|
||||
errors = installer.validate_installation(project_dir)
|
||||
@@ -187,7 +195,7 @@ def test_update_claude_config(test_registry, tmp_path):
|
||||
target_dir=project_dir,
|
||||
claude_config_path=claude_config,
|
||||
create_backup=False,
|
||||
update_docs=False
|
||||
update_docs=False,
|
||||
)
|
||||
|
||||
installer.install_agents(["base-agent"], config)
|
||||
@@ -208,9 +216,7 @@ def test_project_initializer(test_registry, tmp_path):
|
||||
project_dir = tmp_path / "new_project"
|
||||
|
||||
initializer.init_project(
|
||||
project_dir,
|
||||
template="python-basic",
|
||||
project_name="new_project"
|
||||
project_dir, template="python-basic", project_name="new_project"
|
||||
)
|
||||
|
||||
# Check that project structure was created
|
||||
@@ -236,7 +242,7 @@ def test_project_initializer_custom_agents(test_registry, tmp_path):
|
||||
project_dir,
|
||||
template="python-basic",
|
||||
agent_names=["base-agent", "keepaTodofile"],
|
||||
project_name="custom_project"
|
||||
project_name="custom_project",
|
||||
)
|
||||
|
||||
# Check that specific agents were installed
|
||||
@@ -251,7 +257,7 @@ def test_installation_config():
|
||||
claude_config_path=Path("/tmp/test/claude.json"),
|
||||
makefile_path=Path("/tmp/test/Makefile"),
|
||||
update_docs=True,
|
||||
create_backup=False
|
||||
create_backup=False,
|
||||
)
|
||||
|
||||
assert config.target_dir == Path("/tmp/test")
|
||||
|
||||
@@ -158,7 +158,10 @@ This agent uses both base-agent and dependent-agent.
|
||||
|
||||
# Test complex dependency resolution
|
||||
resolved = registry.resolve_dependencies(["complex-agent"])
|
||||
assert all(agent in resolved for agent in ["base-agent", "dependent-agent", "complex-agent"])
|
||||
assert all(
|
||||
agent in resolved
|
||||
for agent in ["base-agent", "dependent-agent", "complex-agent"]
|
||||
)
|
||||
|
||||
|
||||
def test_agent_registry_get_templates(tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user