feat(protocols): add protocols artifact convention, sys-medic protocol + CLI (WP-0002 T17-T24)
- ADR-003: protocols artifact convention (location, structure, lifecycle) - agents/protocols/README.md: directory-level index and usage guide - agents/protocols/sys-medic/k3s-node-health-assessment.md: full structured k3s node health assessment protocol (8 steps: OS baseline, process hygiene, memory, CPU, disk, network, k3s node state, runtime services) - agent-sys-medic.md: add memory: enabled frontmatter, session-start/close protocols, node-profile memory template extensions, protocol reference in Default Task - cli.py: add protocols command group (list, show); extend memory init to hint protocol commands for agents that have protocols Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -820,6 +820,16 @@ session_count: 0
|
||||
memory_path.write_text(content)
|
||||
click.echo(f"Initialized memory for '{agent_name}': {memory_path}")
|
||||
|
||||
# For agents with protocols, note the protocol location
|
||||
registry = _get_registry()
|
||||
protocols_dir = registry.agents_dir / "protocols" / agent_name
|
||||
if protocols_dir.exists():
|
||||
slugs = [f.stem for f in sorted(protocols_dir.glob("*.md")) if f.name != "README.md"]
|
||||
if slugs:
|
||||
click.echo(f" Protocols available for '{agent_name}':")
|
||||
for slug in slugs:
|
||||
click.echo(f" kaizen-agentic protocols show {agent_name} {slug}")
|
||||
|
||||
|
||||
@memory.command("brief")
|
||||
@click.argument("agent_name")
|
||||
@@ -918,6 +928,79 @@ def memory_clear(agent_name: str, target: str):
|
||||
memory_path.parent.rmdir()
|
||||
|
||||
|
||||
@cli.group()
|
||||
def protocols():
|
||||
"""Browse agent protocol runbooks (agents/protocols/<agent>/<slug>.md)."""
|
||||
pass
|
||||
|
||||
|
||||
@protocols.command("list")
|
||||
@click.argument("agent_name", required=False)
|
||||
def protocols_list(agent_name: Optional[str]):
|
||||
"""List available protocols, optionally filtered by agent."""
|
||||
registry = _get_registry()
|
||||
protocols_dir = registry.agents_dir / "protocols"
|
||||
|
||||
if not protocols_dir.exists():
|
||||
click.echo("No protocols directory found.")
|
||||
return
|
||||
|
||||
found = []
|
||||
agent_dirs = (
|
||||
[protocols_dir / agent_name] if agent_name else sorted(protocols_dir.iterdir())
|
||||
)
|
||||
for agent_dir in agent_dirs:
|
||||
if not agent_dir.is_dir() or agent_dir.name == "__pycache__":
|
||||
continue
|
||||
for protocol_file in sorted(agent_dir.glob("*.md")):
|
||||
if protocol_file.name == "README.md":
|
||||
continue
|
||||
# Try to read title from frontmatter
|
||||
title = protocol_file.stem.replace("-", " ").title()
|
||||
try:
|
||||
content = protocol_file.read_text()
|
||||
for line in content.splitlines():
|
||||
if line.startswith("title:"):
|
||||
title = line.split(":", 1)[1].strip().strip('"')
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
found.append((agent_dir.name, protocol_file.stem, title))
|
||||
|
||||
if not found:
|
||||
if agent_name:
|
||||
click.echo(f"No protocols found for agent '{agent_name}'.")
|
||||
else:
|
||||
click.echo("No protocols found.")
|
||||
return
|
||||
|
||||
click.echo("Available Protocols:")
|
||||
click.echo("=" * 40)
|
||||
current_agent = None
|
||||
for agent, slug, title in found:
|
||||
if agent != current_agent:
|
||||
click.echo(f"\n {agent}:")
|
||||
current_agent = agent
|
||||
click.echo(f" • {slug}: {title}")
|
||||
|
||||
|
||||
@protocols.command("show")
|
||||
@click.argument("agent_name")
|
||||
@click.argument("slug")
|
||||
def protocols_show(agent_name: str, slug: str):
|
||||
"""Print a protocol runbook."""
|
||||
registry = _get_registry()
|
||||
protocol_path = registry.agents_dir / "protocols" / agent_name / f"{slug}.md"
|
||||
|
||||
if not protocol_path.exists():
|
||||
click.echo(f"Protocol not found: {agent_name}/{slug}")
|
||||
click.echo(f" Expected: {protocol_path}")
|
||||
click.echo(f" Run: kaizen-agentic protocols list {agent_name}")
|
||||
return
|
||||
|
||||
click.echo(protocol_path.read_text())
|
||||
|
||||
|
||||
def _memory_path(target: str, agent_name: str) -> Path:
|
||||
return Path(target).resolve() / ".kaizen" / "agents" / agent_name / "memory.md"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user