x-ipe 1.0.24__py3-none-any.whl → 1.0.25__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 (139) hide show
  1. x_ipe/app.py +25 -3
  2. x_ipe/handlers/terminal_handlers.py +6 -0
  3. x_ipe/handlers/voice_handlers.py +5 -0
  4. x_ipe/resources/copilot-instructions.md +19 -6
  5. x_ipe/resources/skills/lesson-learned/SKILL.md +208 -0
  6. x_ipe/resources/skills/lesson-learned/references/examples.md +238 -0
  7. x_ipe/resources/skills/project-quality-board-management/SKILL.md +135 -298
  8. x_ipe/resources/skills/project-quality-board-management/references/evaluation-principles.md +213 -0
  9. x_ipe/resources/skills/project-quality-board-management/references/evaluation-procedures.md +214 -0
  10. x_ipe/resources/skills/project-quality-board-management/templates/quality-report.md +70 -18
  11. x_ipe/resources/skills/task-execution-guideline/SKILL.md +2 -2
  12. x_ipe/resources/skills/task-execution-guideline/templates/task-record.yaml +1 -1
  13. x_ipe/resources/skills/task-type-code-implementation/SKILL.md +72 -270
  14. x_ipe/resources/skills/task-type-code-implementation/references/implementation-guidelines.md +432 -0
  15. x_ipe/resources/skills/task-type-code-refactor-v2/SKILL.md +127 -353
  16. x_ipe/resources/skills/task-type-code-refactor-v2/references/refactoring-techniques.md +373 -0
  17. x_ipe/resources/skills/task-type-feature-breakdown/SKILL.md +31 -243
  18. x_ipe/resources/skills/task-type-feature-breakdown/references/breakdown-guidelines.md +330 -0
  19. x_ipe/resources/skills/task-type-feature-refinement/SKILL.md +27 -180
  20. x_ipe/resources/skills/task-type-feature-refinement/references/specification-writing-guide.md +267 -0
  21. x_ipe/resources/skills/task-type-idea-mockup/SKILL.md +38 -276
  22. x_ipe/resources/skills/task-type-idea-mockup/references/mockup-guidelines.md +299 -0
  23. x_ipe/resources/skills/task-type-idea-to-architecture/SKILL.md +20 -218
  24. x_ipe/resources/skills/task-type-idea-to-architecture/references/architecture-patterns.md +342 -0
  25. x_ipe/resources/skills/task-type-ideation/SKILL.md +10 -266
  26. x_ipe/resources/skills/task-type-ideation/references/folder-naming-guide.md +55 -0
  27. x_ipe/resources/skills/task-type-ideation/references/tool-usage-guide.md +236 -0
  28. x_ipe/resources/skills/task-type-ideation-v2/SKILL.md +488 -0
  29. x_ipe/resources/skills/task-type-ideation-v2/references/examples.md +377 -0
  30. x_ipe/resources/skills/task-type-ideation-v2/references/folder-naming-guide.md +74 -0
  31. x_ipe/resources/skills/task-type-ideation-v2/references/tool-usage-guide.md +145 -0
  32. x_ipe/resources/skills/task-type-ideation-v2/references/visualization-guide.md +160 -0
  33. x_ipe/resources/skills/task-type-ideation-v2/templates/idea-summary.md +86 -0
  34. x_ipe/resources/skills/task-type-refactoring-analysis/SKILL.md +83 -145
  35. x_ipe/resources/skills/task-type-refactoring-analysis/references/output-schema.md +172 -0
  36. x_ipe/resources/skills/task-type-technical-design/SKILL.md +28 -214
  37. x_ipe/resources/skills/task-type-technical-design/references/design-templates.md +422 -0
  38. x_ipe/resources/skills/task-type-test-generation/SKILL.md +47 -332
  39. x_ipe/resources/skills/task-type-test-generation/references/test-patterns.md +368 -0
  40. x_ipe/resources/skills/tool-tracing-creator/SKILL.md +312 -0
  41. x_ipe/resources/skills/tool-tracing-creator/references/examples.md +324 -0
  42. x_ipe/resources/skills/tool-tracing-instrumentation/SKILL.md +373 -0
  43. x_ipe/resources/skills/tool-tracing-instrumentation/references/examples.md +264 -0
  44. x_ipe/resources/skills/x-ipe-skill-creator-v3/SKILL.md +486 -0
  45. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/10. example-gate-conditions.md +73 -0
  46. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/11. reference-quality-standards.md +127 -0
  47. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/2. reference-section-order.md +127 -0
  48. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/3. example-step-based-code-review.md +84 -0
  49. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/4. example-step-based-feature-implementation.md +113 -0
  50. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/5. example-function-based-validation.md +73 -0
  51. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/6. example-function-based-analysis.md +94 -0
  52. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/7. example-task-io-code-implementation.md +36 -0
  53. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/8. example-structured-summary.md +43 -0
  54. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/9. example-dor-dod.md +77 -0
  55. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/examples.md +429 -0
  56. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/skill-general-guidelines-v2.md +611 -0
  57. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-meta.md +153 -0
  58. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-based.md +324 -0
  59. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-category.md +109 -0
  60. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-tool.md +205 -0
  61. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-meta.md +334 -0
  62. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-task-based.md +279 -0
  63. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-tool.md +175 -0
  64. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-workflow-orchestration.md +329 -0
  65. x_ipe/resources/skills/x-ipe-task-based-ideation/SKILL.md +487 -0
  66. x_ipe/resources/skills/x-ipe-task-based-ideation/references/examples.md +377 -0
  67. x_ipe/resources/skills/x-ipe-task-based-ideation/references/folder-naming-guide.md +74 -0
  68. x_ipe/resources/skills/x-ipe-task-based-ideation/references/tool-usage-guide.md +145 -0
  69. x_ipe/resources/skills/x-ipe-task-based-ideation/references/visualization-guide.md +160 -0
  70. x_ipe/resources/skills/x-ipe-task-based-ideation/templates/idea-summary.md +86 -0
  71. x_ipe/routes/__init__.py +2 -0
  72. x_ipe/routes/ideas_routes.py +17 -0
  73. x_ipe/routes/kb_routes.py +80 -0
  74. x_ipe/routes/main_routes.py +18 -0
  75. x_ipe/routes/project_routes.py +7 -0
  76. x_ipe/routes/proxy_routes.py +2 -0
  77. x_ipe/routes/quality_evaluation_routes.py +193 -0
  78. x_ipe/routes/settings_routes.py +6 -0
  79. x_ipe/routes/tools_routes.py +6 -0
  80. x_ipe/routes/tracing_routes.py +232 -0
  81. x_ipe/routes/uiux_feedback_routes.py +30 -0
  82. x_ipe/services/__init__.py +5 -0
  83. x_ipe/services/config_service.py +6 -0
  84. x_ipe/services/file_service.py +20 -0
  85. x_ipe/services/homepage_service.py +160 -0
  86. x_ipe/services/ideas_service.py +19 -0
  87. x_ipe/services/kb_service.py +378 -0
  88. x_ipe/services/proxy_service.py +4 -0
  89. x_ipe/services/settings_service.py +13 -0
  90. x_ipe/services/skills_service.py +4 -0
  91. x_ipe/services/terminal_service.py +24 -0
  92. x_ipe/services/themes_service.py +4 -0
  93. x_ipe/services/tools_config_service.py +4 -0
  94. x_ipe/services/tracing_service.py +333 -0
  95. x_ipe/services/uiux_feedback_service.py +32 -0
  96. x_ipe/services/voice_input_service_v2.py +11 -0
  97. x_ipe/static/css/base.css +7 -0
  98. x_ipe/static/css/homepage-infinity.css +330 -0
  99. x_ipe/static/css/kb-core.css +301 -0
  100. x_ipe/static/css/quality-evaluation.css +345 -0
  101. x_ipe/static/css/sidebar.css +14 -4
  102. x_ipe/static/css/terminal.css +1 -0
  103. x_ipe/static/css/tracing-dashboard.css +796 -0
  104. x_ipe/static/css/workplace.css +20 -0
  105. x_ipe/static/img/homepage-infinity-loop.png +0 -0
  106. x_ipe/static/js/features/homepage-infinity.js +314 -0
  107. x_ipe/static/js/features/kb-core.js +371 -0
  108. x_ipe/static/js/features/quality-evaluation.js +387 -0
  109. x_ipe/static/js/features/sidebar.js +255 -12
  110. x_ipe/static/js/features/tracing-dashboard.js +855 -0
  111. x_ipe/static/js/features/tracing-graph.js +1031 -0
  112. x_ipe/static/js/features/tree-search.js +6 -2
  113. x_ipe/static/js/features/workplace.js +200 -6
  114. x_ipe/static/js/init.js +76 -0
  115. x_ipe/static/js/uiux-feedback.js +18 -2
  116. x_ipe/templates/base.html +19 -0
  117. x_ipe/templates/index.html +7 -1
  118. x_ipe/templates/knowledge-base.html +110 -0
  119. x_ipe/templates/workplace.html +4 -0
  120. x_ipe/tracing/__init__.py +37 -0
  121. x_ipe/tracing/buffer.py +135 -0
  122. x_ipe/tracing/context.py +125 -0
  123. x_ipe/tracing/decorator.py +288 -0
  124. x_ipe/tracing/middleware.py +197 -0
  125. x_ipe/tracing/parser.py +235 -0
  126. x_ipe/tracing/redactor.py +111 -0
  127. x_ipe/tracing/writer.py +122 -0
  128. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/METADATA +2 -2
  129. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/RECORD +132 -62
  130. x_ipe/resources/skills/x-ipe-skill-creator/SKILL.md +0 -329
  131. x_ipe/resources/skills/x-ipe-skill-creator/references/output-patterns.md +0 -169
  132. x_ipe/resources/skills/x-ipe-skill-creator/references/skill-structure.md +0 -162
  133. x_ipe/resources/skills/x-ipe-skill-creator/references/workflows.md +0 -110
  134. x_ipe/resources/skills/x-ipe-skill-creator/templates/references/examples.md +0 -113
  135. x_ipe/resources/skills/x-ipe-skill-creator/templates/skill-category-skill.md +0 -296
  136. x_ipe/resources/skills/x-ipe-skill-creator/templates/task-type-skill.md +0 -269
  137. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/WHEEL +0 -0
  138. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/entry_points.txt +0 -0
  139. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/licenses/LICENSE +0 -0
