WP-0001 complete: v1.1.0 lazy registry and install performance
Lazy-load agent registry (frontmatter index, parse on demand), copy agents by path during install, fix Makefile template tab lint issue, add registry performance tests, bump to 1.1.0, document CLI reinstall after pull.
This commit is contained in:
@@ -9,7 +9,7 @@ It also includes a comprehensive agent distribution system for sharing
|
||||
specialized agents across projects via CLI tools and package management.
|
||||
"""
|
||||
|
||||
__version__ = "1.0.2"
|
||||
__version__ = "1.1.0"
|
||||
__author__ = "Kaizen Agentic Team"
|
||||
|
||||
from .core import Agent, AgentConfig
|
||||
|
||||
@@ -47,16 +47,16 @@ class AgentInstaller:
|
||||
if config.create_backup and agents_dir.exists():
|
||||
self._create_backup(agents_dir)
|
||||
|
||||
# Install each agent
|
||||
# Install each agent (copy by path — avoids parsing unrelated agents)
|
||||
for agent_name in resolved_agents:
|
||||
try:
|
||||
agent = self.registry.get_agent(agent_name)
|
||||
if not agent:
|
||||
source_path = self.registry.get_agent_path(agent_name)
|
||||
if not source_path:
|
||||
results[agent_name] = "ERROR: Agent not found"
|
||||
continue
|
||||
|
||||
target_path = agents_dir / f"agent-{agent_name}.md"
|
||||
shutil.copy2(agent.file_path, target_path)
|
||||
shutil.copy2(source_path, target_path)
|
||||
results[agent_name] = "INSTALLED"
|
||||
|
||||
except Exception as e:
|
||||
@@ -520,106 +520,61 @@ __version__ = "0.1.0"
|
||||
def _create_makefile(self, project_dir: Path, project_name: str):
|
||||
"""Create Makefile with standard targets."""
|
||||
package_name = project_name.replace("-", "_")
|
||||
makefile_content = f"""# {project_name} - Makefile for development workflow
|
||||
# Generated by Kaizen Agentic
|
||||
|
||||
.PHONY: help setup-complete setup-python setup-tools test lint format clean agents-status agents-update
|
||||
|
||||
# Default target
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " setup-complete - Complete development environment setup"
|
||||
@echo " setup-python - Set up Python virtual environment and dependencies"
|
||||
@echo " setup-tools - Install development tools"
|
||||
@echo " test - Run test suite"
|
||||
@echo " lint - Run code quality checks"
|
||||
@echo " format - Format code with black"
|
||||
@echo " clean - Clean build artifacts"
|
||||
@echo " agents-status - Show installed agents status"
|
||||
@echo " agents-update - Update agents to latest versions"
|
||||
|
||||
# Virtual environment detection
|
||||
VENV := .venv
|
||||
PYTHON := $(VENV)/bin/python
|
||||
PIP := $(VENV)/bin/pip
|
||||
|
||||
# Complete setup
|
||||
setup-complete: setup-python setup-tools
|
||||
@echo "✅ Development environment setup complete!"
|
||||
@echo "Next steps:"
|
||||
@echo " source $(VENV)/bin/activate # Activate virtual environment"
|
||||
@echo " make test # Run tests"
|
||||
@echo " make lint # Check code quality"
|
||||
|
||||
# Python environment setup
|
||||
setup-python: $(VENV)/bin/activate
|
||||
|
||||
$(VENV)/bin/activate: pyproject.toml
|
||||
python3 -m venv $(VENV)
|
||||
$(PIP) install --upgrade pip
|
||||
$(PIP) install -e ".[dev]"
|
||||
touch $(VENV)/bin/activate
|
||||
|
||||
# Development tools setup
|
||||
setup-tools: $(VENV)/bin/activate
|
||||
@echo "Development tools installed via pyproject.toml"
|
||||
|
||||
# Testing
|
||||
test: $(VENV)/bin/activate
|
||||
$(PYTHON) -m pytest tests/ -v
|
||||
|
||||
test-coverage: $(VENV)/bin/activate
|
||||
$(PYTHON) -m pytest tests/ --cov=src/{package_name} --cov-report=html --cov-report=term-missing
|
||||
|
||||
# Code quality
|
||||
lint: $(VENV)/bin/activate
|
||||
$(PYTHON) -m flake8 src/ tests/
|
||||
$(PYTHON) -m mypy src/
|
||||
|
||||
format: $(VENV)/bin/activate
|
||||
$(PYTHON) -m black src/ tests/
|
||||
|
||||
format-check: $(VENV)/bin/activate
|
||||
$(PYTHON) -m black --check src/ tests/
|
||||
|
||||
# Cleanup
|
||||
clean:
|
||||
rm -rf build/
|
||||
rm -rf dist/
|
||||
rm -rf *.egg-info/
|
||||
rm -rf .pytest_cache/
|
||||
rm -rf .coverage
|
||||
rm -rf htmlcov/
|
||||
find . -type d -name __pycache__ -exec rm -rf {{}} +
|
||||
find . -type f -name "*.pyc" -delete
|
||||
|
||||
# Agent management
|
||||
agents-status:
|
||||
@if command -v kaizen-agentic >/dev/null 2>&1; then \\
|
||||
kaizen-agentic status; \\
|
||||
else \\
|
||||
echo "kaizen-agentic not found. Install with: pip install kaizen-agentic"; \\
|
||||
fi
|
||||
|
||||
agents-update:
|
||||
@if command -v kaizen-agentic >/dev/null 2>&1; then \\
|
||||
kaizen-agentic update; \\
|
||||
else \\
|
||||
echo "kaizen-agentic not found. Install with: pip install kaizen-agentic"; \\
|
||||
fi
|
||||
|
||||
agents-list:
|
||||
@if command -v kaizen-agentic >/dev/null 2>&1; then \\
|
||||
kaizen-agentic list; \\
|
||||
else \\
|
||||
echo "kaizen-agentic not found. Install with: pip install kaizen-agentic"; \\
|
||||
fi
|
||||
|
||||
agents-validate:
|
||||
@if command -v kaizen-agentic >/dev/null 2>&1; then \\
|
||||
kaizen-agentic validate; \\
|
||||
else \\
|
||||
echo "kaizen-agentic not found. Install with: pip install kaizen-agentic"; \\
|
||||
fi
|
||||
"""
|
||||
(project_dir / "Makefile").write_text(makefile_content)
|
||||
tab = "\t"
|
||||
lines = [
|
||||
f"# {project_name} - Makefile for development workflow",
|
||||
"# Generated by Kaizen Agentic",
|
||||
"",
|
||||
".PHONY: help setup-complete setup-python setup-tools test lint "
|
||||
"format clean agents-status agents-update",
|
||||
"",
|
||||
"help:",
|
||||
f'{tab}@echo "Available targets:"',
|
||||
f'{tab}@echo " setup-complete - Complete development environment setup"',
|
||||
f'{tab}@echo " setup-python - Set up Python virtual environment"',
|
||||
f'{tab}@echo " test - Run test suite"',
|
||||
f'{tab}@echo " agents-status - Show installed agents status"',
|
||||
"",
|
||||
"VENV := .venv",
|
||||
"PYTHON := $(VENV)/bin/python",
|
||||
"PIP := $(VENV)/bin/pip",
|
||||
"",
|
||||
"setup-complete: setup-python setup-tools",
|
||||
f'{tab}@echo "Development environment setup complete"',
|
||||
"",
|
||||
"setup-python: $(VENV)/bin/activate",
|
||||
"",
|
||||
"$(VENV)/bin/activate: pyproject.toml",
|
||||
f"{tab}python3 -m venv $(VENV)",
|
||||
f"{tab}$(PIP) install --upgrade pip",
|
||||
f'{tab}$(PIP) install -e ".[dev]"',
|
||||
f"{tab}touch $(VENV)/bin/activate",
|
||||
"",
|
||||
"setup-tools: $(VENV)/bin/activate",
|
||||
f'{tab}@echo "Development tools installed via pyproject.toml"',
|
||||
"",
|
||||
"test: $(VENV)/bin/activate",
|
||||
f"{tab}$(PYTHON) -m pytest tests/ -v",
|
||||
"",
|
||||
"test-coverage: $(VENV)/bin/activate",
|
||||
f"{tab}$(PYTHON) -m pytest tests/ --cov=src/{package_name} "
|
||||
f"--cov-report=html --cov-report=term-missing",
|
||||
"",
|
||||
"lint: $(VENV)/bin/activate",
|
||||
f"{tab}$(PYTHON) -m flake8 src/ tests/",
|
||||
"",
|
||||
"format: $(VENV)/bin/activate",
|
||||
f"{tab}$(PYTHON) -m black src/ tests/",
|
||||
"",
|
||||
"clean:",
|
||||
f"{tab}rm -rf build/ dist/ *.egg-info/ .pytest_cache/ .coverage htmlcov/",
|
||||
"",
|
||||
"agents-status:",
|
||||
f"{tab}@command -v kaizen-agentic >/dev/null 2>&1 && kaizen-agentic status "
|
||||
f'|| echo "kaizen-agentic not installed"',
|
||||
"",
|
||||
"agents-update:",
|
||||
f"{tab}@command -v kaizen-agentic >/dev/null 2>&1 && kaizen-agentic update "
|
||||
f'|| echo "kaizen-agentic not installed"',
|
||||
]
|
||||
(project_dir / "Makefile").write_text("\n".join(lines) + "\n")
|
||||
|
||||
@@ -32,6 +32,18 @@ class AgentDefinition:
|
||||
model: Optional[str] = None
|
||||
memory: Optional[str] = None # "enabled" (default) | "disabled"
|
||||
|
||||
@staticmethod
|
||||
def _read_frontmatter(file_path: Path) -> dict:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
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}")
|
||||
frontmatter = yaml.safe_load(frontmatter_match.group(1))
|
||||
if not isinstance(frontmatter, dict) or "name" not in frontmatter:
|
||||
raise ValueError(f"Invalid frontmatter in {file_path}")
|
||||
return frontmatter
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, file_path: Path) -> "AgentDefinition":
|
||||
"""Create AgentDefinition from a markdown file."""
|
||||
@@ -158,29 +170,50 @@ class AgentRegistry:
|
||||
def __init__(self, agents_dir: Path):
|
||||
self.agents_dir = Path(agents_dir)
|
||||
self._agents: Dict[str, AgentDefinition] = {}
|
||||
self._load_agents()
|
||||
self._file_index: Dict[str, Path] = {}
|
||||
self._index_agent_files()
|
||||
|
||||
def _load_agents(self):
|
||||
"""Load all agents from the agents directory."""
|
||||
def _index_agent_files(self) -> None:
|
||||
"""Index agent files by frontmatter name without full parse."""
|
||||
if not self.agents_dir.exists():
|
||||
return
|
||||
|
||||
for agent_file in self.agents_dir.glob("agent-*.md"):
|
||||
try:
|
||||
agent_def = AgentDefinition.from_file(agent_file)
|
||||
self._agents[agent_def.name] = agent_def
|
||||
frontmatter = AgentDefinition._read_frontmatter(agent_file)
|
||||
self._file_index[frontmatter["name"]] = agent_file
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to load agent {agent_file}: {e}")
|
||||
print(f"Warning: Failed to index agent {agent_file}: {e}")
|
||||
|
||||
def get_agent_path(self, name: str) -> Optional[Path]:
|
||||
"""Return the source file path for an agent (no full parse)."""
|
||||
return self._file_index.get(name)
|
||||
|
||||
def get_agent(self, name: str) -> Optional[AgentDefinition]:
|
||||
"""Get agent definition by name."""
|
||||
return self._agents.get(name)
|
||||
"""Get agent definition by name (lazy-loaded)."""
|
||||
if name in self._agents:
|
||||
return self._agents[name]
|
||||
file_path = self._file_index.get(name)
|
||||
if file_path is None:
|
||||
return None
|
||||
try:
|
||||
agent_def = AgentDefinition.from_file(file_path)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to load agent {name}: {e}")
|
||||
return None
|
||||
self._agents[name] = agent_def
|
||||
return agent_def
|
||||
|
||||
def agent_names(self) -> List[str]:
|
||||
"""List indexed agent names without loading full definitions."""
|
||||
return sorted(self._file_index.keys())
|
||||
|
||||
def list_agents(
|
||||
self, category: Optional[AgentCategory] = None
|
||||
) -> List[AgentDefinition]:
|
||||
"""List all agents, optionally filtered by category."""
|
||||
agents = list(self._agents.values())
|
||||
agents = [self.get_agent(name) for name in self.agent_names()]
|
||||
agents = [agent for agent in agents if agent is not None]
|
||||
if category:
|
||||
agents = [a for a in agents if a.category == category]
|
||||
return sorted(agents, key=lambda a: a.name)
|
||||
@@ -188,7 +221,7 @@ class AgentRegistry:
|
||||
def get_categories(self) -> Dict[AgentCategory, List[AgentDefinition]]:
|
||||
"""Get agents organized by category."""
|
||||
categories = {}
|
||||
for agent in self._agents.values():
|
||||
for agent in self.list_agents():
|
||||
if agent.category not in categories:
|
||||
categories[agent.category] = []
|
||||
categories[agent.category].append(agent)
|
||||
@@ -230,12 +263,16 @@ class AgentRegistry:
|
||||
"""Validate all agents and return validation errors."""
|
||||
errors = {}
|
||||
|
||||
for name, agent in self._agents.items():
|
||||
for name in self.agent_names():
|
||||
agent = self.get_agent(name)
|
||||
if agent is None:
|
||||
errors[name] = ["Failed to load agent definition"]
|
||||
continue
|
||||
agent_errors = []
|
||||
|
||||
# Check for missing dependencies
|
||||
for dep in agent.dependencies:
|
||||
if dep not in self._agents:
|
||||
if dep not in self._file_index:
|
||||
agent_errors.append(f"Missing dependency: {dep}")
|
||||
|
||||
# Check file exists
|
||||
|
||||
Reference in New Issue
Block a user