#!/usr/bin/env python3 """ GNS-2 Agent Mass Update Script Updates all remaining Tier 0/1 agents with GNS-2 protocol: - Checkpoint read requirement (read-only for Tier 0) - Event footer template (mandatory) - Tier classification (Tier 0 or 1) """ import os import re import glob # Root directory of agents AGENTS_DIR = '.kilo/agents' # Tier classification TIER_0_AGENTS = [ 'history-miner', 'code-skeptic', 'performance-engineer', 'security-auditor', 'visual-tester', 'browser-automation', 'markdown-validator', 'planner', 'reflector', 'memory-manager', 'pipeline-judge', 'architect-indexer' ] TIER_1_AGENTS = [ 'lead-developer', 'the-fixer', 'sdet-engineer', 'frontend-developer', 'backend-developer', 'go-developer', 'flutter-developer', 'php-developer', 'python-developer', 'devops-engineer', 'release-manager', 'requirement-refiner', 'product-owner', 'prompt-optimizer', 'system-analyst', 'workflow-architect', 'orchestrator' ] def get_tier(agent_name: str) -> int: if agent_name in TIER_0_AGENTS: return 0 if agent_name in TIER_1_AGENTS: return 1 return -1 # Unknown def extract_frontmatter(content: str) -> tuple: """Extract YAML frontmatter from markdown content.""" if not content.startswith('---'): return None, content parts = content.split('---', 2) if len(parts) < 3: return None, content return parts[1].strip(), parts[2].strip() def update_frontmatter(fm: str, tier: int) -> str: """Update frontmatter with GNS-2 metadata.""" lines = fm.split('\n') new_lines = [] # Add tier comment new_lines.append(f"# GNS-2 Agent (Tier {tier})") for line in lines: # Ensure permission.task exists if line.strip().startswith('permission:'): new_lines.append(line) continue new_lines.append(line) return '\n'.join(new_lines) def generate_gns_protocol(tier: int) -> str: """Generate GNS-2 protocol section for an agent.""" if tier == 0: return """## GNS-2 Protocol ### Tier Tier 0 (Leaf Agent / No Cascade) - `max_cascade_depth: 0` (no subagent calls) - Read checkpoint only (do not modify) - Write event footer on completion ### On Entry (MANDATORY) 1. Read issue body from Gitea API 2. Parse `## GNS Checkpoint` YAML block 3. Extract task from checkpoint or last event ### During Work - Execute atomic task as specified in checkpoint - Follow existing behavior guidelines - Do NOT spawn subagents ### On Exit (MANDATORY) 1. Post comment with result + GNS_EVENT footer 2. Do NOT modify checkpoint (read-only) 3. Set `next_agent` recommendation in event footer ### Next Recommendation After completion, recommend next agent in event footer: - `code-skeptic`: after code written - `performance-engineer`: after code tested - `security-auditor`: after performance reviewed """ elif tier == 1: return """## GNS-2 Protocol ### Tier Tier 1 (Task Agent / Orchestrator-Mediated Cascade) - `max_cascade_depth: 1` (request orchestrator to spawn, do not spawn directly) - Can read checkpoint and recommend next agent - Event footer triggers orchestrator polling ### On Entry (MANDATORY) 1. Read issue body from Gitea API 2. Parse `## GNS Checkpoint` YAML block 3. Verify `checkpoint.budget.remaining > estimated_cost` ### During Work - Execute task as specified - If subagent needed, write recommendation in event footer - Do NOT call `task` tool directly (Tier 1) ### On Exit (MANDATORY) 1. Update labels if needed (quality::*, phase::*) 2. Post comment with result + GNS_EVENT footer 3. Include `next_agent` recommendation ### GNS Event Footer Template ```markdown --- ``` """ return "" def update_agent_file(filepath: str) -> bool: """Update a single agent file with GNS-2 protocol.""" agent_name = os.path.basename(filepath).replace('.md', '') tier = get_tier(agent_name) if tier < 0: print(f"⚠️ Unknown agent: {agent_name}, skipping") return False with open(filepath, 'r') as f: content = f.read() # Check if already updated if 'GNS-2 Protocol' in content: print(f"⏭️ {agent_name} already has GNS-2 protocol") return False fm_raw, body = extract_frontmatter(content) if fm_raw is None: print(f"❌ {agent_name}: no frontmatter found") return False # Update description to mention GNS-2 fm_lines = fm_raw.split('\n') new_fm_lines = [] for line in fm_lines: if line.startswith('description:'): desc = line.replace('description:', '').strip() new_fm_lines.append(f'description: {desc} (GNS-2 Tier {tier})') else: new_fm_lines.append(line) new_fm = '---\n' + '\n'.join(new_fm_lines) + '\n---' # Generate GNS-2 section gns_section = generate_gns_protocol(tier) # Combine: frontmatter + original body + GNS section # Insert GNS section before gitea_pattern = r'' if re.search(gitea_pattern, body): # Insert before gitea-commenting tag new_body = re.sub( gitea_pattern, f"{gns_section}\n\n\\g<0>", body ) else: # Append at end new_body = body + '\n\n' + gns_section new_content = new_fm + '\n' + new_body with open(filepath, 'w') as f: f.write(new_content) print(f"✅ {agent_name} (Tier {tier})") return True def main(): print("GNS-2 Agent Mass Update") print(f"Target: {AGENTS_DIR}") print(f"Tier 0 (Leaf): {len(TIER_0_AGENTS)}") print(f"Tier 1 (Task): {len(TIER_1_AGENTS)}") print() updated = 0 skipped = 0 failed = 0 for filepath in sorted(glob.glob(os.path.join(AGENTS_DIR, '*.md'))): agent_name = os.path.basename(filepath).replace('.md', '') # Skip already updated agents if agent_name in ['capability-analyst', 'agent-architect', 'evaluator']: print(f"⏭️ {agent_name} (already GNS-2)") skipped += 1 continue result = update_agent_file(filepath) if result: updated += 1 elif 'already' in f'{result}': skipped += 1 else: failed += 1 print() print(f"Done: {updated} updated, {skipped} skipped, {failed} failed") print(f"Total: {updated + skipped + failed} agents processed") if __name__ == '__main__': main()