@@ -11,6 +11,8 @@ import copy
11
11
  from pathlib import Path
12
12
  from typing import Dict, Any
13
13
 
14
+ from x_ipe.tracing import x_ipe_tracing
15
+
14
16
 
15
17
  CONFIG_DIR = 'x-ipe-docs/config'
16
18
  CONFIG_FILE = 'tools.json'
@@ -56,6 +58,7 @@ class ToolsConfigService:
56
58
  self.config_path = self.config_dir / CONFIG_FILE
57
59
  self.legacy_path = self.project_root / LEGACY_PATH
58
60
 
61
+ @x_ipe_tracing()
59
62
  def load(self) -> Dict[str, Any]:
60
63
  """
61
64
  Load config, migrating from legacy if needed.
@@ -76,6 +79,7 @@ class ToolsConfigService:
76
79
 
77
80
  return self._create_default()
78
81
 
82
+ @x_ipe_tracing()
79
83
  def save(self, config: Dict[str, Any]) -> bool:
80
84
  """
81
85
  Save config to file.
@@ -0,0 +1,333 @@
1
+ """
2
+ FEATURE-023: Application Action Tracing - Core
3
+
4
+ TracingService for managing tracing configuration and lifecycle.
5
+
6
+ Provides high-level API for starting/stopping tracing, reading
7
+ configuration from tools.json, and cleaning up old log files.
8
+ """
9
+ from datetime import datetime, timedelta, timezone
10
+ from typing import Dict, List, Any, Optional
11
+ from pathlib import Path
12
+
13
+ from x_ipe.services.tools_config_service import ToolsConfigService
14
+ from x_ipe.tracing.writer import TraceLogWriter
15
+ from x_ipe.tracing.parser import TraceLogParser
16
+ from x_ipe.tracing import x_ipe_tracing
17
+
18
+
19
+ class TracingService:
20
+ """
21
+ Service for managing tracing configuration and lifecycle.
22
+
23
+ Integrates with tools.json for configuration persistence and
24
+ provides methods for controlling tracing state.
25
+
26
+ Usage:
27
+ service = TracingService("/path/to/project")
28
+
29
+ # Check status
30
+ config = service.get_config()
31
+ is_active = service.is_active()
32
+
33
+ # Control tracing
34
+ service.start(duration_minutes=15)
35
+ service.stop()
36
+
37
+ # List/cleanup logs
38
+ logs = service.list_logs()
39
+ deleted = service.cleanup_on_startup()
40
+ """
41
+
42
+ def __init__(self, project_root: str):
43
+ """
44
+ Initialize TracingService.
45
+
46
+ Args:
47
+ project_root: Path to the project root directory
48
+ """
49
+ self.project_root = Path(project_root)
50
+ self.tools_config = ToolsConfigService(str(project_root))
51
+
52
+ @x_ipe_tracing()
53
+ def get_config(self) -> Dict[str, Any]:
54
+ """
55
+ Get current tracing configuration.
56
+
57
+ Returns:
58
+ Dictionary with tracing settings:
59
+ - enabled: bool
60
+ - stop_at: str or None (ISO timestamp)
61
+ - log_path: str
62
+ - retention_hours: int
63
+ - ignored_apis: list
64
+ """
65
+ config = self.tools_config.load()
66
+ return {
67
+ "enabled": config.get("tracing_enabled", False),
68
+ "stop_at": config.get("tracing_stop_at"),
69
+ "log_path": config.get("tracing_log_path", "instance/traces/"),
70
+ "retention_hours": config.get("tracing_retention_hours", 24),
71
+ "ignored_apis": config.get("tracing_ignored_apis", [])
72
+ }
73
+
74
+ @x_ipe_tracing()
75
+ def is_active(self) -> bool:
76
+ """
77
+ Check if tracing is currently active.
78
+
79
+ Tracing is active if:
80
+ - tracing_enabled is True, OR
81
+ - tracing_stop_at is set and in the future
82
+
83
+ Returns:
84
+ True if tracing should be performed
85
+ """
86
+ config = self.get_config()
87
+
88
+ if config["enabled"]:
89
+ return True
90
+
91
+ stop_at = config["stop_at"]
92
+ if stop_at:
93
+ try:
94
+ # Parse ISO timestamp - keep it timezone-aware
95
+ stop_time = datetime.fromisoformat(
96
+ stop_at.replace("Z", "+00:00")
97
+ )
98
+ return datetime.now(timezone.utc) < stop_time
99
+ except (ValueError, AttributeError):
100
+ return False
101
+
102
+ return False
103
+
104
+ @x_ipe_tracing()
105
+ def start(self, duration_minutes: int) -> Dict[str, Any]:
106
+ """
107
+ Start tracing for specified duration.
108
+
109
+ Args:
110
+ duration_minutes: Duration in minutes (must be 3, 15, or 30)
111
+
112
+ Returns:
113
+ Dictionary with success status and stop_at timestamp
114
+
115
+ Raises:
116
+ ValueError: If duration is not 3, 15, or 30
117
+ """
118
+ if duration_minutes not in [3, 15, 30]:
119
+ raise ValueError("Duration must be 3, 15, or 30 minutes")
120
+
121
+ stop_at = datetime.now(timezone.utc) + timedelta(minutes=duration_minutes)
122
+ # Convert +00:00 to Z for consistent format
123
+ stop_at_str = stop_at.isoformat().replace("+00:00", "Z")
124
+
125
+ config = self.tools_config.load()
126
+ config["tracing_stop_at"] = stop_at_str
127
+ self.tools_config.save(config)
128
+
129
+ return {"success": True, "stop_at": stop_at_str}
130
+
131
+ @x_ipe_tracing()
132
+ def stop(self) -> Dict[str, Any]:
133
+ """
134
+ Stop tracing immediately.
135
+
136
+ Clears tracing_stop_at and sets tracing_enabled to False.
137
+
138
+ Returns:
139
+ Dictionary with success status
140
+ """
141
+ config = self.tools_config.load()
142
+ config["tracing_stop_at"] = None
143
+ config["tracing_enabled"] = False
144
+ self.tools_config.save(config)
145
+
146
+ return {"success": True}
147
+
148
+ @x_ipe_tracing()
149
+ def update_ignored_apis(self, patterns: List[str]) -> None:
150
+ """
151
+ Update the list of ignored API patterns.
152
+
153
+ Args:
154
+ patterns: List of API path patterns to ignore
155
+ """
156
+ config = self.tools_config.load()
157
+ config["tracing_ignored_apis"] = patterns
158
+ self.tools_config.save(config)
159
+
160
+ @x_ipe_tracing()
161
+ def list_logs(self) -> List[Dict[str, Any]]:
162
+ """
163
+ List all trace log files.
164
+
165
+ Returns:
166
+ List of log file metadata dictionaries:
167
+ - trace_id: str
168
+ - api: str (e.g., "GET /api/project/structure")
169
+ - filename: str
170
+ - size: int (bytes)
171
+ - timestamp: str (ISO format)
172
+ """
173
+ config = self.get_config()
174
+ log_path = self.project_root / config["log_path"]
175
+
176
+ if not log_path.exists():
177
+ return []
178
+
179
+ logs = []
180
+ for filepath in sorted(log_path.glob("*.log"), reverse=True):
181
+ try:
182
+ # Parse filename: {timestamp}-{api}-{trace_id}.log
183
+ # Example: 20260202-072505-get-api-project-structure-a649c048-3d73.log
184
+ stem = filepath.stem
185
+
186
+ # Extract trace_id (last 2 UUID segments: xxxxxxxx-xxxx)
187
+ # Split and find the UUID pattern at the end
188
+ parts = stem.split("-")
189
+ if len(parts) >= 4:
190
+ # Last 2 parts form the trace_id (e.g., "a649c048-3d73")
191
+ trace_id = f"{parts[-2]}-{parts[-1]}"
192
+ # First 2 parts are timestamp (YYYYMMDD-HHMMSS)
193
+ # Middle parts are the API name
194
+ api_parts = parts[2:-2] # Skip timestamp and trace_id
195
+ api_name = "-".join(api_parts) if api_parts else "unknown"
196
+ # Convert api_name back to path format (e.g., "get-api-project-structure" -> "GET /api/project/structure")
197
+ api = self._filename_to_api(api_name)
198
+ else:
199
+ trace_id = stem
200
+ api = "/unknown"
201
+
202
+ stat = filepath.stat()
203
+ logs.append({
204
+ "trace_id": trace_id,
205
+ "api": api,
206
+ "filename": filepath.name,
207
+ "size": stat.st_size,
208
+ "timestamp": datetime.fromtimestamp(stat.st_mtime).isoformat()
209
+ })
210
+ except OSError:
211
+ continue
212
+
213
+ return logs
214
+
215
+ def _filename_to_api(self, api_name: str) -> str:
216
+ """
217
+ Convert sanitized API filename component back to API format.
218
+
219
+ Args:
220
+ api_name: Sanitized name (e.g., "get-api-project-structure")
221
+
222
+ Returns:
223
+ API string (e.g., "GET /api/project/structure")
224
+ """
225
+ if not api_name or api_name == "unknown":
226
+ return "/unknown"
227
+
228
+ # Split by first hyphen to get method
229
+ parts = api_name.split("-", 1)
230
+ if len(parts) < 2:
231
+ return f"/{api_name}"
232
+
233
+ method = parts[0].upper()
234
+ path_part = parts[1]
235
+
236
+ # Convert hyphens back to slashes for path
237
+ path = "/" + path_part.replace("-", "/")
238
+
239
+ return f"{method} {path}"
240
+
241
+ @x_ipe_tracing()
242
+ def cleanup_on_startup(self) -> int:
243
+ """
244
+ Clean up old log files on backend startup.
245
+
246
+ Uses retention_hours from configuration to determine
247
+ which files to delete.
248
+
249
+ Returns:
250
+ Number of files deleted
251
+ """
252
+ config = self.get_config()
253
+ log_path = self.project_root / config["log_path"]
254
+
255
+ writer = TraceLogWriter(str(log_path))
256
+ deleted = writer.cleanup(config["retention_hours"])
257
+
258
+ if deleted > 0:
259
+ print(f"[TRACING] Cleaned up {deleted} old trace log(s)")
260
+
261
+ return deleted
262
+
263
+ @x_ipe_tracing()
264
+ def delete_all_logs(self) -> int:
265
+ """
266
+ Delete all trace log files.
267
+
268
+ Returns:
269
+ Number of files deleted
270
+ """
271
+ config = self.get_config()
272
+ log_path = self.project_root / config["log_path"]
273
+
274
+ if not log_path.exists():
275
+ return 0
276
+
277
+ deleted = 0
278
+ for filepath in log_path.glob("*.log"):
279
+ try:
280
+ filepath.unlink()
281
+ deleted += 1
282
+ except OSError:
283
+ continue
284
+
285
+ return deleted
286
+
287
+ @x_ipe_tracing()
288
+ def get_trace(self, trace_id: str) -> Optional[Dict[str, Any]]:
289
+ """
290
+ Get parsed trace data for visualization.
291
+
292
+ Searches for a log file matching the trace_id (exact or partial)
293
+ and parses it into visualization-ready structure.
294
+
295
+ Args:
296
+ trace_id: Full or partial trace ID to search for
297
+
298
+ Returns:
299
+ Parsed trace data or None if not found:
300
+ {
301
+ "trace_id": str,
302
+ "api": str,
303
+ "timestamp": str,
304
+ "total_time_ms": int,
305
+ "status": str,
306
+ "nodes": [...],
307
+ "edges": [...]
308
+ }
309
+ """
310
+ config = self.get_config()
311
+ log_path = self.project_root / config["log_path"]
312
+
313
+ if not log_path.exists():
314
+ return None
315
+
316
+ # Search for matching file
317
+ matching_file = None
318
+ for filepath in log_path.glob("*.log"):
319
+ if trace_id in filepath.stem:
320
+ matching_file = filepath
321
+ break
322
+
323
+ if not matching_file:
324
+ return None
325
+
326
+ # Parse the file
327
+ parser = TraceLogParser()
328
+ result = parser.parse(matching_file)
329
+
330
+ # Add filename for reference
331
+ result["filename"] = matching_file.name
332
+
333
+ return result
@@ -9,6 +9,8 @@ import re
9
9
  from pathlib import Path
