nc1709 1.15.4__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 (86) hide show
  1. nc1709/__init__.py +13 -0
  2. nc1709/agent/__init__.py +36 -0
  3. nc1709/agent/core.py +505 -0
  4. nc1709/agent/mcp_bridge.py +245 -0
  5. nc1709/agent/permissions.py +298 -0
  6. nc1709/agent/tools/__init__.py +21 -0
  7. nc1709/agent/tools/base.py +440 -0
  8. nc1709/agent/tools/bash_tool.py +367 -0
  9. nc1709/agent/tools/file_tools.py +454 -0
  10. nc1709/agent/tools/notebook_tools.py +516 -0
  11. nc1709/agent/tools/search_tools.py +322 -0
  12. nc1709/agent/tools/task_tool.py +284 -0
  13. nc1709/agent/tools/web_tools.py +555 -0
  14. nc1709/agents/__init__.py +17 -0
  15. nc1709/agents/auto_fix.py +506 -0
  16. nc1709/agents/test_generator.py +507 -0
  17. nc1709/checkpoints.py +372 -0
  18. nc1709/cli.py +3380 -0
  19. nc1709/cli_ui.py +1080 -0
  20. nc1709/cognitive/__init__.py +149 -0
  21. nc1709/cognitive/anticipation.py +594 -0
  22. nc1709/cognitive/context_engine.py +1046 -0
  23. nc1709/cognitive/council.py +824 -0
  24. nc1709/cognitive/learning.py +761 -0
  25. nc1709/cognitive/router.py +583 -0
  26. nc1709/cognitive/system.py +519 -0
  27. nc1709/config.py +155 -0
  28. nc1709/custom_commands.py +300 -0
  29. nc1709/executor.py +333 -0
  30. nc1709/file_controller.py +354 -0
  31. nc1709/git_integration.py +308 -0
  32. nc1709/github_integration.py +477 -0
  33. nc1709/image_input.py +446 -0
  34. nc1709/linting.py +519 -0
  35. nc1709/llm_adapter.py +667 -0
  36. nc1709/logger.py +192 -0
  37. nc1709/mcp/__init__.py +18 -0
  38. nc1709/mcp/client.py +370 -0
  39. nc1709/mcp/manager.py +407 -0
  40. nc1709/mcp/protocol.py +210 -0
  41. nc1709/mcp/server.py +473 -0
  42. nc1709/memory/__init__.py +20 -0
  43. nc1709/memory/embeddings.py +325 -0
  44. nc1709/memory/indexer.py +474 -0
  45. nc1709/memory/sessions.py +432 -0
  46. nc1709/memory/vector_store.py +451 -0
  47. nc1709/models/__init__.py +86 -0
  48. nc1709/models/detector.py +377 -0
  49. nc1709/models/formats.py +315 -0
  50. nc1709/models/manager.py +438 -0
  51. nc1709/models/registry.py +497 -0
  52. nc1709/performance/__init__.py +343 -0
  53. nc1709/performance/cache.py +705 -0
  54. nc1709/performance/pipeline.py +611 -0
  55. nc1709/performance/tiering.py +543 -0
  56. nc1709/plan_mode.py +362 -0
  57. nc1709/plugins/__init__.py +17 -0
  58. nc1709/plugins/agents/__init__.py +18 -0
  59. nc1709/plugins/agents/django_agent.py +912 -0
  60. nc1709/plugins/agents/docker_agent.py +623 -0
  61. nc1709/plugins/agents/fastapi_agent.py +887 -0
  62. nc1709/plugins/agents/git_agent.py +731 -0
  63. nc1709/plugins/agents/nextjs_agent.py +867 -0
  64. nc1709/plugins/base.py +359 -0
  65. nc1709/plugins/manager.py +411 -0
  66. nc1709/plugins/registry.py +337 -0
  67. nc1709/progress.py +443 -0
  68. nc1709/prompts/__init__.py +22 -0
  69. nc1709/prompts/agent_system.py +180 -0
  70. nc1709/prompts/task_prompts.py +340 -0
  71. nc1709/prompts/unified_prompt.py +133 -0
  72. nc1709/reasoning_engine.py +541 -0
  73. nc1709/remote_client.py +266 -0
  74. nc1709/shell_completions.py +349 -0
  75. nc1709/slash_commands.py +649 -0
  76. nc1709/task_classifier.py +408 -0
  77. nc1709/version_check.py +177 -0
  78. nc1709/web/__init__.py +8 -0
  79. nc1709/web/server.py +950 -0
  80. nc1709/web/templates/index.html +1127 -0
  81. nc1709-1.15.4.dist-info/METADATA +858 -0
  82. nc1709-1.15.4.dist-info/RECORD +86 -0
  83. nc1709-1.15.4.dist-info/WHEEL +5 -0
  84. nc1709-1.15.4.dist-info/entry_points.txt +2 -0
  85. nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
  86. nc1709-1.15.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1127 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>NC1709 Dashboard</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/11.1.1/marked.min.js"></script>
