feat: agent authoring & doc generation (WP-0007, v1.4.0)
New authoring tooling and a fix for the doc-regeneration defect it exposed. Added: - src/kaizen_agentic/agent_docs.py — render + idempotent upsert of the CLAUDE.md "## Installed Agents" section (shared by installer and CLI) - `kaizen-agentic docs generate [--check]` — idempotent doc refresh / CI gate - `kaizen-agentic create-agent` — scaffold a schema-valid agent - Frontmatter schema validation in `kaizen-agentic validate` (required name/description/category, known category, valid memory/model) - tests: test_agent_docs, test_validate_schema, test_create_agent Fixed: - _update_documentation regex duplicated the Installed Agents block on every run (stopped at the first ### subheading) — now idempotent - declared frontmatter `category` is authoritative (heuristic is fallback) - list_installed_agents reads the frontmatter name, not the filename - renamed agent-project-management.md -> agent-project-assistant.md to satisfy the agent-<name>.md convention (eliminates a name/filename collision that caused install/update to write a divergent duplicate) - test_cli_error_handling no longer installs into the repo root (uses tmp) Version 1.4.0; CHANGELOG, CLI cheat sheet, agency-framework, TODO updated. Workplan KAIZEN-WP-0007 closed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,11 @@ from pathlib import Path
|
||||
from typing import List, Dict, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .registry import AgentRegistry
|
||||
from .registry import AgentRegistry, AgentDefinition
|
||||
from .agent_docs import (
|
||||
render_installed_agents_section,
|
||||
upsert_installed_agents_section,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -82,7 +86,15 @@ class AgentInstaller:
|
||||
|
||||
installed = []
|
||||
for agent_file in agents_dir.glob("agent-*.md"):
|
||||
agent_name = agent_file.stem.replace("agent-", "")
|
||||
# Prefer the frontmatter name (registry-authoritative); fall back to
|
||||
# the filename when frontmatter is missing/unreadable. The filename
|
||||
# encodes the category for a few agents (e.g. agent-project-
|
||||
# management.md → name: project-assistant), so a pure filename derive
|
||||
# produces names the registry cannot resolve (WP-0007 T02).
|
||||
try:
|
||||
agent_name = AgentDefinition._read_frontmatter(agent_file)["name"]
|
||||
except Exception:
|
||||
agent_name = agent_file.stem.replace("agent-", "")
|
||||
installed.append(agent_name)
|
||||
|
||||
return sorted(installed)
|
||||
@@ -235,60 +247,25 @@ agents-validate:
|
||||
try:
|
||||
claude_md = project_dir / "CLAUDE.md"
|
||||
|
||||
agent_section = "## Installed Agents\n\n"
|
||||
agent_section += (
|
||||
"This project includes the following specialized agents:\n\n"
|
||||
)
|
||||
agents = [
|
||||
agent
|
||||
for agent in (self.registry.get_agent(name) for name in agent_names)
|
||||
if agent is not None
|
||||
]
|
||||
agent_section = render_installed_agents_section(agents)
|
||||
|
||||
# Group agents by category
|
||||
categories = {}
|
||||
for agent_name in agent_names:
|
||||
agent = self.registry.get_agent(agent_name)
|
||||
if agent:
|
||||
category = agent.category.value
|
||||
if category not in categories:
|
||||
categories[category] = []
|
||||
categories[category].append(agent)
|
||||
|
||||
# Generate documentation
|
||||
for category, agents in categories.items():
|
||||
agent_section += f"### {category.replace('-', ' ').title()}\n\n"
|
||||
for agent in agents:
|
||||
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"
|
||||
)
|
||||
|
||||
# Update or create CLAUDE.md
|
||||
# Update or create CLAUDE.md (idempotent upsert — WP-0007 T01/T02)
|
||||
if claude_md.exists():
|
||||
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)",
|
||||
agent_section,
|
||||
content,
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
else:
|
||||
content += "\n" + agent_section
|
||||
|
||||
with open(claude_md, "w") as f:
|
||||
f.write(content)
|
||||
content = claude_md.read_text()
|
||||
content = upsert_installed_agents_section(content, agent_section)
|
||||
claude_md.write_text(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:
|
||||
f.write(header + agent_section)
|
||||
header += (
|
||||
"This file contains Claude Code configuration and agent "
|
||||
"information.\n\n"
|
||||
)
|
||||
claude_md.write_text(header + agent_section)
|
||||
|
||||
print(f"Updated documentation: {claude_md}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user