/ scripts / mesh_query.py
mesh_query.py
  1  #!/usr/bin/env python3
  2  """
  3  Mesh Query - Cross-instance contextual queries for Claude sessions.
  4  
  5  Query the sovereign mesh network for specific context:
  6  - Search by topic keywords
  7  - Get context from specific instances
  8  - Search insights by keyword/axiom
  9  - Check principle status
 10  - Get recent activity
 11  
 12  Usage:
 13      python3 scripts/mesh_query.py topic mesh,principle     # Search for topics
 14      python3 scripts/mesh_query.py instance nodebox         # Get instance context
 15      python3 scripts/mesh_query.py insights --axiom A4      # Search insights
 16      python3 scripts/mesh_query.py principles               # List principles
 17      python3 scripts/mesh_query.py recent                   # Recent activity
 18  """
 19  
 20  import argparse
 21  import json
 22  import sys
 23  import urllib.request
 24  import urllib.error
 25  
 26  MESH_URL = "http://localhost:7778"
 27  
 28  
 29  def query_mesh(query_data):
 30      """Send query to mesh and return result."""
 31      try:
 32          data = json.dumps(query_data).encode()
 33          req = urllib.request.Request(
 34              f"{MESH_URL}/query",
 35              data=data,
 36              headers={"Content-Type": "application/json"},
 37              method="POST"
 38          )
 39          with urllib.request.urlopen(req, timeout=5) as resp:
 40              return json.loads(resp.read())
 41      except urllib.error.URLError:
 42          return {"error": "Mesh not running"}
 43      except Exception as e:
 44          return {"error": str(e)}
 45  
 46  
 47  def format_topic_results(result):
 48      """Format topic search results."""
 49      print(f"\nšŸ” Topic Search: {result.get('query', {}).get('topics', [])}")
 50      print(f"   Total results: {result.get('total', 0)}")
 51      print()
 52  
 53      if result.get('aha_moments'):
 54          print("šŸ’” Aha Moments:")
 55          for a in result['aha_moments']:
 56              axioms = ', '.join(a.get('axioms', []))
 57              print(f"   [{a.get('from', '?')}] {a.get('content', '')[:60]}...")
 58              if axioms:
 59                  print(f"      Axioms: {axioms}")
 60          print()
 61  
 62      if result.get('principles'):
 63          print("šŸ“œ Principles:")
 64          for p in result['principles']:
 65              votes = p.get('votes', {})
 66              print(f"   {p.get('name')} [{p.get('status')}] +{votes.get('support', 0)}/-{votes.get('reject', 0)}")
 67              print(f"      {p.get('statement', '')[:60]}...")
 68          print()
 69  
 70      if result.get('fo_states'):
 71          print("šŸ–„ļø Instance States:")
 72          for s in result['fo_states']:
 73              wells = ', '.join(s.get('gravityWells', [])[:3])
 74              print(f"   {s.get('nodeId')}: [{wells}]")
 75          print()
 76  
 77      if result.get('messages'):
 78          print("šŸ“Ø Messages:")
 79          for m in result['messages'][:5]:
 80              print(f"   [{m.get('from', '?')}] {m.get('content', '')[:60]}...")
 81          print()
 82  
 83  
 84  def format_instance_results(result):
 85      """Format instance context results."""
 86      if result.get('instances'):
 87          print("\nšŸ–„ļø All Instances:")
 88          for inst in result['instances']:
 89              wells = ', '.join(inst.get('gravityWells', [])[:3]) or 'none'
 90              energy = inst.get('energyState', '?')
 91              print(f"   {inst.get('nodeId')}")
 92              print(f"      Topics: [{wells}]")
 93              print(f"      Energy: {energy}")
 94          return
 95  
 96      if not result.get('found'):
 97          print(f"\nāŒ Instance not found: {result.get('query', {}).get('instance')}")
 98          avail = result.get('available_instances', [])
 99          if avail:
