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
@@ -0,0 +1,378 @@
1
+ """
2
+ FEATURE-025-A: KB Core Infrastructure
3
+
4
+ KBService: Core Knowledge Base operations
5
+ - Folder structure initialization
6
+ - File index management
7
+ - Topic metadata management
8
+ """
9
+ import json
10
+ import re
11
+ from datetime import datetime, timezone
12
+ from pathlib import Path
13
+ from typing import Dict, List, Any, Optional
14
+
15
+ from x_ipe.tracing import x_ipe_tracing
16
+
17
+
18
+ class KBService:
19
+ """
20
+ Service for managing Knowledge Base infrastructure.
21
+
22
+ Provides core operations for the x-ipe-docs/knowledge-base/ directory:
23
+ - initialize_structure(): Create KB folder structure
24
+ - get_index(): Get current file index
25
+ - refresh_index(): Rebuild index from file system
26
+ - get_topics(): List all topics
27
+ - get_topic_metadata(): Get topic metadata
28
+ """
29
+
30
+ KB_PATH = 'x-ipe-docs/knowledge-base'
31
+
32
+ # File type mapping
33
+ FILE_TYPE_MAP = {
34
+ # Documents
35
+ '.pdf': 'pdf',
36
+ '.md': 'markdown',
37
+ '.markdown': 'markdown',
38
+ '.txt': 'text',
39
+ '.docx': 'docx',
40
+ '.xlsx': 'xlsx',
41
+
42
+ # Code
43
+ '.py': 'python',
44
+ '.js': 'javascript',
45
+ '.ts': 'typescript',
46
+ '.java': 'java',
47
+ '.go': 'go',
48
+ '.rs': 'rust',
49
+ '.c': 'c',
50
+ '.cpp': 'cpp',
51
+ '.h': 'header',
52
+ '.html': 'html',
53
+ '.css': 'css',
54
+ '.json': 'json',
55
+ '.yaml': 'yaml',
56
+ '.yml': 'yaml',
57
+
58
+ # Images
59
+ '.png': 'image',
60
+ '.jpg': 'image',
61
+ '.jpeg': 'image',
62
+ '.gif': 'image',
63
+ '.svg': 'image',
64
+ '.webp': 'image',
65
+ }
66
+
67
+ def __init__(self, project_root: str):
68
+ """
69
+ Initialize KBService.
70
+
71
+ Args:
72
+ project_root: Absolute path to the project root directory
73
+ """
74
+ self.project_root = Path(project_root).resolve()
75
+ self.kb_root = self.project_root / self.KB_PATH
76
+ self.index_path = self.kb_root / 'index' / 'file-index.json'
77
+
78
+ @x_ipe_tracing(level="INFO")
79
+ def initialize_structure(self) -> bool:
80
+ """
81
+ Create KB folder structure if it doesn't exist.
82
+
83
+ Creates:
84
+ - landing/: Raw uploads
85
+ - topics/: Organized by topic
86
+ - processed/: AI summaries
87
+ - index/: Search index files
88
+
89
+ Returns:
90
+ True if successful
91
+ """
92
+ try:
93
+ # Create main KB directory and subfolders
94
+ folders = [
95
+ self.kb_root / 'landing',
96
+ self.kb_root / 'topics',
97
+ self.kb_root / 'processed',
98
+ self.kb_root / 'index',
99
+ ]
100
+
101
+ for folder in folders:
102
+ folder.mkdir(parents=True, exist_ok=True)
103
+
104
+ # Create empty index if it doesn't exist
105
+ if not self.index_path.exists():
106
+ empty_index = {
107
+ "version": "1.0",
108
+ "last_updated": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
109
+ "files": []
110
+ }
111
+ self._write_json(self.index_path, empty_index)
112
+
113
+ return True
114
+ except Exception as e:
115
+ # Log error but don't raise
116
+ return False
117
+
118
+ @x_ipe_tracing(level="INFO")
119
+ def get_index(self) -> Dict:
120
+ """
121
+ Get current file index.
122
+
123
+ If KB folder doesn't exist, initializes structure first.
124
+ If index is corrupted, recreates empty index.
125
+
126
+ Returns:
127
+ File index dictionary with version, last_updated, and files list
128
+ """
129
+ # Initialize structure if needed
130
+ if not self.kb_root.exists():
131
+ self.initialize_structure()
132
+
133
+ # Read index
134
+ try:
135
+ if self.index_path.exists():
136
+ with open(self.index_path, 'r', encoding='utf-8') as f:
137
+ return json.load(f)
138
+ except (json.JSONDecodeError, IOError):
139
+ # Corrupted index - recreate
140
+ pass
141
+
142
+ # Return/create empty index
143
+ empty_index = {
144
+ "version": "1.0",
145
+ "last_updated": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
146
+ "files": []
147
+ }
148
+ self._write_json(self.index_path, empty_index)
149
+ return empty_index
150
+
151
+ @x_ipe_tracing(level="INFO")
152
+ def refresh_index(self) -> Dict:
153
+ """
154
+ Rebuild index from file system.
155
+
156
+ Scans landing/, topics/, and processed/ folders.
157
+ Updates topic metadata files.
158
+
159
+ Returns:
160
+ Updated file index dictionary
161
+ """
162
+ # Initialize structure if needed
163
+ if not self.kb_root.exists():
164
+ self.initialize_structure()
165
+
166
+ files = []
167
+
168
+ # Scan landing folder
169
+ landing_path = self.kb_root / 'landing'
170
+ if landing_path.exists():
171
+ files.extend(self._scan_folder(landing_path, topic=None))
172
+
173
+ # Scan topics folder
174
+ topics_path = self.kb_root / 'topics'
175
+ if topics_path.exists():
176
+ for topic_dir in topics_path.iterdir():
177
+ if topic_dir.is_dir() and not topic_dir.name.startswith('.'):
178
+ topic_name = topic_dir.name
179
+ # Scan raw subfolder
180
+ raw_path = topic_dir / 'raw'
181
+ if raw_path.exists():
182
+ files.extend(self._scan_folder(raw_path, topic=topic_name))
183
+ # Update topic metadata
184
+ self._update_topic_metadata(topic_name)
185
+
186
+ # Scan processed folder
187
+ processed_path = self.kb_root / 'processed'
188
+ if processed_path.exists():
189
+ for topic_dir in processed_path.iterdir():
190
+ if topic_dir.is_dir() and not topic_dir.name.startswith('.'):
191
+ topic_name = topic_dir.name
192
+ files.extend(self._scan_folder(topic_dir, topic=topic_name, prefix='processed'))
193
+
194
+ # Build index
195
+ index = {
196
+ "version": "1.0",
197
+ "last_updated": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
198
+ "files": files
199
+ }
200
+
201
+ # Write index
202
+ self._write_json(self.index_path, index)
203
+
204
+ return index
205
+
206
+ @x_ipe_tracing(level="INFO")
207
+ def get_topics(self) -> List[str]:
208
+ """
209
+ Get list of all topic names.
210
+
211
+ Returns:
212
+ List of topic folder names
213
+ """
214
+ topics = []
215
+ topics_path = self.kb_root / 'topics'
216
+
217
+ if topics_path.exists():
218
+ for item in topics_path.iterdir():
219
+ if item.is_dir() and not item.name.startswith('.'):
220
+ topics.append(item.name)
221
+
222
+ return sorted(topics)
223
+
224
+ @x_ipe_tracing(level="INFO")
225
+ def get_topic_metadata(self, topic: str) -> Optional[Dict]:
226
+ """
227
+ Get metadata for a specific topic.
228
+
229
+ Args:
230
+ topic: Topic folder name
231
+
232
+ Returns:
233
+ Topic metadata dictionary or None if not found
234
+ """
235
+ metadata_path = self.kb_root / 'topics' / topic / 'metadata.json'
236
+
237
+ if not metadata_path.exists():
238
+ return None
239
+
240
+ try:
241
+ with open(metadata_path, 'r', encoding='utf-8') as f:
242
+ return json.load(f)
243
+ except (json.JSONDecodeError, IOError):
244
+ return None
245
+
246
+ def _scan_folder(self, folder: Path, topic: Optional[str], prefix: str = None) -> List[Dict]:
247
+ """
248
+ Scan a folder and return file entries.
249
+
250
+ Args:
251
+ folder: Path to folder to scan
252
+ topic: Topic name (None for landing)
253
+ prefix: Path prefix (e.g., 'processed')
254
+
255
+ Returns:
256
+ List of file entry dictionaries
257
+ """
258
+ files = []
259
+
260
+ try:
261
+ for entry in folder.iterdir():
262
+ if entry.name.startswith('.'):
263
+ continue # Skip hidden files
264
+
265
+ if entry.is_file():
266
+ # Build relative path
267
+ if prefix:
268
+ rel_path = f"{prefix}/{topic}/{entry.name}"
269
+ elif topic:
270
+ rel_path = f"topics/{topic}/raw/{entry.name}"
271
+ else:
272
+ rel_path = f"landing/{entry.name}"
273
+
274
+ # Get file stats
275
+ stat = entry.stat()
276
+
277
+ file_entry = {
278
+ "path": rel_path,
279
+ "name": entry.name,
280
+ "type": self._get_file_type(entry.suffix),
281
+ "size": stat.st_size,
282
+ "topic": topic,
283
+ "created_date": datetime.fromtimestamp(
284
+ stat.st_mtime, tz=timezone.utc
285
+ ).isoformat().replace('+00:00', 'Z'),
286
+ "keywords": self._extract_keywords(entry.name)
287
+ }
288
+ files.append(file_entry)
289
+ except PermissionError:
290
+ pass # Skip folders we can't read
291
+
292
+ return files
293
+
294
+ def _get_file_type(self, extension: str) -> str:
295
+ """
296
+ Get file type from extension.
297
+
298
+ Args:
299
+ extension: File extension (e.g., '.pdf')
300
+
301
+ Returns:
302
+ File type string (e.g., 'pdf', 'unknown')
303
+ """
304
+ return self.FILE_TYPE_MAP.get(extension.lower(), 'unknown')
305
+
306
+ def _extract_keywords(self, filename: str) -> List[str]:
307
+ """
308
+ Extract keywords from filename.
309
+
310
+ Splits on hyphens, underscores, spaces, and removes extension.
311
+
312
+ Args:
313
+ filename: Filename to extract keywords from
314
+
315
+ Returns:
316
+ List of lowercase keyword strings
317
+ """
318
+ # Remove extension
319
+ name_without_ext = Path(filename).stem
320
+
321
+ # Split on common separators
322
+ parts = re.split(r'[-_\s]+', name_without_ext)
323
+
324
+ # Filter and lowercase
325
+ keywords = [p.lower() for p in parts if p]
326
+
327
+ return keywords
328
+
329
+ def _update_topic_metadata(self, topic: str) -> None:
330
+ """
331
+ Update or create metadata.json for a topic.
332
+
333
+ Args:
334
+ topic: Topic folder name
335
+ """
336
+ topic_path = self.kb_root / 'topics' / topic
337
+ metadata_path = topic_path / 'metadata.json'
338
+ raw_path = topic_path / 'raw'
339
+
340
+ # Count files in raw folder
341
+ file_count = 0
342
+ if raw_path.exists():
343
+ file_count = len([f for f in raw_path.iterdir()
344
+ if f.is_file() and not f.name.startswith('.')])
345
+
346
+ # Load existing or create new
347
+ if metadata_path.exists():
348
+ try:
349
+ with open(metadata_path, 'r', encoding='utf-8') as f:
350
+ metadata = json.load(f)
351
+ except (json.JSONDecodeError, IOError):
352
+ metadata = {}
353
+ else:
354
+ metadata = {}
355
+
356
+ # Update fields
357
+ metadata.update({
358
+ "name": topic,
359
+ "description": metadata.get("description", ""),
360
+ "file_count": file_count,
361
+ "last_updated": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
362
+ "tags": metadata.get("tags", [])
363
+ })
364
+
365
+ # Write
366
+ self._write_json(metadata_path, metadata)
367
+
368
+ def _write_json(self, path: Path, data: Dict) -> None:
369
+ """
370
+ Write JSON data to file with pretty formatting.
371
+
372
+ Args:
373
+ path: Path to write to
374
+ data: Dictionary to write
375
+ """
376
+ path.parent.mkdir(parents=True, exist_ok=True)
377
+ with open(path, 'w', encoding='utf-8') as f:
378
+ json.dump(data, f, indent=2, ensure_ascii=False)
@@ -12,6 +12,8 @@ from bs4 import BeautifulSoup
12
12
  from dataclasses import dataclass
