x-ipe 1.0.23__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 (146) hide show
  1. x_ipe/app.py +32 -1
  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 +289 -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 +10 -2
  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 +50 -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 +535 -2
  87. x_ipe/services/kb_service.py +378 -0
  88. x_ipe/services/proxy_service.py +37 -7
  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 +148 -1
  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 +23 -0
  103. x_ipe/static/css/tracing-dashboard.css +796 -0
  104. x_ipe/static/css/uiux-feedback.css +7 -1
  105. x_ipe/static/css/workplace.css +636 -0
  106. x_ipe/static/img/homepage-infinity-loop.png +0 -0
  107. x_ipe/static/js/features/confirm-dialog.js +169 -0
  108. x_ipe/static/js/features/folder-view.js +742 -0
  109. x_ipe/static/js/features/homepage-infinity.js +314 -0
  110. x_ipe/static/js/features/kb-core.js +371 -0
  111. x_ipe/static/js/features/quality-evaluation.js +387 -0
  112. x_ipe/static/js/features/sidebar.js +255 -12
  113. x_ipe/static/js/features/tracing-dashboard.js +855 -0
  114. x_ipe/static/js/features/tracing-graph.js +1031 -0
  115. x_ipe/static/js/features/tree-drag.js +227 -0
  116. x_ipe/static/js/features/tree-search.js +228 -0
  117. x_ipe/static/js/features/workplace.js +661 -33
  118. x_ipe/static/js/init.js +76 -0
  119. x_ipe/static/js/terminal-v2.js +45 -14
  120. x_ipe/static/js/terminal.js +50 -49
  121. x_ipe/static/js/uiux-feedback.js +75 -16
  122. x_ipe/templates/base.html +24 -0
  123. x_ipe/templates/index.html +10 -1
  124. x_ipe/templates/knowledge-base.html +110 -0
  125. x_ipe/templates/workplace.html +4 -0
  126. x_ipe/tracing/__init__.py +37 -0
  127. x_ipe/tracing/buffer.py +135 -0
  128. x_ipe/tracing/context.py +125 -0
  129. x_ipe/tracing/decorator.py +288 -0
  130. x_ipe/tracing/middleware.py +197 -0
  131. x_ipe/tracing/parser.py +235 -0
  132. x_ipe/tracing/redactor.py +111 -0
  133. x_ipe/tracing/writer.py +122 -0
  134. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/METADATA +2 -2
  135. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/RECORD +138 -65
  136. x_ipe/app.py.bak +0 -1333
  137. x_ipe/resources/skills/x-ipe-skill-creator/SKILL.md +0 -329
  138. x_ipe/resources/skills/x-ipe-skill-creator/references/output-patterns.md +0 -169
  139. x_ipe/resources/skills/x-ipe-skill-creator/references/skill-structure.md +0 -162
  140. x_ipe/resources/skills/x-ipe-skill-creator/references/workflows.md +0 -110
  141. x_ipe/resources/skills/x-ipe-skill-creator/templates/references/examples.md +0 -113
  142. x_ipe/resources/skills/x-ipe-skill-creator/templates/skill-category-skill.md +0 -296
  143. x_ipe/resources/skills/x-ipe-skill-creator/templates/task-type-skill.md +0 -269
  144. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/WHEEL +0 -0
  145. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/entry_points.txt +0 -0
  146. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Homepage Infinity Loop - FEATURE-026
