-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgrep.py
More file actions
executable file
·143 lines (123 loc) · 4.69 KB
/
Copy pathgrep.py
File metadata and controls
executable file
·143 lines (123 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
"""
CodeAlive Grep Search — exact text or regex search across indexed repositories.
Finds code containing a specific string or pattern. Use when you know the
exact identifier, error message, config key, or regex to match.
For concept-based discovery, use search.py instead.
Usage:
python grep.py "AuthService" my-repo
python grep.py "auth\\(" my-repo --regex --max-results 25
python grep.py "TODO" workspace:backend-team --path src --ext .py
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / "lib"))
from api_client import CodeAliveClient
def format_grep_results(results: dict) -> str:
items = results.get("results", []) if isinstance(results, dict) else []
if not items:
return (
"No grep matches found. This does NOT mean the code doesn't exist.\n"
"Try: (1) check case — grep is case-sensitive by default; "
"(2) use search.py for concept-based discovery if unsure of exact naming; "
"(3) check that the data source is correct (run datasources.py); "
"(4) remove --path/--ext filters if used."
)
output = []
for idx, result in enumerate(items, 1):
location = result.get("location", {})
file_path = location.get("path") or result.get("path")
matches = result.get("matches", [])
output.append(f"\n--- Result #{idx} [{result.get('kind', 'Artifact')}] ---")
if file_path:
output.append(f" File: {file_path}")
if result.get("identifier"):
output.append(f" Identifier: {result['identifier']}")
# Surface the data-source name/id so they can be passed back as --data-source to
# fetch.py / relationships.py when an identifier is branch-ambiguous.
# dataSource may be a {name, id} object or a bare string, depending on the API response
# shape — handle both, mirroring search.py.
ds = result.get("dataSource")
if isinstance(ds, dict):
ds_name = ds.get("name")
ds_id = ds.get("id")
else:
ds_name = ds
ds_id = None
if ds_name and ds_id:
output.append(f" Source: {ds_name} (id: {ds_id})")
elif ds_name:
output.append(f" Source: {ds_name}")
elif ds_id:
output.append(f" Source: (id: {ds_id})")
if result.get("matchCount") is not None:
output.append(f" Match count: {result['matchCount']}")
for match in matches:
output.append(
" "
f"{match.get('lineNumber', '?')}:{match.get('startColumn', '?')}-"
f"{match.get('endColumn', '?')} {match.get('lineText', '')}"
)
output.append(
"\nHint: match previews are search evidence only. Fetch the full source "
"with `python fetch.py <identifier>` or read the local file before reasoning about behavior."
)
return "\n".join(output)
def main():
if len(sys.argv) < 3:
print("Error: Missing required arguments.", file=sys.stderr)
print(
"Usage: python grep.py <query> <data_source> [data_source2...] "
"[--regex] [--max-results N] [--path PATH] [--ext EXT]",
file=sys.stderr,
)
sys.exit(1)
query = sys.argv[1]
data_sources = []
paths = []
extensions = []
max_results = None
regex = False
i = 2
while i < len(sys.argv):
arg = sys.argv[i]
if arg == "--regex":
regex = True
i += 1
elif arg == "--max-results" and i + 1 < len(sys.argv):
max_results = int(sys.argv[i + 1])
i += 2
elif arg == "--path" and i + 1 < len(sys.argv):
paths.append(sys.argv[i + 1])
i += 2
elif arg == "--ext" and i + 1 < len(sys.argv):
extensions.append(sys.argv[i + 1])
i += 2
elif arg == "--help":
print(__doc__)
sys.exit(0)
else:
data_sources.append(arg)
i += 1
if not data_sources:
print(
"Error: At least one data source is required. Run datasources.py to see available sources.",
file=sys.stderr,
)
sys.exit(1)
try:
client = CodeAliveClient()
results = client.grep_search(
query=query,
data_sources=data_sources,
paths=paths or None,
extensions=extensions or None,
max_results=max_results,
regex=regex,
)
print(format_grep_results(results))
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()