13
13
  from typing import Tuple
14
14
 
15
+ from x_ipe.tracing import x_ipe_tracing
16
+
15
17
  ALLOWED_HOSTS = {'localhost', '127.0.0.1'}
16
18
  ALLOWED_SCHEMES = {'http', 'https', 'file'}
17
19
  PROXY_TIMEOUT = 10 # seconds
@@ -142,6 +144,7 @@ def _is_binary_content_type(content_type: str) -> bool:
142
144
  class ProxyService:
143
145
  """Service for proxying localhost URLs."""
144
146
 
147
+ @x_ipe_tracing()
145
148
  def validate_url(self, url: str) -> Tuple[bool, str]:
146
149
  """
147
150
  Validate URL is localhost or local file.
@@ -182,6 +185,7 @@ class ProxyService:
182
185
  except Exception as e:
183
186
  return False, f"Invalid URL format: {str(e)}"
184
187
 
188
+ @x_ipe_tracing()
185
189
  def fetch_and_rewrite(self, url: str) -> ProxyResult:
186
190
  """
187
191
  Fetch URL and rewrite asset paths for proxy.
@@ -8,6 +8,8 @@ import os
8
8
  import sqlite3
9
9
  from typing import Dict, List, Optional, Any
10
10
 
11
+ from x_ipe.tracing import x_ipe_tracing
12
+
11
13
 