10
10
  from datetime import datetime, timedelta
11
11
 
12
+ from x_ipe.tracing import x_ipe_tracing
13
+
12
14
 
13
15
  class UiuxFeedbackService:
14
16
  """Service for saving UI/UX feedback to file system"""
@@ -17,6 +19,7 @@ class UiuxFeedbackService:
17
19
  self.project_root = Path(project_root)
18
20
  self.feedback_dir = self.project_root / 'x-ipe-docs' / 'uiux-feedback'
19
21
 
22
+ @x_ipe_tracing()
20
23
  def list_feedback(self, days: int = 2) -> list:
21
24
  """
22
25
  List feedback entries from the last N days.
@@ -99,6 +102,34 @@ class UiuxFeedbackService:
99
102
  except Exception:
100
103
  return None
101
104
 
105
+ @x_ipe_tracing()
106
+ def delete_feedback(self, feedback_id: str) -> dict:
107
+ """
108
+ Delete a feedback folder by ID.
109
+
110
+ Args:
111
+ feedback_id: The feedback folder name/ID
112
+
113
+ Returns:
114
+ dict with success (or error on failure)
115
+ """
116
+ try:
117
+ folder_path = self.feedback_dir / feedback_id
118
+
119
+ if not folder_path.exists():
120
+ return {'success': False, 'error': 'Feedback not found'}
121
+
122
+ if not folder_path.is_dir():
123
+ return {'success': False, 'error': 'Invalid feedback ID'}
124
+
125
+ # Delete the folder
126
+ shutil.rmtree(folder_path)
127
+
128
+ return {'success': True}
129
+ except Exception as e:
130
+ return {'success': False, 'error': str(e)}
131
+
132
+ @x_ipe_tracing()
102
133
  def cleanup_old_feedback(self, days: int = 7) -> int:
103
134
  """