10
+ <style>
11
+ :root {
12
+ --bg-primary: #0d1117;
13
+ --bg-secondary: #161b22;
14
+ --bg-tertiary: #21262d;
15
+ --border-color: #30363d;
16
+ --text-primary: #e6edf3;
17
+ --text-secondary: #8b949e;
18
+ --accent: #58a6ff;
19
+ --accent-hover: #79b8ff;
20
+ --success: #3fb950;
21
+ --warning: #d29922;
22
+ --error: #f85149;
23
+ --sidebar-width: 260px;
24
+ }
25
+
26
+ * {
27
+ margin: 0;
28
+ padding: 0;
29
+ box-sizing: border-box;
30
+ }
31
+
32
+ body {
33
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
34
+ background: var(--bg-primary);
35
+ color: var(--text-primary);
36
+ line-height: 1.5;
37
+ }
38
+
39
+ /* Layout */
40
+ .app {
41
+ display: flex;
42
+ height: 100vh;
43
+ }
44
+
45
+ /* Sidebar */
46
+ .sidebar {
47
+ width: var(--sidebar-width);
48
+ background: var(--bg-secondary);
49
+ border-right: 1px solid var(--border-color);
50
+ display: flex;
51
+ flex-direction: column;
52
+ flex-shrink: 0;
53
+ }
54
+
55
+ .sidebar-header {
56
+ padding: 16px;
57
+ border-bottom: 1px solid var(--border-color);
58
+ }
59
+
60
+ .sidebar-header h1 {
61
+ font-size: 20px;
62
+ font-weight: 600;
63
+ color: var(--accent);
64
+ }
65
+
66
+ .sidebar-header .version {
67
+ font-size: 12px;
68
+ color: var(--text-secondary);
69
+ }
70
+
71
+ .nav {
72
+ flex: 1;
73
+ padding: 8px;
74
+ overflow-y: auto;
75
+ }
76
+
77
+ .nav-item {
78
+ display: flex;
79
+ align-items: center;
80
+ padding: 10px 12px;
81
+ border-radius: 6px;
82
+ cursor: pointer;
83
+ color: var(--text-secondary);
84
+ transition: all 0.15s ease;
85
+ margin-bottom: 2px;
86
+ }
87
+
88
+ .nav-item:hover {
89
+ background: var(--bg-tertiary);
90
+ color: var(--text-primary);
91
+ }
92
+
93
+ .nav-item.active {
94
+ background: var(--bg-tertiary);
95
+ color: var(--text-primary);
96
+ }
97
+
98
+ .nav-item svg {
99
+ width: 16px;
100
+ height: 16px;
101
+ margin-right: 10px;
102
+ }
103
+
104
+ .nav-section {
105
+ padding: 8px 12px 4px;
106
+ font-size: 11px;
107
+ font-weight: 600;
108
+ text-transform: uppercase;
109
+ color: var(--text-secondary);
110
+ margin-top: 16px;
111
+ }
112
+
113
+ /* Main Content */
114
+ .main {
115
+ flex: 1;
116
+ display: flex;
117
+ flex-direction: column;
118
+ overflow: hidden;
119
+ }
120
+
121
+ .header {
122
+ padding: 16px 24px;
123
+ border-bottom: 1px solid var(--border-color);
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: space-between;
127
+ }
128
+
129
+ .header h2 {
130
+ font-size: 18px;
131
+ font-weight: 600;
132
+ }
133
+
134
+ .content {
135
+ flex: 1;
136
+ overflow-y: auto;
137
+ padding: 24px;
138
+ }
139
+
140
+ /* Chat Panel */
141
+ .chat-container {
142
+ display: flex;
143
+ flex-direction: column;
144
+ height: 100%;
145
+ }
146
+
147
+ .chat-messages {
148
+ flex: 1;
149
+ overflow-y: auto;
150
+ padding: 16px;
151
+ }
152
+
153
+ .message {
154
+ margin-bottom: 16px;
155
+ max-width: 85%;
156
+ }
157
+
158
+ .message.user {
159
+ margin-left: auto;
160
+ }
161
+
162
+ .message-content {
163
+ padding: 12px 16px;
164
+ border-radius: 12px;
165
+ background: var(--bg-tertiary);
166
+ }
167
+
168
+ .message.user .message-content {
169
+ background: var(--accent);
170
+ color: white;
171
+ }
172
+
173
+ .message-content pre {
174
+ margin: 8px 0;
175
+ padding: 12px;
176
+ background: var(--bg-primary);
177
+ border-radius: 6px;
178
+ overflow-x: auto;
179
+ }
180
+
181
+ .message-content code {
182
+ font-family: 'SF Mono', 'Fira Code', monospace;
183
+ font-size: 13px;
184
+ }
185
+
186
+ .message-content p {
187
+ margin-bottom: 8px;
188
+ }
189
+
190
+ .message-content p:last-child {
191
+ margin-bottom: 0;
192
+ }
193
+
194
+ .chat-input-container {
195
+ padding: 16px;
196
+ border-top: 1px solid var(--border-color);
197
+ }
198
+
199
+ .chat-input-wrapper {
200
+ display: flex;
201
+ gap: 12px;
202
+ }
203
+
204
+ .chat-input {
205
+ flex: 1;
206
+ padding: 12px 16px;
207
+ background: var(--bg-tertiary);
208
+ border: 1px solid var(--border-color);
209
+ border-radius: 8px;
210
+ color: var(--text-primary);
211
+ font-size: 14px;
212
+ resize: none;
213
+ }
214
+
215
+ .chat-input:focus {
216
+ outline: none;
217
+ border-color: var(--accent);
218
+ }
219
+
220
+ .send-btn {
221
+ padding: 12px 24px;
222
+ background: var(--accent);
223
+ color: white;
224
+ border: none;
225
+ border-radius: 8px;
226
+ cursor: pointer;
227
+ font-weight: 500;
228
+ transition: background 0.15s ease;
229
+ }
230
+
231
+ .send-btn:hover {
232
+ background: var(--accent-hover);
233
+ }
234
+
235
+ .send-btn:disabled {
236
+ opacity: 0.5;
237
+ cursor: not-allowed;
238
+ }
239
+
240
+ /* Cards */
241
+ .card {
242
+ background: var(--bg-secondary);
243
+ border: 1px solid var(--border-color);
244
+ border-radius: 8px;
245
+ margin-bottom: 16px;
246
+ }
247
+
248
+ .card-header {
249
+ padding: 16px;
250
+ border-bottom: 1px solid var(--border-color);
251
+ font-weight: 600;
252
+ }
253
+
254
+ .card-body {
255
+ padding: 16px;
256
+ }
257
+
258
+ /* Stats Grid */
259
+ .stats-grid {
260
+ display: grid;
261
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
262
+ gap: 16px;
263
+ margin-bottom: 24px;
264
+ }
265
+
266
+ .stat-card {
267
+ background: var(--bg-secondary);
268
+ border: 1px solid var(--border-color);
269
+ border-radius: 8px;
270
+ padding: 20px;
271
+ }
272
+
273
+ .stat-value {
274
+ font-size: 32px;
275
+ font-weight: 700;
276
+ color: var(--accent);
277
+ }
278
+
279
+ .stat-label {
280
+ font-size: 14px;
281
+ color: var(--text-secondary);
282
+ margin-top: 4px;
283
+ }
284
+
285
+ /* Tables */
286
+ .table {
287
+ width: 100%;
288
+ border-collapse: collapse;
289
+ }
290
+
291
+ .table th,
292
+ .table td {
293
+ padding: 12px;
294
+ text-align: left;
295
+ border-bottom: 1px solid var(--border-color);
296
+ }
297
+
298
+ .table th {
299
+ font-weight: 600;
300
+ color: var(--text-secondary);
301
+ font-size: 12px;
302
+ text-transform: uppercase;
303
+ }
304
+
305
+ /* Buttons */
306
+ .btn {
307
+ padding: 8px 16px;
308
+ border-radius: 6px;
309
+ border: 1px solid var(--border-color);
310
+ background: var(--bg-tertiary);
311
+ color: var(--text-primary);
312
+ cursor: pointer;
313
+ font-size: 14px;
314
+ transition: all 0.15s ease;
315
+ }
316
+
317
+ .btn:hover {
318
+ background: var(--bg-secondary);
319
+ border-color: var(--accent);
320
+ }
321
+
322
+ .btn-primary {
323
+ background: var(--accent);
324
+ border-color: var(--accent);
325
+ color: white;
326
+ }
327
+
328
+ .btn-primary:hover {
329
+ background: var(--accent-hover);
330
+ }
331
+
332
+ /* Status badges */
333
+ .badge {
334
+ display: inline-block;
335
+ padding: 2px 8px;
336
+ border-radius: 12px;
337
+ font-size: 12px;
338
+ font-weight: 500;
339
+ }
340
+
341
+ .badge-success {
342
+ background: rgba(63, 185, 80, 0.2);
343
+ color: var(--success);
344
+ }
345
+
346
+ .badge-warning {
347
+ background: rgba(210, 153, 34, 0.2);
348
+ color: var(--warning);
349
+ }
350
+
351
+ .badge-error {
352
+ background: rgba(248, 81, 73, 0.2);
353
+ color: var(--error);
354
+ }
355
+
356
+ /* Search */
357
+ .search-container {
358
+ margin-bottom: 24px;
359
+ }
360
+
361
+ .search-input {
362
+ width: 100%;
363
+ padding: 12px 16px;
364
+ background: var(--bg-tertiary);
365
+ border: 1px solid var(--border-color);
366
+ border-radius: 8px;
367
+ color: var(--text-primary);
368
+ font-size: 14px;
369
+ }
370
+
371
+ .search-input:focus {
372
+ outline: none;
373
+ border-color: var(--accent);
374
+ }
375
+
376
+ .search-results {
377
+ margin-top: 16px;
378
+ }
379
+
380
+ .search-result {
381
+ background: var(--bg-secondary);
382
+ border: 1px solid var(--border-color);
383
+ border-radius: 8px;
384
+ margin-bottom: 12px;
385
+ overflow: hidden;
386
+ }
387
+
388
+ .search-result-header {
389
+ padding: 12px 16px;
390
+ background: var(--bg-tertiary);
391
+ font-size: 14px;
392
+ display: flex;
393
+ justify-content: space-between;
394
+ }
395
+
396
+ .search-result-code {
397
+ padding: 12px 16px;
398
+ overflow-x: auto;
399
+ }
400
+
401
+ .search-result-code pre {
402
+ margin: 0;
403
+ }
404
+
405
+ /* Loading */
406
+ .loading {
407
+ display: flex;
408
+ align-items: center;
409
+ justify-content: center;
410
+ padding: 40px;
411
+ color: var(--text-secondary);
412
+ }
413
+
414
+ .spinner {
415
+ width: 20px;
416
+ height: 20px;
417
+ border: 2px solid var(--border-color);
418
+ border-top-color: var(--accent);
419
+ border-radius: 50%;
420
+ animation: spin 1s linear infinite;
421
+ margin-right: 12px;
422
+ }
423
+
424
+ @keyframes spin {
425
+ to { transform: rotate(360deg); }
426
+ }
427
+
428
+ /* Hidden panels */
429
+ .panel {
430
+ display: none;
431
+ }
432
+
433
+ .panel.active {
434
+ display: block;
435
+ }
436
+
437
+ /* Sessions list */
438
+ .session-item {
439
+ padding: 12px 16px;
440
+ border-bottom: 1px solid var(--border-color);
441
+ cursor: pointer;
442
+ transition: background 0.15s ease;
443
+ }
444
+
445
+ .session-item:hover {
446
+ background: var(--bg-tertiary);
447
+ }
448
+
449
+ .session-item-name {
450
+ font-weight: 500;
451
+ }
452
+
453
+ .session-item-meta {
454
+ font-size: 12px;
455
+ color: var(--text-secondary);
456
+ margin-top: 4px;
457
+ }
458
+
459
+ /* Plugin/Tool lists */
460
+ .tool-item {
461
+ padding: 16px;
462
+ border-bottom: 1px solid var(--border-color);
463
+ }
464
+
465
+ .tool-item:last-child {
466
+ border-bottom: none;
467
+ }
468
+
469
+ .tool-name {
470
+ font-weight: 600;
471
+ margin-bottom: 4px;
472
+ }
473
+
474
+ .tool-description {
475
+ font-size: 14px;
476
+ color: var(--text-secondary);
477
+ }
478
+
479
+ .tool-params {
480
+ margin-top: 8px;
481
+ font-size: 12px;
482
+ color: var(--text-secondary);
483
+ }
484
+
485
+ /* Toast notifications */
486
+ .toast-container {
487
+ position: fixed;
488
+ bottom: 20px;
489
+ right: 20px;
490
+ z-index: 1000;
491
+ }
492
+
493
+ .toast {
494
+ background: var(--bg-secondary);
495
+ border: 1px solid var(--border-color);
496
+ border-radius: 8px;
497
+ padding: 12px 20px;
498
+ margin-top: 8px;
499
+ animation: slideIn 0.3s ease;
500
+ }
501
+
502
+ @keyframes slideIn {
503
+ from {
504
+ transform: translateX(100%);
505
+ opacity: 0;
506
+ }
507
+ to {
508
+ transform: translateX(0);
509
+ opacity: 1;
510
+ }
511
+ }
512
+ </style>
513
+ </head>
514
+ <body>
515
+ <div class="app">
516
+ <!-- Sidebar -->
517
+ <aside class="sidebar">
518
+ <div class="sidebar-header">
519
+ <h1>NC1709</h1>
520
+ <span class="version">v1.0.0 - Local AI Assistant</span>
521
+ </div>
522
+ <nav class="nav">
523
+ <div class="nav-item active" data-panel="chat">
524
+ <svg viewBox="0 0 16 16" fill="currentColor"><path d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.5 4.5a.5.5 0 00-1 0v4a.5.5 0 00.5.5h2.5a.5.5 0 000-1H8.5v-3.5z"/></svg>
525
+ Chat
526
+ </div>
527
+
528
+ <div class="nav-section">Memory</div>
529
+ <div class="nav-item" data-panel="sessions">
530
+ <svg viewBox="0 0 16 16" fill="currentColor"><path d="M0 1.75A.75.75 0 01.75 1h4.253a.75.75 0 01.592.288l1.67 2.168H14.25a.75.75 0 01.75.75v8.044a.75.75 0 01-.75.75H.75a.75.75 0 01-.75-.75V1.75z"/></svg>
531
+ Sessions
532
+ </div>
533
+ <div class="nav-item" data-panel="search">
534
+ <svg viewBox="0 0 16 16" fill="currentColor"><path d="M11.5 7a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zm-.82 4.74a6 6 0 111.06-1.06l2.28 2.28a.75.75 0 11-1.06 1.06l-2.28-2.28z"/></svg>
535
+ Search Code
536
+ </div>
537
+
538
+ <div class="nav-section">Tools</div>
539
+ <div class="nav-item" data-panel="plugins">
540
+ <svg viewBox="0 0 16 16" fill="currentColor"><path d="M2 3.75A.75.75 0 012.75 3h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 3.75zm0 4A.75.75 0 012.75 7h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 7.75zm0 4a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75z"/></svg>
541
+ Plugins
542
+ </div>
543
+ <div class="nav-item" data-panel="mcp">
544
+ <svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0a8 8 0 100 16A8 8 0 008 0zm3.78 5.22a.75.75 0 010 1.06l-4.5 4.5a.75.75 0 01-1.06 0l-2-2a.75.75 0 011.06-1.06L6.75 9.19l3.97-3.97a.75.75 0 011.06 0z"/></svg>
545
+ MCP Tools
546
+ </div>
547
+
548
+ <div class="nav-section">System</div>
549
+ <div class="nav-item" data-panel="config">
550
+ <svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0a8.2 8.2 0 01.701.031C10.042.11 10.989.39 11.5 1.5c.4.865.464 1.92.32 2.984l.15.022c.732.1 1.332.48 1.78 1.084.447.604.67 1.39.564 2.16-.106.77-.513 1.47-1.128 2-.61.53-1.406.845-2.266.96l-.003.053a3.49 3.49 0 01-.064.498c-.164.775-.576 1.454-1.238 1.914-.66.46-1.52.629-2.315.48-.79-.15-1.513-.61-1.972-1.27a3.24 3.24 0 01-.52-1.667l-.026-.003c-.76-.11-1.456-.5-1.97-1.084a3.26 3.26 0 01-.768-2.16c.045-.77.37-1.472.928-2.005.557-.533 1.296-.852 2.101-.968l.068-.011c-.034-.355-.024-.71.03-1.064.09-.59.303-1.15.633-1.64.33-.49.77-.88 1.28-1.15A3.18 3.18 0 018 0z"/></svg>
551
+ Settings
552
+ </div>
553
+ </nav>
554
+ </aside>
555
+
556
+ <!-- Main Content -->
557
+ <main class="main">
558
+ <!-- Chat Panel -->
559
+ <div id="panel-chat" class="panel active">
560
+ <div class="header">
561
+ <h2>Chat</h2>
562
+ <button class="btn" onclick="clearChat()">Clear Chat</button>
563
+ </div>
564
+ <div class="content chat-container">
565
+ <div class="chat-messages" id="chat-messages">
566
+ <div class="message">
567
+ <div class="message-content">
568
+ <p>Welcome to NC1709! I'm your local AI developer assistant.</p>
569
+ <p>Ask me to help with code, search your project, manage git, docker, and more.</p>
570
+ </div>
571
+ </div>
572
+ </div>
573
+ <div class="chat-input-container">
574
+ <div class="chat-input-wrapper">
575
+ <textarea
576
+ class="chat-input"
577
+ id="chat-input"
578
+ placeholder="Ask me anything..."
579
+ rows="1"
580
+ onkeydown="handleChatKeydown(event)"
581
+ ></textarea>
582
+ <button class="send-btn" id="send-btn" onclick="sendMessage()">Send</button>
583
+ </div>
584
+ </div>
585
+ </div>
586
+ </div>
587
+
588
+ <!-- Sessions Panel -->
589
+ <div id="panel-sessions" class="panel">
590
+ <div class="header">
591
+ <h2>Sessions</h2>
592
+ <button class="btn btn-primary" onclick="createSession()">New Session</button>
593
+ </div>
594
+ <div class="content">
595
+ <div id="sessions-list" class="card">
596
+ <div class="loading"><div class="spinner"></div> Loading sessions...</div>
597
+ </div>
598
+ </div>
599
+ </div>
600
+
601
+ <!-- Search Panel -->
602
+ <div id="panel-search" class="panel">
603
+ <div class="header">
604
+ <h2>Semantic Code Search</h2>
605
+ <button class="btn" onclick="indexProject()">Re-index Project</button>
606
+ </div>
607
+ <div class="content">
608
+ <div id="index-status" class="stats-grid">
609
+ <!-- Index stats loaded here -->
610
+ </div>
611
+ <div class="search-container">
612
+ <input
613
+ type="text"
614
+ class="search-input"
615
+ id="search-input"
616
+ placeholder="Search your code semantically... (e.g., 'authentication logic')"
617
+ onkeydown="if(event.key==='Enter')searchCode()"
618
+ >
619
+ </div>
620
+ <div id="search-results" class="search-results">
621
+ <!-- Results loaded here -->
622
+ </div>
623
+ </div>
624
+ </div>
625
+
626
+ <!-- Plugins Panel -->
627
+ <div id="panel-plugins" class="panel">
628
+ <div class="header">
629
+ <h2>Plugins</h2>
630
+ </div>
631
+ <div class="content">
632
+ <div id="plugins-list">
633
+ <div class="loading"><div class="spinner"></div> Loading plugins...</div>
634
+ </div>
635
+ </div>
636
+ </div>
637
+
638
+ <!-- MCP Panel -->
639
+ <div id="panel-mcp" class="panel">
640
+ <div class="header">
641
+ <h2>MCP Tools</h2>
642
+ </div>
643
+ <div class="content">
644
+ <div id="mcp-status" class="stats-grid">
645
+ <!-- MCP stats loaded here -->
646
+ </div>
647
+ <div id="mcp-tools" class="card">
648
+ <div class="card-header">Available Tools</div>
649
+ <div class="card-body" id="mcp-tools-list">
650
+ <div class="loading"><div class="spinner"></div> Loading tools...</div>
651
+ </div>
652
+ </div>
653
+ </div>
654
+ </div>
655
+
656
+ <!-- Config Panel -->
657
+ <div id="panel-config" class="panel">
658
+ <div class="header">
659
+ <h2>Settings</h2>
660
+ </div>
661
+ <div class="content">
662
+ <div class="stats-grid" id="system-status">
663
+ <!-- Status loaded here -->
664
+ </div>
665
+ <div class="card">
666
+ <div class="card-header">Configuration</div>
667
+ <div class="card-body">
668
+ <pre id="config-display" style="background: var(--bg-primary); padding: 16px; border-radius: 6px; overflow-x: auto;"></pre>
669
+ </div>
670
+ </div>
671
+ </div>
672
+ </div>
673
+ </main>
674
+ </div>
675
+
676
+ <div class="toast-container" id="toast-container"></div>
677
+
678
+ <script>
679
+ // Configure marked
680
+ marked.setOptions({
681
+ highlight: function(code, lang) {
682
+ if (lang && hljs.getLanguage(lang)) {
683
+ return hljs.highlight(code, { language: lang }).value;
684
+ }
685
+ return hljs.highlightAuto(code).value;
686
+ },
687
+ breaks: true
688
+ });
689
+
690
+ // Navigation
691
+ document.querySelectorAll('.nav-item').forEach(item => {
692
+ item.addEventListener('click', () => {
693
+ const panel = item.dataset.panel;
694
+ if (panel) {
695
+ // Update nav
696
+ document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
697
+ item.classList.add('active');
698
+
699
+ // Update panels
700
+ document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
701
+ document.getElementById(`panel-${panel}`).classList.add('active');
702
+
703
+ // Load data for panel
704
+ loadPanelData(panel);
705
+ }
706
+ });
707
+ });
708
+
709
+ // Load panel data
710
+ function loadPanelData(panel) {
711
+ switch(panel) {
712
+ case 'sessions':
713
+ loadSessions();
714
+ break;
715
+ case 'search':
716
+ loadIndexStatus();
717
+ break;
718
+ case 'plugins':
719
+ loadPlugins();
720
+ break;
721
+ case 'mcp':
722
+ loadMCPStatus();
723
+ loadMCPTools();
724
+ break;
725
+ case 'config':
726
+ loadConfig();
727
+ loadStatus();
728
+ break;
729
+ }
730
+ }
731
+
732
+ // Toast notifications
733
+ function showToast(message, type = 'info') {
734
+ const container = document.getElementById('toast-container');
735
+ const toast = document.createElement('div');
736
+ toast.className = 'toast';
737
+ toast.textContent = message;
738
+ container.appendChild(toast);
739
+
740
+ setTimeout(() => toast.remove(), 3000);
741
+ }
742
+
743
+ // Chat functions
744
+ function addMessage(role, content) {
745
+ const messages = document.getElementById('chat-messages');
746
+ const div = document.createElement('div');
747
+ div.className = `message ${role}`;
748
+
749
+ const contentDiv = document.createElement('div');
750
+ contentDiv.className = 'message-content';
751
+
752
+ if (role === 'assistant') {
753
+ contentDiv.innerHTML = marked.parse(content);
754
+ contentDiv.querySelectorAll('pre code').forEach(block => {
755
+ hljs.highlightElement(block);
756
+ });
757
+ } else {
758
+ contentDiv.textContent = content;
759
+ }
760
+
761
+ div.appendChild(contentDiv);
762
+ messages.appendChild(div);
763
+ messages.scrollTop = messages.scrollHeight;
764
+ }
765
+
766
+ async function sendMessage() {
767
+ const input = document.getElementById('chat-input');
768
+ const btn = document.getElementById('send-btn');
769
+ const message = input.value.trim();
770
+
771
+ if (!message) return;
772
+
773
+ // Add user message
774
+ addMessage('user', message);
775
+ input.value = '';
776
+ btn.disabled = true;
777
+
778
+ try {
779
+ const response = await fetch('/api/chat', {
780
+ method: 'POST',
781
+ headers: { 'Content-Type': 'application/json' },
782
+ body: JSON.stringify({ message })
783
+ });
784
+
785
+ const data = await response.json();
786
+
787
+ if (response.ok) {
788
+ addMessage('assistant', data.response);
789
+ } else {
790
+ addMessage('assistant', `Error: ${data.detail}`);
791
+ }
792
+ } catch (e) {
793
+ addMessage('assistant', `Error: ${e.message}`);
794
+ } finally {
795
+ btn.disabled = false;
796
+ input.focus();
797
+ }
798
+ }
799
+
800
+ function handleChatKeydown(e) {
801
+ if (e.key === 'Enter' && !e.shiftKey) {
802
+ e.preventDefault();
803
+ sendMessage();
804
+ }
805
+ }
806
+
807
+ function clearChat() {
808
+ const messages = document.getElementById('chat-messages');
809
+ messages.innerHTML = `
810
+ <div class="message">
811
+ <div class="message-content">
812
+ <p>Chat cleared. How can I help you?</p>
813
+ </div>
814
+ </div>
815
+ `;
816
+ }
817
+
818
+ // Sessions
819
+ async function loadSessions() {
820
+ const container = document.getElementById('sessions-list');
821
+
822
+ try {
823
+ const response = await fetch('/api/sessions');
824
+ const data = await response.json();
825
+
826
+ if (data.sessions.length === 0) {
827
+ container.innerHTML = '<div class="card-body">No sessions yet. Start chatting to create one!</div>';
828
+ return;
829
+ }
830
+
831
+ container.innerHTML = data.sessions.map(s => `
832
+ <div class="session-item" onclick="loadSession('${s.id}')">
833
+ <div class="session-item-name">${s.name || s.id}</div>
834
+ <div class="session-item-meta">
835
+ ${s.message_count || 0} messages • ${s.updated_at ? s.updated_at.slice(0, 16) : 'N/A'}
836
+ </div>
837
+ </div>
838
+ `).join('');
839
+ } catch (e) {
840
+ container.innerHTML = `<div class="card-body">Error loading sessions: ${e.message}</div>`;
841
+ }
842
+ }
843
+
844
+ async function loadSession(id) {
845
+ try {
846
+ const response = await fetch(`/api/sessions/${id}`);
847
+ const data = await response.json();
848
+
849
+ // Switch to chat panel and load messages
850
+ document.querySelector('[data-panel="chat"]').click();
851
+
852
+ const messages = document.getElementById('chat-messages');
853
+ messages.innerHTML = '';
854
+
855
+ data.messages.forEach(m => {
856
+ addMessage(m.role, m.content);
857
+ });
858
+
859
+ showToast(`Loaded session: ${data.name || id}`);
860
+ } catch (e) {
861
+ showToast(`Error loading session: ${e.message}`, 'error');
862
+ }
863
+ }
864
+
865
+ async function createSession() {
866
+ try {
867
+ const response = await fetch('/api/sessions', { method: 'POST' });
868
+ const data = await response.json();
869
+ showToast(`Created session: ${data.id}`);
870
+ loadSessions();
871
+ } catch (e) {
872
+ showToast(`Error: ${e.message}`, 'error');
873
+ }
874
+ }
875
+
876
+ // Search
877
+ async function loadIndexStatus() {
878
+ const container = document.getElementById('index-status');
879
+
880
+ try {
881
+ const response = await fetch('/api/index/status');
882
+ const data = await response.json();
883
+
884
+ container.innerHTML = `
885
+ <div class="stat-card">
886
+ <div class="stat-value">${data.total_files || 0}</div>
887
+ <div class="stat-label">Files Indexed</div>
888
+ </div>
889
+ <div class="stat-card">
890
+ <div class="stat-value">${data.total_chunks || 0}</div>
891
+ <div class="stat-label">Code Chunks</div>
892
+ </div>
893
+ <div class="stat-card">
894
+ <div class="stat-value">${Object.keys(data.languages || {}).length}</div>
895
+ <div class="stat-label">Languages</div>
896
+ </div>
897
+ `;
898
+ } catch (e) {
899
+ container.innerHTML = `<div class="stat-card">Error loading index status</div>`;
900
+ }
901
+ }
902
+
903
+ async function searchCode() {
904
+ const input = document.getElementById('search-input');
905
+ const results = document.getElementById('search-results');
906
+ const query = input.value.trim();
907
+
908
+ if (!query) return;
909
+
910
+ results.innerHTML = '<div class="loading"><div class="spinner"></div> Searching...</div>';
911
+
912
+ try {
913
+ const response = await fetch('/api/search', {
914
+ method: 'POST',
915
+ headers: { 'Content-Type': 'application/json' },
916
+ body: JSON.stringify({ query, n_results: 5 })
917
+ });
918
+
919
+ const data = await response.json();
920
+
921
+ if (data.results.length === 0) {
922
+ results.innerHTML = '<p>No results found.</p>';
923
+ return;
924
+ }
925
+
926
+ results.innerHTML = data.results.map(r => `
927
+ <div class="search-result">
928
+ <div class="search-result-header">
929
+ <span>${r.location}</span>
930
+ <span class="badge badge-success">${(r.similarity * 100).toFixed(1)}% match</span>
931
+ </div>
932
+ <div class="search-result-code">
933
+ <pre><code class="language-${r.language || 'plaintext'}">${escapeHtml(r.content)}</code></pre>
934
+ </div>
935
+ </div>
936
+ `).join('');
937
+
938
+ // Highlight code
939
+ results.querySelectorAll('pre code').forEach(block => {
940
+ hljs.highlightElement(block);
941
+ });
942
+ } catch (e) {
943
+ results.innerHTML = `<p>Error: ${e.message}</p>`;
944
+ }
945
+ }
946
+
947
+ async function indexProject() {
948
+ showToast('Indexing project...');
949
+
950
+ try {
951
+ const response = await fetch('/api/index', { method: 'POST' });
952
+ const data = await response.json();
953
+
954
+ showToast(`Indexed ${data.files_indexed} files, ${data.chunks_created} chunks`);
955
+ loadIndexStatus();
956
+ } catch (e) {
957
+ showToast(`Error: ${e.message}`, 'error');
958
+ }
959
+ }
960
+
961
+ // Plugins
962
+ async function loadPlugins() {
963
+ const container = document.getElementById('plugins-list');
964
+
965
+ try {
966
+ const response = await fetch('/api/plugins');
967
+ const data = await response.json();
968
+
969
+ if (data.plugins.length === 0) {
970
+ container.innerHTML = '<p>No plugins available.</p>';
971
+ return;
972
+ }
973
+
974
+ container.innerHTML = data.plugins.map(p => `
975
+ <div class="card" style="margin-bottom: 16px;">
976
+ <div class="card-header" style="display: flex; justify-content: space-between; align-items: center;">
977
+ <span>${p.name} <span style="color: var(--text-secondary);">v${p.version}</span></span>
978
+ <span class="badge ${p.status === 'active' ? 'badge-success' : 'badge-warning'}">${p.status}</span>
979
+ </div>
980
+ <div class="card-body">
981
+ <p style="color: var(--text-secondary); margin-bottom: 12px;">
982
+ ${p.builtin ? 'Built-in plugin' : 'Custom plugin'}
983
+ </p>
984
+ <div>
985
+ <strong>Actions:</strong>
986
+ ${p.actions.map(a => `<button class="btn" style="margin: 4px;" onclick="executePlugin('${p.name}', '${a}')">${a}</button>`).join('')}
987
+ </div>
988
+ </div>
989
+ </div>
990
+ `).join('');
991
+ } catch (e) {
992
+ container.innerHTML = `<p>Error: ${e.message}</p>`;
993
+ }
994
+ }
995
+
996
+ async function executePlugin(plugin, action) {
997
+ try {
998
+ const response = await fetch('/api/plugins/execute', {
999
+ method: 'POST',
1000
+ headers: { 'Content-Type': 'application/json' },
1001
+ body: JSON.stringify({ plugin, action })
1002
+ });
1003
+
1004
+ const data = await response.json();
1005
+
1006
+ if (data.success) {
1007
+ showToast(data.message);
1008
+ // Show result in chat
1009
+ document.querySelector('[data-panel="chat"]').click();
1010
+ addMessage('assistant', `**${plugin}:${action}**\n\n${data.message}\n\n${typeof data.data === 'string' ? data.data : JSON.stringify(data.data, null, 2)}`);
1011
+ } else {
1012
+ showToast(`Error: ${data.error}`, 'error');
1013
+ }
1014
+ } catch (e) {
1015
+ showToast(`Error: ${e.message}`, 'error');
1016
+ }
1017
+ }
1018
+
1019
+ // MCP
1020
+ async function loadMCPStatus() {
1021
+ const container = document.getElementById('mcp-status');
1022
+
1023
+ try {
1024
+ const response = await fetch('/api/mcp/status');
1025
+ const data = await response.json();
1026
+
1027
+ if (!data.available) {
1028
+ container.innerHTML = '<div class="stat-card">MCP not available</div>';
1029
+ return;
1030
+ }
1031
+
1032
+ container.innerHTML = `
1033
+ <div class="stat-card">
1034
+ <div class="stat-value">${data.server.tools}</div>
1035
+ <div class="stat-label">Local Tools</div>
1036
+ </div>
1037
+ <div class="stat-card">
1038
+ <div class="stat-value">${data.client.connected_servers}</div>
1039
+ <div class="stat-label">Connected Servers</div>
1040
+ </div>
1041
+ <div class="stat-card">
1042
+ <div class="stat-value">${data.server.running ? 'Yes' : 'No'}</div>
1043
+ <div class="stat-label">Server Running</div>
1044
+ </div>
1045
+ `;
1046
+ } catch (e) {
1047
+ container.innerHTML = `<div class="stat-card">Error loading MCP status</div>`;
1048
+ }
1049
+ }
1050
+
1051
+ async function loadMCPTools() {
1052
+ const container = document.getElementById('mcp-tools-list');
1053
+
1054
+ try {
1055
+ const response = await fetch('/api/mcp/tools');
1056
+ const data = await response.json();
1057
+
1058
+ const tools = [...data.local, ...data.remote];
1059
+
1060
+ if (tools.length === 0) {
1061
+ container.innerHTML = '<p>No tools available.</p>';
1062
+ return;
1063
+ }
1064
+
1065
+ container.innerHTML = tools.map(t => `
1066
+ <div class="tool-item">
1067
+ <div class="tool-name">${t.name}</div>
1068
+ <div class="tool-description">${t.description}</div>
1069
+ ${t.parameters ? `<div class="tool-params">Parameters: ${t.parameters.map(p => p.name).join(', ')}</div>` : ''}
1070
+ </div>
1071
+ `).join('');
1072
+ } catch (e) {
1073
+ container.innerHTML = `<p>Error: ${e.message}</p>`;
1074
+ }
1075
+ }
1076
+
1077
+ // Config
1078
+ async function loadConfig() {
1079
+ const display = document.getElementById('config-display');
1080
+
1081
+ try {
1082
+ const response = await fetch('/api/config');
1083
+ const data = await response.json();
1084
+ display.textContent = JSON.stringify(data.config, null, 2);
1085
+ } catch (e) {
1086
+ display.textContent = `Error: ${e.message}`;
1087
+ }
1088
+ }
1089
+
1090
+ async function loadStatus() {
1091
+ const container = document.getElementById('system-status');
1092
+
1093
+ try {
1094
+ const response = await fetch('/api/status');
1095
+ const data = await response.json();
1096
+
1097
+ container.innerHTML = `
1098
+ <div class="stat-card">
1099
+ <div class="stat-value" style="font-size: 20px;">${data.status === 'ok' ? '✅' : '❌'}</div>
1100
+ <div class="stat-label">System Status</div>
1101
+ </div>
1102
+ <div class="stat-card">
1103
+ <div class="stat-value" style="font-size: 16px;">${data.version}</div>
1104
+ <div class="stat-label">Version</div>
1105
+ </div>
1106
+ <div class="stat-card">
1107
+ <div class="stat-value" style="font-size: 14px; word-break: break-all;">${data.project.slice(-30)}</div>
1108
+ <div class="stat-label">Project</div>
1109
+ </div>
1110
+ `;
1111
+ } catch (e) {
1112
+ container.innerHTML = `<div class="stat-card">Error loading status</div>`;
1113
+ }
1114
+ }
1115
+
1116
+ // Utilities
1117
+ function escapeHtml(text) {
1118
+ const div = document.createElement('div');
1119
+ div.textContent = text;
1120
+ return div.innerHTML;
1121
+ }
1122
+
1123
+ // Initial load
1124
+ loadStatus();
1125
+ </script>
1126
+ </body>
1127
+ </html>