fraude-code 0.1.0

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 (127) hide show
  1. package/README.md +68 -0
  2. package/dist/index.js +179297 -0
  3. package/package.json +88 -0
  4. package/src/agent/agent.ts +475 -0
  5. package/src/agent/contextManager.ts +141 -0
  6. package/src/agent/index.ts +14 -0
  7. package/src/agent/pendingChanges.ts +270 -0
  8. package/src/agent/prompts/AskPrompt.txt +10 -0
  9. package/src/agent/prompts/FastPrompt.txt +40 -0
  10. package/src/agent/prompts/PlannerPrompt.txt +51 -0
  11. package/src/agent/prompts/ReviewerPrompt.txt +57 -0
  12. package/src/agent/prompts/WorkerPrompt.txt +33 -0
  13. package/src/agent/subagents/askAgent.ts +37 -0
  14. package/src/agent/subagents/extractionAgent.ts +123 -0
  15. package/src/agent/subagents/fastAgent.ts +45 -0
  16. package/src/agent/subagents/managerAgent.ts +36 -0
  17. package/src/agent/subagents/relationAgent.ts +76 -0
  18. package/src/agent/subagents/researchSubAgent.ts +79 -0
  19. package/src/agent/subagents/reviewerSubAgent.ts +42 -0
  20. package/src/agent/subagents/workerSubAgent.ts +42 -0
  21. package/src/agent/tools/bashTool.ts +94 -0
  22. package/src/agent/tools/descriptions/bash.txt +47 -0
  23. package/src/agent/tools/descriptions/edit.txt +7 -0
  24. package/src/agent/tools/descriptions/glob.txt +4 -0
  25. package/src/agent/tools/descriptions/grep.txt +8 -0
  26. package/src/agent/tools/descriptions/lsp.txt +20 -0
  27. package/src/agent/tools/descriptions/plan.txt +3 -0
  28. package/src/agent/tools/descriptions/read.txt +9 -0
  29. package/src/agent/tools/descriptions/todo.txt +12 -0
  30. package/src/agent/tools/descriptions/write.txt +8 -0
  31. package/src/agent/tools/editTool.ts +44 -0
  32. package/src/agent/tools/globTool.ts +59 -0
  33. package/src/agent/tools/grepTool.ts +343 -0
  34. package/src/agent/tools/lspTool.ts +429 -0
  35. package/src/agent/tools/planTool.ts +118 -0
  36. package/src/agent/tools/readTool.ts +78 -0
  37. package/src/agent/tools/rememberTool.ts +91 -0
  38. package/src/agent/tools/testRunnerTool.ts +77 -0
  39. package/src/agent/tools/testTool.ts +44 -0
  40. package/src/agent/tools/todoTool.ts +224 -0
  41. package/src/agent/tools/writeTool.ts +33 -0
  42. package/src/commands/COMMANDS.ts +38 -0
  43. package/src/commands/cerebras/auth.ts +27 -0
  44. package/src/commands/cerebras/index.ts +31 -0
  45. package/src/commands/forget.ts +29 -0
  46. package/src/commands/google/auth.ts +24 -0
  47. package/src/commands/google/index.ts +31 -0
  48. package/src/commands/groq/add_model.ts +60 -0
  49. package/src/commands/groq/auth.ts +24 -0
  50. package/src/commands/groq/index.ts +33 -0
  51. package/src/commands/index.ts +65 -0
  52. package/src/commands/knowledge.ts +92 -0
  53. package/src/commands/log.ts +32 -0
  54. package/src/commands/mistral/auth.ts +27 -0
  55. package/src/commands/mistral/index.ts +31 -0
  56. package/src/commands/model/index.ts +145 -0
  57. package/src/commands/models/index.ts +16 -0
  58. package/src/commands/ollama/index.ts +29 -0
  59. package/src/commands/openrouter/add_model.ts +64 -0
  60. package/src/commands/openrouter/auth.ts +24 -0
  61. package/src/commands/openrouter/index.ts +33 -0
  62. package/src/commands/remember.ts +48 -0
  63. package/src/commands/serve.ts +31 -0
  64. package/src/commands/session/index.ts +21 -0
  65. package/src/commands/usage.ts +15 -0
  66. package/src/commands/visualize.ts +773 -0
  67. package/src/components/App.tsx +55 -0
  68. package/src/components/IntroComponent.tsx +70 -0
  69. package/src/components/LoaderComponent.tsx +68 -0
  70. package/src/components/OutputRenderer.tsx +88 -0
  71. package/src/components/SettingsRenderer.tsx +23 -0
  72. package/src/components/input/CommandSuggestions.tsx +41 -0
  73. package/src/components/input/FileSuggestions.tsx +61 -0
  74. package/src/components/input/InputBox.tsx +371 -0
  75. package/src/components/output/CheckpointView.tsx +13 -0
  76. package/src/components/output/CommandView.tsx +13 -0
  77. package/src/components/output/CommentView.tsx +12 -0
  78. package/src/components/output/ConfirmationView.tsx +179 -0
  79. package/src/components/output/ContextUsage.tsx +62 -0
  80. package/src/components/output/DiffView.tsx +202 -0
  81. package/src/components/output/ErrorView.tsx +14 -0
  82. package/src/components/output/InteractiveServerView.tsx +69 -0
  83. package/src/components/output/KnowledgeView.tsx +220 -0
  84. package/src/components/output/MarkdownView.tsx +15 -0
  85. package/src/components/output/ModelSelectView.tsx +71 -0
  86. package/src/components/output/ReasoningView.tsx +21 -0
  87. package/src/components/output/ToolCallView.tsx +45 -0
  88. package/src/components/settings/ModelList.tsx +250 -0
  89. package/src/components/settings/TokenUsage.tsx +274 -0
  90. package/src/config/schema.ts +19 -0
  91. package/src/config/settings.ts +229 -0
  92. package/src/index.tsx +100 -0
  93. package/src/parsers/tree-sitter-python.wasm +0 -0
  94. package/src/providers/providers.ts +71 -0
  95. package/src/services/PluginLoader.ts +123 -0
  96. package/src/services/cerebras.ts +69 -0
  97. package/src/services/embeddingService.ts +229 -0
  98. package/src/services/google.ts +65 -0
  99. package/src/services/graphSerializer.ts +248 -0
  100. package/src/services/groq.ts +23 -0
  101. package/src/services/knowledgeOrchestrator.ts +286 -0
  102. package/src/services/mistral.ts +79 -0
  103. package/src/services/ollama.ts +109 -0
  104. package/src/services/openrouter.ts +23 -0
  105. package/src/services/symbolExtractor.ts +277 -0
  106. package/src/store/useFraudeStore.ts +123 -0
  107. package/src/store/useSettingsStore.ts +38 -0
  108. package/src/theme.ts +26 -0
  109. package/src/types/Agent.ts +147 -0
  110. package/src/types/CommandDefinition.ts +8 -0
  111. package/src/types/Model.ts +94 -0
  112. package/src/types/OutputItem.ts +24 -0
  113. package/src/types/PluginContext.ts +55 -0
  114. package/src/types/TokenUsage.ts +5 -0
  115. package/src/types/assets.d.ts +4 -0
  116. package/src/utils/agentCognition.ts +1152 -0
  117. package/src/utils/fileSuggestions.ts +111 -0
  118. package/src/utils/index.ts +17 -0
  119. package/src/utils/initFraude.ts +8 -0
  120. package/src/utils/logger.ts +24 -0
  121. package/src/utils/lspClient.ts +1415 -0
  122. package/src/utils/paths.ts +24 -0
  123. package/src/utils/queryHandler.ts +227 -0
  124. package/src/utils/router.ts +278 -0
  125. package/src/utils/streamHandler.ts +132 -0
  126. package/src/utils/treeSitterQueries.ts +125 -0
  127. package/tsconfig.json +33 -0