104
135
  Delete feedback folders older than N days.
@@ -130,6 +161,7 @@ class UiuxFeedbackService:
130
161
 
131
162
  return deleted
132
163
 
164
+ @x_ipe_tracing()
133
165
  def save_feedback(self, data: dict) -> dict:
134
166
  """
135
167
  Save feedback entry to file system.
@@ -24,6 +24,8 @@ from dashscope.audio.asr import (
24
24
  TranslationResult,
25
25
  )
26
26
 
27
+ from x_ipe.tracing import x_ipe_tracing
28
+
27
29
 
28
30
  # Constants
29
31
  VOICE_MAX_DURATION = 30 # Maximum recording duration in seconds
@@ -225,6 +227,7 @@ class VoiceInputService:
225
227
  self.sessions: Dict[str, VoiceSession] = {}
226
228
  print(f"[Voice] VoiceInputService initialized")
227
229
 
230
+ @x_ipe_tracing()
228
231
  def create_session(
229
232
  self,
230
233
  socket_sid: str,
@@ -260,10 +263,12 @@ class VoiceInputService:
260
263
  print(f"[Voice] 📦 Session created: {session_id} for socket {socket_sid}")
261
264
  return session_id
262
265
 
266
+ @x_ipe_tracing()
263
267
  def get_session(self, session_id: str) -> Optional[VoiceSession]:
264
268
  """Get session by ID."""
265
269
  return self.sessions.get(session_id)
266
270
 
271
+ @x_ipe_tracing()
267
272
  def start_recognition(self, session_id: str) -> bool:
268
273
  """
