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,705 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Structured semantic output formatters for frontend files (Phase 10).
|
|
4
|
+
|
|
5
|
+
Converts indexed frontend data into compressed, structured text for agent
|
|
6
|
+
consumption. Instead of dumping raw HTML/CSS/JSX source, these formatters
|
|
7
|
+
produce a concise summary of components, markup, styles, events, and bindings.
|
|
8
|
+
|
|
9
|
+
Four public functions:
|
|
10
|
+
- format_html_semantics(conn, file_id) → HTML/JSX/TSX files
|
|
11
|
+
- format_css_semantics(conn, file_id) → CSS files
|
|
12
|
+
- format_component_semantics(conn, component_id) → single component deep-dive
|
|
13
|
+
- format_frontend_overview(conn, file_id) → combined view for any frontend file
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import sqlite3
|
|
18
|
+
from typing import Dict, List, Optional, Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ──────────────────────────────────────────────────────────────
|
|
22
|
+
# Helpers
|
|
23
|
+
# ──────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
def _parse_json(raw: Optional[str]) -> Optional[Any]:
|
|
26
|
+
if not raw:
|
|
27
|
+
return None
|
|
28
|
+
try:
|
|
29
|
+
return json.loads(raw)
|
|
30
|
+
except (json.JSONDecodeError, TypeError):
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_file(conn, file_id: int) -> Optional[Dict[str, Any]]:
|
|
35
|
+
row = conn.execute(
|
|
36
|
+
"SELECT id, path, language, content_hash FROM files WHERE id = ?",
|
|
37
|
+
(file_id,)
|
|
38
|
+
).fetchone()
|
|
39
|
+
return dict(row) if row else None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _get_file_by_path(conn, file_path: str) -> Optional[Dict[str, Any]]:
|
|
43
|
+
normalized = file_path[2:] if file_path.startswith("./") else file_path
|
|
44
|
+
row = conn.execute(
|
|
45
|
+
"SELECT id, path, language, content_hash FROM files WHERE path = ?",
|
|
46
|
+
(normalized,)
|
|
47
|
+
).fetchone()
|
|
48
|
+
if not row:
|
|
49
|
+
import os
|
|
50
|
+
filename = os.path.basename(normalized)
|
|
51
|
+
row = conn.execute(
|
|
52
|
+
"SELECT id, path, language, content_hash FROM files WHERE path LIKE ?",
|
|
53
|
+
(f"%{filename}",)
|
|
54
|
+
).fetchone()
|
|
55
|
+
return dict(row) if row else None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _is_frontend_file(file_info: Dict[str, Any]) -> bool:
|
|
59
|
+
lang = file_info.get("language", "")
|
|
60
|
+
return lang in ("html", "css", "javascript", "tsx", "typescript")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _is_html_like(file_info: Dict[str, Any]) -> bool:
|
|
64
|
+
lang = file_info.get("language", "")
|
|
65
|
+
path = file_info.get("path", "")
|
|
66
|
+
return lang in ("html", "tsx", "javascript") or path.endswith((".html", ".htm", ".tsx", ".jsx"))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _is_css(file_info: Dict[str, Any]) -> bool:
|
|
70
|
+
lang = file_info.get("language", "")
|
|
71
|
+
path = file_info.get("path", "")
|
|
72
|
+
return lang == "css" or path.endswith(".css")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _format_source_range(sr: Optional[Dict]) -> str:
|
|
76
|
+
if not sr:
|
|
77
|
+
return "?"
|
|
78
|
+
return f"L{sr.get('start_line', '?')}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _format_classes(classes: Optional[List[str]]) -> str:
|
|
82
|
+
if not classes:
|
|
83
|
+
return ""
|
|
84
|
+
return "." + ".".join(classes)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _format_element_id(elem_id: Optional[str]) -> str:
|
|
88
|
+
if not elem_id:
|
|
89
|
+
return ""
|
|
90
|
+
return f"#{elem_id}"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _relationship_tag(status: str) -> str:
|
|
94
|
+
"""Tag for distinguishing relationship confidence."""
|
|
95
|
+
if status == "resolved":
|
|
96
|
+
return "exact"
|
|
97
|
+
elif status == "conditional":
|
|
98
|
+
return "conditional"
|
|
99
|
+
elif status == "heuristic":
|
|
100
|
+
return "heuristic"
|
|
101
|
+
elif status == "unresolved":
|
|
102
|
+
return "unresolved"
|
|
103
|
+
return status
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# ──────────────────────────────────────────────────────────────
|
|
107
|
+
# format_html_semantics
|
|
108
|
+
# ──────────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
def format_html_semantics(conn: sqlite3.Connection, file_id: int) -> str:
|
|
111
|
+
"""Structured semantic output for HTML/JSX/TSX files.
|
|
112
|
+
|
|
113
|
+
Returns a concise summary of components, markup elements, events,
|
|
114
|
+
bindings, and style relationships — not raw HTML.
|
|
115
|
+
"""
|
|
116
|
+
file_info = _get_file(conn, file_id)
|
|
117
|
+
if not file_info:
|
|
118
|
+
return f"Error: File not found (id={file_id})"
|
|
119
|
+
|
|
120
|
+
lines = [f'file "{file_info["path"]}" (frontend semantics):']
|
|
121
|
+
|
|
122
|
+
# 1. Components
|
|
123
|
+
components = conn.execute(
|
|
124
|
+
"""SELECT c.id, c.name, c.framework, c.is_exported, c.source_range,
|
|
125
|
+
c.impl_function_id, c.impl_class_id
|
|
126
|
+
FROM frontend_components c
|
|
127
|
+
WHERE c.file_id = ?
|
|
128
|
+
ORDER BY c.id""",
|
|
129
|
+
(file_id,)
|
|
130
|
+
).fetchall()
|
|
131
|
+
|
|
132
|
+
if components:
|
|
133
|
+
lines.append(" components:")
|
|
134
|
+
for comp in components:
|
|
135
|
+
exported = " (exported)" if comp["is_exported"] else ""
|
|
136
|
+
sr = _parse_json(comp["source_range"])
|
|
137
|
+
impl = ""
|
|
138
|
+
if comp["impl_function_id"]:
|
|
139
|
+
impl = f" → function#{comp['impl_function_id']}"
|
|
140
|
+
elif comp["impl_class_id"]:
|
|
141
|
+
impl = f" → class#{comp['impl_class_id']}"
|
|
142
|
+
lines.append(f" - {comp['name']} [{comp['framework']}]{exported} @ {_format_source_range(sr)}{impl}")
|
|
143
|
+
|
|
144
|
+
# Rendered children
|
|
145
|
+
children = conn.execute(
|
|
146
|
+
"""SELECT rr.child_component_id, rr.child_component_name, rr.render_type,
|
|
147
|
+
rr.controlling_expr, rr.child_element_id
|
|
148
|
+
FROM render_relationships rr
|
|
149
|
+
WHERE rr.parent_component_id = ?
|
|
150
|
+
ORDER BY rr.id""",
|
|
151
|
+
(comp["id"],)
|
|
152
|
+
).fetchall()
|
|
153
|
+
if children:
|
|
154
|
+
lines.append(" renders:")
|
|
155
|
+
for ch in children:
|
|
156
|
+
if ch["child_component_id"]:
|
|
157
|
+
tag = "exact"
|
|
158
|
+
cond = f" (conditional: {ch['controlling_expr']})" if ch["controlling_expr"] else ""
|
|
159
|
+
lines.append(f" - component {ch['child_component_name']} [{tag}]{cond}")
|
|
160
|
+
elif ch["child_component_name"]:
|
|
161
|
+
tag = "unresolved"
|
|
162
|
+
lines.append(f" - component {ch['child_component_name']} [{tag}] (unresolved)")
|
|
163
|
+
elif ch["child_element_id"]:
|
|
164
|
+
elem = conn.execute(
|
|
165
|
+
"SELECT tag_name FROM markup_elements WHERE id = ?",
|
|
166
|
+
(ch["child_element_id"],)
|
|
167
|
+
).fetchone()
|
|
168
|
+
tag_name = elem["tag_name"] if elem else "?"
|
|
169
|
+
lines.append(f" - element <{tag_name}> [exact]")
|
|
170
|
+
|
|
171
|
+
# 2. Markup elements (summary)
|
|
172
|
+
elements = conn.execute(
|
|
173
|
+
"""SELECT m.id, m.tag_name, m.element_type, m.element_id_attr,
|
|
174
|
+
m.static_classes, m.is_conditional, m.is_repeated,
|
|
175
|
+
m.source_range, m.component_id
|
|
176
|
+
FROM markup_elements m
|
|
177
|
+
WHERE m.file_id = ?
|
|
178
|
+
ORDER BY m.id""",
|
|
179
|
+
(file_id,)
|
|
180
|
+
).fetchall()
|
|
181
|
+
|
|
182
|
+
if elements:
|
|
183
|
+
lines.append(" markup:")
|
|
184
|
+
for elem in elements:
|
|
185
|
+
etype = elem["element_type"]
|
|
186
|
+
if etype in ("text", "#text"):
|
|
187
|
+
continue # Skip text nodes in summary
|
|
188
|
+
if etype == "expression":
|
|
189
|
+
expr_tag = " (expression)"
|
|
190
|
+
sr = _parse_json(elem["source_range"])
|
|
191
|
+
lines.append(f" - {{expr}}{expr_tag} @ {_format_source_range(sr)}")
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
classes = _parse_json(elem["static_classes"]) or []
|
|
195
|
+
class_str = _format_classes(classes)
|
|
196
|
+
id_str = _format_element_id(elem["element_id_attr"])
|
|
197
|
+
cond = " (conditional)" if elem["is_conditional"] else ""
|
|
198
|
+
rep = " (repeated)" if elem["is_repeated"] else ""
|
|
199
|
+
sr = _parse_json(elem["source_range"])
|
|
200
|
+
comp_ref = f" in {elem['component_id']}" if elem["component_id"] else ""
|
|
201
|
+
lines.append(f" - <{elem['tag_name']}>{id_str}{class_str}{cond}{rep} @ {_format_source_range(sr)}{comp_ref}")
|
|
202
|
+
|
|
203
|
+
# 3. Events
|
|
204
|
+
events = conn.execute(
|
|
205
|
+
"""SELECT e.id, e.element_id, e.event_name, e.handler_type,
|
|
206
|
+
e.handler_expression, e.handler_symbol_id, e.resolution_status
|
|
207
|
+
FROM frontend_events e
|
|
208
|
+
WHERE e.file_id = ?
|
|
209
|
+
ORDER BY e.id""",
|
|
210
|
+
(file_id,)
|
|
211
|
+
).fetchall()
|
|
212
|
+
|
|
213
|
+
if events:
|
|
214
|
+
lines.append(" events:")
|
|
215
|
+
for ev in events:
|
|
216
|
+
elem = conn.execute(
|
|
217
|
+
"SELECT tag_name, element_id_attr FROM markup_elements WHERE id = ?",
|
|
218
|
+
(ev["element_id"],)
|
|
219
|
+
).fetchone()
|
|
220
|
+
elem_desc = f"<{elem['tag_name']}>" if elem else "?"
|
|
221
|
+
if elem and elem["element_id_attr"]:
|
|
222
|
+
elem_desc += f"#{elem['element_id_attr']}"
|
|
223
|
+
handler = ev["handler_expression"] or "?"
|
|
224
|
+
sym = f" → function#{ev['handler_symbol_id']}" if ev["handler_symbol_id"] else ""
|
|
225
|
+
tag = _relationship_tag(ev["resolution_status"])
|
|
226
|
+
lines.append(f" - {ev['event_name']} on {elem_desc} → {handler}{sym} [{tag}]")
|
|
227
|
+
|
|
228
|
+
# 4. Bindings
|
|
229
|
+
bindings = conn.execute(
|
|
230
|
+
"""SELECT b.id, b.element_id, b.binding_type, b.binding_name,
|
|
231
|
+
b.binding_expression, b.resolution_status
|
|
232
|
+
FROM frontend_bindings b
|
|
233
|
+
WHERE b.file_id = ?
|
|
234
|
+
ORDER BY b.id""",
|
|
235
|
+
(file_id,)
|
|
236
|
+
).fetchall()
|
|
237
|
+
|
|
238
|
+
if bindings:
|
|
239
|
+
lines.append(" bindings:")
|
|
240
|
+
for b in bindings:
|
|
241
|
+
elem = conn.execute(
|
|
242
|
+
"SELECT tag_name, element_id_attr FROM markup_elements WHERE id = ?",
|
|
243
|
+
(b["element_id"],)
|
|
244
|
+
).fetchone()
|
|
245
|
+
elem_desc = f"<{elem['tag_name']}>" if elem else "?"
|
|
246
|
+
if elem and elem["element_id_attr"]:
|
|
247
|
+
elem_desc += f"#{elem['element_id_attr']}"
|
|
248
|
+
tag = _relationship_tag(b["resolution_status"])
|
|
249
|
+
lines.append(f" - {b['binding_type']}:{b['binding_name']} on {elem_desc} = {b['binding_expression']} [{tag}]")
|
|
250
|
+
|
|
251
|
+
# 5. Style relationships (selectors defined in this file)
|
|
252
|
+
selectors = conn.execute(
|
|
253
|
+
"""SELECT s.id, s.selector_text, s.selector_type, s.is_scoped
|
|
254
|
+
FROM style_selectors s
|
|
255
|
+
WHERE s.file_id = ?
|
|
256
|
+
ORDER BY s.id""",
|
|
257
|
+
(file_id,)
|
|
258
|
+
).fetchall()
|
|
259
|
+
|
|
260
|
+
if selectors:
|
|
261
|
+
lines.append(" styles defined:")
|
|
262
|
+
for sel in selectors:
|
|
263
|
+
scoped = " (scoped)" if sel["is_scoped"] else ""
|
|
264
|
+
lines.append(f" - {sel['selector_text']} [{sel['selector_type']}]{scoped}")
|
|
265
|
+
|
|
266
|
+
# 6. Stylesheet imports
|
|
267
|
+
imports = conn.execute(
|
|
268
|
+
"""SELECT si.import_path, si.is_external, si.resolved_file_id
|
|
269
|
+
FROM style_imports si
|
|
270
|
+
WHERE si.file_id = ?
|
|
271
|
+
ORDER BY si.id""",
|
|
272
|
+
(file_id,)
|
|
273
|
+
).fetchall()
|
|
274
|
+
|
|
275
|
+
if imports:
|
|
276
|
+
lines.append(" imports:")
|
|
277
|
+
for imp in imports:
|
|
278
|
+
if imp["is_external"]:
|
|
279
|
+
tag = "external"
|
|
280
|
+
elif imp["resolved_file_id"]:
|
|
281
|
+
tag = "resolved"
|
|
282
|
+
else:
|
|
283
|
+
tag = "unresolved"
|
|
284
|
+
lines.append(f" - {imp['import_path']} [{tag}]")
|
|
285
|
+
|
|
286
|
+
# 7. Embedded scripts
|
|
287
|
+
scripts = conn.execute(
|
|
288
|
+
"""SELECT DISTINCT f2.id, f2.path
|
|
289
|
+
FROM files f2
|
|
290
|
+
WHERE f2.path LIKE '%script%' AND f2.id != ?
|
|
291
|
+
LIMIT 10""",
|
|
292
|
+
(file_id,)
|
|
293
|
+
).fetchall()
|
|
294
|
+
|
|
295
|
+
# Check for inline scripts via diagnostics or other markers
|
|
296
|
+
# This is a simplified approach — in practice inline scripts are parsed
|
|
297
|
+
# as part of the HTML file's functions
|
|
298
|
+
funcs = conn.execute(
|
|
299
|
+
"SELECT id, name, type FROM functions WHERE file_id = ? ORDER BY id",
|
|
300
|
+
(file_id,)
|
|
301
|
+
).fetchall()
|
|
302
|
+
if funcs:
|
|
303
|
+
lines.append(" embedded symbols:")
|
|
304
|
+
for fn in funcs:
|
|
305
|
+
lines.append(f" - {fn['type']} {fn['name']}")
|
|
306
|
+
|
|
307
|
+
if len(lines) == 1:
|
|
308
|
+
lines.append(" (no frontend semantic entities found)")
|
|
309
|
+
|
|
310
|
+
return "\n".join(lines)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# ──────────────────────────────────────────────────────────────
|
|
314
|
+
# format_css_semantics
|
|
315
|
+
# ──────────────────────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
def format_css_semantics(conn: sqlite3.Connection, file_id: int) -> str:
|
|
318
|
+
"""Structured semantic output for CSS files.
|
|
319
|
+
|
|
320
|
+
Returns selectors, custom properties, keyframes, imports, and
|
|
321
|
+
selector usages — not raw CSS.
|
|
322
|
+
"""
|
|
323
|
+
file_info = _get_file(conn, file_id)
|
|
324
|
+
if not file_info:
|
|
325
|
+
return f"Error: File not found (id={file_id})"
|
|
326
|
+
|
|
327
|
+
lines = [f'file "{file_info["path"]}" (CSS semantics):']
|
|
328
|
+
|
|
329
|
+
# 1. Selectors
|
|
330
|
+
selectors = conn.execute(
|
|
331
|
+
"""SELECT s.id, s.selector_text, s.selector_type, s.is_scoped,
|
|
332
|
+
s.component_id, s.source_range
|
|
333
|
+
FROM style_selectors s
|
|
334
|
+
WHERE s.file_id = ?
|
|
335
|
+
ORDER BY s.id""",
|
|
336
|
+
(file_id,)
|
|
337
|
+
).fetchall()
|
|
338
|
+
|
|
339
|
+
if selectors:
|
|
340
|
+
lines.append(" selectors:")
|
|
341
|
+
for sel in selectors:
|
|
342
|
+
scoped = " (scoped)" if sel["is_scoped"] else ""
|
|
343
|
+
comp = f" → component#{sel['component_id']}" if sel["component_id"] else ""
|
|
344
|
+
sr = _parse_json(sel["source_range"])
|
|
345
|
+
lines.append(f" - {sel['selector_text']} [{sel['selector_type']}]{scoped} @ {_format_source_range(sr)}{comp}")
|
|
346
|
+
|
|
347
|
+
# Selector usages (which elements match)
|
|
348
|
+
matches = conn.execute(
|
|
349
|
+
"""SELECT sm.element_id, sm.match_type, sm.confidence,
|
|
350
|
+
m.tag_name, m.element_id_attr, m.static_classes
|
|
351
|
+
FROM style_selector_matches sm
|
|
352
|
+
JOIN markup_elements m ON sm.element_id = m.id
|
|
353
|
+
WHERE sm.selector_id = ?
|
|
354
|
+
ORDER BY sm.id""",
|
|
355
|
+
(sel["id"],)
|
|
356
|
+
).fetchall()
|
|
357
|
+
if matches:
|
|
358
|
+
for m in matches:
|
|
359
|
+
classes = _parse_json(m["static_classes"]) or []
|
|
360
|
+
class_str = _format_classes(classes)
|
|
361
|
+
id_str = _format_element_id(m["element_id_attr"])
|
|
362
|
+
tag = _relationship_tag(m["match_type"])
|
|
363
|
+
conf = ""
|
|
364
|
+
if m["confidence"]:
|
|
365
|
+
try:
|
|
366
|
+
conf = f" (conf={m['confidence']})" if float(m["confidence"]) < 1.0 else ""
|
|
367
|
+
except (ValueError, TypeError):
|
|
368
|
+
pass
|
|
369
|
+
lines.append(f" matches: <{m['tag_name']}>{id_str}{class_str} [{tag}]{conf}")
|
|
370
|
+
|
|
371
|
+
# 2. Custom properties
|
|
372
|
+
custom_props = conn.execute(
|
|
373
|
+
"""SELECT cp.id, cp.name, cp.value, cp.scope_selector, cp.source_range
|
|
374
|
+
FROM style_custom_properties cp
|
|
375
|
+
WHERE cp.file_id = ?
|
|
376
|
+
ORDER BY cp.id""",
|
|
377
|
+
(file_id,)
|
|
378
|
+
).fetchall()
|
|
379
|
+
|
|
380
|
+
if custom_props:
|
|
381
|
+
lines.append(" custom properties:")
|
|
382
|
+
for cp in custom_props:
|
|
383
|
+
scope = f" (scope: {cp['scope_selector']})" if cp["scope_selector"] else ""
|
|
384
|
+
sr = _parse_json(cp["source_range"])
|
|
385
|
+
lines.append(f" - {cp['name']}: {cp['value']}{scope} @ {_format_source_range(sr)}")
|
|
386
|
+
|
|
387
|
+
# 3. Custom property usages
|
|
388
|
+
prop_usages = conn.execute(
|
|
389
|
+
"""SELECT cpu.id, cpu.property_name, cpu.resolved_property_id, cpu.selector_id
|
|
390
|
+
FROM style_custom_property_usages cpu
|
|
391
|
+
WHERE cpu.file_id = ?
|
|
392
|
+
ORDER BY cpu.id""",
|
|
393
|
+
(file_id,)
|
|
394
|
+
).fetchall()
|
|
395
|
+
|
|
396
|
+
if prop_usages:
|
|
397
|
+
lines.append(" custom property usages:")
|
|
398
|
+
for pu in prop_usages:
|
|
399
|
+
if pu["resolved_property_id"]:
|
|
400
|
+
tag = "resolved"
|
|
401
|
+
ref = f" → property#{pu['resolved_property_id']}"
|
|
402
|
+
else:
|
|
403
|
+
tag = "unresolved"
|
|
404
|
+
ref = ""
|
|
405
|
+
sel_ref = f" in selector#{pu['selector_id']}" if pu["selector_id"] else ""
|
|
406
|
+
lines.append(f" - var({pu['property_name']}) [{tag}]{ref}{sel_ref}")
|
|
407
|
+
|
|
408
|
+
# 4. Keyframes
|
|
409
|
+
keyframes = conn.execute(
|
|
410
|
+
"""SELECT k.id, k.name, k.source_range
|
|
411
|
+
FROM style_keyframes k
|
|
412
|
+
WHERE k.file_id = ?
|
|
413
|
+
ORDER BY k.id""",
|
|
414
|
+
(file_id,)
|
|
415
|
+
).fetchall()
|
|
416
|
+
|
|
417
|
+
if keyframes:
|
|
418
|
+
lines.append(" keyframes:")
|
|
419
|
+
for kf in keyframes:
|
|
420
|
+
sr = _parse_json(kf["source_range"])
|
|
421
|
+
lines.append(f" - @keyframes {kf['name']} @ {_format_source_range(sr)}")
|
|
422
|
+
|
|
423
|
+
# 5. Imports
|
|
424
|
+
imports = conn.execute(
|
|
425
|
+
"""SELECT si.import_path, si.is_external, si.resolved_file_id
|
|
426
|
+
FROM style_imports si
|
|
427
|
+
WHERE si.file_id = ?
|
|
428
|
+
ORDER BY si.id""",
|
|
429
|
+
(file_id,)
|
|
430
|
+
).fetchall()
|
|
431
|
+
|
|
432
|
+
if imports:
|
|
433
|
+
lines.append(" imports:")
|
|
434
|
+
for imp in imports:
|
|
435
|
+
if imp["is_external"]:
|
|
436
|
+
tag = "external"
|
|
437
|
+
elif imp["resolved_file_id"]:
|
|
438
|
+
tag = "resolved"
|
|
439
|
+
else:
|
|
440
|
+
tag = "unresolved"
|
|
441
|
+
lines.append(f" - @import {imp['import_path']} [{tag}]")
|
|
442
|
+
|
|
443
|
+
# 6. Component associations
|
|
444
|
+
comp_associations = conn.execute(
|
|
445
|
+
"""SELECT DISTINCT s.component_id, c.name
|
|
446
|
+
FROM style_selectors s
|
|
447
|
+
JOIN frontend_components c ON s.component_id = c.id
|
|
448
|
+
WHERE s.file_id = ? AND s.component_id IS NOT NULL
|
|
449
|
+
ORDER BY c.name""",
|
|
450
|
+
(file_id,)
|
|
451
|
+
).fetchall()
|
|
452
|
+
|
|
453
|
+
if comp_associations:
|
|
454
|
+
lines.append(" component associations:")
|
|
455
|
+
for ca in comp_associations:
|
|
456
|
+
lines.append(f" - {ca['name']} (component#{ca['component_id']})")
|
|
457
|
+
|
|
458
|
+
if len(lines) == 1:
|
|
459
|
+
lines.append(" (no CSS semantic entities found)")
|
|
460
|
+
|
|
461
|
+
return "\n".join(lines)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# ──────────────────────────────────────────────────────────────
|
|
465
|
+
# format_component_semantics
|
|
466
|
+
# ──────────────────────────────────────────────────────────────
|
|
467
|
+
|
|
468
|
+
def format_component_semantics(conn: sqlite3.Connection, component_id: int) -> str:
|
|
469
|
+
"""Deep-dive semantic output for a single component.
|
|
470
|
+
|
|
471
|
+
Returns component info, rendered children, markup tree summary,
|
|
472
|
+
events, bindings, styles, and rendering parents.
|
|
473
|
+
"""
|
|
474
|
+
comp = conn.execute(
|
|
475
|
+
"""SELECT c.*, f.path as file_path
|
|
476
|
+
FROM frontend_components c
|
|
477
|
+
JOIN files f ON c.file_id = f.id
|
|
478
|
+
WHERE c.id = ?""",
|
|
479
|
+
(component_id,)
|
|
480
|
+
).fetchone()
|
|
481
|
+
|
|
482
|
+
if not comp:
|
|
483
|
+
return f"Error: Component not found (id={component_id})"
|
|
484
|
+
|
|
485
|
+
sr = _parse_json(comp["source_range"])
|
|
486
|
+
exported = " (exported)" if comp["is_exported"] else ""
|
|
487
|
+
impl = ""
|
|
488
|
+
if comp["impl_function_id"]:
|
|
489
|
+
impl = f" → function#{comp['impl_function_id']}"
|
|
490
|
+
elif comp["impl_class_id"]:
|
|
491
|
+
impl = f" → class#{comp['impl_class_id']}"
|
|
492
|
+
|
|
493
|
+
lines = [
|
|
494
|
+
f'component "{comp["name"]}" [{comp["framework"]}]{exported} @ {comp["file_path"]}:{_format_source_range(sr)}{impl}',
|
|
495
|
+
]
|
|
496
|
+
|
|
497
|
+
# 1. Rendered children
|
|
498
|
+
children = conn.execute(
|
|
499
|
+
"""SELECT rr.child_component_id, rr.child_component_name, rr.render_type,
|
|
500
|
+
rr.controlling_expr, rr.child_element_id
|
|
501
|
+
FROM render_relationships rr
|
|
502
|
+
WHERE rr.parent_component_id = ?
|
|
503
|
+
ORDER BY rr.id""",
|
|
504
|
+
(component_id,)
|
|
505
|
+
).fetchall()
|
|
506
|
+
|
|
507
|
+
if children:
|
|
508
|
+
lines.append(" renders:")
|
|
509
|
+
for ch in children:
|
|
510
|
+
if ch["child_component_id"]:
|
|
511
|
+
tag = "exact"
|
|
512
|
+
cond = f" (conditional: {ch['controlling_expr']})" if ch["controlling_expr"] else ""
|
|
513
|
+
lines.append(f" - component {ch['child_component_name']} [{tag}]{cond}")
|
|
514
|
+
elif ch["child_component_name"]:
|
|
515
|
+
lines.append(f" - component {ch['child_component_name']} [unresolved] (unresolved reference)")
|
|
516
|
+
elif ch["child_element_id"]:
|
|
517
|
+
elem = conn.execute(
|
|
518
|
+
"SELECT tag_name FROM markup_elements WHERE id = ?",
|
|
519
|
+
(ch["child_element_id"],)
|
|
520
|
+
).fetchone()
|
|
521
|
+
tag_name = elem["tag_name"] if elem else "?"
|
|
522
|
+
lines.append(f" - element <{tag_name}> [exact]")
|
|
523
|
+
|
|
524
|
+
# 2. Markup tree summary
|
|
525
|
+
elements = conn.execute(
|
|
526
|
+
"""SELECT m.id, m.tag_name, m.element_type, m.element_id_attr,
|
|
527
|
+
m.static_classes, m.is_conditional, m.is_repeated,
|
|
528
|
+
m.source_range, m.parent_element_id
|
|
529
|
+
FROM markup_elements m
|
|
530
|
+
WHERE m.component_id = ?
|
|
531
|
+
ORDER BY m.id""",
|
|
532
|
+
(component_id,)
|
|
533
|
+
).fetchall()
|
|
534
|
+
|
|
535
|
+
if elements:
|
|
536
|
+
lines.append(" markup tree:")
|
|
537
|
+
# Build a simple indented tree
|
|
538
|
+
by_id = {e["id"]: e for e in elements}
|
|
539
|
+
roots = [e for e in elements if e["parent_element_id"] is None or e["parent_element_id"] not in by_id]
|
|
540
|
+
|
|
541
|
+
def _render_element(elem, depth=1):
|
|
542
|
+
indent = " " * depth
|
|
543
|
+
etype = elem["element_type"]
|
|
544
|
+
if etype in ("text", "#text"):
|
|
545
|
+
return
|
|
546
|
+
if etype == "expression":
|
|
547
|
+
sr = _parse_json(elem["source_range"])
|
|
548
|
+
lines.append(f"{indent}{{expr}} @ {_format_source_range(sr)}")
|
|
549
|
+
return
|
|
550
|
+
|
|
551
|
+
classes = _parse_json(elem["static_classes"]) or []
|
|
552
|
+
class_str = _format_classes(classes)
|
|
553
|
+
id_str = _format_element_id(elem["element_id_attr"])
|
|
554
|
+
cond = " (conditional)" if elem["is_conditional"] else ""
|
|
555
|
+
rep = " (repeated)" if elem["is_repeated"] else ""
|
|
556
|
+
sr = _parse_json(elem["source_range"])
|
|
557
|
+
lines.append(f"{indent}<{elem['tag_name']}>{id_str}{class_str}{cond}{rep} @ {_format_source_range(sr)}")
|
|
558
|
+
|
|
559
|
+
# Children
|
|
560
|
+
children = [e for e in elements if e["parent_element_id"] == elem["id"]]
|
|
561
|
+
for child in children:
|
|
562
|
+
_render_element(child, depth + 1)
|
|
563
|
+
|
|
564
|
+
for root in roots:
|
|
565
|
+
_render_element(root)
|
|
566
|
+
|
|
567
|
+
# 3. Events
|
|
568
|
+
events = conn.execute(
|
|
569
|
+
"""SELECT e.id, e.element_id, e.event_name, e.handler_type,
|
|
570
|
+
e.handler_expression, e.handler_symbol_id, e.resolution_status
|
|
571
|
+
FROM frontend_events e
|
|
572
|
+
WHERE e.element_id IN (
|
|
573
|
+
SELECT id FROM markup_elements WHERE component_id = ?
|
|
574
|
+
)
|
|
575
|
+
ORDER BY e.id""",
|
|
576
|
+
(component_id,)
|
|
577
|
+
).fetchall()
|
|
578
|
+
|
|
579
|
+
if events:
|
|
580
|
+
lines.append(" events:")
|
|
581
|
+
for ev in events:
|
|
582
|
+
elem = conn.execute(
|
|
583
|
+
"SELECT tag_name, element_id_attr FROM markup_elements WHERE id = ?",
|
|
584
|
+
(ev["element_id"],)
|
|
585
|
+
).fetchone()
|
|
586
|
+
elem_desc = f"<{elem['tag_name']}>" if elem else "?"
|
|
587
|
+
if elem and elem["element_id_attr"]:
|
|
588
|
+
elem_desc += f"#{elem['element_id_attr']}"
|
|
589
|
+
handler = ev["handler_expression"] or "?"
|
|
590
|
+
sym = f" → function#{ev['handler_symbol_id']}" if ev["handler_symbol_id"] else ""
|
|
591
|
+
tag = _relationship_tag(ev["resolution_status"])
|
|
592
|
+
lines.append(f" - {ev['event_name']} on {elem_desc} → {handler}{sym} [{tag}]")
|
|
593
|
+
|
|
594
|
+
# 4. Bindings
|
|
595
|
+
bindings = conn.execute(
|
|
596
|
+
"""SELECT b.id, b.element_id, b.binding_type, b.binding_name,
|
|
597
|
+
b.binding_expression, b.resolution_status
|
|
598
|
+
FROM frontend_bindings b
|
|
599
|
+
WHERE b.element_id IN (
|
|
600
|
+
SELECT id FROM markup_elements WHERE component_id = ?
|
|
601
|
+
)
|
|
602
|
+
ORDER BY b.id""",
|
|
603
|
+
(component_id,)
|
|
604
|
+
).fetchall()
|
|
605
|
+
|
|
606
|
+
if bindings:
|
|
607
|
+
lines.append(" bindings:")
|
|
608
|
+
for b in bindings:
|
|
609
|
+
elem = conn.execute(
|
|
610
|
+
"SELECT tag_name, element_id_attr FROM markup_elements WHERE id = ?",
|
|
611
|
+
(b["element_id"],)
|
|
612
|
+
).fetchone()
|
|
613
|
+
elem_desc = f"<{elem['tag_name']}>" if elem else "?"
|
|
614
|
+
if elem and elem["element_id_attr"]:
|
|
615
|
+
elem_desc += f"#{elem['element_id_attr']}"
|
|
616
|
+
tag = _relationship_tag(b["resolution_status"])
|
|
617
|
+
lines.append(f" - {b['binding_type']}:{b['binding_name']} on {elem_desc} = {b['binding_expression']} [{tag}]")
|
|
618
|
+
|
|
619
|
+
# 5. Styles
|
|
620
|
+
selectors = conn.execute(
|
|
621
|
+
"""SELECT s.id, s.selector_text, s.selector_type, s.is_scoped
|
|
622
|
+
FROM style_selectors s
|
|
623
|
+
WHERE s.component_id = ?
|
|
624
|
+
ORDER BY s.id""",
|
|
625
|
+
(component_id,)
|
|
626
|
+
).fetchall()
|
|
627
|
+
|
|
628
|
+
if selectors:
|
|
629
|
+
lines.append(" styles:")
|
|
630
|
+
for sel in selectors:
|
|
631
|
+
scoped = " (scoped)" if sel["is_scoped"] else ""
|
|
632
|
+
lines.append(f" - {sel['selector_text']} [{sel['selector_type']}]{scoped}")
|
|
633
|
+
|
|
634
|
+
# 6. Rendering parents
|
|
635
|
+
parents = conn.execute(
|
|
636
|
+
"""SELECT rr.parent_component_id, c.name
|
|
637
|
+
FROM render_relationships rr
|
|
638
|
+
JOIN frontend_components c ON rr.parent_component_id = c.id
|
|
639
|
+
WHERE rr.child_component_id = ?
|
|
640
|
+
ORDER BY c.name""",
|
|
641
|
+
(component_id,)
|
|
642
|
+
).fetchall()
|
|
643
|
+
|
|
644
|
+
if parents:
|
|
645
|
+
lines.append(" rendered by:")
|
|
646
|
+
for p in parents:
|
|
647
|
+
lines.append(f" - {p['name']} (component#{p['parent_component_id']})")
|
|
648
|
+
|
|
649
|
+
if len(lines) == 1:
|
|
650
|
+
lines.append(" (no semantic data found for this component)")
|
|
651
|
+
|
|
652
|
+
return "\n".join(lines)
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
# ──────────────────────────────────────────────────────────────
|
|
656
|
+
# format_frontend_overview
|
|
657
|
+
# ──────────────────────────────────────────────────────────────
|
|
658
|
+
|
|
659
|
+
def format_frontend_overview(conn: sqlite3.Connection, file_id: int) -> str:
|
|
660
|
+
"""Combined semantic view for any frontend file.
|
|
661
|
+
|
|
662
|
+
Dispatches to format_html_semantics or format_css_semantics based on
|
|
663
|
+
file type, then appends per-component deep-dives.
|
|
664
|
+
"""
|
|
665
|
+
file_info = _get_file(conn, file_id)
|
|
666
|
+
if not file_info:
|
|
667
|
+
return f"Error: File not found (id={file_id})"
|
|
668
|
+
|
|
669
|
+
if _is_css(file_info):
|
|
670
|
+
base = format_css_semantics(conn, file_id)
|
|
671
|
+
else:
|
|
672
|
+
base = format_html_semantics(conn, file_id)
|
|
673
|
+
|
|
674
|
+
# Append component deep-dives
|
|
675
|
+
components = conn.execute(
|
|
676
|
+
"SELECT id FROM frontend_components WHERE file_id = ? ORDER BY id",
|
|
677
|
+
(file_id,)
|
|
678
|
+
).fetchall()
|
|
679
|
+
|
|
680
|
+
if components and len(components) > 1:
|
|
681
|
+
sections = [base, "\n## Component Details"]
|
|
682
|
+
for comp in components:
|
|
683
|
+
sections.append("\n" + format_component_semantics(conn, comp["id"]))
|
|
684
|
+
return "\n".join(sections)
|
|
685
|
+
|
|
686
|
+
return base
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
# ──────────────────────────────────────────────────────────────
|
|
690
|
+
# Dispatch by file path (for integration with explore_code_structure)
|
|
691
|
+
# ──────────────────────────────────────────────────────────────
|
|
692
|
+
|
|
693
|
+
def format_file_semantics(conn: sqlite3.Connection, file_path: str) -> Optional[str]:
|
|
694
|
+
"""Format semantic output for a file by path.
|
|
695
|
+
|
|
696
|
+
Returns None if the file is not a frontend file or not found.
|
|
697
|
+
"""
|
|
698
|
+
file_info = _get_file_by_path(conn, file_path)
|
|
699
|
+
if not file_info:
|
|
700
|
+
return None
|
|
701
|
+
|
|
702
|
+
if not _is_frontend_file(file_info):
|
|
703
|
+
return None
|
|
704
|
+
|
|
705
|
+
return format_frontend_overview(conn, file_info["id"])
|