3
+ *
4
+ * Interactive visualization of the X-IPE development lifecycle.
5
+ * Displays 8 stages on an infinity loop (∞) with clickable navigation.
6
+ */
7
+
8
+ class HomepageInfinity {
9
+ static sidebar = null;
10
+ static tooltipTimeout = null;
11
+
12
+ static STAGE_MAPPING = {
13
+ ideation: {
14
+ icon: '📋',
15
+ label: 'REQUIREMENT',
16
+ theme: 'control',
17
+ status: 'ready',
18
+ section: 'requirements',
19
+ selector: '[data-section-id="requirements"]',
20
+ position: { left: '7.33%', top: '68.37%' }
21
+ },
22
+ requirement: {
23
+ icon: '💡',
24
+ label: 'IDEATION',
25
+ theme: 'control',
26
+ status: 'ready',
27
+ section: 'ideation',
28
+ selector: '[data-section-id="ideation"]',
29
+ position: { left: '15%', top: '17.6%' }
30
+ },
31
+ implementation: {
32
+ icon: '⚙️',
33
+ label: 'IMPLEMENT',
34
+ theme: 'control',
35
+ status: 'ready',
36
+ section: 'code',
37
+ selector: '[data-section-id="code"]',
38
+ position: { left: '34.44%', top: '82%' }
39
+ },
40
+ deployment: {
41
+ icon: '🚀',
42
+ label: 'DEPLOY',
43
+ theme: 'control',
44
+ status: 'tbd',
45
+ section: 'management',
46
+ selector: null,
47
+ position: { left: '60.33%', top: '26.69%' }
48
+ },
49
+ validation: {
50
+ icon: '✅',
51
+ label: 'VALIDATION',
52
+ theme: 'transparency',
53
+ status: 'ready',
54
+ section: 'quality-evaluation',
55
+ selector: '[data-section-id="quality-evaluation"]',
56
+ position: { left: '87.11%', top: '19.78%' }
57
+ },
58
+ monitoring: {
59
+ icon: '📊',
60
+ label: 'MONITORING',
61
+ theme: 'transparency',
62
+ status: 'ready',
63
+ section: 'tracing',
64
+ selector: '[data-section-id="tracing"]',
65
+ position: { left: '90.67%', top: '73.9%' }
66
+ },
67
+ feedback: {
68
+ icon: '💬',
69
+ label: 'FEEDBACK',
70
+ theme: 'transparency',
71
+ status: 'ready',
72
+ section: 'uiux-feedbacks',
73
+ selector: '[data-section-id="uiux-feedbacks"]',
74
+ position: { left: '63.33%', top: '79.04%' }
75
+ },
76
+ planning: {
77
+ icon: '📅',
78
+ label: 'PLANNING',
79
+ theme: 'transparency',
80
+ status: 'ready',
81
+ section: 'planning',
82
+ selector: '[data-section-id="planning"]',
83
+ position: { left: '41.56%', top: '30.25%' }
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Get HTML template for homepage
89
+ * @returns {string} HTML template
90
+ */
91
+ static getTemplate() {
92
+ const stageButtons = this._renderStageButtons();
93
+
94
+ return `
95
+ <div class="homepage-infinity-container">
96
+ <header class="homepage-header">
97
+ <h1>X-IPE</h1>
98
+ <p>An AI native integrated project environment for end to end business value delivery</p>
99
+ </header>
100
+
101
+ <div class="infinity-loop-container">
102
+ <div class="infinity-loop">
103
+ <img src="/static/img/homepage-infinity-loop.png"
104
+ alt="Development Lifecycle"
105
+ class="infinity-bg"
106
+ onerror="this.style.display='none'">
107
+
108
+ ${stageButtons}
109
+
110
+ <span class="loop-label control">CONTROL</span>
111
+ <span class="loop-label transparency">TRANSPARENCY</span>
112
+ </div>
113
+ </div>
114
+
115
+ <footer class="homepage-legend">
116
+ <span class="legend-control">● Control (What AI Does)</span>
117
+ <span class="legend-transparency">● Transparency (What We See)</span>
118
+ </footer>
119
+
120
+ <div class="homepage-tooltip" id="homepage-tooltip" style="display: none;">
121
+ <span class="tooltip-text"></span>
122
+ </div>
123
+ </div>
124
+ `;
125
+ }
126
+
127
+ /**
128
+ * Render stage buttons HTML
129
+ * @returns {string} Stage buttons HTML
130
+ */
131
+ static _renderStageButtons() {
132
+ const buttons = [];
133
+
134
+ for (const [stageId, config] of Object.entries(this.STAGE_MAPPING)) {
135
+ const tbdClass = config.status === 'tbd' ? ' tbd' : '';
136
+ const tbdAttr = config.status === 'tbd' ? ' data-tbd="true"' : '';
137
+ const tbdBadge = config.status === 'tbd' ? '<span class="tbd-badge">TBD</span>' : '';
138
+
139
+ buttons.push(`
140
+ <button class="stage-btn ${config.theme}${tbdClass}"
141
+ data-stage="${stageId}"
142
+ data-section="${config.section}"
143
+ data-selector="${config.selector || ''}"
144
+ style="left: ${config.position.left}; top: ${config.position.top};"
145
+ ${tbdAttr}>
146
+ <span class="stage-icon">${config.icon}</span>
147
+ <span class="stage-label">${config.label}</span>
148
+ ${tbdBadge}
149
+ </button>
150
+ `);
151
+ }
152
+
153
+ return buttons.join('\n');
154
+ }
155
+
156
+ /**
157
+ * Initialize homepage with sidebar reference
158
+ * @param {ProjectSidebar} sidebar - Sidebar instance for navigation
159
+ */
160
+ static init(sidebar) {
161
+ this.sidebar = sidebar;
162
+ this._bindStageClicks();
163
+ }
164
+
165
+ /**
166
+ * Bind click handlers to stage buttons
167
+ */
168
+ static _bindStageClicks() {
169
+ const buttons = document.querySelectorAll('.stage-btn');
170
+
171
+ buttons.forEach(btn => {
172
+ btn.addEventListener('click', (e) => {
173
+ const stage = e.currentTarget.dataset.stage;
174
+ this.navigateToStage(stage);
175
+ });
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Navigate to a stage's corresponding sidebar item
181
+ * @param {string} stage - Stage name (e.g., 'ideation')
182
+ */
183
+ static navigateToStage(stage) {
184
+ const config = this.STAGE_MAPPING[stage];
185
+
186
+ if (!config) {
187
+ console.warn(`Unknown stage: ${stage}`);
188
+ return;
189
+ }
190
+
191
+ // Handle TBD stages
192
+ if (config.status === 'tbd') {
193
+ this._showTooltip(stage, 'Coming Soon');
194
+ return;
195
+ }
196
+
197
+ // Navigate to sidebar item
198
+ if (config.selector && this.sidebar) {
199
+ // Expand the section first
200
+ this._expandSection(config.section);
201
+
202
+ // Then highlight and scroll to item
203
+ this._highlightItem(config.selector);
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Expand a sidebar section
209
+ * @param {string} sectionId - Section ID to expand
210
+ */
211
+ static _expandSection(sectionId) {
212
+ // Main app uses data-section-id, workplace uses data-section
213
+ const sectionHeader = document.querySelector(`[data-section-id="${sectionId}"] .nav-section-header`) ||
214
+ document.querySelector(`[data-section="${sectionId}"] .section-header`);
215
+ if (sectionHeader) {
216
+ const section = sectionHeader.closest('.nav-section, .sidebar-section');
217
+ // Check if section is collapsed (submenu hidden)
218
+ const submenu = section?.querySelector('.sidebar-submenu');
219
+ if (submenu && submenu.style.display === 'none') {
220
+ sectionHeader.click();
221
+ }
222
+ }
223
+
224
+ // Also try using sidebar API if available
225
+ if (this.sidebar && typeof this.sidebar.expandSection === 'function') {
226
+ this.sidebar.expandSection(sectionId);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Highlight a sidebar item
232
+ * @param {string} selector - CSS selector for target item
233
+ */
234
+ static _highlightItem(selector) {
235
+ // Try multiple selector patterns
236
+ let item = document.querySelector(selector);
237
+
238
+ // If not found, try finding by text content in sidebar
239
+ if (!item && selector.includes('ideas')) {
240
+ item = document.querySelector('[data-section-id="workplace"] .nav-section-header');
241
+ } else if (!item && selector.includes('requirements')) {
242
+ item = document.querySelector('[data-section-id="project"] .nav-section-header');
243
+ } else if (!item && selector.includes('planning')) {
244
+ item = document.querySelector('[data-section-id="management"] .nav-section-header');
245
+ }
246
+
247
+ if (!item) {
248
+ console.warn(`Sidebar item not found: ${selector}`);
249
+ return;
250
+ }
251
+
252
+ // Remove any existing highlights
253
+ document.querySelectorAll('.homepage-highlight').forEach(el => {
254
+ el.classList.remove('homepage-highlight');
255
+ });
256
+
257
+ // Add highlight class
258
+ item.classList.add('homepage-highlight');
259
+
260
+ // Scroll into view
261
+ item.scrollIntoView({ behavior: 'smooth', block: 'center' });
262
+
263
+ // Remove highlight after delay (3 seconds)
264
+ setTimeout(() => {
265
+ item.classList.remove('homepage-highlight');
266
+ }, 3000);
267
+ }
268
+
269
+ /**
270
+ * Show tooltip near a stage button
271
+ * @param {string} stage - Stage name
272
+ * @param {string} message - Tooltip message
273
+ */
274
+ static _showTooltip(stage, message) {
275
+ const tooltip = document.getElementById('homepage-tooltip');
276
+ const button = document.querySelector(`[data-stage="${stage}"]`);
277
+
278
+ if (!tooltip || !button) return;
279
+
280
+ // Set message
281
+ tooltip.querySelector('.tooltip-text').textContent = message;
282
+
283
+ // Position near button
284
+ const rect = button.getBoundingClientRect();
285
+ tooltip.style.left = `${rect.left + rect.width / 2}px`;
286
+ tooltip.style.top = `${rect.bottom + 8}px`;
287
+ tooltip.style.display = 'block';
288
+
289
+ // Clear existing timeout
290
+ if (this.tooltipTimeout) {
291
+ clearTimeout(this.tooltipTimeout);
292
+ }
293
+
294
+ // Hide after 2 seconds
295
+ this.tooltipTimeout = setTimeout(() => {
296
+ this._hideTooltip();
297
+ }, 2000);
298
+ }
299
+
300
+ /**
301
+ * Hide tooltip
302
+ */
303
+ static _hideTooltip() {
304
+ const tooltip = document.getElementById('homepage-tooltip');
305
+ if (tooltip) {
306
+ tooltip.style.display = 'none';
307
+ }
308
+ }
309
+ }
310
+
311
+ // Export for use in workplace.js
312
+ if (typeof window !== 'undefined') {
313
+ window.HomepageInfinity = HomepageInfinity;
314
+ }
@@ -0,0 +1,371 @@
1
+ /**
2
+ * FEATURE-025-A: KB Core Infrastructure
3
+ *
4
+ * Frontend JavaScript for Knowledge Base page.
5
+ * Uses two-column layout (like Ideation view) with tree on left of content area.
6
+ */
7
+
8
+ const kbCore = {
9
+ index: null,
10
+ topics: [],
11
+ searchTerm: '',
12
+ container: null,
13
+
14
+ /**
15
+ * Render the Knowledge Base view in a container (like WorkplaceManager)
16
+ */
17
+ render(container) {
18
+ this.container = container;
19
+ container.innerHTML = `
20
+ <div class="kb-container">
21
+ <div class="kb-sidebar pinned" id="kb-sidebar">
22
+ <div class="kb-sidebar-header">
23
+ <span class="kb-sidebar-title">Knowledge Base</span>
24
+ <button class="kb-refresh-btn" id="btn-refresh-index" title="Refresh index">
25
+ <i class="bi bi-arrow-clockwise"></i>
26
+ </button>
27
+ </div>
28
+ <div class="kb-search-box">
29
+ <input type="text" class="kb-search-input" id="kb-search" placeholder="Search files...">
30
+ </div>
31
+ <div class="kb-tree" id="kb-tree">
32
+ <div class="kb-loading"><i class="bi bi-hourglass-split"></i> Loading...</div>
33
+ </div>
34
+ </div>
35
+ <div class="kb-content" id="kb-content">
36
+ <div class="kb-placeholder">
37
+ <i class="bi bi-archive"></i>
38
+ <h5>Knowledge Base</h5>
39
+ <p class="text-muted">Select a file from the tree to view</p>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ `;
44
+ this.init();
45
+ },
46
+
47
+ /**
48
+ * Initialize the Knowledge Base view.
49
+ * Fetches index and renders tree + content.
50
+ */
51
+ async init() {
52
+ console.log('[KB] Initializing Knowledge Base view');
53
+
54
+ // Bind event handlers
55
+ this.bindEvents();
56
+
57
+ // Load initial data
58
+ await this.loadIndex();
59
+
60
+ // Render UI
61
+ this.renderTree();
62
+ this.renderWelcome();
63
+ },
64
+
65
+ /**
66
+ * Bind DOM event handlers.
67
+ */
68
+ bindEvents() {
69
+ // Refresh button
70
+ const refreshBtn = document.getElementById('btn-refresh-index');
71
+ if (refreshBtn) {
72
+ refreshBtn.addEventListener('click', () => this.refreshIndex());
73
+ }
74
+
75
+ // Search input
76
+ const searchInput = document.getElementById('kb-search');
77
+ if (searchInput) {
78
+ searchInput.addEventListener('input', (e) => {
79
+ this.searchTerm = e.target.value.toLowerCase();
80
+ this.renderTree();
81
+ });
82
+ }
83
+ },
84
+
85
+ /**
86
+ * Load file index from API.
87
+ */
88
+ async loadIndex() {
89
+ try {
90
+ const response = await fetch('/api/kb/index');
91
+ if (response.ok) {
92
+ this.index = await response.json();
93
+ console.log('[KB] Index loaded:', this.index.files?.length || 0, 'files');
94
+ } else {
95
+ console.error('[KB] Failed to load index:', response.status);
96
+ this.index = { version: '1.0', last_updated: null, files: [] };
97
+ }
98
+ } catch (error) {
99
+ console.error('[KB] Error loading index:', error);
100
+ this.index = { version: '1.0', last_updated: null, files: [] };
101
+ }
102
+
103
+ // Also load topics
104
+ try {
105
+ const response = await fetch('/api/kb/topics');
106
+ if (response.ok) {
107
+ const data = await response.json();
108
+ this.topics = data.topics || [];
109
+ }
110
+ } catch (error) {
111
+ console.error('[KB] Error loading topics:', error);
112
+ this.topics = [];
113
+ }
114
+ },
115
+
116
+ /**
117
+ * Refresh index from file system.
118
+ */
119
+ async refreshIndex() {
120
+ const refreshBtn = document.getElementById('btn-refresh-index');
121
+ if (refreshBtn) {
122
+ refreshBtn.disabled = true;
123
+ refreshBtn.innerHTML = '<i class="bi bi-hourglass-split"></i>';
124
+ }
125
+
126
+ try {
127
+ const response = await fetch('/api/kb/index/refresh', { method: 'POST' });
128
+ if (response.ok) {
129
+ this.index = await response.json();
130
+ console.log('[KB] Index refreshed:', this.index.files?.length || 0, 'files');
131
+
132
+ // Reload topics
133
+ await this.loadIndex();
134
+
135
+ // Re-render
136
+ this.renderTree();
137
+ }
138
+ } catch (error) {
139
+ console.error('[KB] Error refreshing index:', error);
140
+ } finally {
141
+ if (refreshBtn) {
142
+ refreshBtn.disabled = false;
143
+ refreshBtn.innerHTML = '<i class="bi bi-arrow-clockwise"></i>';
144
+ }
145
+ }
146
+ },
147
+
148
+ /**
149
+ * Render the tree in kb-sidebar (within content area).
150
+ */
151
+ renderTree() {
152
+ const container = document.getElementById('kb-tree');
153
+ if (!container) return;
154
+
155
+ const files = this.index?.files || [];
156
+
157
+ // Filter by search term
158
+ const filteredFiles = this.searchTerm
159
+ ? files.filter(f =>
160
+ f.name.toLowerCase().includes(this.searchTerm) ||
161
+ f.topic?.toLowerCase().includes(this.searchTerm) ||
162
+ f.keywords?.some(k => k.includes(this.searchTerm))
163
+ )
164
+ : files;
165
+
166
+ // Group by folder structure
167
+ const landing = filteredFiles.filter(f => f.path.startsWith('landing/'));
168
+ const byTopic = {};
169
+
170
+ filteredFiles.forEach(f => {
171
+ if (f.topic && !f.path.startsWith('landing/')) {
172
+ if (!byTopic[f.topic]) byTopic[f.topic] = [];
173
+ byTopic[f.topic].push(f);
174
+ }
175
+ });
176
+
177
+ // Build HTML
178
+ let html = '';
179
+
180
+ // Landing folder
181
+ html += `
182
+ <div class="kb-folder">
183
+ <div class="kb-folder-header" data-folder="landing">
184
+ <i class="bi bi-chevron-down"></i>
185
+ <i class="bi bi-inbox text-warning"></i>
186
+ <span>Landing (${landing.length})</span>
187
+ </div>
188
+ <div class="kb-folder-files">
189
+ ${landing.map(f => this.renderFileItem(f)).join('')}
190
+ ${landing.length === 0 ? '<div class="kb-empty">No files</div>' : ''}
191
+ </div>
192
+ </div>
193
+ `;
194
+
195
+ // Topic folders
196
+ const sortedTopics = Object.keys(byTopic).sort();
197
+ sortedTopics.forEach(topic => {
198
+ const topicFiles = byTopic[topic];
199
+ html += `
200
+ <div class="kb-folder">
201
+ <div class="kb-folder-header" data-folder="${topic}">
202
+ <i class="bi bi-chevron-down"></i>
203
+ <i class="bi bi-folder text-info"></i>
204
+ <span>${topic} (${topicFiles.length})</span>
205
+ </div>
206
+ <div class="kb-folder-files">
207
+ ${topicFiles.map(f => this.renderFileItem(f)).join('')}
208
+ </div>
209
+ </div>
210
+ `;
211
+ });
212
+
213
+ // Empty topics placeholder
214
+ if (sortedTopics.length === 0 && landing.length === 0) {
215
+ html += '<div class="kb-empty-state"><i class="bi bi-archive"></i><p>No files in Knowledge Base</p></div>';
216
+ }
217
+
218
+ container.innerHTML = html;
219
+
220
+ // Bind folder toggle events
221
+ container.querySelectorAll('.kb-folder-header').forEach(header => {
222
+ header.addEventListener('click', (e) => {
223
+ const folder = e.currentTarget.closest('.kb-folder');
224
+ folder.classList.toggle('collapsed');
225
+ const icon = e.currentTarget.querySelector('i:first-child');
226
+ icon.classList.toggle('bi-chevron-down');
227
+ icon.classList.toggle('bi-chevron-right');
228
+ });
229
+ });
230
+
231
+ // Bind file click events
232
+ container.querySelectorAll('.kb-file-item').forEach(item => {
233
+ item.addEventListener('click', () => {
234
+ // Remove active from others
235
+ container.querySelectorAll('.kb-file-item').forEach(i => i.classList.remove('active'));
236
+ item.classList.add('active');
237
+
238
+ const path = item.dataset.path;
239
+ this.showFilePreview(path);
240
+ });
241
+ });
242
+ },
243
+
244
+ /**
245
+ * Render a single file item in the sidebar.
246
+ */
247
+ renderFileItem(file) {
248
+ const icon = this.getFileIcon(file.type);
249
+ return `
250
+ <div class="kb-file-item" data-path="${file.path}" title="${file.name}">
251
+ <i class="bi ${icon}"></i>
252
+ <span class="kb-file-name">${file.name}</span>
253
+ </div>
254
+ `;
255
+ },
256
+
257
+ /**
258
+ * Get Bootstrap icon class for file type.
259
+ */
260
+ getFileIcon(type) {
261
+ const icons = {
262
+ 'pdf': 'bi-file-earmark-pdf text-danger',
263
+ 'markdown': 'bi-markdown text-info',
264
+ 'text': 'bi-file-text',
265
+ 'docx': 'bi-file-earmark-word text-primary',
266
+ 'xlsx': 'bi-file-earmark-excel text-success',
267
+ 'python': 'bi-filetype-py text-warning',
268
+ 'javascript': 'bi-filetype-js text-warning',
269
+ 'typescript': 'bi-filetype-tsx text-primary',
270
+ 'java': 'bi-filetype-java text-danger',
271
+ 'json': 'bi-filetype-json text-warning',
272
+ 'html': 'bi-filetype-html text-danger',
273
+ 'css': 'bi-filetype-css text-info',
274
+ 'image': 'bi-file-image text-success',
275
+ 'unknown': 'bi-file-earmark'
276
+ };
277
+ return icons[type] || icons['unknown'];
278
+ },
279
+
280
+ /**
281
+ * Render welcome/placeholder in content area.
282
+ */
283
+ renderWelcome() {
284
+ const content = document.getElementById('kb-content');
285
+ if (!content) return;
286
+
287
+ const files = this.index?.files || [];
288
+ const landing = files.filter(f => f.path.startsWith('landing/'));
289
+
290
+ content.innerHTML = `
291
+ <div class="kb-welcome">
292
+ <div class="kb-stats mb-4">
293
+ <span class="badge bg-secondary me-2">${files.length} files</span>
294
+ <span class="badge bg-info">${this.topics.length} topics</span>
295
+ </div>
296
+ <div class="kb-section">
297
+ <h6><i class="bi bi-inbox text-warning me-2"></i>Landing (Unprocessed)</h6>
298
+ <div class="kb-landing-list">
299
+ ${landing.length > 0 ? landing.map(f => `
300
+ <div class="kb-landing-file">
301
+ <i class="bi ${this.getFileIcon(f.type)}"></i>
302
+ <span>${f.name}</span>
303
+ <span class="text-muted small ms-auto">${this.formatSize(f.size)}</span>
304
+ </div>
305
+ `).join('') : '<p class="text-muted small">No files in landing folder</p>'}
306
+ </div>
307
+ </div>
308
+ <div class="kb-section mt-4">
309
+ <h6><i class="bi bi-folder text-info me-2"></i>Topics</h6>
310
+ <div class="kb-topics-grid">
311
+ ${this.topics.length > 0 ? this.topics.map(topic => {
312
+ const topicFiles = files.filter(f => f.topic === topic);
313
+ return `
314
+ <div class="kb-topic-card">
315
+ <i class="bi bi-folder me-2"></i>${topic}
316
+ <span class="badge bg-secondary ms-auto">${topicFiles.length}</span>
317
+ </div>
318
+ `;
319
+ }).join('') : '<p class="text-muted small">No topics created yet</p>'}
320
+ </div>
321
+ </div>
322
+ </div>
323
+ `;
324
+ },
325
+
326
+ /**
327
+ * Show file preview in content area.
328
+ */
329
+ async showFilePreview(path) {
330
+ const content = document.getElementById('kb-content');
331
+ if (!content) return;
332
+
333
+ const file = this.index?.files?.find(f => f.path === path);
334
+ if (!file) {
335
+ content.innerHTML = '<div class="kb-error">File not found</div>';
336
+ return;
337
+ }
338
+
339
+ content.innerHTML = `
340
+ <div class="kb-file-preview">
341
+ <div class="kb-file-header">
342
+ <i class="bi ${this.getFileIcon(file.type)} fs-4"></i>
343
+ <div class="kb-file-info">
344
+ <h6 class="mb-0">${file.name}</h6>
345
+ <small class="text-muted">${file.path} · ${this.formatSize(file.size)}</small>
346
+ </div>
347
+ </div>
348
+ <div class="kb-file-meta mt-3">
349
+ <span class="badge bg-secondary me-1">${file.type}</span>
350
+ ${file.topic ? `<span class="badge bg-info me-1">${file.topic}</span>` : ''}
351
+ ${(file.keywords || []).map(k => `<span class="badge bg-outline-secondary me-1">${k}</span>`).join('')}
352
+ </div>
353
+ <div class="kb-file-content mt-3">
354
+ <p class="text-muted small">File preview coming in a future release.</p>
355
+ </div>
356
+ </div>
357
+ `;
358
+ },
359
+
360
+ /**
361
+ * Format file size for display.
362
+ */
363
+ formatSize(bytes) {
364
+ if (bytes < 1024) return bytes + ' B';
365
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
366
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
367
+ }
368
+ };
369
+
370
+ // Export for use in template
371
+ window.kbCore = kbCore;