generated from coulomb/repo-seed
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:
@@ -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
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user