/ scripts / building_steward.py
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()