12
14
  class SettingsService:
13
15
  """
@@ -74,6 +76,7 @@ class SettingsService:
74
76
  finally:
75
77
  conn.close()
76
78
 
79
+ @x_ipe_tracing()
77
80
  def get(self, key: str, default: Any = None) -> Optional[str]:
78
81
  """
79
82
  Get a single setting value.
@@ -94,6 +97,7 @@ class SettingsService:
94
97
  finally:
95
98
  conn.close()
96
99
 
100
+ @x_ipe_tracing()
97
101
  def get_all(self) -> Dict[str, str]:
98
102
  """
99
103
  Get all settings as dictionary.
@@ -109,6 +113,7 @@ class SettingsService:
109
113
  finally:
110
114
  conn.close()
111
115
 
116
+ @x_ipe_tracing()
112
117
  def set(self, key: str, value: str) -> None:
113
118
  """
114
119
  Set a single setting value.
@@ -133,6 +138,7 @@ class SettingsService:
133
138
  finally:
134
139
  conn.close()
135
140
 
141
+ @x_ipe_tracing()
136
142
  def validate_project_root(self, path: str) -> Dict[str, str]:
137
143
  """
138
144
  Validate project root path.
@@ -265,6 +271,7 @@ class ProjectFoldersService:
265
271
  finally:
266
272
  conn.close()
267
273
 
274
+ @x_ipe_tracing()
268
275
  def get_all(self) -> List[Dict]:
269
276
  """
