raggiecode 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Agent/__init__.py +0 -0
- Agent/agent.py +891 -0
- Agent/chat_history_db.py +1500 -0
- Agent/command.py +49 -0
- Agent/config.py +46 -0
- Agent/effort_levels.py +33 -0
- Agent/git_manager.py +727 -0
- Agent/tools.py +35 -0
- Commands/__init__.py +18 -0
- Commands/effort.py +42 -0
- Commands/global_todo.py +23 -0
- Commands/help.py +22 -0
- Commands/reasoning.py +24 -0
- Commands/redo.py +11 -0
- Commands/reindex.py +27 -0
- Commands/shell.py +28 -0
- Commands/stream.py +24 -0
- Commands/undo.py +13 -0
- Commands/unlimited_effort.py +8 -0
- Commands/window_size.py +29 -0
- RAG/__init__.py +0 -0
- RAG/document.py +119 -0
- RAG/find.py +408 -0
- RAG/graph.py +231 -0
- Tools/GetFileCodeStructure.py +43 -0
- Tools/GetSymbolSourceCode.py +27 -0
- Tools/__init__.py +39 -0
- Tools/ask_user.py +102 -0
- Tools/dispatch_subagent.py +215 -0
- Tools/document.py +35 -0
- Tools/edit_symbol.py +250 -0
- Tools/fuzzy_search.py +119 -0
- Tools/list_dir.py +51 -0
- Tools/read.py +49 -0
- Tools/read_image.py +75 -0
- Tools/remove.py +75 -0
- Tools/replace.py +305 -0
- Tools/search.py +41 -0
- Tools/shell.py +149 -0
- Tools/shell_kill.py +87 -0
- Tools/temp_background_service.py +113 -0
- Tools/todo_list.py +481 -0
- Tools/utils.py +116 -0
- Tools/view_changes.py +179 -0
- Tools/walk_call_tree.py +30 -0
- Tools/web_fetch.py +175 -0
- Tools/web_search.py +69 -0
- Tools/write.py +48 -0
- cli.py +111 -0
- config/__init__.py +0 -0
- config/coder_system_prompt.md +119 -0
- config/roles.json +43 -0
- config/tools.json +709 -0
- indexing/__init__.py +0 -0
- indexing/cli.py +128 -0
- indexing/code_index_sdk.py +832 -0
- indexing/code_indexer.py +1763 -0
- indexing/db_schema.py +396 -0
- indexing/export_to_json.py +346 -0
- indexing/extractors.py +189 -0
- indexing/file_utils.py +97 -0
- indexing/frontend/__init__.py +0 -0
- indexing/frontend/css_extractor.py +195 -0
- indexing/frontend/css_parser.py +387 -0
- indexing/frontend/css_selector_utils.py +226 -0
- indexing/frontend/edit_safety.py +573 -0
- indexing/frontend/graph.py +838 -0
- indexing/frontend/html_extractor.py +496 -0
- indexing/frontend/html_parser.py +314 -0
- indexing/frontend/jsx_extractor.py +1204 -0
- indexing/frontend/location_lookup.py +247 -0
- indexing/frontend/resolver.py +485 -0
- indexing/frontend/runtime_resolver.py +862 -0
- indexing/frontend/semantic_output.py +705 -0
- indexing/frontend/source_location.py +69 -0
- indexing/frontend_config.py +72 -0
- indexing/frontend_models.py +347 -0
- indexing/language_config.py +360 -0
- indexing/models.py +284 -0
- indexing/node_utils.py +1112 -0
- indexing/parse_worker.py +1082 -0
- indexing/queries.py +1542 -0
- indexing/sdk_examples.py +426 -0
- interactive.py +248 -0
- raggie.py +673 -0
- raggiecode-0.2.1.dist-info/METADATA +944 -0
- raggiecode-0.2.1.dist-info/RECORD +93 -0
- raggiecode-0.2.1.dist-info/WHEEL +5 -0
- raggiecode-0.2.1.dist-info/entry_points.txt +2 -0
- raggiecode-0.2.1.dist-info/top_level.txt +10 -0
- skills/__init__.py +3 -0
- skills/manager.py +114 -0
- skills/tool.py +121 -0
RAG/find.py
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from indexing.code_index_sdk import CodeIndexSDK
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def search_descriptions(query: str, symbol_types: list = None, limit: int = 50) -> str:
|
|
7
|
+
"""Search for symbols by description content.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
query: Search query string
|
|
11
|
+
symbol_types: Optional list of symbol types to search (e.g., ['function', 'class'])
|
|
12
|
+
limit: Maximum number of results to return
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Formatted string with search results
|
|
16
|
+
"""
|
|
17
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
18
|
+
|
|
19
|
+
if not db_path.exists():
|
|
20
|
+
return f"Error: Code index database not found at {db_path}"
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
24
|
+
results = sdk.search_descriptions(query, symbol_types, limit)
|
|
25
|
+
|
|
26
|
+
if not results:
|
|
27
|
+
return f"No symbols found with descriptions matching '{query}'"
|
|
28
|
+
|
|
29
|
+
lines = [f"Found {len(results)} symbols with descriptions matching '{query}':"]
|
|
30
|
+
for r in results:
|
|
31
|
+
lines.append(f" - {r['type']}: {r['name']} in {r['file_path']}")
|
|
32
|
+
lines.append(f" Description: {r['description']}")
|
|
33
|
+
|
|
34
|
+
return "\n".join(lines)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
return f"Error searching descriptions: {str(e)}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_undocumented_symbols(symbol_types: list = None, file_path: str = None) -> str:
|
|
40
|
+
"""Get symbols that have no description.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
symbol_types: Optional list of symbol types to check (e.g., ['function', 'class'])
|
|
44
|
+
file_path: Optional file path to filter by file
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Formatted string with undocumented symbols
|
|
48
|
+
"""
|
|
49
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
50
|
+
|
|
51
|
+
if not db_path.exists():
|
|
52
|
+
return f"Error: Code index database not found at {db_path}"
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
56
|
+
file_id = None
|
|
57
|
+
if file_path:
|
|
58
|
+
file = sdk.get_file_by_path(file_path)
|
|
59
|
+
if file:
|
|
60
|
+
file_id = file.id
|
|
61
|
+
else:
|
|
62
|
+
return f"Error: File not found in database: {file_path}"
|
|
63
|
+
|
|
64
|
+
undocumented = sdk.get_undocumented_symbols(symbol_types, file_id)
|
|
65
|
+
|
|
66
|
+
if not undocumented:
|
|
67
|
+
return "All symbols have descriptions" + (f" in {file_path}" if file_path else "")
|
|
68
|
+
|
|
69
|
+
lines = [f"Undocumented symbols" + (f" in {file_path}" if file_path else "") + ":"]
|
|
70
|
+
for symbol_type, symbols in undocumented.items():
|
|
71
|
+
lines.append(f"\n {symbol_type}s ({len(symbols)}):")
|
|
72
|
+
for s in symbols:
|
|
73
|
+
lines.append(f" - {s['name']} in {s['file_path']}")
|
|
74
|
+
|
|
75
|
+
return "\n".join(lines)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
return f"Error getting undocumented symbols: {str(e)}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def find_symbol_location(symbol_name: str, file_path: str = None):
|
|
81
|
+
"""Find the file path, line range, and current source of a symbol.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
symbol_name: Name of the symbol to find
|
|
85
|
+
file_path: Optional file path to disambiguate same-name symbols
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
dict with keys: file_path, start_line, end_line, source, kind
|
|
89
|
+
or None if not found
|
|
90
|
+
"""
|
|
91
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
92
|
+
|
|
93
|
+
if not db_path.exists():
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
98
|
+
file_id = None
|
|
99
|
+
if file_path:
|
|
100
|
+
f = sdk.get_file_by_path(file_path)
|
|
101
|
+
if f:
|
|
102
|
+
file_id = f.id
|
|
103
|
+
|
|
104
|
+
# Try function first
|
|
105
|
+
funcs = sdk.get_function_by_name(symbol_name, file_id)
|
|
106
|
+
if funcs:
|
|
107
|
+
func = funcs[0]
|
|
108
|
+
if len(funcs) > 1:
|
|
109
|
+
impls = [m for m in funcs if m.parent_type == 'class']
|
|
110
|
+
if impls:
|
|
111
|
+
func = impls[0]
|
|
112
|
+
source = sdk._read_source_lines(func.file_id, func.location.start_line, func.location.end_line)
|
|
113
|
+
if source is not None:
|
|
114
|
+
return {
|
|
115
|
+
"file_path": func.file_path,
|
|
116
|
+
"start_line": func.location.start_line,
|
|
117
|
+
"end_line": func.location.end_line,
|
|
118
|
+
"source": source,
|
|
119
|
+
"kind": "function",
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Try class
|
|
123
|
+
classes = sdk.get_class_by_name(symbol_name, file_id)
|
|
124
|
+
if classes:
|
|
125
|
+
cls = classes[0]
|
|
126
|
+
source = sdk._read_source_lines(cls.file_id, cls.location.start_line, cls.location.end_line)
|
|
127
|
+
if source is not None:
|
|
128
|
+
return {
|
|
129
|
+
"file_path": cls.file_path,
|
|
130
|
+
"start_line": cls.location.start_line,
|
|
131
|
+
"end_line": cls.location.end_line,
|
|
132
|
+
"source": source,
|
|
133
|
+
"kind": "class",
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Try frontend entities (components, CSS selectors, custom properties)
|
|
137
|
+
frontend_result = find_frontend_by_name(symbol_name, file_path)
|
|
138
|
+
if frontend_result:
|
|
139
|
+
return frontend_result
|
|
140
|
+
|
|
141
|
+
return None
|
|
142
|
+
except Exception:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def find_frontend_entity_location(entity_type: str, entity_id: int):
|
|
147
|
+
"""Find the file path and source range of a frontend entity by type and ID.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
entity_type: One of "markup_element", "css_rule", "custom_property",
|
|
151
|
+
"event_binding", "property_binding", "jsx_subtree", "component".
|
|
152
|
+
entity_id: ID of the entity in its table.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
dict with keys: file_path, start_line, end_line, source, kind
|
|
156
|
+
or None if not found.
|
|
157
|
+
"""
|
|
158
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
159
|
+
|
|
160
|
+
if not db_path.exists():
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
table_map = {
|
|
164
|
+
"markup_element": "markup_elements",
|
|
165
|
+
"jsx_subtree": "markup_elements",
|
|
166
|
+
"css_rule": "style_selectors",
|
|
167
|
+
"custom_property": "style_custom_properties",
|
|
168
|
+
"event_binding": "frontend_events",
|
|
169
|
+
"property_binding": "frontend_bindings",
|
|
170
|
+
"component": "frontend_components",
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
table = table_map.get(entity_type)
|
|
174
|
+
if not table:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
179
|
+
import json as _json
|
|
180
|
+
row = sdk.conn.execute(
|
|
181
|
+
f"""SELECT m.*, f.path as file_path
|
|
182
|
+
FROM {table} m
|
|
183
|
+
JOIN files f ON m.file_id = f.id
|
|
184
|
+
WHERE m.id = ?""",
|
|
185
|
+
(entity_id,)
|
|
186
|
+
).fetchone()
|
|
187
|
+
if not row:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
sr = _json.loads(row["source_range"]) if row["source_range"] else None
|
|
191
|
+
if not sr:
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
start_line = sr.get("start_line", 1)
|
|
195
|
+
end_line = sr.get("end_line", start_line)
|
|
196
|
+
|
|
197
|
+
source = sdk._read_source_lines(row["file_id"], start_line, end_line)
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
"file_path": row["file_path"],
|
|
201
|
+
"start_line": start_line,
|
|
202
|
+
"end_line": end_line,
|
|
203
|
+
"source": source,
|
|
204
|
+
"kind": entity_type,
|
|
205
|
+
}
|
|
206
|
+
except Exception:
|
|
207
|
+
return None
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def find_frontend_by_name(name: str, file_path: str = None):
|
|
211
|
+
"""Find a frontend entity by name (component name, selector text, etc.).
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
name: Component name, selector text, or custom property name.
|
|
215
|
+
file_path: Optional file path to disambiguate.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
dict with keys: file_path, start_line, end_line, source, kind, entity_type, entity_id
|
|
219
|
+
or None if not found.
|
|
220
|
+
"""
|
|
221
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
222
|
+
|
|
223
|
+
if not db_path.exists():
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
228
|
+
import json as _json
|
|
229
|
+
conn = sdk.conn
|
|
230
|
+
file_id = None
|
|
231
|
+
if file_path:
|
|
232
|
+
f = sdk.get_file_by_path(file_path)
|
|
233
|
+
if f:
|
|
234
|
+
file_id = f.id
|
|
235
|
+
|
|
236
|
+
# Try component name
|
|
237
|
+
if file_id:
|
|
238
|
+
row = conn.execute(
|
|
239
|
+
"""SELECT c.*, f.path as file_path
|
|
240
|
+
FROM frontend_components c
|
|
241
|
+
JOIN files f ON c.file_id = f.id
|
|
242
|
+
WHERE c.name = ? AND c.file_id = ?
|
|
243
|
+
LIMIT 1""",
|
|
244
|
+
(name, file_id)
|
|
245
|
+
).fetchone()
|
|
246
|
+
else:
|
|
247
|
+
row = conn.execute(
|
|
248
|
+
"""SELECT c.*, f.path as file_path
|
|
249
|
+
FROM frontend_components c
|
|
250
|
+
JOIN files f ON c.file_id = f.id
|
|
251
|
+
WHERE c.name = ?
|
|
252
|
+
LIMIT 1""",
|
|
253
|
+
(name,)
|
|
254
|
+
).fetchone()
|
|
255
|
+
|
|
256
|
+
if row:
|
|
257
|
+
sr = _json.loads(row["source_range"]) if row["source_range"] else None
|
|
258
|
+
start_line = sr.get("start_line", 1) if sr else 1
|
|
259
|
+
end_line = sr.get("end_line", start_line) if sr else start_line
|
|
260
|
+
source = sdk._read_source_lines(row["file_id"], start_line, end_line)
|
|
261
|
+
return {
|
|
262
|
+
"file_path": row["file_path"],
|
|
263
|
+
"start_line": start_line,
|
|
264
|
+
"end_line": end_line,
|
|
265
|
+
"source": source,
|
|
266
|
+
"kind": "component",
|
|
267
|
+
"entity_type": "component",
|
|
268
|
+
"entity_id": row["id"],
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# Try selector text
|
|
272
|
+
if file_id:
|
|
273
|
+
row = conn.execute(
|
|
274
|
+
"""SELECT s.*, f.path as file_path
|
|
275
|
+
FROM style_selectors s
|
|
276
|
+
JOIN files f ON s.file_id = f.id
|
|
277
|
+
WHERE s.selector_text = ? AND s.file_id = ?
|
|
278
|
+
LIMIT 1""",
|
|
279
|
+
(name, file_id)
|
|
280
|
+
).fetchone()
|
|
281
|
+
else:
|
|
282
|
+
row = conn.execute(
|
|
283
|
+
"""SELECT s.*, f.path as file_path
|
|
284
|
+
FROM style_selectors s
|
|
285
|
+
JOIN files f ON s.file_id = f.id
|
|
286
|
+
WHERE s.selector_text = ?
|
|
287
|
+
LIMIT 1""",
|
|
288
|
+
(name,)
|
|
289
|
+
).fetchone()
|
|
290
|
+
|
|
291
|
+
if row:
|
|
292
|
+
sr = _json.loads(row["source_range"]) if row["source_range"] else None
|
|
293
|
+
start_line = sr.get("start_line", 1) if sr else 1
|
|
294
|
+
end_line = sr.get("end_line", start_line) if sr else start_line
|
|
295
|
+
source = sdk._read_source_lines(row["file_id"], start_line, end_line)
|
|
296
|
+
return {
|
|
297
|
+
"file_path": row["file_path"],
|
|
298
|
+
"start_line": start_line,
|
|
299
|
+
"end_line": end_line,
|
|
300
|
+
"source": source,
|
|
301
|
+
"kind": "css_rule",
|
|
302
|
+
"entity_type": "css_rule",
|
|
303
|
+
"entity_id": row["id"],
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# Try custom property name
|
|
307
|
+
if file_id:
|
|
308
|
+
row = conn.execute(
|
|
309
|
+
"""SELECT cp.*, f.path as file_path
|
|
310
|
+
FROM style_custom_properties cp
|
|
311
|
+
JOIN files f ON cp.file_id = f.id
|
|
312
|
+
WHERE cp.name = ? AND cp.file_id = ?
|
|
313
|
+
LIMIT 1""",
|
|
314
|
+
(name, file_id)
|
|
315
|
+
).fetchone()
|
|
316
|
+
else:
|
|
317
|
+
row = conn.execute(
|
|
318
|
+
"""SELECT cp.*, f.path as file_path
|
|
319
|
+
FROM style_custom_properties cp
|
|
320
|
+
JOIN files f ON cp.file_id = f.id
|
|
321
|
+
WHERE cp.name = ?
|
|
322
|
+
LIMIT 1""",
|
|
323
|
+
(name,)
|
|
324
|
+
).fetchone()
|
|
325
|
+
|
|
326
|
+
if row:
|
|
327
|
+
sr = _json.loads(row["source_range"]) if row["source_range"] else None
|
|
328
|
+
start_line = sr.get("start_line", 1) if sr else 1
|
|
329
|
+
end_line = sr.get("end_line", start_line) if sr else start_line
|
|
330
|
+
source = sdk._read_source_lines(row["file_id"], start_line, end_line)
|
|
331
|
+
return {
|
|
332
|
+
"file_path": row["file_path"],
|
|
333
|
+
"start_line": start_line,
|
|
334
|
+
"end_line": end_line,
|
|
335
|
+
"source": source,
|
|
336
|
+
"kind": "custom_property",
|
|
337
|
+
"entity_type": "custom_property",
|
|
338
|
+
"entity_id": row["id"],
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return None
|
|
342
|
+
except Exception:
|
|
343
|
+
return None
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def find_frontend_entity(entity_type: str, name: str, file_path: str = None):
|
|
347
|
+
"""Find a frontend entity by type and name.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
entity_type: One of "component", "css_rule", "custom_property",
|
|
351
|
+
"markup_element", "event_binding", "property_binding".
|
|
352
|
+
name: Component name, selector text, custom property name, or tag name.
|
|
353
|
+
file_path: Optional file path to disambiguate.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
dict with keys: file_path, start_line, end_line, source, kind, entity_type, entity_id
|
|
357
|
+
or None if not found.
|
|
358
|
+
"""
|
|
359
|
+
if entity_type == "component":
|
|
360
|
+
return find_frontend_by_name(name, file_path)
|
|
361
|
+
if entity_type == "css_rule":
|
|
362
|
+
return find_frontend_by_name(name, file_path)
|
|
363
|
+
if entity_type == "custom_property":
|
|
364
|
+
return find_frontend_by_name(name, file_path)
|
|
365
|
+
# For markup_element, event_binding, property_binding — search by name in respective tables
|
|
366
|
+
return find_frontend_by_name(name, file_path)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def find_symbol_implementation(symbol_name: str, file_path: str = None) -> str:
|
|
370
|
+
"""Find and return the source implementation of a symbol (function or class).
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
symbol_name: Name of the symbol to find
|
|
374
|
+
file_path: Optional file path to disambiguate same-name symbols
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Source code string of the symbol, or error message if not found
|
|
378
|
+
"""
|
|
379
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
380
|
+
|
|
381
|
+
if not db_path.exists():
|
|
382
|
+
return f"Error: Code index database not found at {db_path}"
|
|
383
|
+
|
|
384
|
+
try:
|
|
385
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
386
|
+
# Try to find as function first
|
|
387
|
+
func_body = sdk.get_function_body(symbol_name, file_path)
|
|
388
|
+
if func_body:
|
|
389
|
+
return func_body
|
|
390
|
+
|
|
391
|
+
# Try to find as class
|
|
392
|
+
class_body = sdk.get_class_body(symbol_name, file_path)
|
|
393
|
+
if class_body:
|
|
394
|
+
return class_body
|
|
395
|
+
|
|
396
|
+
# Search for similar symbols by name and description
|
|
397
|
+
matches = sdk.search_symbols(symbol_name, limit=10)
|
|
398
|
+
|
|
399
|
+
if matches:
|
|
400
|
+
lines = [f"Symbol '{symbol_name}' not found by exact name. Similar symbols:"]
|
|
401
|
+
for m in matches:
|
|
402
|
+
desc_suffix = f" — {m['description']}" if m.get('description') else ""
|
|
403
|
+
lines.append(f" - {m['type']}: {m['name']} in {m['file_path']} ({m['match_reason']}){desc_suffix}")
|
|
404
|
+
return "\n".join(lines)
|
|
405
|
+
|
|
406
|
+
return f"Error: Symbol '{symbol_name}' not found in code index."
|
|
407
|
+
except Exception as e:
|
|
408
|
+
return f"Error querying code index: {str(e)}"
|
RAG/graph.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from indexing.code_index_sdk import CodeIndexSDK
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def explore_code_structure(file_path: str, include_bodies: bool = False) -> str:
|
|
8
|
+
"""Explore the code structure and dependencies of a file.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
file_path: Path to the file to analyze
|
|
12
|
+
include_bodies: If True, include full source code for all functions and classes in the file
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
YAML-like formatted string showing the dependency graph, optionally with symbol bodies.
|
|
16
|
+
For frontend files (HTML, CSS, JSX, TSX), returns structured semantic output instead.
|
|
17
|
+
"""
|
|
18
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
19
|
+
|
|
20
|
+
if not db_path.exists():
|
|
21
|
+
return f"Error: Code index database not found at {db_path}"
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
25
|
+
# Check if this is a frontend file
|
|
26
|
+
from indexing.frontend.semantic_output import format_file_semantics
|
|
27
|
+
semantic = format_file_semantics(sdk.conn, file_path)
|
|
28
|
+
if semantic is not None:
|
|
29
|
+
# Frontend file — return semantic output
|
|
30
|
+
if include_bodies:
|
|
31
|
+
# Append raw source when bodies are explicitly requested
|
|
32
|
+
normalized = file_path[2:] if file_path.startswith('./') else file_path
|
|
33
|
+
file = sdk.get_file_by_path(normalized)
|
|
34
|
+
if file:
|
|
35
|
+
raw = sdk._read_source_lines(file.id, 1, 0) if hasattr(sdk, '_read_source_lines') else ""
|
|
36
|
+
if raw:
|
|
37
|
+
return semantic + "\n\n## Raw Source\n" + raw
|
|
38
|
+
return semantic
|
|
39
|
+
|
|
40
|
+
graph = sdk.get_dependency_graph(file_path)
|
|
41
|
+
|
|
42
|
+
if not include_bodies:
|
|
43
|
+
return graph
|
|
44
|
+
|
|
45
|
+
# Get the file to fetch all symbols
|
|
46
|
+
file = sdk.get_file_by_path(file_path)
|
|
47
|
+
if not file:
|
|
48
|
+
# Fallback: try matching by filename (same logic as get_dependency_graph)
|
|
49
|
+
from pathlib import Path as _Path
|
|
50
|
+
filename = _Path(file_path[2:] if file_path.startswith('./') else file_path).name
|
|
51
|
+
cursor = sdk.conn.cursor()
|
|
52
|
+
cursor.execute("SELECT * FROM files WHERE path LIKE ?", (f"%{filename}",))
|
|
53
|
+
row = cursor.fetchone()
|
|
54
|
+
if row:
|
|
55
|
+
from indexing.models import File
|
|
56
|
+
file = File.from_row(row)
|
|
57
|
+
if not file:
|
|
58
|
+
return graph
|
|
59
|
+
|
|
60
|
+
# Get all functions and classes in the file
|
|
61
|
+
functions = sdk.get_file_functions(file.id)
|
|
62
|
+
classes = sdk.get_file_classes(file.id)
|
|
63
|
+
|
|
64
|
+
# Build the bodies section
|
|
65
|
+
bodies_section = []
|
|
66
|
+
if functions:
|
|
67
|
+
bodies_section.append("\n## Function Bodies")
|
|
68
|
+
for func in functions:
|
|
69
|
+
body = sdk._read_source_lines(func.file_id, func.location.start_line, func.location.end_line)
|
|
70
|
+
desc = f"# Description: {func.description}\n\n" if func.description else ""
|
|
71
|
+
bodies_section.append(f"\n### {func.name}\n{desc}{body}")
|
|
72
|
+
|
|
73
|
+
if classes:
|
|
74
|
+
bodies_section.append("\n## Class Bodies")
|
|
75
|
+
for cls in classes:
|
|
76
|
+
body = sdk._read_source_lines(cls.file_id, cls.location.start_line, cls.location.end_line)
|
|
77
|
+
desc = f"# Description: {cls.description}\n\n" if cls.description else ""
|
|
78
|
+
bodies_section.append(f"\n### {cls.name}\n{desc}{body}")
|
|
79
|
+
|
|
80
|
+
if bodies_section:
|
|
81
|
+
return graph + "\n" + "".join(bodies_section)
|
|
82
|
+
|
|
83
|
+
return graph
|
|
84
|
+
except Exception as e:
|
|
85
|
+
return f"Error querying code index: {str(e)}"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def walk_call_tree(symbol_name: str, file_path: str = None,
|
|
89
|
+
max_depth: int = 5, include_external: bool = False,
|
|
90
|
+
exclude: list = None) -> str:
|
|
91
|
+
"""Walk the call tree starting from a function/method, depth-limited with cycle detection.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
symbol_name: Name of the starting function/method.
|
|
95
|
+
file_path: Optional file path to disambiguate same-name symbols.
|
|
96
|
+
max_depth: Maximum depth to traverse (default 5).
|
|
97
|
+
include_external: If True, include external/third-party calls.
|
|
98
|
+
exclude: Optional list of path prefixes to exclude (e.g. ["tests/"]).
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
JSON lines string, one object per node, sorted by depth then name.
|
|
102
|
+
"""
|
|
103
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
104
|
+
|
|
105
|
+
if not db_path.exists():
|
|
106
|
+
return json.dumps({"error": f"Code index database not found at {db_path}"})
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
110
|
+
return sdk.walk_call_tree(symbol_name, file_path, max_depth, include_external, exclude)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
return json.dumps({"error": f"Error walking call tree: {str(e)}"})
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def explore_frontend_structure(file_path: str) -> str:
|
|
116
|
+
"""Explore the frontend structure of a file (components, markup, styles, events).
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
file_path: Path to the frontend file (TSX, HTML, CSS).
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
JSON string with component info, markup trees, events, bindings, and styles.
|
|
123
|
+
"""
|
|
124
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
125
|
+
|
|
126
|
+
if not db_path.exists():
|
|
127
|
+
return json.dumps({"error": f"Code index database not found at {db_path}"})
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
131
|
+
file = sdk.get_file_by_path(file_path)
|
|
132
|
+
if not file:
|
|
133
|
+
from pathlib import Path as _Path
|
|
134
|
+
filename = _Path(file_path[2:] if file_path.startswith('./') else file_path).name
|
|
135
|
+
cursor = sdk.conn.cursor()
|
|
136
|
+
cursor.execute("SELECT * FROM files WHERE path LIKE ?", (f"%{filename}",))
|
|
137
|
+
row = cursor.fetchone()
|
|
138
|
+
if row:
|
|
139
|
+
from indexing.models import File
|
|
140
|
+
file = File.from_row(row)
|
|
141
|
+
if not file:
|
|
142
|
+
return json.dumps({"error": f"File not found: {file_path}"})
|
|
143
|
+
|
|
144
|
+
cursor = sdk.conn.cursor()
|
|
145
|
+
|
|
146
|
+
# Get components in this file
|
|
147
|
+
cursor.execute(
|
|
148
|
+
"SELECT id, name, framework, is_exported FROM frontend_components WHERE file_id = ?",
|
|
149
|
+
(file.id,)
|
|
150
|
+
)
|
|
151
|
+
components = [dict(row) for row in cursor.fetchall()]
|
|
152
|
+
|
|
153
|
+
result = {
|
|
154
|
+
"file_path": file_path,
|
|
155
|
+
"components": [],
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for comp in components:
|
|
159
|
+
traversal = sdk.traverse_full_frontend(comp["id"])
|
|
160
|
+
result["components"].append(traversal)
|
|
161
|
+
|
|
162
|
+
# If no components, try markup elements (HTML)
|
|
163
|
+
if not components:
|
|
164
|
+
cursor.execute(
|
|
165
|
+
"SELECT id, tag_name, element_type FROM markup_elements WHERE file_id = ? ORDER BY id",
|
|
166
|
+
(file.id,)
|
|
167
|
+
)
|
|
168
|
+
elements = [dict(row) for row in cursor.fetchall()]
|
|
169
|
+
if elements:
|
|
170
|
+
result["markup_elements"] = elements
|
|
171
|
+
|
|
172
|
+
cursor.execute(
|
|
173
|
+
"SELECT id, selector_text, selector_type FROM style_selectors WHERE file_id = ? ORDER BY id",
|
|
174
|
+
(file.id,)
|
|
175
|
+
)
|
|
176
|
+
selectors = [dict(row) for row in cursor.fetchall()]
|
|
177
|
+
if selectors:
|
|
178
|
+
result["style_selectors"] = selectors
|
|
179
|
+
|
|
180
|
+
return json.dumps(result, ensure_ascii=False, default=str)
|
|
181
|
+
except Exception as e:
|
|
182
|
+
return json.dumps({"error": f"Error exploring frontend structure: {str(e)}"})
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def walk_render_tree(component_name: str, file_path: str = None,
|
|
186
|
+
max_depth: int = 10) -> str:
|
|
187
|
+
"""Walk the render tree starting from a component, depth-limited with cycle detection.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
component_name: Name of the starting component.
|
|
191
|
+
file_path: Optional file path to disambiguate same-name components.
|
|
192
|
+
max_depth: Maximum traversal depth (default 10).
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
JSON string with the render tree (children direction).
|
|
196
|
+
"""
|
|
197
|
+
db_path = Path.cwd() / ".raggie" / ".code_index.raggie"
|
|
198
|
+
|
|
199
|
+
if not db_path.exists():
|
|
200
|
+
return json.dumps({"error": f"Code index database not found at {db_path}"})
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
with CodeIndexSDK(str(db_path)) as sdk:
|
|
204
|
+
cursor = sdk.conn.cursor()
|
|
205
|
+
|
|
206
|
+
if file_path:
|
|
207
|
+
file = sdk.get_file_by_path(file_path)
|
|
208
|
+
if file:
|
|
209
|
+
cursor.execute(
|
|
210
|
+
"SELECT id FROM frontend_components WHERE name = ? AND file_id = ? LIMIT 1",
|
|
211
|
+
(component_name, file.id)
|
|
212
|
+
)
|
|
213
|
+
else:
|
|
214
|
+
cursor.execute(
|
|
215
|
+
"SELECT id FROM frontend_components WHERE name = ? LIMIT 1",
|
|
216
|
+
(component_name,)
|
|
217
|
+
)
|
|
218
|
+
else:
|
|
219
|
+
cursor.execute(
|
|
220
|
+
"SELECT id FROM frontend_components WHERE name = ? LIMIT 1",
|
|
221
|
+
(component_name,)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
row = cursor.fetchone()
|
|
225
|
+
if not row:
|
|
226
|
+
return json.dumps({"error": f"Component not found: {component_name}"})
|
|
227
|
+
|
|
228
|
+
result = sdk.traverse_render_graph(row["id"], "children", max_depth)
|
|
229
|
+
return json.dumps(result, ensure_ascii=False, default=str)
|
|
230
|
+
except Exception as e:
|
|
231
|
+
return json.dumps({"error": f"Error walking render tree: {str(e)}"})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from RAG.graph import explore_code_structure
|
|
4
|
+
from .utils import is_within_cwd, BLUE, RESET
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def handle(arguments, toolcall_id):
|
|
8
|
+
file_path = arguments["file_path"]
|
|
9
|
+
include_bodies = arguments.get("include_bodies", False)
|
|
10
|
+
print(f"{BLUE}GetFileCodeSemantics {file_path}{RESET}")
|
|
11
|
+
|
|
12
|
+
# Check if the file is outside the current working directory
|
|
13
|
+
if not is_within_cwd(file_path):
|
|
14
|
+
return {
|
|
15
|
+
"role": "tool",
|
|
16
|
+
"tool_call_id": toolcall_id,
|
|
17
|
+
"content": "Error: access denied - path is outside the current working directory",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
path = Path(file_path)
|
|
22
|
+
if path.is_dir():
|
|
23
|
+
entries = sorted(path.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower()))
|
|
24
|
+
lines = [f"Contents of {file_path} ({len(entries)} items):"]
|
|
25
|
+
for entry in entries:
|
|
26
|
+
if entry.is_dir():
|
|
27
|
+
lines.append(f" [DIR] {entry.name}/")
|
|
28
|
+
else:
|
|
29
|
+
lines.append(f" [FILE] {entry.name}")
|
|
30
|
+
result = "\n".join(lines)
|
|
31
|
+
else:
|
|
32
|
+
result = explore_code_structure(file_path, include_bodies=include_bodies)
|
|
33
|
+
if result.startswith("Error: File not found in database"):
|
|
34
|
+
with open(file_path, "r") as f:
|
|
35
|
+
result = f.read()
|
|
36
|
+
except Exception as e:
|
|
37
|
+
result = f"Error exploring code structure: {str(e)}"
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
"role": "tool",
|
|
41
|
+
"tool_call_id": toolcall_id,
|
|
42
|
+
"content": result,
|
|
43
|
+
}
|