269
274
  Start speech recognition for a session.
@@ -333,6 +338,7 @@ class VoiceInputService:
333
338
  session.on_error(str(e))
334
339
  return False
335
340
 
341
+ @x_ipe_tracing()
336
342
  def send_audio(self, session_id: str, audio_data: bytes) -> None:
337
343
  """
338
344
  Send audio data to recognizer.
@@ -366,6 +372,7 @@ class VoiceInputService:
366
372
  else:
367
373
  print(f"[Voice] ⚠️ send_audio: no recognizer for session {session_id}")
368
374
 
375
+ @x_ipe_tracing()
369
376
  def stop_recognition(self, session_id: str) -> str:
370
377
  """
371
378
  Stop recognition and get final result.
@@ -409,6 +416,7 @@ class VoiceInputService:
409
416
  print(f"[Voice] ✅ Recognition stopped, transcription result: '{result}'")
410
417
  return result
411
418
 
419
+ @x_ipe_tracing()
412
420
  def cancel_recognition(self, session_id: str) -> None:
413
421
  """
414
422
  Cancel recognition without getting result.
@@ -434,6 +442,7 @@ class VoiceInputService:
434
442
  session.partial_text = ""
435
443
  session.translation_text = ""
436
444
 
445
+ @x_ipe_tracing()
437
446
  def remove_session(self, session_id: str) -> None:
438
447
  """
439
448
  Remove a session.
@@ -448,6 +457,7 @@ class VoiceInputService:
448
457
  except Exception:
449
458
  pass
450
459
 
460
+ @x_ipe_tracing()
451
461
  def is_session_expired(self, session_id: str) -> bool:
452
462
  """
453
463
  Check if session has exceeded max duration.
@@ -465,6 +475,7 @@ class VoiceInputService:
465
475
  elapsed = (datetime.now() - session.created_at).total_seconds()
466
476
  return elapsed > VOICE_MAX_DURATION
467
477
 
478
+ @x_ipe_tracing()
468
479
  def process_transcription(self, session_id: str, text: str) -> Optional[str]:
469
480
  """
470
481
  Process transcription result.
x_ipe/static/css/base.css CHANGED
@@ -62,6 +62,13 @@ html, body {
62
62
  font-size: 1rem;
63
63
  font-weight: 700;
64
64
  letter-spacing: 0.5px;
65
+ color: inherit;
66
+ text-decoration: none;
67
+ cursor: pointer;
68
+ }
69
+
70
+ .top-menu .brand-name:hover {
71
+ color: #3b82f6;
65
72
  }
66
73
 
67
74
  .top-menu .brand-subtitle {