building_steward.py
1 #!/usr/bin/env python3 2 """ 3 Building Steward CLI - Override and Inspection 4 5 This CLI exists for override and inspection, not primary interaction. 6 The Building Steward runs automatically as a layer within the First Officer. 7 8 Usage: 9 python scripts/building_steward.py --status # Show all patterns 10 python scripts/building_steward.py --maturity PATTERN # Show specific pattern 11 python scripts/building_steward.py --force-track PATTERN # Force track a pattern 12 python scripts/building_steward.py --suggest CONTEXT # Get pattern suggestions 13 """ 14 15 import argparse 16 import json 17 import sys 18 from pathlib import Path 19 from datetime import datetime 20 21 # Add parent to path for imports 22 sys.path.insert(0, str(Path(__file__).parent.parent)) 23 24 from core.consciousness.first_officer import ( 25 get_first_officer, 26 BuildingStewardLayer, 27 PatternTopology, 28 PatternStatus, 29 BUILDING_TERMS, 30 ELEVATION_THRESHOLDS, 31 ) 32 33 34 def format_pattern_status(status: dict) -> str: 35 """Format pattern status for display.""" 36 if not status: 37 return " (not found)" 38 39 lines = [] 40 lines.append(f" Pattern: {status['pattern_id']}") 41 lines.append(f" Status: {status['status']}") 42 lines.append(f" Invocations: {status['invocation_count']}") 43 lines.append(f" Contexts: {', '.join(status['contexts'][:5]) if status['contexts'] else '(none)'}") 44 lines.append(f" Maturity: {status['maturity_score']:.2f} (threshold: 0.50 for TESTING)") 45 lines.append(f" Elevation: {status['elevation_score']:.2f} (threshold: 0.70 for ELEVATION_CANDIDATE)") 46 if status['last_invoked']: 47 lines.append(f" Last invoked: {status['last_invoked']}") 48 49 return "\n".join(lines) 50 51 52 def cmd_status(officer): 53 """Show status of all tracked patterns.""" 54 steward = officer._building_steward 55 patterns = steward.get_all_patterns_status() 56 57 if not patterns or all(p is None for p in patterns): 58 print("No patterns currently tracked.") 59 print("\nPatterns are automatically tracked when:") 60 print(" - A pattern name is referenced (e.g., 'flight-protocol')") 61 print(" - A proto-ID is mentioned (e.g., 'proto-007')") 62 print(" - A wiki-link is used (e.g., '[[phoenix-state-protocol]]')") 63 return 64 65 print(f"Tracked Patterns ({len([p for p in patterns if p])})") 66 print("=" * 60) 67 68 # Group by status 69 by_status = {} 70 for p in patterns: 71 if p: 72 status = p['status'] 73 if status not in by_status: 74 by_status[status] = [] 75 by_status[status].append(p) 76 77 # Display in order: ELEVATION_CANDIDATE, TESTING, OPERATIONAL, DOCUMENTED, CANDIDATE 78 status_order = ['ELEVATION_CANDIDATE', 'TESTING', 'OPERATIONAL', 'DOCUMENTED', 'CANDIDATE'] 79 for status in status_order: 80 if status in by_status: 81 print(f"\n{status}:") 82 for p in by_status[status]: 83 print(f" • {p['pattern_id']}: {p['invocation_count']} invocations, " 84 f"maturity={p['maturity_score']:.2f}, " 85 f"elevation={p['elevation_score']:.2f}") 86 87 88 def cmd_maturity(officer, pattern_id: str): 89 """Show detailed status for a specific pattern.""" 90 steward = officer._building_steward 91 status = steward.get_pattern_status(pattern_id.lower()) 92 93 print(f"Pattern Status: {pattern_id}") 94 print("=" * 60) 95 print(format_pattern_status(status)) 96 97 if status: 98 # Show elevation thresholds 99 print("\nElevation Thresholds:") 100 print(f" Min invocations: {ELEVATION_THRESHOLDS['min_invocations']} (current: {status['invocation_count']})") 101 print(f" Min contexts: {ELEVATION_THRESHOLDS['min_contexts']} (current: {len(status['contexts'])})") 102 print(f" Min success rate: {ELEVATION_THRESHOLDS['min_success_rate']:.0%}") 103 print(f" Min edges: {ELEVATION_THRESHOLDS['min_edges']}") 104 105 # Check progress 106 inv_pct = min(status['invocation_count'] / ELEVATION_THRESHOLDS['min_invocations'], 1.0) 107 ctx_pct = min(len(status['contexts']) / ELEVATION_THRESHOLDS['min_contexts'], 1.0) 108 print(f"\nProgress toward elevation:") 109 print(f" Invocations: {'█' * int(inv_pct * 10)}{'░' * (10 - int(inv_pct * 10))} {inv_pct:.0%}") 110 print(f" Contexts: {'█' * int(ctx_pct * 10)}{'░' * (10 - int(ctx_pct * 10))} {ctx_pct:.0%}") 111 112 113 def cmd_force_track(officer, pattern_id: str, context: str = None): 114 """Force tracking of a pattern (manual invocation).""" 115 steward = officer._building_steward 116 117 ctx = context or f"Manual tracking via CLI at {datetime.now().isoformat()}" 118 instance = steward._track_invocation(pattern_id.lower(), ctx, source="cli") 119 120 print(f"Tracked pattern invocation:") 121 print(f" Pattern: {instance.pattern_id}") 122 print(f" Instance ID: {instance.instance_id}") 123 print(f" Context: {ctx[:80]}...") 124 print(f" Detection: manual (force-track)") 125 126 # Show updated status 127 print("\nUpdated status:") 128 status = steward.get_pattern_status(pattern_id.lower()) 129 print(format_pattern_status(status)) 130 131 132 def cmd_suggest(officer, context: str): 133 """Get pattern suggestions for a building context.""" 134 steward = officer._building_steward 135 136 # Check if building context 137 is_building = steward._is_building_context_text(context) 138 print(f"Building context detected: {'Yes' if is_building else 'No'}") 139 140 if not is_building: 141 print("\nTo trigger building context detection, include terms like:") 142 print(f" Strong: {', '.join(list(BUILDING_TERMS['strong'])[:5])}") 143 print(f" Medium: {', '.join(list(BUILDING_TERMS['medium'])[:5])}") 144 145 # Get suggestions 146 suggestions = steward._get_pattern_suggestions(context, []) 147 148 if suggestions: 149 print(f"\nRelevant patterns ({len(suggestions)}):") 150 for pattern_id, relevance in suggestions: 151 print(f" • {pattern_id}: {relevance:.0%} relevance") 152 else: 153 print("\nNo pattern suggestions available.") 154 print("Patterns are learned from usage. Try referencing patterns in your work first.") 155 156 157 def cmd_instances(officer, pattern_id: str = None): 158 """Show recent invocation instances.""" 159 steward = officer._building_steward 160 161 instances = steward._instances 162 if pattern_id: 163 instances = [i for i in instances if i.pattern_id == pattern_id.lower()] 164 165 if not instances: 166 print(f"No instances found{' for ' + pattern_id if pattern_id else ''}.") 167 return 168 169 print(f"Recent Instances ({len(instances)})") 170 print("=" * 60) 171 172 # Show most recent first 173 for inst in sorted(instances, key=lambda x: x.timestamp, reverse=True)[:20]: 174 print(f"\n {inst.instance_id}") 175 print(f" Pattern: {inst.pattern_id}") 176 print(f" Method: {inst.detection_method}") 177 print(f" Time: {inst.timestamp.isoformat()}") 178 print(f" Context: {inst.context[:60]}...") 179 180 181 def main(): 182 parser = argparse.ArgumentParser( 183 description="Building Steward CLI - Override and Inspection", 184 formatter_class=argparse.RawDescriptionHelpFormatter, 185 epilog=""" 186 Examples: 187 %(prog)s --status Show all tracked patterns 188 %(prog)s --maturity flight-protocol Show specific pattern status 189 %(prog)s --force-track my-new-pattern Force track a pattern 190 %(prog)s --suggest "designing a new agent" Get pattern suggestions 191 %(prog)s --instances Show recent invocations 192 %(prog)s --instances flight-protocol Show invocations for pattern 193 """ 194 ) 195 196 parser.add_argument("--status", action="store_true", 197 help="Show status of all tracked patterns") 198 parser.add_argument("--maturity", metavar="PATTERN", 199 help="Show detailed status for a specific pattern") 200 parser.add_argument("--force-track", metavar="PATTERN", 201 help="Force tracking of a pattern invocation") 202 parser.add_argument("--context", metavar="TEXT", 203 help="Context for force-track (optional)") 204 parser.add_argument("--suggest", metavar="CONTEXT", 205 help="Get pattern suggestions for a context") 206 parser.add_argument("--instances", nargs="?", const="__all__", metavar="PATTERN", 207 help="Show recent invocation instances (optionally for a pattern)") 208 parser.add_argument("--json", action="store_true", 209 help="Output in JSON format") 210 211 args = parser.parse_args() 212 213 # Get First Officer instance 214 officer = get_first_officer() 215 216 if args.status: 217 if args.json: 218 patterns = officer._building_steward.get_all_patterns_status() 219 print(json.dumps([p for p in patterns if p], indent=2)) 220 else: 221 cmd_status(officer) 222 223 elif args.maturity: 224 if args.json: 225 status = officer._building_steward.get_pattern_status(args.maturity.lower()) 226 print(json.dumps(status, indent=2)) 227 else: 228 cmd_maturity(officer, args.maturity) 229 230 elif args.force_track: 231 cmd_force_track(officer, args.force_track, args.context) 232 233 elif args.suggest: 234 cmd_suggest(officer, args.suggest) 235 236 elif args.instances is not None: 237 pattern = args.instances if args.instances != "__all__" else None 238 cmd_instances(officer, pattern) 239 240 else: 241 parser.print_help() 242 243 244 if __name__ == "__main__": 245 main()