feat(CUST-WP-0016): kaizen-agentic integration — MCP tools, templates, direct install

- Fix /domains/{slug}/ 500: EP/TD queries now use domain_id FK (not string column)
- Remove dead cascade-slug code in rename_domain (FK handles it)
- MCP: list_kaizen_agents(category?) + get_kaizen_agent(name) via resolve_repo_path()
- TOOLS.md: Kaizen Agents section with discovery/load pattern
- agents.template: new project rule for consumer repos
- claude-md.template + register_project.sh: include agents.md in new-project scaffolding
- agents/: direct install of 6 curated agents for hub sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 22:24:30 +01:00
parent 196e6c5aed
commit 8619cd2218
6 changed files with 132 additions and 17 deletions

View File

@@ -992,6 +992,89 @@ def update_repo_path(repo_slug: str, path: str, host: str | None = None) -> str:
return json.dumps(repo, indent=2)
# ---------------------------------------------------------------------------
# Kaizen Agents
# ---------------------------------------------------------------------------
def _kaizen_agents_dir() -> Path:
"""Resolve the kaizen-agentic agents/ directory via host_paths → local_path fallback."""
import socket as _socket
repo = _get("/repos/kaizen-agentic")
hostname = _socket.gethostname()
host_paths = repo.get("host_paths") or {}
base = host_paths.get(hostname) or repo.get("local_path") or ""
if not base:
raise FileNotFoundError("kaizen-agentic path not found for this host. Register it with update_repo_path().")
agents_dir = Path(base) / "agents"
if not agents_dir.is_dir():
raise FileNotFoundError(f"agents/ directory not found at {agents_dir}")
return agents_dir
@mcp.tool()
def list_kaizen_agents(category: str | None = None) -> str:
"""List all available kaizen agent personas.
Reads agent metadata from kaizen-agentic/agents/agent-*.md frontmatter.
Each agent is a specialized instruction set Claude can load and follow.
Args:
category: Optional filter (e.g. 'testing', 'quality', 'process', 'infrastructure').
Returns all agents when omitted.
Returns:
JSON list of {name, description, category, file} objects.
"""
import re as _re
agents_dir = _kaizen_agents_dir()
result = []
for f in sorted(agents_dir.glob("agent-*.md")):
name = f.stem.removeprefix("agent-")
text = f.read_text(encoding="utf-8")
# Extract optional YAML frontmatter fields
fm_match = _re.match(r"^---\n(.*?\n)---\n", text, _re.DOTALL)
meta: dict = {}
if fm_match:
for line in fm_match.group(1).splitlines():
if ":" in line:
k, _, v = line.partition(":")
meta[k.strip()] = v.strip()
agent_category = meta.get("category", "")
if category and agent_category.lower() != category.lower():
continue
# Fall back to first non-empty line after frontmatter as description
desc = meta.get("description", "")
if not desc:
for line in text.split("\n"):
line = line.strip()
if line and not line.startswith("#") and not line.startswith("---"):
desc = line[:120]
break
result.append({"name": name, "description": desc, "category": agent_category, "file": f.name})
return json.dumps(result, indent=2)
@mcp.tool()
def get_kaizen_agent(name: str) -> str:
"""Load the full instructions for a kaizen agent persona.
Read the returned markdown and follow the instructions it contains.
Use list_kaizen_agents() to discover available agent names.
Args:
name: Agent name without 'agent-' prefix (e.g. 'tdd-workflow', 'code-refactoring').
Returns:
Full markdown content of the agent definition file.
"""
agents_dir = _kaizen_agents_dir()
agent_file = agents_dir / f"agent-{name}.md"
if not agent_file.exists():
available = [f.stem.removeprefix("agent-") for f in sorted(agents_dir.glob("agent-*.md"))]
return json.dumps({"error": f"Agent '{name}' not found.", "available": available})
return agent_file.read_text(encoding="utf-8")
# ---------------------------------------------------------------------------
# ADR-001 compliance validation
# ---------------------------------------------------------------------------