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
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
HTML parser using tree-sitter.
|
|
4
|
+
Parses HTML source and builds a document tree of elements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import List, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from indexing.language_config import LANGUAGE_CONFIG
|
|
11
|
+
from indexing.node_utils import create_parser
|
|
12
|
+
from indexing.frontend.source_location import SourceLocation, node_to_location, extract_range
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ParsedAttribute:
|
|
17
|
+
name: str
|
|
18
|
+
value: Optional[str]
|
|
19
|
+
location: SourceLocation
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ParsedElement:
|
|
24
|
+
tag_name: str
|
|
25
|
+
element_type: str # 'element', 'script_element', 'style_element'
|
|
26
|
+
location: SourceLocation
|
|
27
|
+
start_tag_location: SourceLocation
|
|
28
|
+
attributes: List[ParsedAttribute] = field(default_factory=list)
|
|
29
|
+
children: List["ParsedElement"] = field(default_factory=list)
|
|
30
|
+
element_id_attr: Optional[str] = None
|
|
31
|
+
static_classes: List[str] = field(default_factory=list)
|
|
32
|
+
attributes_dict: Dict[str, str] = field(default_factory=dict)
|
|
33
|
+
raw_text: Optional[str] = None # for script/style content
|
|
34
|
+
has_end_tag: bool = True
|
|
35
|
+
parent: Optional["ParsedElement"] = None
|
|
36
|
+
depth: int = 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class HTMLDocument:
|
|
41
|
+
elements: List[ParsedElement] = field(default_factory=list)
|
|
42
|
+
scripts: List[ParsedElement] = field(default_factory=list)
|
|
43
|
+
styles: List[ParsedElement] = field(default_factory=list)
|
|
44
|
+
errors: List[Dict] = field(default_factory=list)
|
|
45
|
+
source_bytes: bytes = b""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Tags that are always void (no end tag expected)
|
|
49
|
+
VOID_TAGS = {"area", "base", "br", "col", "embed", "hr", "img", "input",
|
|
50
|
+
"link", "meta", "param", "source", "track", "wbr"}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_html_parser = None
|
|
54
|
+
|
|
55
|
+
def _get_parser():
|
|
56
|
+
"""Get or create the HTML parser (cached)."""
|
|
57
|
+
global _html_parser
|
|
58
|
+
if _html_parser is None:
|
|
59
|
+
lang_module = LANGUAGE_CONFIG.get("html", {}).get("language_module")
|
|
60
|
+
if lang_module is None:
|
|
61
|
+
return None
|
|
62
|
+
_html_parser = create_parser(lang_module)
|
|
63
|
+
return _html_parser
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _extract_attributes(start_tag_node, source_bytes) -> List[ParsedAttribute]:
|
|
67
|
+
"""Extract attributes from a start_tag node."""
|
|
68
|
+
attrs = []
|
|
69
|
+
for i in range(start_tag_node.child_count):
|
|
70
|
+
child = start_tag_node.child(i)
|
|
71
|
+
if child.type == "attribute":
|
|
72
|
+
name = None
|
|
73
|
+
value = None
|
|
74
|
+
for j in range(child.child_count):
|
|
75
|
+
ac = child.child(j)
|
|
76
|
+
if ac.type == "attribute_name":
|
|
77
|
+
name = extract_range(source_bytes, ac.start_byte, ac.end_byte)
|
|
78
|
+
elif ac.type == "quoted_attribute_value":
|
|
79
|
+
raw = extract_range(source_bytes, ac.start_byte, ac.end_byte)
|
|
80
|
+
if len(raw) >= 2 and raw[0] in ('"', "'") and raw[-1] == raw[0]:
|
|
81
|
+
value = raw[1:-1]
|
|
82
|
+
else:
|
|
83
|
+
value = raw
|
|
84
|
+
elif ac.type == "attribute_value":
|
|
85
|
+
value = extract_range(source_bytes, ac.start_byte, ac.end_byte)
|
|
86
|
+
if name:
|
|
87
|
+
attrs.append(ParsedAttribute(
|
|
88
|
+
name=name,
|
|
89
|
+
value=value,
|
|
90
|
+
location=node_to_location(child),
|
|
91
|
+
))
|
|
92
|
+
return attrs
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _parse_element(node, source_bytes, parent=None, depth=0) -> Optional[ParsedElement]:
|
|
96
|
+
"""Parse a tree-sitter element node into a ParsedElement.
|
|
97
|
+
|
|
98
|
+
Iterative implementation using explicit stack to avoid recursion.
|
|
99
|
+
"""
|
|
100
|
+
if node.type not in ("element", "script_element", "style_element"):
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
# Stack entries: (ts_node, parent_elem, depth, children_list)
|
|
104
|
+
# Returns the parsed element for each stack entry.
|
|
105
|
+
root_elem = [None]
|
|
106
|
+
stack = [(node, parent, depth, None)]
|
|
107
|
+
|
|
108
|
+
while stack:
|
|
109
|
+
ts_node, parent_elem, d, children_list = stack.pop()
|
|
110
|
+
|
|
111
|
+
start_tag = None
|
|
112
|
+
end_tag = None
|
|
113
|
+
raw_text = None
|
|
114
|
+
child_ts_nodes = []
|
|
115
|
+
|
|
116
|
+
for i in range(ts_node.child_count):
|
|
117
|
+
child = ts_node.child(i)
|
|
118
|
+
if child.type == "start_tag":
|
|
119
|
+
start_tag = child
|
|
120
|
+
elif child.type == "end_tag":
|
|
121
|
+
end_tag = child
|
|
122
|
+
elif child.type == "raw_text":
|
|
123
|
+
raw_text = extract_range(source_bytes, child.start_byte, child.end_byte)
|
|
124
|
+
elif child.type in ("element", "script_element", "style_element"):
|
|
125
|
+
child_ts_nodes.append(child)
|
|
126
|
+
|
|
127
|
+
if start_tag is None:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
tag_name = ""
|
|
131
|
+
for i in range(start_tag.child_count):
|
|
132
|
+
c = start_tag.child(i)
|
|
133
|
+
if c.type == "tag_name":
|
|
134
|
+
tag_name = extract_range(source_bytes, c.start_byte, c.end_byte)
|
|
135
|
+
break
|
|
136
|
+
|
|
137
|
+
parsed_attrs = _extract_attributes(start_tag, source_bytes)
|
|
138
|
+
attrs_dict = {}
|
|
139
|
+
element_id = None
|
|
140
|
+
classes = []
|
|
141
|
+
for attr in parsed_attrs:
|
|
142
|
+
attrs_dict[attr.name] = attr.value if attr.value is not None else ""
|
|
143
|
+
if attr.name == "id":
|
|
144
|
+
element_id = attr.value
|
|
145
|
+
elif attr.name == "class":
|
|
146
|
+
if attr.value:
|
|
147
|
+
classes = attr.value.split()
|
|
148
|
+
|
|
149
|
+
elem = ParsedElement(
|
|
150
|
+
tag_name=tag_name,
|
|
151
|
+
element_type=ts_node.type,
|
|
152
|
+
location=node_to_location(ts_node),
|
|
153
|
+
start_tag_location=node_to_location(start_tag),
|
|
154
|
+
attributes=parsed_attrs,
|
|
155
|
+
attributes_dict=attrs_dict,
|
|
156
|
+
element_id_attr=element_id,
|
|
157
|
+
static_classes=classes,
|
|
158
|
+
raw_text=raw_text,
|
|
159
|
+
has_end_tag=end_tag is not None,
|
|
160
|
+
parent=parent_elem,
|
|
161
|
+
depth=d,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if children_list is not None:
|
|
165
|
+
# This was a child being processed — add it to parent's children
|
|
166
|
+
children_list.append(elem)
|
|
167
|
+
|
|
168
|
+
if root_elem[0] is None and ts_node == node:
|
|
169
|
+
root_elem[0] = elem
|
|
170
|
+
|
|
171
|
+
# Push child TS nodes onto the stack for processing
|
|
172
|
+
# We need a children list to collect parsed children
|
|
173
|
+
elem_children = []
|
|
174
|
+
elem.children = elem_children # Will be filled as children are processed
|
|
175
|
+
|
|
176
|
+
for child_ts in reversed(child_ts_nodes):
|
|
177
|
+
stack.append((child_ts, elem, d + 1, elem_children))
|
|
178
|
+
|
|
179
|
+
return root_elem[0]
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _collect_errors(node, source_bytes, errors: list):
|
|
183
|
+
"""Collect parse errors from the tree (iterative)."""
|
|
184
|
+
if not node.has_error:
|
|
185
|
+
if node.type == "ERROR":
|
|
186
|
+
text = extract_range(source_bytes, node.start_byte, node.end_byte)[:80]
|
|
187
|
+
errors.append({
|
|
188
|
+
"type": "parse_error",
|
|
189
|
+
"location": node_to_location(node).to_dict(),
|
|
190
|
+
"text": text,
|
|
191
|
+
})
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
stack = [node]
|
|
195
|
+
while stack:
|
|
196
|
+
n = stack.pop()
|
|
197
|
+
if n.type == "ERROR":
|
|
198
|
+
text = extract_range(source_bytes, n.start_byte, n.end_byte)[:80]
|
|
199
|
+
errors.append({
|
|
200
|
+
"type": "parse_error",
|
|
201
|
+
"location": node_to_location(n).to_dict(),
|
|
202
|
+
"text": text,
|
|
203
|
+
})
|
|
204
|
+
if n.has_error:
|
|
205
|
+
for i in range(n.child_count):
|
|
206
|
+
stack.append(n.child(i))
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _collect_from_error(error_node, source_bytes, doc: HTMLDocument):
|
|
210
|
+
"""Iteratively collect elements, scripts, and styles from an ERROR node."""
|
|
211
|
+
stack = [error_node]
|
|
212
|
+
while stack:
|
|
213
|
+
n = stack.pop()
|
|
214
|
+
error_children = []
|
|
215
|
+
for i in range(n.child_count):
|
|
216
|
+
child = n.child(i)
|
|
217
|
+
if child.type in ("element", "script_element", "style_element"):
|
|
218
|
+
elem = _parse_element(child, source_bytes)
|
|
219
|
+
if elem:
|
|
220
|
+
if elem.element_type == "script_element":
|
|
221
|
+
doc.scripts.append(elem)
|
|
222
|
+
elif elem.element_type == "style_element":
|
|
223
|
+
doc.styles.append(elem)
|
|
224
|
+
else:
|
|
225
|
+
doc.elements.append(elem)
|
|
226
|
+
elif child.type == "ERROR":
|
|
227
|
+
error_children.append(child)
|
|
228
|
+
# Push error children in reverse so earlier ones are processed first
|
|
229
|
+
for child in reversed(error_children):
|
|
230
|
+
stack.append(child)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _flatten_elements(elem: ParsedElement, result: list):
|
|
234
|
+
"""Flatten element tree into a list (depth-first, iterative)."""
|
|
235
|
+
stack = [elem]
|
|
236
|
+
while stack:
|
|
237
|
+
e = stack.pop()
|
|
238
|
+
result.append(e)
|
|
239
|
+
# Push children in reverse so leftmost is processed first
|
|
240
|
+
for i in range(len(e.children) - 1, -1, -1):
|
|
241
|
+
stack.append(e.children[i])
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def parse_html(source_bytes: bytes) -> HTMLDocument:
|
|
245
|
+
"""Parse HTML source bytes and return an HTMLDocument.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
source_bytes: Raw HTML file content as bytes.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
HTMLDocument with parsed elements, scripts, styles, and errors.
|
|
252
|
+
"""
|
|
253
|
+
parser = _get_parser()
|
|
254
|
+
if parser is None:
|
|
255
|
+
return HTMLDocument(source_bytes=source_bytes, errors=[{
|
|
256
|
+
"type": "missing_parser",
|
|
257
|
+
"message": "HTML tree-sitter grammar not available",
|
|
258
|
+
}])
|
|
259
|
+
|
|
260
|
+
tree = parser.parse(source_bytes)
|
|
261
|
+
root = tree.root_node
|
|
262
|
+
|
|
263
|
+
doc = HTMLDocument(source_bytes=source_bytes)
|
|
264
|
+
|
|
265
|
+
# Collect parse errors
|
|
266
|
+
if root.has_error:
|
|
267
|
+
_collect_errors(root, source_bytes, doc.errors)
|
|
268
|
+
|
|
269
|
+
# Walk top-level nodes (including ERROR nodes for malformed HTML recovery)
|
|
270
|
+
for i in range(root.child_count):
|
|
271
|
+
child = root.child(i)
|
|
272
|
+
if child.type in ("element", "script_element", "style_element"):
|
|
273
|
+
elem = _parse_element(child, source_bytes)
|
|
274
|
+
if elem:
|
|
275
|
+
if elem.element_type == "script_element":
|
|
276
|
+
doc.scripts.append(elem)
|
|
277
|
+
elif elem.element_type == "style_element":
|
|
278
|
+
doc.styles.append(elem)
|
|
279
|
+
else:
|
|
280
|
+
doc.elements.append(elem)
|
|
281
|
+
elif child.type == "doctype":
|
|
282
|
+
pass # Skip doctype
|
|
283
|
+
elif child.type == "ERROR":
|
|
284
|
+
# Malformed HTML: recurse into ERROR node to find parseable elements
|
|
285
|
+
_collect_from_error(child, source_bytes, doc)
|
|
286
|
+
|
|
287
|
+
# Also collect nested scripts and styles from within elements
|
|
288
|
+
all_elements = []
|
|
289
|
+
for elem in doc.elements:
|
|
290
|
+
_flatten_elements(elem, all_elements)
|
|
291
|
+
|
|
292
|
+
for elem in all_elements:
|
|
293
|
+
for child in elem.children:
|
|
294
|
+
if child.element_type == "script_element" and child not in doc.scripts:
|
|
295
|
+
doc.scripts.append(child)
|
|
296
|
+
elif child.element_type == "style_element" and child not in doc.styles:
|
|
297
|
+
doc.styles.append(child)
|
|
298
|
+
|
|
299
|
+
return doc
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def get_all_elements(doc: HTMLDocument) -> List[ParsedElement]:
|
|
303
|
+
"""Get all elements (including scripts and styles) as a flat list."""
|
|
304
|
+
result = []
|
|
305
|
+
for elem in doc.elements:
|
|
306
|
+
_flatten_elements(elem, result)
|
|
307
|
+
# Scripts and styles that are top-level (not nested in elements)
|
|
308
|
+
for elem in doc.scripts:
|
|
309
|
+
if elem.parent is None:
|
|
310
|
+
result.append(elem)
|
|
311
|
+
for elem in doc.styles:
|
|
312
|
+
if elem.parent is None:
|
|
313
|
+
result.append(elem)
|
|
314
|
+
return result
|