270
277
  Get all project folders.
@@ -280,6 +287,7 @@ class ProjectFoldersService:
280
287
  finally:
281
288
  conn.close()
282
289
 
290
+ @x_ipe_tracing()
283
291
  def get_by_id(self, project_id: int) -> Optional[Dict]:
284
292
  """
285
293
  Get project folder by ID.
@@ -299,6 +307,7 @@ class ProjectFoldersService:
299
307
  finally:
300
308
  conn.close()
301
309
 
310
+ @x_ipe_tracing()
302
311
  def add(self, name: str, path: str) -> Dict:
303
312
  """
304
313
  Add a new project folder.
@@ -333,6 +342,7 @@ class ProjectFoldersService:
333
342
  finally:
334
343
  conn.close()
335
344
 
345
+ @x_ipe_tracing()
336
346
  def update(self, project_id: int, name: str = None, path: str = None) -> Dict:
337
347
  """
338
348
  Update an existing project folder.
@@ -374,6 +384,7 @@ class ProjectFoldersService:
374
384
  finally:
375
385
  conn.close()
376
386
 
387
+ @x_ipe_tracing()
377
388
  def delete(self, project_id: int, active_project_id: int = None) -> Dict:
378
389
  """
379
390
  Delete a project folder.
@@ -437,6 +448,7 @@ class ProjectFoldersService:
437
448
 
438
449
  return errors
439
450
 
451
+ @x_ipe_tracing()
440
452
  def get_active_id(self) -> int:
441
453
  """
442
454
  Get the active project ID from settings.
@@ -453,6 +465,7 @@ class ProjectFoldersService:
453
465
  finally:
454
466
  conn.close()
455
467
 
468
+ @x_ipe_tracing()
456
469
  def set_active(self, project_id: int) -> Dict:
457
470
  """
458
471
  Set the active project ID.
@@ -5,6 +5,8 @@ import yaml
5
5
  from pathlib import Path
6
6
  from typing import Dict, List, Optional
7
7
 
8
+ from x_ipe.tracing import x_ipe_tracing
9
+
8
10
 
9
11
  class SkillsService:
