/ scripts / search_before_build.py
search_before_build.py
  1  #!/usr/bin/env python3
  2  """
  3  Search Before Build - Torah/Talmud Convergence Check
  4  
  5  Before building new functionality, search for existing implementations.
  6  Integration > Invention.
  7  
  8  Usage:
  9      python3 scripts/search_before_build.py "resonance threshold"
 10      python3 scripts/search_before_build.py "graph sink" --verbose
 11      python3 scripts/search_before_build.py "broadcast mesh" --suggest
 12  
 13  This enforces the "Search before build" protocol from CLAUDE.md.
 14  """
 15  
 16  import subprocess
 17  import sys
 18  from pathlib import Path
 19  from typing import List, Tuple
 20  
 21  REPO_ROOT = Path(__file__).parent.parent
 22  
 23  # Key directories to search
 24  SEARCH_DIRS = [
 25      "core/",      # Core modules
 26      "scripts/",   # Tools and utilities
 27      "patterns/",  # Design patterns
 28  ]
 29  
 30  # File patterns that often contain reusable code
 31  CODE_PATTERNS = ["*.py", "*.js", "*.md"]
 32  
 33  
 34  def search_codebase(query: str, verbose: bool = False) -> List[Tuple[str, str, int]]:
 35      """
 36      Search codebase for query terms.
 37  
 38      Returns list of (file, matching_line, line_number).
 39      """
 40      results = []
 41  
 42      # Split query into terms
 43      terms = query.lower().split()
 44  
 45      for search_dir in SEARCH_DIRS:
 46          dir_path = REPO_ROOT / search_dir
 47          if not dir_path.exists():
 48              continue
 49  
 50          # Use grep for fast search
 51          try:
 52              for term in terms:
 53                  cmd = [
 54                      "grep", "-r", "-i", "-n",
 55                      "--include=*.py", "--include=*.js", "--include=*.md",
 56                      term, str(dir_path)
 57                  ]
 58                  result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
 59  
 60                  for line in result.stdout.strip().split('\n'):
 61                      if line and ':' in line:
 62                          parts = line.split(':', 2)
 63                          if len(parts) >= 3:
 64                              filepath = parts[0].replace(str(REPO_ROOT) + '/', '')
 65                              lineno = int(parts[1]) if parts[1].isdigit() else 0
 66                              content = parts[2].strip()[:100]
 67                              results.append((filepath, content, lineno))
 68          except Exception as e:
 69              if verbose:
 70                  print(f"Search error: {e}")
 71  
 72      # Deduplicate by file+line
 73      seen = set()
 74      unique = []
 75      for r in results:
 76          key = (r[0], r[2])
 77          if key not in seen:
 78              seen.add(key)
 79              unique.append(r)
 80  
 81      return unique[:30]  # Limit results
 82  
 83  
 84  def analyze_results(results: List[Tuple[str, str, int]], query: str) -> dict:
 85      """
 86      Analyze search results to suggest existing systems.
 87      """
 88      analysis = {
 89          "total_matches": len(results),
 90          "files_touched": len(set(r[0] for r in results)),
 91          "core_modules": [],
 92          "scripts": [],
 93          "patterns": [],
 94          "recommendation": ""
 95      }
 96  
 97      for filepath, content, lineno in results:
 98          if filepath.startswith("core/"):
 99              analysis["core_modules"].append(f"{filepath}:{lineno}")
100          elif filepath.startswith("scripts/"):
101              analysis["scripts"].append(f"{filepath}:{lineno}")
102          elif filepath.startswith("patterns/"):
103              analysis["patterns"].append(f"{filepath}:{lineno}")
104  
105      # Generate recommendation
106      if analysis["core_modules"]:
107          analysis["recommendation"] = (
108              f"FOUND in core/: Check {analysis['core_modules'][0]} first. "
109              "Extend existing module rather than creating new one."
110          )
111      elif analysis["scripts"]:
112          analysis["recommendation"] = (
113              f"FOUND in scripts/: Check {analysis['scripts'][0]}. "
114              "Can this be extended or composed with?"
115          )
116      elif analysis["patterns"]:
117          analysis["recommendation"] = (
118              f"Pattern exists: See {analysis['patterns'][0]}. "
119              "Follow existing pattern rather than inventing new approach."
120          )
121      else:
122          analysis["recommendation"] = (
123              "No existing implementation found. "
124              "This may be genuinely new - document why."
125          )
126  
127      return analysis
128  
129  
130  def main():
131      if len(sys.argv) < 2:
132          print("Usage: python3 scripts/search_before_build.py <query> [--verbose] [--suggest]")
133          print("\nExamples:")
134          print('  python3 scripts/search_before_build.py "resonance threshold"')
135          print('  python3 scripts/search_before_build.py "mesh broadcast" --suggest')
136          sys.exit(1)
137  
138      query = sys.argv[1]
139      verbose = "--verbose" in sys.argv or "-v" in sys.argv
140      suggest = "--suggest" in sys.argv or "-s" in sys.argv
141  
142      print(f"=" * 60)
143      print(f"SEARCH BEFORE BUILD: '{query}'")
144      print(f"=" * 60)
145      print()
146  
147      results = search_codebase(query, verbose)
148  
149      if not results:
150          print("No matches found.")
151          print("\nThis may be genuinely new functionality.")
152          print("If building: Document why this is necessary.")
153      else:
154          print(f"Found {len(results)} matches:\n")
155  
156          # Group by file
157          by_file = {}
158          for filepath, content, lineno in results:
159              if filepath not in by_file:
160                  by_file[filepath] = []
161              by_file[filepath].append((content, lineno))
162  
163          for filepath, matches in sorted(by_file.items()):
164              print(f"  {filepath}")
165              for content, lineno in matches[:3]:
166                  print(f"    L{lineno}: {content[:60]}...")
167              if len(matches) > 3:
168                  print(f"    ... and {len(matches) - 3} more")
169              print()
170  
171      if suggest or results:
172          analysis = analyze_results(results, query)
173          print("-" * 60)
174          print("RECOMMENDATION:")
175          print(f"  {analysis['recommendation']}")
176          print("-" * 60)
177  
178      # Exit code: 0 if found (suggests integration), 1 if not found (may build new)
179      sys.exit(0 if results else 1)
180  
181  
182  if __name__ == "__main__":
183      main()