#!/usr/bin/env python3 """ 路径探子 · Path Scout · ICE-GL-ZY-AGT-PATH-002 铸渊需要找文件?路径探子瞬间定位。 不需要grep翻遍整个仓库——探子知道每个角落。 用法: python3 scout.py find gatekeeper # 模糊搜索文件名 python3 scout.py where brain/ # 列出目录结构 python3 scout.py index # 重建文件索引 """ import json import os import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent.parent INDEX_FILE = Path(__file__).resolve().parent / "file-index.json" # 忽略目录 IGNORE_DIRS = {".git", "node_modules", "__pycache__", ".DS_Store", "output", "dist", ".next"} def build_index(): """扫描整个仓库,建立文件索引""" index = {"files": {}, "dirs": {}} for root, dirs, files in os.walk(REPO_ROOT): # 过滤忽略目录 dirs[:] = [d for d in dirs if d not in IGNORE_DIRS] rel_root = os.path.relpath(root, REPO_ROOT) if rel_root == ".": rel_root = "" # 记录目录 if rel_root: index["dirs"][rel_root] = { "file_count": len(files), "subdir_count": len(dirs) } # 记录文件 for f in files: rel_path = os.path.join(rel_root, f) if rel_root else f full_path = os.path.join(root, f) try: size = os.path.getsize(full_path) except: size = 0 index["files"][rel_path] = { "size": size, "ext": os.path.splitext(f)[1] } # 保存索引 INDEX_FILE.parent.mkdir(parents=True, exist_ok=True) with open(INDEX_FILE, "w") as f: json.dump(index, f, indent=2, ensure_ascii=False) return { "status": "ok", "total_files": len(index["files"]), "total_dirs": len(index["dirs"]), "index_file": str(INDEX_FILE) } def load_index(): """加载已有索引""" if INDEX_FILE.exists(): with open(INDEX_FILE) as f: return json.load(f) return None def find_file(pattern): """搜索文件""" index = load_index() if not index: return {"error": "索引不存在,先运行 scout.py index"} matches = [] pattern_lower = pattern.lower() for path, info in index["files"].items(): if pattern_lower in path.lower(): matches.append({"path": path, "size": info["size"]}) # 按路径排序 matches.sort(key=lambda x: x["path"]) return {"pattern": pattern, "matches": matches, "count": len(matches)} def list_dir(path=""): """列出目录结构""" index = load_index() if not index: return {"error": "索引不存在,先运行 scout.py index"} # 列出直接子目录和文件 subdirs = [] files = [] prefix = path.rstrip("/") + "/" if path else "" for d in index["dirs"]: if d.startswith(prefix) and d != prefix.rstrip("/"): rel = d[len(prefix):] if "/" not in rel: subdirs.append(d) for f in index["files"]: if f.startswith(prefix): rel = f[len(prefix):] if "/" not in rel: files.append({"name": rel, "size": index["files"][f]["size"]}) return {"path": path or "/", "subdirs": subdirs, "files": files} def search_content(pattern, file_pattern="*"): """在文件中搜索内容""" import subprocess cmd = ["grep", "-rl", "--include=" + file_pattern, pattern, str(REPO_ROOT)] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) files = [os.path.relpath(f, REPO_ROOT) for f in result.stdout.strip().split("\n") if f] return {"pattern": pattern, "matches": files, "count": len(files)} except: return {"error": "搜索失败"} if __name__ == "__main__": if len(sys.argv) < 2: print("用法: scout.py [参数]") sys.exit(1) action = sys.argv[1] if action == "index": result = build_index() print(json.dumps(result, ensure_ascii=False, indent=2)) elif action == "find" and len(sys.argv) > 2: result = find_file(sys.argv[2]) print(json.dumps(result, ensure_ascii=False, indent=2)) elif action == "where": path = sys.argv[2] if len(sys.argv) > 2 else "" result = list_dir(path) print(json.dumps(result, ensure_ascii=False, indent=2)) elif action == "grep" and len(sys.argv) > 2: pattern = sys.argv[2] fp = sys.argv[3] if len(sys.argv) > 3 else "*" result = search_content(pattern, fp) print(json.dumps(result, ensure_ascii=False, indent=2))