82 lines
2.6 KiB
Python
Executable File
82 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Simple workspace search tool.
|
|
Searches markdown files and returns ranked results.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import re
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
|
|
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
|
EXTENSIONS = {'.md', '.txt', '.sh', '.py', '.json'}
|
|
|
|
def search(query: str, max_results: int = 10) -> list:
|
|
"""Search workspace for query, return list of (file, line_num, line, score)."""
|
|
results = []
|
|
query_lower = query.lower()
|
|
query_words = query_lower.split()
|
|
|
|
for root, dirs, files in os.walk(WORKSPACE):
|
|
# Skip hidden directories
|
|
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
|
|
|
for fname in files:
|
|
fpath = Path(root) / fname
|
|
if fpath.suffix not in EXTENSIONS:
|
|
continue
|
|
|
|
try:
|
|
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
|
for i, line in enumerate(f, 1):
|
|
line_lower = line.lower()
|
|
|
|
# Calculate score
|
|
score = 0
|
|
for word in query_words:
|
|
if word in line_lower:
|
|
score += 1
|
|
# Bonus for exact phrase
|
|
if query_lower in line_lower:
|
|
score += 2
|
|
# Bonus for word boundaries
|
|
if re.search(rf'\b{re.escape(word)}\b', line_lower):
|
|
score += 1
|
|
|
|
if score > 0:
|
|
rel_path = fpath.relative_to(WORKSPACE)
|
|
results.append((str(rel_path), i, line.strip(), score))
|
|
except Exception:
|
|
continue
|
|
|
|
# Sort by score descending
|
|
results.sort(key=lambda x: -x[3])
|
|
return results[:max_results]
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: search.py <query> [max_results]")
|
|
sys.exit(1)
|
|
|
|
query = sys.argv[1]
|
|
max_results = int(sys.argv[2]) if len(sys.argv) > 2 else 10
|
|
|
|
results = search(query, max_results)
|
|
|
|
if not results:
|
|
print(f"No results for: {query}")
|
|
return
|
|
|
|
print(f"Results for: {query}\n")
|
|
for fpath, line_num, line, score in results:
|
|
# Truncate long lines
|
|
display = line[:80] + "..." if len(line) > 80 else line
|
|
print(f"[{fpath}:{line_num}] {display}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|