10
12
  """
@@ -16,6 +18,7 @@ class SkillsService:
16
18
 
17
19
  SKILLS_PATH = '.github/skills'
18
20
 
21
+ @x_ipe_tracing()
19
22
  def __init__(self, project_root: str):
20
23
  """
21
24
  Initialize SkillsService.
@@ -26,6 +29,7 @@ class SkillsService:
26
29
  self.project_root = Path(project_root).resolve()
27
30
  self.skills_dir = self.project_root / self.SKILLS_PATH
28
31
 
32
+ @x_ipe_tracing()
29
33
  def get_all(self) -> List[Dict[str, str]]:
30
34
  """
31
35
  Get all skills with their name and description.
@@ -13,6 +13,8 @@ from collections import deque
13
13
  from datetime import datetime
14
14
  from typing import Dict, Optional, Any, Callable
15
15
 
16
+ from x_ipe.tracing import x_ipe_tracing
17
+
16
18
 
17
19
  # Constants for session management
18
20
  BUFFER_MAX_CHARS = 10240 # 10KB limit for output buffer
@@ -30,15 +32,18 @@ class OutputBuffer:
30
32
  def __init__(self, max_chars: int = BUFFER_MAX_CHARS):
31
33
  self._buffer: deque = deque(maxlen=max_chars)
32
34
 
35
+ @x_ipe_tracing()
33
36
  def append(self, data: str) -> None:
34
37
  """Append data character by character to maintain limit."""
35
38
  for char in data:
36
39
  self._buffer.append(char)
37
40
 
41
+ @x_ipe_tracing()
38
42
  def get_contents(self) -> str:
39
43
  """Get all buffered content as string."""
40
44
  return ''.join(self._buffer)
41
45
 
46
+ @x_ipe_tracing()
42
47
  def clear(self) -> None:
43
48
  """Clear the buffer."""
44
49
  self._buffer.clear()
@@ -68,6 +73,7 @@ class PersistentSession:
68
73
  self.created_at = datetime.now()
69
74
  self._lock = threading.Lock()
70
75
 
76
+ @x_ipe_tracing()
71
77
  def start_pty(self, rows: int = 24, cols: int = 80) -> None:
72
78
  """Start the underlying PTY process."""
73
79
  def buffered_emit(data: str) -> None:
@@ -80,6 +86,7 @@ class PersistentSession:
80
86
  self.pty_session = PTYSession(self.session_id, buffered_emit)
81
87
  self.pty_session.start(rows, cols)
82
88
 
89
+ @x_ipe_tracing()
83
90
  def attach(self, socket_sid: str, emit_callback: Callable[[str], None]) -> None:
84
91
  """Attach a WebSocket connection to this session."""
85
92
  with self._lock:
@@ -88,6 +95,7 @@ class PersistentSession:
88
95
  self.state = 'connected'
89
96
  self.disconnect_time = None
90
97
 
98
+ @x_ipe_tracing()
91
99
  def detach(self) -> None:
92
100
  """Detach WebSocket, keeping PTY alive for reconnection."""
93
101
  with self._lock:
@@ -96,20 +104,24 @@ class PersistentSession:
96
104
  self.state = 'disconnected'
97
105
  self.disconnect_time = datetime.now()
98
106
 
107
+ @x_ipe_tracing()
99
108
  def get_buffer(self) -> str:
100
109
  """Get buffered output for replay."""
101
110
  return self.output_buffer.get_contents()
102
111
 
112
+ @x_ipe_tracing()
103
113
  def write(self, data: str) -> None:
104
114
  """Write input to PTY."""
105
115
  if self.pty_session:
106
116
  self.pty_session.write(data)
107
117
 
118
+ @x_ipe_tracing()
108
119
  def resize(self, rows: int, cols: int) -> None:
109
120
  """Resize the PTY."""
110
121
  if self.pty_session:
111
122
  self.pty_session._set_size(rows, cols)
112
123
 
124
+ @x_ipe_tracing()
113
125
  def is_expired(self, timeout_seconds: int = SESSION_TIMEOUT) -> bool:
114
126
  """Check if session has expired (1hr after disconnect)."""
115
127
  if self.state == 'connected':
@@ -119,6 +131,7 @@ class PersistentSession:
119
131
  elapsed = datetime.now() - self.disconnect_time
120
132
  return elapsed.total_seconds() > timeout_seconds
121
133
 
134
+ @x_ipe_tracing()
122
135
  def close(self) -> None:
123
136
  """Close session and cleanup resources."""
124
137
  if self.pty_session:
@@ -139,6 +152,7 @@ class SessionManager:
139
152
  self._cleanup_timer: Optional[threading.Timer] = None
140
153
  self._running = False
141
154
 
155
+ @x_ipe_tracing()
142
156
  def create_session(self, emit_callback: Callable[[str], None],
143
157
  rows: int = 24, cols: int = 80) -> str:
144
158
  """Create new persistent session, returns session_id."""
@@ -152,16 +166,19 @@ class SessionManager:
152
166
 
153
167
  return session_id
154
168
 
169
+ @x_ipe_tracing()
155
170
  def get_session(self, session_id: str) -> Optional[PersistentSession]:
156
171
  """Get session by ID."""
157
172
  with self._lock:
158
173
  return self.sessions.get(session_id)
159
174
 
175
+ @x_ipe_tracing()
160
176
  def has_session(self, session_id: str) -> bool:
161
177
  """Check if session exists."""
162
178
  with self._lock:
163
179
  return session_id in self.sessions
164
180
 
181
+ @x_ipe_tracing()
165
182
  def remove_session(self, session_id: str) -> None:
166
183
  """Remove and close a session."""
167
184
  with self._lock:
@@ -169,6 +186,7 @@ class SessionManager:
169
186
  if session:
170
187
  session.close()
171
188
 
189
+ @x_ipe_tracing()
172
190
  def cleanup_expired(self) -> int:
173
191
  """Remove expired sessions. Returns count removed."""
174
192
  expired_ids = []
@@ -182,11 +200,13 @@ class SessionManager:
182
200
 
183
201
  return len(expired_ids)
184
202
 
203
+ @x_ipe_tracing()
185
204
  def start_cleanup_task(self) -> None:
186
205
  """Start background cleanup task (every 5 minutes)."""
187
206
  self._running = True
188
207
  self._schedule_cleanup()
189
208
 
209
+ @x_ipe_tracing()
190
210
  def stop_cleanup_task(self) -> None:
191
211
  """Stop the cleanup task."""
192
212
  self._running = False
@@ -230,6 +250,7 @@ class PTYSession:
230
250
  self.rows = 24
231
251
  self.cols = 80
232
252
 
253
+ @x_ipe_tracing()
233
254
  def start(self, rows: int = 24, cols: int = 80) -> None:
234
255
  """Spawn PTY with shell and start output reader."""
235
256
  import pty
@@ -310,6 +331,7 @@ class PTYSession:
310
331
  except Exception:
311
332
  pass
312
333
 
334
+ @x_ipe_tracing()
313
335
  def write(self, data: str) -> None:
314
336
  """Write input to PTY."""
315
337
  if self.fd is not None:
@@ -328,6 +350,7 @@ class PTYSession:
328
350
  winsize = struct.pack('HHHH', self.rows, self.cols, 0, 0)
329
351
  fcntl.ioctl(self.fd, termios.TIOCSWINSZ, winsize)
330
352
 
353
+ @x_ipe_tracing()
331
354
  def close(self) -> None:
332
355
  """Terminate PTY session and cleanup."""
333
356
  import signal
@@ -351,6 +374,7 @@ class PTYSession:
351
374
  pass
352
375
  self.pid = None
353
376
 
377
+ @x_ipe_tracing()
354
378
  def isalive(self) -> bool:
355
379
  """Check if the PTY process is still running."""
356
380
  return self._running and self.fd is not None
@@ -10,6 +10,8 @@ import re
10
10
  from pathlib import Path
11
11
  from typing import Dict, List, Any, Optional
12
12
 
13
+ from x_ipe.tracing import x_ipe_tracing
14
+
13
15
 
14
16
  THEMES_DIR = 'x-ipe-docs/themes'
15
17
  THEME_PREFIX = 'theme-'
@@ -45,6 +47,7 @@ class ThemesService:
45
47
  self.project_root = Path(project_root).resolve()
46
48
  self.themes_dir = self.project_root / THEMES_DIR
47
49
 
50
+ @x_ipe_tracing()
48
51
  def list_themes(self) -> List[Dict[str, Any]]:
49
52
  """
50
53
  List all valid themes with metadata.
@@ -92,6 +95,7 @@ class ThemesService:
92
95
 
93
96
  return themes
94
97
 
98
+ @x_ipe_tracing()
95
99
  def get_theme(self, name: str) -> Optional[Dict[str, Any]]:
96
100
  """
97
101
  Get detailed information about a specific theme.