pikakit 1.0.14 → 1.0.15

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.
@@ -0,0 +1,324 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dashboard Server v7.0 - PikaKit Precision Learning Engine
4
+ *
5
+ * Modern ES Modules server with REST API endpoints.
6
+ * Serves real-time metrics from PikaKit learning system.
7
+ *
8
+ * @version 7.0.0
9
+ * @author PikaKit
10
+ */
11
+
12
+ import http from 'http';
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+
20
+ // Colors for terminal
21
+ const c = {
22
+ reset: '\x1b[0m',
23
+ bold: '\x1b[1m',
24
+ cyan: '\x1b[36m',
25
+ green: '\x1b[32m',
26
+ yellow: '\x1b[33m',
27
+ gray: '\x1b[90m'
28
+ };
29
+
30
+ // Find project root
31
+ function findProjectRoot() {
32
+ let dir = process.cwd();
33
+ while (dir !== path.dirname(dir)) {
34
+ if (fs.existsSync(path.join(dir, '.agent'))) return dir;
35
+ if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
36
+ dir = path.dirname(dir);
37
+ }
38
+ return process.cwd();
39
+ }
40
+
41
+ const projectRoot = findProjectRoot();
42
+ const dashboardPath = path.join(__dirname, '..', 'dashboard');
43
+
44
+ // ============================================================================
45
+ // DATA PROVIDERS
46
+ // ============================================================================
47
+
48
+ // Safe import helper
49
+ async function safeImport(modulePath) {
50
+ try {
51
+ return await import(modulePath);
52
+ } catch (e) {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ // Load data modules dynamically
58
+ let metricsCollector = null;
59
+ let causalityEngine = null;
60
+ let skillGenerator = null;
61
+ let abTesting = null;
62
+ let reinforcement = null;
63
+
64
+ async function loadModules() {
65
+ const libPath = path.join(__dirname, '..', 'lib');
66
+ metricsCollector = await safeImport(path.join(libPath, 'metrics-collector.js'));
67
+ causalityEngine = await safeImport(path.join(libPath, 'causality-engine.js'));
68
+ skillGenerator = await safeImport(path.join(libPath, 'skill-generator.js'));
69
+ abTesting = await safeImport(path.join(libPath, 'ab-testing.js'));
70
+ reinforcement = await safeImport(path.join(libPath, 'reinforcement-loop.js'));
71
+ }
72
+
73
+ // ============================================================================
74
+ // API HANDLERS
75
+ // ============================================================================
76
+
77
+ const api = {
78
+ // Full dashboard data
79
+ '/api/dashboard': () => {
80
+ try {
81
+ const realKpis = metricsCollector?.getKPIs?.() || null;
82
+ const hasRealData = realKpis?.kpis && Object.keys(realKpis.kpis).length > 0;
83
+
84
+ const kpis = hasRealData ? realKpis.kpis : {};
85
+
86
+ const totalTasks = metricsCollector?.getMetricValue?.('total_tasks') || 0;
87
+ const patternsLearned = causalityEngine?.getPatternCount?.() || 0;
88
+ const skillsGenerated = skillGenerator?.getSkillCount?.() || 0;
89
+
90
+ // Determine if this is a new user (no data yet)
91
+ const isNewUser = totalTasks === 0 && patternsLearned === 0;
92
+
93
+ const summary = {
94
+ totalTasks,
95
+ patternsLearned,
96
+ skillsGenerated,
97
+ version: '7.0.0',
98
+ isNewUser
99
+ };
100
+ return { kpis: { kpis }, summary, version: '7.0.0', isNewUser };
101
+ } catch (e) {
102
+ return { kpis: { kpis: {} }, summary: { isNewUser: true }, error: e.message, version: '7.0.0', isNewUser: true };
103
+ }
104
+ },
105
+
106
+ // KPIs only
107
+ '/api/kpis': () => {
108
+ try {
109
+ return metricsCollector?.getKPIs?.() || { kpis: {} };
110
+ } catch (e) {
111
+ return { kpis: {}, error: e.message };
112
+ }
113
+ },
114
+
115
+ // Alerts
116
+ '/api/alerts': () => {
117
+ try {
118
+ const kpis = metricsCollector?.getKPIs?.()?.kpis || {};
119
+ const alerts = [];
120
+
121
+ // Generate alerts based on KPI thresholds
122
+ if (kpis.task_success_rate && parseFloat(kpis.task_success_rate.value) < 80) {
123
+ alerts.push({ id: 'low_success', severity: 'warning', message: 'Task success rate below 80%' });
124
+ }
125
+ if (kpis.error_repeat_rate && parseFloat(kpis.error_repeat_rate.value) > 10) {
126
+ alerts.push({ id: 'high_error', severity: 'warning', message: 'Error repeat rate above 10%' });
127
+ }
128
+
129
+ return { alerts, count: alerts.length };
130
+ } catch (e) {
131
+ return { alerts: [], error: e.message };
132
+ }
133
+ },
134
+
135
+ // Skills
136
+ '/api/skills': () => {
137
+ try {
138
+ const skills = skillGenerator?.getAllSkills?.() || [];
139
+ return {
140
+ skills,
141
+ stats: { total: skills.length }
142
+ };
143
+ } catch (e) {
144
+ return { skills: [], stats: { total: 0 }, error: e.message };
145
+ }
146
+ },
147
+
148
+ // A/B Testing
149
+ '/api/ab-testing': () => {
150
+ try {
151
+ const active = abTesting?.getActiveTests?.() || [];
152
+ const completed = abTesting?.getCompletedTests?.() || [];
153
+ return {
154
+ active,
155
+ completed,
156
+ stats: { running: active.length, completed: completed.length }
157
+ };
158
+ } catch (e) {
159
+ return { active: [], completed: [], stats: { running: 0, completed: 0 }, error: e.message };
160
+ }
161
+ },
162
+
163
+ // Reinforcement Loop
164
+ '/api/reinforcement': () => {
165
+ try {
166
+ const stats = reinforcement?.getStats?.() || {};
167
+ return {
168
+ totalRewards: stats.rewards || 0,
169
+ totalPenalties: stats.penalties || 0,
170
+ averageConfidence: stats.avgConfidence || null
171
+ };
172
+ } catch (e) {
173
+ return { totalRewards: 0, totalPenalties: 0, averageConfidence: null };
174
+ }
175
+ },
176
+
177
+ // Patterns
178
+ '/api/patterns': () => {
179
+ try {
180
+ const patterns = causalityEngine?.loadCausalPatterns?.() || [];
181
+ return { total: patterns.length, patterns: patterns.slice(0, 20) };
182
+ } catch (e) {
183
+ return { total: 0, patterns: [], error: e.message };
184
+ }
185
+ },
186
+
187
+ // Summary (legacy support)
188
+ '/api/summary': () => {
189
+ return { status: 'ok', version: '7.0.0', server: 'PikaKit Dashboard Server' };
190
+ }
191
+ };
192
+
193
+ // ============================================================================
194
+ // MIME TYPES
195
+ // ============================================================================
196
+
197
+ const mimeTypes = {
198
+ '.html': 'text/html',
199
+ '.css': 'text/css',
200
+ '.js': 'application/javascript',
201
+ '.json': 'application/json',
202
+ '.png': 'image/png',
203
+ '.jpg': 'image/jpeg',
204
+ '.svg': 'image/svg+xml'
205
+ };
206
+
207
+ // ============================================================================
208
+ // SERVER
209
+ // ============================================================================
210
+
211
+ function createServer(port) {
212
+ const server = http.createServer(async (req, res) => {
213
+ // CORS headers
214
+ res.setHeader('Access-Control-Allow-Origin', '*');
215
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
216
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
217
+
218
+ if (req.method === 'OPTIONS') {
219
+ res.writeHead(204);
220
+ res.end();
221
+ return;
222
+ }
223
+
224
+ const url = new URL(req.url, `http://localhost:${port}`);
225
+ const pathname = url.pathname;
226
+
227
+ // API routes
228
+ if (api[pathname]) {
229
+ const data = api[pathname](url.searchParams);
230
+ res.writeHead(200, { 'Content-Type': 'application/json' });
231
+ res.end(JSON.stringify(data, null, 2));
232
+ return;
233
+ }
234
+
235
+ // Static files
236
+ let filePath = pathname === '/' ? 'index.html' : pathname.slice(1);
237
+ filePath = path.join(dashboardPath, filePath);
238
+
239
+ if (fs.existsSync(filePath)) {
240
+ const ext = path.extname(filePath);
241
+ const contentType = mimeTypes[ext] || 'application/octet-stream';
242
+ const content = fs.readFileSync(filePath);
243
+ res.writeHead(200, { 'Content-Type': contentType });
244
+ res.end(content);
245
+ return;
246
+ }
247
+
248
+ // 404
249
+ res.writeHead(404, { 'Content-Type': 'application/json' });
250
+ res.end(JSON.stringify({ error: 'Not found', path: pathname }));
251
+ });
252
+
253
+ return server;
254
+ }
255
+
256
+ async function startServer(port = 3030) {
257
+ await loadModules();
258
+
259
+ const server = createServer(port);
260
+
261
+ server.listen(port, () => {
262
+ console.log(`
263
+ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
264
+ 🧠 ${c.bold}PikaKit Dashboard Server v7.0${c.reset}
265
+ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
266
+
267
+ ${c.green}→${c.reset} Dashboard: ${c.yellow}http://localhost:${port}${c.reset}
268
+ ${c.green}→${c.reset} API: ${c.yellow}http://localhost:${port}/api/dashboard${c.reset}
269
+
270
+ ${c.gray}Press Ctrl+C to stop${c.reset}
271
+
272
+ ${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
273
+ `);
274
+ });
275
+
276
+ server.on('error', (e) => {
277
+ if (e.code === 'EADDRINUSE') {
278
+ console.log(`${c.yellow}Port ${port} in use, trying ${port + 1}...${c.reset}`);
279
+ startServer(port + 1);
280
+ } else {
281
+ console.error(`${c.red}Server error:${c.reset}`, e.message);
282
+ }
283
+ });
284
+
285
+ return server;
286
+ }
287
+
288
+ // CLI handling
289
+ const args = process.argv.slice(2);
290
+
291
+ if (args.includes('--help') || args.includes('-h')) {
292
+ console.log(`
293
+ ${c.bold}PikaKit Dashboard Server v7.0${c.reset}
294
+
295
+ ${c.bold}Usage:${c.reset}
296
+ node dashboard-server.js [options]
297
+
298
+ ${c.bold}Options:${c.reset}
299
+ --port, -p <number> Port to run on (default: 3030)
300
+ --help, -h Show this help
301
+
302
+ ${c.bold}API Endpoints:${c.reset}
303
+ GET /api/dashboard Full dashboard data
304
+ GET /api/kpis KPI metrics only
305
+ GET /api/alerts Active alerts
306
+ GET /api/skills Auto-generated skills
307
+ GET /api/ab-testing A/B test experiments
308
+ GET /api/reinforcement Reinforcement loop stats
309
+ GET /api/patterns Causal patterns
310
+ GET /api/summary Server status
311
+
312
+ ${c.bold}Example:${c.reset}
313
+ node dashboard-server.js --port 3030
314
+ `);
315
+ } else {
316
+ let port = 3030;
317
+ const portIdx = args.findIndex(a => a === '--port' || a === '-p');
318
+ if (portIdx !== -1 && args[portIdx + 1]) {
319
+ port = parseInt(args[portIdx + 1], 10) || 3030;
320
+ }
321
+ startServer(port);
322
+ }
323
+
324
+ export { createServer, startServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pikakit",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Enterprise-grade Agent Skill Manager with Antigravity Skills support, Progressive Disclosure detection, and semantic routing validation",
5
5
  "license": "MIT",
6
6
  "author": "pikakit <pikakit@gmail.com>",
@@ -78,4 +78,4 @@
78
78
  "prettier": "^3.2.5",
79
79
  "vitest": "^4.0.18"
80
80
  }
81
- }
81
+ }
@@ -1,344 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Dashboard Server v6.0 - AutoLearn Precision Learning Engine
4
- *
5
- * Serves real-time metrics from the new v6.0 modules:
6
- * - metrics-collector.js (18 KPIs)
7
- * - dashboard-data.js (aggregation)
8
- * - causality-engine.js (patterns)
9
- * - reinforcement.js (loop stats)
10
- * - ab-testing.js (experiment stats)
11
- * - precision-skill-generator.js (skills)
12
- *
13
- * Usage:
14
- * node dashboard_server.js --start
15
- * node dashboard_server.js --port 3030
16
- */
17
-
18
- import fs from 'fs';
19
- import path from 'path';
20
- import http from 'http';
21
- import { fileURLToPath } from 'url';
22
-
23
- // Import v6.0 modules
24
- import { getDashboardData, getSummary, getMetricHistory } from '../lib/metrics-collector.js';
25
- import { getFullDashboardData, getKeyTrends, generateAlerts, getGaugeWidgets, getCounterWidgets } from '../lib/dashboard-data.js';
26
- import { getReinforcementStats } from '../lib/reinforcement.js';
27
- import { getABTestStats, getActiveTests } from '../lib/ab-testing.js';
28
- import { getSkillStats, loadAutoSkills } from '../lib/precision-skill-generator.js';
29
- import { loadCausalPatterns } from '../lib/causality-engine.js';
30
-
31
- const __filename = fileURLToPath(import.meta.url);
32
- const __dirname = path.dirname(__filename);
33
-
34
- // Colors
35
- const c = {
36
- reset: '\x1b[0m',
37
- red: '\x1b[31m',
38
- green: '\x1b[32m',
39
- yellow: '\x1b[33m',
40
- cyan: '\x1b[36m',
41
- gray: '\x1b[90m',
42
- bold: '\x1b[1m'
43
- };
44
-
45
- // Find project root
46
- function findProjectRoot() {
47
- let current = process.cwd();
48
- while (current !== path.dirname(current)) {
49
- if (fs.existsSync(path.join(current, '.agent'))) {
50
- return current;
51
- }
52
- current = path.dirname(current);
53
- }
54
- return process.cwd();
55
- }
56
-
57
- const projectRoot = findProjectRoot();
58
- const dashboardPath = path.join(__dirname, '..', 'dashboard');
59
-
60
- // ============================================================================
61
- // API v6.0 HANDLERS
62
- // ============================================================================
63
-
64
- const api = {
65
- // Full dashboard data (all metrics aggregated)
66
- '/api/dashboard': () => {
67
- try {
68
- return getFullDashboardData();
69
- } catch (e) {
70
- return { error: e.message, version: '6.0.0' };
71
- }
72
- },
73
-
74
- // KPIs only
75
- '/api/kpis': () => {
76
- try {
77
- return getDashboardData();
78
- } catch (e) {
79
- return { error: e.message };
80
- }
81
- },
82
-
83
- // Summary stats
84
- '/api/summary': () => {
85
- try {
86
- return getSummary();
87
- } catch (e) {
88
- return { error: e.message };
89
- }
90
- },
91
-
92
- // Trends over time
93
- '/api/trends': () => {
94
- try {
95
- return getKeyTrends();
96
- } catch (e) {
97
- return { error: e.message };
98
- }
99
- },
100
-
101
- // Active alerts
102
- '/api/alerts': () => {
103
- try {
104
- return { alerts: generateAlerts() };
105
- } catch (e) {
106
- return { alerts: [], error: e.message };
107
- }
108
- },
109
-
110
- // Gauge widget data
111
- '/api/gauges': () => {
112
- try {
113
- return { gauges: getGaugeWidgets() };
114
- } catch (e) {
115
- return { gauges: [], error: e.message };
116
- }
117
- },
118
-
119
- // Counter widget data
120
- '/api/counters': () => {
121
- try {
122
- return { counters: getCounterWidgets() };
123
- } catch (e) {
124
- return { counters: [], error: e.message };
125
- }
126
- },
127
-
128
- // Reinforcement loop stats
129
- '/api/reinforcement': () => {
130
- try {
131
- return getReinforcementStats();
132
- } catch (e) {
133
- return { error: e.message };
134
- }
135
- },
136
-
137
- // A/B testing stats
138
- '/api/ab-testing': () => {
139
- try {
140
- return {
141
- stats: getABTestStats(),
142
- active: getActiveTests()
143
- };
144
- } catch (e) {
145
- return { error: e.message };
146
- }
147
- },
148
-
149
- // Auto-generated skills
150
- '/api/skills': () => {
151
- try {
152
- return {
153
- stats: getSkillStats(),
154
- skills: loadAutoSkills()
155
- };
156
- } catch (e) {
157
- return { error: e.message };
158
- }
159
- },
160
-
161
- // Causal patterns
162
- '/api/patterns': () => {
163
- try {
164
- const patterns = loadCausalPatterns();
165
- return {
166
- total: patterns.length,
167
- patterns: patterns.slice(0, 20) // Limit to 20 most recent
168
- };
169
- } catch (e) {
170
- return { total: 0, patterns: [], error: e.message };
171
- }
172
- },
173
-
174
- // Metric history (query param: ?metric=task_success_rate&limit=168)
175
- '/api/history': (query) => {
176
- try {
177
- const metric = query.get('metric') || 'task_success_rate';
178
- const limit = parseInt(query.get('limit') || '168', 10);
179
- return {
180
- metric,
181
- history: getMetricHistory(metric, limit)
182
- };
183
- } catch (e) {
184
- return { error: e.message };
185
- }
186
- }
187
- };
188
-
189
- // ============================================================================
190
- // LEGACY API COMPATIBILITY (v4.0 endpoints that still work)
191
- // ============================================================================
192
-
193
- const legacyApi = {
194
- '/api/errors': () => ({
195
- deprecation: 'Use /api/patterns instead',
196
- redirect: '/api/patterns'
197
- }),
198
- '/api/corrections': () => ({
199
- deprecation: 'Use /api/patterns instead',
200
- redirect: '/api/patterns'
201
- }),
202
- '/api/lessons': () => {
203
- const filePath = path.join(projectRoot, '.agent', 'knowledge', 'lessons-learned.json');
204
- try {
205
- if (fs.existsSync(filePath)) {
206
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
207
- }
208
- } catch { }
209
- return { lessons: [] };
210
- }
211
- };
212
-
213
- // MIME types
214
- const mimeTypes = {
215
- '.html': 'text/html',
216
- '.css': 'text/css',
217
- '.js': 'application/javascript',
218
- '.json': 'application/json',
219
- '.png': 'image/png',
220
- '.jpg': 'image/jpeg',
221
- '.svg': 'image/svg+xml'
222
- };
223
-
224
- // Create server
225
- function createServer(port) {
226
- const server = http.createServer((req, res) => {
227
- const urlParts = req.url.split('?');
228
- const url = urlParts[0];
229
- const query = new URLSearchParams(urlParts[1] || '');
230
-
231
- // CORS headers for local development
232
- res.setHeader('Access-Control-Allow-Origin', '*');
233
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
234
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
235
-
236
- // Handle API requests
237
- if (url.startsWith('/api/')) {
238
- const handler = api[url] || legacyApi[url];
239
- if (handler) {
240
- res.setHeader('Content-Type', 'application/json');
241
- res.writeHead(200);
242
- res.end(JSON.stringify(handler(query)));
243
- } else {
244
- res.writeHead(404);
245
- res.end(JSON.stringify({ error: 'Not found', availableEndpoints: Object.keys(api) }));
246
- }
247
- return;
248
- }
249
-
250
- // Serve static files
251
- let filePath = url === '/' ? '/index.html' : url;
252
- filePath = path.join(dashboardPath, filePath);
253
-
254
- if (fs.existsSync(filePath)) {
255
- const ext = path.extname(filePath);
256
- const mimeType = mimeTypes[ext] || 'text/plain';
257
-
258
- res.setHeader('Content-Type', mimeType);
259
- res.writeHead(200);
260
- res.end(fs.readFileSync(filePath));
261
- } else {
262
- res.writeHead(404);
263
- res.end('Not found');
264
- }
265
- });
266
-
267
- return server;
268
- }
269
-
270
- function startServer(port = 3030) {
271
- const server = createServer(port);
272
-
273
- server.listen(port, () => {
274
- console.log(`${c.cyan}╔════════════════════════════════════════════════════╗${c.reset}`);
275
- console.log(`${c.cyan}║${c.reset} 🧠 AutoLearn v6.0 Dashboard Server ${c.cyan}║${c.reset}`);
276
- console.log(`${c.cyan}║${c.reset} ${c.green}Precision Learning Engine${c.reset} ${c.cyan}║${c.reset}`);
277
- console.log(`${c.cyan}╚════════════════════════════════════════════════════╝${c.reset}\n`);
278
- console.log(`${c.green}✓ Server running at:${c.reset}`);
279
- console.log(` ${c.bold}http://localhost:${port}${c.reset}\n`);
280
- console.log(`${c.gray}API Endpoints (v6.0):${c.reset}`);
281
- console.log(` GET /api/dashboard - Full dashboard data`);
282
- console.log(` GET /api/kpis - 18 KPIs`);
283
- console.log(` GET /api/summary - Summary stats`);
284
- console.log(` GET /api/trends - Key trends`);
285
- console.log(` GET /api/alerts - Active alerts`);
286
- console.log(` GET /api/reinforcement - Reinforcement loop`);
287
- console.log(` GET /api/ab-testing - A/B experiments`);
288
- console.log(` GET /api/skills - Auto-generated skills`);
289
- console.log(` GET /api/patterns - Causal patterns\n`);
290
- console.log(`${c.yellow}Press Ctrl+C to stop${c.reset}`);
291
- });
292
-
293
- server.on('error', (err) => {
294
- if (err.code === 'EADDRINUSE') {
295
- console.log(`${c.red}Error: Port ${port} is already in use${c.reset}`);
296
- console.log(`${c.gray}Try: node dashboard_server.js --port ${port + 1}${c.reset}`);
297
- } else {
298
- console.error(`${c.red}Server error:${c.reset}`, err);
299
- }
300
- process.exit(1);
301
- });
302
-
303
- return server;
304
- }
305
-
306
- // Parse CLI args
307
- const args = process.argv.slice(2);
308
-
309
- if (args.includes('--start') || args.includes('-s') || args.length === 0 || args.includes('--port') || args.includes('-p')) {
310
- let port = 3030;
311
- const portIdx = args.findIndex(a => a === '--port' || a === '-p');
312
- if (portIdx >= 0 && args[portIdx + 1]) {
313
- port = parseInt(args[portIdx + 1], 10);
314
- }
315
- startServer(port);
316
- } else if (args.includes('--help') || args.includes('-h')) {
317
- console.log(`${c.cyan}AutoLearn v6.0 Dashboard Server${c.reset}
318
-
319
- ${c.bold}Usage:${c.reset}
320
- node dashboard_server.js Start server (default port 3030)
321
- node dashboard_server.js --port 8080 Start on custom port
322
- node dashboard_server.js --help Show this help
323
-
324
- ${c.bold}API Endpoints (v6.0):${c.reset}
325
- GET /api/dashboard - Full dashboard aggregation
326
- GET /api/kpis - 18 KPIs for Dashboard
327
- GET /api/summary - Summary statistics
328
- GET /api/trends - Week-over-week trends
329
- GET /api/alerts - Active alerts
330
- GET /api/gauges - Gauge widget data
331
- GET /api/counters - Counter widget data
332
- GET /api/reinforcement - Reinforcement loop stats
333
- GET /api/ab-testing - A/B testing experiments
334
- GET /api/skills - Auto-generated skills
335
- GET /api/patterns - Causal patterns
336
- GET /api/history?metric=X - Metric history
337
-
338
- ${c.bold}Example:${c.reset}
339
- node dashboard_server.js --port 3030
340
- # Open http://localhost:3030 in browser
341
- `);
342
- }
343
-
344
- export { createServer, startServer };