100              print(f"   Available: {', '.join(avail)}")
101          return
102  
103      print(f"\nšŸ–„ļø Instance: {result.get('nodeId')}")
104      ctx = result.get('context', {})
105      wells = ', '.join(ctx.get('gravityWells', [])[:5]) or 'none'
106      print(f"   Session: {ctx.get('sessionId', '?')[:20]}")
107      print(f"   Topics: [{wells}]")
108      print(f"   Energy: {ctx.get('energyState', '?')}")
109      print()
110  
111      if result.get('aha_moments'):
112          print("   šŸ’” Recent Insights:")
113          for a in result['aha_moments'][:3]:
114              print(f"      - {a.get('content', '')[:50]}...")
115      print()
116  
117  
118  def format_insight_results(result):
119      """Format insight search results."""
120      query = result.get('query', {})
121      filters = []
122      if query.get('keyword'):
123          filters.append(f"keyword='{query['keyword']}'")
124      if query.get('axiom'):
125          filters.append(f"axiom={query['axiom']}")
126      if query.get('minImportance'):
127          filters.append(f"importance>={query['minImportance']}")
128  
129      print(f"\nšŸ’” Insight Search: {', '.join(filters) or 'all'}")
130      print(f"   Total: {result.get('total', 0)}")
131      print()
132  
133      for insight in result.get('insights', []):
134          axioms = ', '.join(insight.get('axioms', []))
135          imp = insight.get('importance', 0)
136          print(f"   [{insight.get('from', '?')}] (imp={imp:.2f}) [{axioms}]")
137          print(f"      {insight.get('content', '')}")
138          print()
139  
140  
141  def format_principle_results(result):
142      """Format principle status results."""
143      print(f"\nšŸ“œ Principles: {result.get('total', 0)} found")
144      print()
145  
146      for p in result.get('principles', []):
147          votes = p.get('votes', {})
148          status = p.get('status', '?')
149          emoji = {'candidate': 'šŸ”µ', 'testing': '🟢', 'confirmed': 'āœ…', 'rejected': 'šŸ”“'}.get(status, '⚪')
150          axioms = ', '.join(p.get('axiom_connections', []))
151  
152          print(f"   {emoji} {p.get('name')} [{status}]")
153          print(f"      \"{p.get('statement', '')[:60]}...\"")
154          print(f"      Votes: +{votes.get('support', 0)}/-{votes.get('reject', 0)} | Axioms: {axioms}")
155          if p.get('writtenToClaude'):
156              print(f"      āœļø Written to CLAUDE.md")
157          print()
158  
159  
160  def format_activity_results(result):
161      """Format recent activity results."""
162      print(f"\nšŸ“Š Recent Activity: {result.get('total', 0)} items")
163      print()
164  
165      for item in result.get('items', []):
166          itype = item.get('type', '?')
167          emoji = {
168              'aha_moment': 'šŸ’”',
169              'principle': 'šŸ“œ',
170              'convergence': 'šŸ”„',
171              'message': 'šŸ“Ø'
172          }.get(itype, '•')
173  
174          timestamp = item.get('timestamp', '')[:19].replace('T', ' ')
175          content = item.get('content', '')[:60]
176  
177          print(f"   {emoji} [{timestamp}] {itype}")
178          print(f"      {content}...")
179          print()
180  
181  
182  def main():
183      parser = argparse.ArgumentParser(description='Query the sovereign mesh network')
184      subparsers = parser.add_subparsers(dest='command', help='Query type')
185  
186      # Topic search
187      topic_parser = subparsers.add_parser('topic', help='Search by topic keywords')
188      topic_parser.add_argument('topics', help='Comma-separated topics')
189      topic_parser.add_argument('--limit', type=int, default=10, help='Max results')
190  
191      # Instance context
192      inst_parser = subparsers.add_parser('instance', help='Get instance context')
193      inst_parser.add_argument('name', nargs='?', help='Instance name (partial match)')
194  
195      # Insight search
196      insight_parser = subparsers.add_parser('insights', help='Search insights')
197      insight_parser.add_argument('--keyword', '-k', help='Keyword to search')
198      insight_parser.add_argument('--axiom', '-a', help='Filter by axiom (A0-A4)')
199      insight_parser.add_argument('--min-importance', '-i', type=float, default=0, help='Minimum importance')
200  
201      # Principle status
202      principle_parser = subparsers.add_parser('principles', help='Check principle status')
203      principle_parser.add_argument('--name', '-n', help='Filter by name')
204      principle_parser.add_argument('--status', '-s', choices=['candidate', 'testing', 'confirmed', 'rejected'],
205                                    help='Filter by status')
206  
207      # Recent activity
208      recent_parser = subparsers.add_parser('recent', help='Get recent activity')
209      recent_parser.add_argument('--limit', '-l', type=int, default=10, help='Max items')
210      recent_parser.add_argument('--type', '-t', choices=['aha', 'principle', 'convergence', 'message'],
211                                 help='Filter by type')
212  
213      args = parser.parse_args()
214  
215      if not args.command:
216          parser.print_help()
217          return
218  
219      # Build and execute query
220      if args.command == 'topic':
221          topics = [t.strip() for t in args.topics.split(',')]
222          result = query_mesh({'type': 'topic_search', 'topics': topics, 'limit': args.limit})
223          format_topic_results(result)
224  
225      elif args.command == 'instance':
226          query = {'type': 'instance_context'}
227          if args.name:
228              query['instance'] = args.name
229          result = query_mesh(query)
230          format_instance_results(result)
231  
232      elif args.command == 'insights':
233          query = {'type': 'insight_search'}
234          if args.keyword:
235              query['keyword'] = args.keyword
236          if args.axiom:
237              query['axiom'] = args.axiom
238          if args.min_importance:
239              query['minImportance'] = args.min_importance
240          result = query_mesh(query)
241          format_insight_results(result)
242  
243      elif args.command == 'principles':
244          query = {'type': 'principle_status'}
245          if args.name:
246              query['name'] = args.name
247          if args.status:
248              query['status'] = args.status
249          result = query_mesh(query)
250          format_principle_results(result)
251  
252      elif args.command == 'recent':
253          query = {'type': 'recent_activity', 'limit': args.limit}
254          if args.type:
255              query['type'] = args.type
256          result = query_mesh(query)
257          format_activity_results(result)
258  
259  
260  if __name__ == '__main__':
261      main()