@@ -0,0 +1,773 @@
1
+ import { BunApiRouter } from "@/utils/router";
2
+ import type { Command } from "@/types/CommandDefinition";
3
+ import log from "@/utils/logger";
4
+ import AgentCognition from "@/utils/agentCognition";
5
+ import { exec } from "child_process";
6
+
7
+ const VISUALIZATION_HTML = `
8
+ <!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <title>Fraude | Neural Matrix</title>
13
+ <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
14
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@300;400;600&display=swap" rel="stylesheet">
15
+ <style>
16
+ :root {
17
+ --bg-dark: #050510;
18
+ --bg-panel: rgba(20, 20, 35, 0.7);
19
+ --border-color: rgba(255, 255, 255, 0.1);
20
+ --accent-primary: #00f2ea;
21
+ --accent-secondary: #7c3aed;
22
+ --text-main: #e0e0e0;
23
+ --text-dim: #94a3b8;
24
+ --glass-blur: blur(12px);
25
+ }
26
+
27
+ * { box-sizing: border-box; outline: none; }
28
+
29
+ body, html {
30
+ height: 100%;
31
+ margin: 0;
32
+ overflow: hidden;
33
+ font-family: 'Inter', sans-serif;
34
+ background-color: var(--bg-dark);
35
+ background-image:
36
+ radial-gradient(circle at 15% 50%, rgba(124, 58, 237, 0.08), transparent 25%),
37
+ radial-gradient(circle at 85% 30%, rgba(0, 242, 234, 0.08), transparent 25%);
38
+ color: var(--text-main);
39
+ }
40
+
41
+ /* --- Canvas --- */
42
+ #mynetwork {
43
+ width: 100%;
44
+ height: 100%;
45
+ position: absolute;
46
+ top: 0;
47
+ left: 0;
48
+ z-index: 1;
49
+ }
50
+
51
+ /* --- UI Layer --- */
52
+ .ui-layer {
53
+ position: absolute;
54
+ top: 0;
55
+ left: 0;
56
+ width: 100%;
57
+ height: 100%;
58
+ pointer-events: none; /* Let clicks pass through to network */
59
+ z-index: 10;
60
+ display: flex;
61
+ flex-direction: column;
62
+ justify-content: space-between;
63
+ padding: 20px;
64
+ }
65
+
66
+ .interactive { pointer-events: auto; }
67
+
68
+ /* --- Header --- */
69
+ .header {
70
+ display: flex;
71
+ justify-content: space-between;
72
+ align-items: center;
73
+ padding: 12px 24px;
74
+ background: var(--bg-panel);
75
+ border: 1px solid var(--border-color);
76
+ border-radius: 16px;
77
+ backdrop-filter: var(--glass-blur);
78
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
79
+ max-width: 1200px;
80
+ margin: 0 auto;
81
+ width: 90%;
82
+ }
83
+
84
+ .brand {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 12px;
88
+ font-family: 'JetBrains Mono', monospace;
89
+ font-weight: 700;
90
+ font-size: 1.1rem;
91
+ color: #fff;
92
+ text-shadow: 0 0 10px rgba(255,255,255,0.3);
93
+ }
94
+
95
+ .brand-dot {
96
+ width: 10px;
97
+ height: 10px;
98
+ background: var(--accent-primary);
99
+ border-radius: 50%;
100
+ box-shadow: 0 0 10px var(--accent-primary);
101
+ animation: pulse 2s infinite;
102
+ }
103
+
104
+ @keyframes pulse {
105
+ 0% { box-shadow: 0 0 0 0 rgba(0, 242, 234, 0.4); }
106
+ 70% { box-shadow: 0 0 0 10px rgba(0, 242, 234, 0); }
107
+ 100% { box-shadow: 0 0 0 0 rgba(0, 242, 234, 0); }
108
+ }
109
+
110
+ /* --- Search Bar --- */
111
+ .search-container {
112
+ position: relative;
113
+ width: 300px;
114
+ }
115
+
116
+ .search-input {
117
+ width: 100%;
118
+ background: rgba(0, 0, 0, 0.3);
119
+ border: 1px solid var(--border-color);
120
+ border-radius: 8px;
121
+ padding: 8px 16px 8px 36px;
122
+ color: #fff;
123
+ font-family: 'Inter', sans-serif;
124
+ font-size: 0.9rem;
125
+ transition: all 0.3s ease;
126
+ }
127
+
128
+ .search-input:focus {
129
+ border-color: var(--accent-primary);
130
+ background: rgba(0, 0, 0, 0.5);
131
+ box-shadow: 0 0 15px rgba(0, 242, 234, 0.1);
132
+ }
133
+
134
+ .search-icon {
135
+ position: absolute;
136
+ left: 10px;
137
+ top: 50%;
138
+ transform: translateY(-50%);
139
+ opacity: 0.5;
140
+ width: 16px;
141
+ height: 16px;
142
+ fill: currentColor;
143
+ }
144
+
145
+ /* --- Stats --- */
146
+ .stats {
147
+ font-family: 'JetBrains Mono', monospace;
148
+ font-size: 0.8rem;
149
+ color: var(--text-dim);
150
+ display: flex;
151
+ gap: 16px;
152
+ }
153
+ .stat-item b { color: #fff; margin-left: 4px; }
154
+
155
+ /* --- Side Panel --- */
156
+ .side-panel {
157
+ position: absolute;
158
+ right: 20px;
159
+ top: 90px;
160
+ bottom: 20px;
161
+ width: 350px;
162
+ background: rgba(13, 17, 26, 0.85); /* Darker, more opaque */
163
+ border-left: 1px solid var(--border-color);
164
+ backdrop-filter: blur(20px);
165
+ transform: translateX(120%);
166
+ transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
167
+ z-index: 20;
168
+ border-radius: 16px;
169
+ display: flex;
170
+ flex-direction: column;
171
+ overflow: hidden;
172
+ box-shadow: -10px 0 30px rgba(0,0,0,0.5);
173
+ }
174
+
175
+ .side-panel.open { transform: translateX(0); }
176
+
177
+ .panel-header {
178
+ padding: 20px;
179
+ border-bottom: 1px solid var(--border-color);
180
+ background: rgba(255,255,255,0.02);
181
+ }
182
+
183
+ .panel-title {
184
+ font-size: 1.2rem;
185
+ font-weight: 600;
186
+ margin: 0 0 4px 0;
187
+ color: #fff;
188
+ }
189
+
190
+ .panel-subtitle {
191
+ font-size: 0.8rem;
192
+ color: var(--text-dim);
193
+ text-transform: uppercase;
194
+ letter-spacing: 1px;
195
+ display: flex;
196
+ align-items: center;
197
+ gap: 6px;
198
+ }
199
+
200
+ .type-badge {
201
+ display: inline-block;
202
+ width: 8px;
203
+ height: 8px;
204
+ border-radius: 50%;
205
+ }
206
+
207
+ .panel-content {
208
+ padding: 20px;
209
+ flex: 1;
210
+ overflow-y: auto;
211
+ font-family: 'JetBrains Mono', monospace;
212
+ font-size: 0.9rem;
213
+ line-height: 1.5;
214
+ }
215
+
216
+ .prop-row {
217
+ margin-bottom: 16px;
218
+ }
219
+
220
+ .prop-label {
221
+ display: block;
222
+ font-size: 0.75rem;
223
+ color: var(--text-dim);
224
+ margin-bottom: 4px;
225
+ }
226
+
227
+ .prop-value {
228
+ color: var(--text-main);
229
+ word-break: break-word;
230
+ background: rgba(0,0,0,0.2);
231
+ padding: 8px;
232
+ border-radius: 6px;
233
+ border: 1px solid var(--border-color);
234
+ }
235
+
236
+ .close-btn {
237
+ position: absolute;
238
+ top: 15px;
239
+ right: 15px;
240
+ background: none;
241
+ border: none;
242
+ color: var(--text-dim);
243
+ cursor: pointer;
244
+ font-size: 1.2rem;
245
+ transition: color 0.2s;
246
+ }
247
+ .close-btn:hover { color: #fff; }
248
+
249
+ /* --- Dock (Legend/Filter) --- */
250
+ .dock-container {
251
+ position: absolute;
252
+ bottom: 30px;
253
+ left: 50%;
254
+ transform: translateX(-50%);
255
+ background: var(--bg-panel);
256
+ padding: 10px 20px;
257
+ border-radius: 20px;
258
+ border: 1px solid var(--border-color);
259
+ backdrop-filter: var(--glass-blur);
260
+ display: flex;
261
+ gap: 8px;
262
+ box-shadow: 0 10px 40px rgba(0,0,0,0.4);
263
+ overflow-x: auto;
264
+ max-width: 90%;
265
+ }
266
+
267
+ .dock-item {
268
+ display: flex;
269
+ align-items: center;
270
+ gap: 6px;
271
+ padding: 6px 12px;
272
+ border-radius: 12px;
273
+ cursor: pointer;
274
+ transition: all 0.2s;
275
+ border: 1px solid transparent;
276
+ white-space: nowrap;
277
+ }
278
+
279
+ .dock-item:hover {
280
+ background: rgba(255,255,255,0.05);
281
+ border-color: rgba(255,255,255,0.1);
282
+ }
283
+
284
+ .dock-item.active {
285
+ background: rgba(255,255,255,0.1);
286
+ border-color: rgba(255,255,255,0.15);
287
+ }
288
+
289
+ .dock-item.faded { opacity: 0.4; }
290
+
291
+ .dock-dot { width: 8px; height: 8px; border-radius: 50%; box-shadow: 0 0 5px currentColor; }
292
+
293
+ /* --- Loading Screen --- */
294
+ #loading {
295
+ position: absolute;
296
+ top: 0; left: 0; width: 100%; height: 100%;
297
+ background: #050510;
298
+ z-index: 100;
299
+ display: flex;
300
+ flex-direction: column;
301
+ align-items: center;
302
+ justify-content: center;
303
+ transition: opacity 0.8s ease;
304
+ }
305
+
306
+ .loader-ring {
307
+ width: 60px; height: 60px;
308
+ border: 2px solid transparent;
309
+ border-top-color: var(--accent-primary);
310
+ border-right-color: var(--accent-secondary);
311
+ border-radius: 50%;
312
+ animation: spin 1s linear infinite;
313
+ margin-bottom: 20px;
314
+ }
315
+
316
+ .loading-text {
317
+ font-family: 'JetBrains Mono', monospace;
318
+ color: var(--accent-primary);
319
+ font-size: 0.9rem;
320
+ letter-spacing: 2px;
321
+ animation: blink 1.5s infinite;
322
+ }
323
+
324
+ @keyframes spin { 100% { transform: rotate(360deg); } }
325
+ @keyframes blink { 50% { opacity: 0.5; } }
326
+
327
+ </style>
328
+ </head>
329
+ <body>
330
+
331
+ <!-- Loading Screen -->
332
+ <div id="loading">
333
+ <div class="loader-ring"></div>
334
+ <div class="loading-text" id="loading-text">INITIALIZING NEURAL MATRIX...</div>
335
+ </div>
336
+
337
+ <!-- Network Container -->
338
+ <div id="mynetwork"></div>
339
+
340
+ <!-- UI Overlay -->
341
+ <div class="ui-layer">
342
+
343
+ <!-- Top Header -->
344
+ <div class="header interactive">
345
+ <div class="brand">
346
+ <div class="brand-dot"></div>
347
+ FRAUDE CODE
348
+ </div>
349
+
350
+ <div class="search-container">
351
+ <svg class="search-icon" viewBox="0 0 24 24"><path d="M21.71 20.29L18 16.61A9 9 0 1 0 16.61 18l3.68 3.68a1 1 0 0 0 1.42 0 1 1 0 0 0 0-1.42zM11 18a7 7 0 1 1 7-7 7 7 0 0 1-7 7z"/></svg>
352
+ <input type="text" class="search-input" placeholder="Search function, file..." id="search-input" oninput="handleSearch(this.value)">
353
+ </div>
354
+
355
+ <div class="stats">
356
+ <span class="stat-item">NODES<b id="node-count">0</b></span>
357
+ <span class="stat-item">RELATIONS<b id="edge-count">0</b></span>
358
+ </div>
359
+ </div>
360
+
361
+ <!-- Side Panel -->
362
+ <div class="side-panel interactive" id="side-panel">
363
+ <button class="close-btn" onclick="closePanel()">×</button>
364
+ <div class="panel-header">
365
+ <h2 class="panel-title" id="panel-title">Node Name</h2>
366
+ <div class="panel-subtitle">
367
+ <span class="type-badge" id="panel-color"></span>
368
+ <span id="panel-type">Type</span>
369
+ </div>
370
+ </div>
371
+ <div class="panel-content">
372
+ <div class="prop-row">
373
+ <span class="prop-label">Full Identifier</span>
374
+ <div class="prop-value" id="panel-id">...</div>
375
+ </div>
376
+ <div class="prop-row">
377
+ <span class="prop-label">Context / Content</span>
378
+ <div class="prop-value" id="panel-desc">...</div>
379
+ </div>
380
+ <div class="prop-row">
381
+ <span class="prop-label">Relationships</span>
382
+ <div id="panel-rels"></div>
383
+ </div>
384
+ </div>
385
+ </div>
386
+
387
+ <!-- Bottom Dock -->
388
+ <div class="dock-container interactive" id="filter-dock">
389
+ <!-- Generated via JS -->
390
+ </div>
391
+
392
+ </div>
393
+
394
+ <script type="text/javascript">
395
+ let network;
396
+ let allNodes;
397
+ let allEdges;
398
+ let nodesDataSet;
399
+ let edgesDataSet;
400
+ const typeColors = {
401
+ // Semantic
402
+ fact: "#00f2ea", // Cyan
403
+ decision: "#39ff14", // Neon Green
404
+ concept: "#ff00ff", // Magenta
405
+ reference: "#ffaa00", // Orange
406
+ // Code
407
+ file: "#58a6ff", // Blue
408
+ module: "#7c3aed", // Violet
409
+ function: "#f97316", // Orange
410
+ class: "#ec4899", // Pink
411
+ interface: "#14b8a6", // Teal
412
+ variable: "#a855f7", // Purple
413
+ symbol: "#94a3b8" // Slate
414
+ };
415
+
416
+ async function fetchGraph() {
417
+ try {
418
+ const response = await fetch('/api/graph');
419
+ const data = await response.json();
420
+
421
+ // Update Stats
422
+ document.getElementById('node-count').innerText = data.nodes.length;
423
+ document.getElementById('edge-count').innerText = data.edges.length;
424
+
425
+ initGraph(data.nodes, data.edges);
426
+ generateDock(data.nodes);
427
+ } catch (error) {
428
+ document.getElementById('loading-text').textContent = 'SYSTEM FAILURE: ' + error.message;
429
+ document.getElementById('loading-text').style.color = '#ff5555';
430
+ }
431
+ }
432
+
433
+ function initGraph(nodes, edges) {
434
+ var container = document.getElementById('mynetwork');
435
+
436
+ nodesDataSet = new vis.DataSet(nodes);
437
+ edgesDataSet = new vis.DataSet(edges);
438
+
439
+ var data = { nodes: nodesDataSet, edges: edgesDataSet };
440
+
441
+ var options = {
442
+ nodes: {
443
+ shape: 'dot',
444
+ size: 20,
445
+ font: {
446
+ color: '#e0e0e0',
447
+ size: 14,
448
+ face: 'Inter',
449
+ strokeWidth: 4,
450
+ strokeColor: '#050510',
451
+ vadjust: -35 // Push label above node
452
+ },
453
+ borderWidth: 0,
454
+ shadow: {
455
+ enabled: true,
456
+ color: 'rgba(0,0,0,0.5)',
457
+ size: 10,
458
+ x: 0, y: 5
459
+ }
460
+ },
461
+ edges: {
462
+ width: 1,
463
+ color: {
464
+ color: 'rgba(255,255,255,0.15)',
465
+ highlight: '#fff',
466
+ hover: '#fff'
467
+ },
468
+ smooth: { type: 'continuous', roundness: 0.5 },
469
+ selectionWidth: 2
470
+ },
471
+ physics: {
472
+ stabilization: {
473
+ enabled: true,
474
+ iterations: 1000,
475
+ updateInterval: 50
476
+ },
477
+ barnesHut: {
478
+ gravitationalConstant: -10000, // Strong repulsion
479
+ centralGravity: 0.3,
480
+ springLength: 150,
481
+ springConstant: 0.05,
482
+ damping: 0.4
483
+ }
484
+ },
485
+ interaction: {
486
+ hover: true,
487
+ tooltipDelay: 200,
488
+ hideEdgesOnDrag: true
489
+ }
490
+ };
491
+
492
+ network = new vis.Network(container, data, options);
493
+
494
+ // Fade out loading screen when stabilized or after a timeout
495
+ network.once("stabilizationIterationsDone", finishLoading);
496
+ setTimeout(finishLoading, 4000); // Fallback
497
+
498
+ // Click Event
499
+ network.on("click", function (params) {
500
+ if (params.nodes.length > 0) {
501
+ const nodeId = params.nodes[0];
502
+ const node = nodesDataSet.get(nodeId);
503
+ openPanel(node);
504
+ } else {
505
+ closePanel();
506
+ }
507
+ });
508
+
509
+ // Hover Event (Glow effect)
510
+ network.on("hoverNode", function (params) {
511
+ container.style.cursor = 'pointer';
512
+ });
513
+ network.on("blurNode", function () {
514
+ container.style.cursor = 'default';
515
+ });
516
+
517
+ // Custom Rendering for "Neon Glow"
518
+ network.on("beforeDrawing", function(ctx) {
519
+ // Optional: Add global glow or background effects here
520
+ });
521
+ }
522
+
523
+ function finishLoading() {
524
+ const el = document.getElementById('loading');
525
+ if (el.style.opacity !== '0') {
526
+ el.style.opacity = '0';
527
+ setTimeout(() => el.style.display = 'none', 800);
528
+ network.fit();
529
+ }
530
+ }
531
+
532
+ // --- Search Logic ---
533
+ function handleSearch(query) {
534
+ if (!query) {
535
+ // Reset view
536
+ return;
537
+ }
538
+ const lower = query.toLowerCase();
539
+ const matches = nodesDataSet.get({
540
+ filter: function (item) {
541
+ return (item.label && item.label.toLowerCase().includes(lower));
542
+ }
543
+ });
544
+
545
+ if (matches.length > 0) {
546
+ network.selectNodes(matches.map(n => n.id));
547
+ if (matches.length === 1) {
548
+ network.focus(matches[0].id, { scale: 1.5, animation: true });
549
+ openPanel(matches[0]);
550
+ }
551
+ } else {
552
+ network.unselectAll();
553
+ }
554
+ }
555
+
556
+ // --- Side Panel Logic ---
557
+ function openPanel(node) {
558
+ const panel = document.getElementById('side-panel');
559
+ document.getElementById('panel-title').textContent = node.label || 'Unknown Node';
560
+ document.getElementById('panel-type').textContent = node.group;
561
+ document.getElementById('panel-color').style.backgroundColor = node.color.background;
562
+ document.getElementById('panel-id').textContent = node.id;
563
+ document.getElementById('panel-desc').textContent = node.title || 'No details available.';
564
+
565
+ // Find relationships
566
+ const connectedEdges = edgesDataSet.get({
567
+ filter: function (item) {
568
+ return item.from === node.id || item.to === node.id;
569
+ }
570
+ });
571
+
572
+ const relsHtml = connectedEdges.map(edge => {
573
+ const isSource = edge.from === node.id;
574
+ const otherId = isSource ? edge.to : edge.from;
575
+ const otherNode = nodesDataSet.get(otherId);
576
+ const direction = isSource ? "→" : "←";
577
+ return \`<div style="margin-top:4px; font-size:0.8rem; border-left: 2px solid rgba(255,255,255,0.1); padding-left:8px;">
578
+ <span style="color:var(--text-dim)">\${direction} [\${edge.label}]</span>
579
+ <span style="color:var(--accent-primary)">\${otherNode ? otherNode.label : otherId}</span>
580
+ </div>\`;
581
+ }).join('');
582
+
583
+ document.getElementById('panel-rels').innerHTML = relsHtml || '<span style="color:var(--text-dim); font-size: 0.8rem">No connections</span>';
584
+
585
+ panel.classList.add('open');
586
+ }
587
+
588
+ function closePanel() {
589
+ document.getElementById('side-panel').classList.remove('open');
590
+ network.unselectAll();
591
+ }
592
+
593
+ // --- Dock / Filter Logic ---
594
+ function generateDock(nodes) {
595
+ const types = [...new Set(nodes.map(n => n.group))];
596
+ const dock = document.getElementById('filter-dock');
597
+ dock.innerHTML = '';
598
+
599
+ types.forEach(type => {
600
+ const color = typeColors[type] || '#999';
601
+
602
+ const item = document.createElement('div');
603
+ item.className = 'dock-item active';
604
+ item.innerHTML = \`<div class="dock-dot" style="background:\${color}; box-shadow: 0 0 8px \${color}"></div> \${type}\`;
605
+ item.onclick = () => toggleFilter(type, item);
606
+ dock.appendChild(item);
607
+ });
608
+ }
609
+
610
+ const activeFilters = new Set();
611
+
612
+ // Initialize with all active
613
+ setTimeout(() => {
614
+ const types = document.querySelectorAll('.dock-item');
615
+ types.forEach(t => activeFilters.add(t.textContent.trim()));
616
+ }, 1000);
617
+
618
+ function toggleFilter(type, element) {
619
+ // NOTE: For a real filter, we'd use a DataView or hide nodes.
620
+ // For this simplified version, we'll just dim them or hide them.
621
+ // Re-implementing visually:
622
+
623
+ if (element.classList.contains('active')) {
624
+ element.classList.remove('active');
625
+ element.classList.add('faded');
626
+ // Hide nodes
627
+ const toHide = nodesDataSet.get({ filter: n => n.group === type });
628
+ toHide.forEach(n => {
629
+ nodesDataSet.update({id: n.id, hidden: true});
630
+ });
631
+ } else {
632
+ element.classList.add('active');
633
+ element.classList.remove('faded');
634
+ // Show nodes
635
+ const toShow = nodesDataSet.get({ filter: n => n.group === type });
636
+ toShow.forEach(n => {
637
+ nodesDataSet.update({id: n.id, hidden: false});
638
+ });
639
+ }
640
+ }
641
+
642
+ fetchGraph();
643
+ </script>
644
+ </body>
645
+ </html>
646
+ `;
647
+
648
+ const command: Command = {
649
+ name: "visualize",
650
+ description: "Opens a browser visualization of the knowledge graph",
651
+ usage: "/visualize",
652
+ action: async (args: string[]) => {
653
+ const port = args[0] ? parseInt(args[0], 10) : 3001;
654
+ const cognition = AgentCognition.getInstance();
655
+ const router = new BunApiRouter();
656
+
657
+ // Register API endpoint for graph data
658
+ router.register("GET", "/api/graph", async () => {
659
+ try {
660
+ // Fetch all nodes
661
+ const nodesResult = await cognition.query(`
662
+ MATCH (n:Fact)
663
+ RETURN n.id as id, n.type as type, n.content as content
664
+ `);
665
+
666
+ // Fetch all relationships
667
+ const edgesResult = await cognition.query(`
668
+ MATCH (a:Fact)-[r:RELATED_TO]->(b:Fact)
669
+ RETURN a.id as from, b.id as to, r.relation as label
670
+ `);
671
+
672
+ // Format for vis-network
673
+ const typeColors: Record<
674
+ string,
675
+ { background: string; border?: string; highlight?: object }
676
+ > = {
677
+ // Semantic types
678
+ fact: { background: "#00f2ea" },
679
+ decision: { background: "#39ff14" },
680
+ concept: { background: "#ff00ff" },
681
+ reference: { background: "#ffaa00" },
682
+ // Code entity types
683
+ file: { background: "#58a6ff" },
684
+ module: { background: "#7c3aed" },
685
+ function: { background: "#f97316" },
686
+ class: { background: "#ec4899" },
687
+ interface: { background: "#14b8a6" },
688
+ variable: { background: "#a855f7" },
689
+ symbol: { background: "#94a3b8" },
690
+ };
691
+
692
+ const nodes = nodesResult.map((row: any) => {
693
+ const baseColor = typeColors[row.type]?.background || "#97c2fc";
694
+
695
+ return {
696
+ id: row.id,
697
+ label:
698
+ row.content.length > 25
699
+ ? row.content.substring(0, 25) + "..."
700
+ : row.content,
701
+ title: row.content, // Tooltip content
702
+ group: row.type,
703
+ color: {
704
+ background: baseColor,
705
+ border: baseColor,
706
+ highlight: {
707
+ background: "#fff",
708
+ border: "#fff",
709
+ },
710
+ },
711
+ // Custom shadows for glow effect
712
+ shadow: {
713
+ enabled: true,
714
+ color: baseColor,
715
+ size: 10,
716
+ x: 0,
717
+ y: 0,
718
+ },
719
+ };
720
+ });
721
+
722
+ const edges = edgesResult.map((row: any) => ({
723
+ from: row.from,
724
+ to: row.to,
725
+ label: row.label,
726
+ color: { color: "rgba(255,255,255,0.1)" },
727
+ font: {
728
+ align: "middle",
729
+ size: 10,
730
+ strokeWidth: 0,
731
+ color: "#8b949e",
732
+ background: "transparent",
733
+ },
734
+ }));
735
+
736
+ return new Response(JSON.stringify({ nodes, edges }), {
737
+ headers: { "Content-Type": "application/json" },
738
+ });
739
+ } catch (error) {
740
+ log("Error fetching graph data:", error);
741
+ return new Response(JSON.stringify({ error: String(error) }), {
742
+ status: 500,
743
+ headers: { "Content-Type": "application/json" },
744
+ });
745
+ }
746
+ });
747
+
748
+ // Register Visualization IO
749
+ router.register("GET", "/visualize", () => {
750
+ return new Response(VISUALIZATION_HTML, {
751
+ headers: { "Content-Type": "text/html" },
752
+ });
753
+ });
754
+
755
+ log(`Starting visualization server on port ${port}...`);
756
+
757
+ const url = `http://localhost:${port}/visualize`;
758
+ log(`Opening ${url} ...`);
759
+
760
+ exec(`open ${url}`, (err) => {
761
+ if (err) log("Failed to open browser:", err);
762
+ });
763
+
764
+ try {
765
+ await router.serve(port);
766
+ log(`Visualization server on port ${port} stopped.`);
767
+ } catch (error) {
768
+ log("Server error:", error);
769
+ }
770
+ },
771
+ };
772
+
773
+ export default command;