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.
Files changed (93) hide show
  1. Agent/__init__.py +0 -0
  2. Agent/agent.py +891 -0
  3. Agent/chat_history_db.py +1500 -0
  4. Agent/command.py +49 -0
  5. Agent/config.py +46 -0
  6. Agent/effort_levels.py +33 -0
  7. Agent/git_manager.py +727 -0
  8. Agent/tools.py +35 -0
  9. Commands/__init__.py +18 -0
  10. Commands/effort.py +42 -0
  11. Commands/global_todo.py +23 -0
  12. Commands/help.py +22 -0
  13. Commands/reasoning.py +24 -0
  14. Commands/redo.py +11 -0
  15. Commands/reindex.py +27 -0
  16. Commands/shell.py +28 -0
  17. Commands/stream.py +24 -0
  18. Commands/undo.py +13 -0
  19. Commands/unlimited_effort.py +8 -0
  20. Commands/window_size.py +29 -0
  21. RAG/__init__.py +0 -0
  22. RAG/document.py +119 -0
  23. RAG/find.py +408 -0
  24. RAG/graph.py +231 -0
  25. Tools/GetFileCodeStructure.py +43 -0
  26. Tools/GetSymbolSourceCode.py +27 -0
  27. Tools/__init__.py +39 -0
  28. Tools/ask_user.py +102 -0
  29. Tools/dispatch_subagent.py +215 -0
  30. Tools/document.py +35 -0
  31. Tools/edit_symbol.py +250 -0
  32. Tools/fuzzy_search.py +119 -0
  33. Tools/list_dir.py +51 -0
  34. Tools/read.py +49 -0
  35. Tools/read_image.py +75 -0
  36. Tools/remove.py +75 -0
  37. Tools/replace.py +305 -0
  38. Tools/search.py +41 -0
  39. Tools/shell.py +149 -0
  40. Tools/shell_kill.py +87 -0
  41. Tools/temp_background_service.py +113 -0
  42. Tools/todo_list.py +481 -0
  43. Tools/utils.py +116 -0
  44. Tools/view_changes.py +179 -0
  45. Tools/walk_call_tree.py +30 -0
  46. Tools/web_fetch.py +175 -0
  47. Tools/web_search.py +69 -0
  48. Tools/write.py +48 -0
  49. cli.py +111 -0
  50. config/__init__.py +0 -0
  51. config/coder_system_prompt.md +119 -0
  52. config/roles.json +43 -0
  53. config/tools.json +709 -0
  54. indexing/__init__.py +0 -0
  55. indexing/cli.py +128 -0
  56. indexing/code_index_sdk.py +832 -0
  57. indexing/code_indexer.py +1763 -0
  58. indexing/db_schema.py +396 -0
  59. indexing/export_to_json.py +346 -0
  60. indexing/extractors.py +189 -0
  61. indexing/file_utils.py +97 -0
  62. indexing/frontend/__init__.py +0 -0
  63. indexing/frontend/css_extractor.py +195 -0
  64. indexing/frontend/css_parser.py +387 -0
  65. indexing/frontend/css_selector_utils.py +226 -0
  66. indexing/frontend/edit_safety.py +573 -0
  67. indexing/frontend/graph.py +838 -0
  68. indexing/frontend/html_extractor.py +496 -0
  69. indexing/frontend/html_parser.py +314 -0
  70. indexing/frontend/jsx_extractor.py +1204 -0
  71. indexing/frontend/location_lookup.py +247 -0
  72. indexing/frontend/resolver.py +485 -0
  73. indexing/frontend/runtime_resolver.py +862 -0
  74. indexing/frontend/semantic_output.py +705 -0
  75. indexing/frontend/source_location.py +69 -0
  76. indexing/frontend_config.py +72 -0
  77. indexing/frontend_models.py +347 -0
  78. indexing/language_config.py +360 -0
  79. indexing/models.py +284 -0
  80. indexing/node_utils.py +1112 -0
  81. indexing/parse_worker.py +1082 -0
  82. indexing/queries.py +1542 -0
  83. indexing/sdk_examples.py +426 -0
  84. interactive.py +248 -0
  85. raggie.py +673 -0
  86. raggiecode-0.2.1.dist-info/METADATA +944 -0
  87. raggiecode-0.2.1.dist-info/RECORD +93 -0
  88. raggiecode-0.2.1.dist-info/WHEEL +5 -0
  89. raggiecode-0.2.1.dist-info/entry_points.txt +2 -0
  90. raggiecode-0.2.1.dist-info/top_level.txt +10 -0
  91. skills/__init__.py +3 -0
  92. skills/manager.py +114 -0
  93. 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
+ }