pikakit 3.0.2 → 3.0.4

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.
@@ -1,202 +1,312 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Auto-Learn Dashboard Server
3
+ * Dashboard Server v7.0 - PikaKit Precision Learning Engine
4
4
  *
5
- * Simple HTTP server to display lessons, stats, and knowledge base.
6
- * Bundled with pikakit for standalone usage.
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
7
10
  */
8
11
 
9
- import http from "http";
10
- import fs from "fs";
11
- import path from "path";
12
- import { fileURLToPath } from "url";
12
+ import http from 'http';
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
13
16
 
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
15
19
 
16
- // Parse command line args
17
- const args = process.argv.slice(2);
18
- const portIndex = args.indexOf("--port");
19
- const PORT = portIndex !== -1 ? parseInt(args[portIndex + 1], 10) : 3030;
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
+ };
20
29
 
21
- // Paths
22
- const AGENT_DIR = path.join(process.cwd(), ".agent");
23
- const KNOWLEDGE_DIR = path.join(AGENT_DIR, "knowledge");
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
+ }
24
40
 
25
- /**
26
- * Load JSON/YAML data
27
- */
28
- function loadData(filename) {
29
- const jsonPath = path.join(KNOWLEDGE_DIR, filename + ".json");
30
- const yamlPath = path.join(KNOWLEDGE_DIR, filename + ".yaml");
41
+ const projectRoot = findProjectRoot();
42
+ const dashboardPath = path.join(__dirname, '..', 'dashboard');
43
+
44
+ // ============================================================================
45
+ // DATA PROVIDERS
46
+ // ============================================================================
31
47
 
48
+ // Safe import helper
49
+ async function safeImport(modulePath) {
32
50
  try {
33
- if (fs.existsSync(jsonPath)) {
34
- return JSON.parse(fs.readFileSync(jsonPath, "utf8"));
35
- }
36
- if (fs.existsSync(yamlPath)) {
37
- // Simple YAML parsing for basic structures
38
- const content = fs.readFileSync(yamlPath, "utf8");
39
- return { raw: content };
40
- }
51
+ return await import(modulePath);
41
52
  } catch (e) {
42
- console.error(`Error loading ${filename}:`, e.message);
53
+ return null;
43
54
  }
44
- return null;
45
55
  }
46
56
 
47
- /**
48
- * Get summary stats
49
- */
50
- function getSummary() {
51
- const lessonsData = loadData("lessons-learned");
52
- const metricsData = loadData("metrics");
53
-
54
- const lessons = lessonsData?.lessons || [];
55
-
56
- return {
57
- totalLessons: lessons.length,
58
- mistakes: lessons.filter(l => l.severity === "ERROR" || l.type === "mistake").length,
59
- improvements: lessons.filter(l => l.type === "improvement").length,
60
- lastUpdated: lessonsData?.updatedAt || new Date().toISOString(),
61
- metrics: metricsData || {}
62
- };
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'));
63
71
  }
64
72
 
65
- /**
66
- * HTML Dashboard
67
- */
68
- function renderDashboard() {
69
- const summary = getSummary();
70
- const lessonsData = loadData("lessons-learned");
71
- const lessons = lessonsData?.lessons || [];
72
-
73
- return `<!DOCTYPE html>
74
- <html>
75
- <head>
76
- <meta charset="UTF-8">
77
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
78
- <title>PikaKit Dashboard</title>
79
- <style>
80
- * { box-sizing: border-box; margin: 0; padding: 0; }
81
- body {
82
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
83
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
84
- color: #e8e8e8;
85
- min-height: 100vh;
86
- padding: 2rem;
73
+ // ============================================================================
74
+ // API HANDLERS
75
+ // ============================================================================
76
+
77
+ const api = {
78
+ // Full dashboard data
79
+ '/api/dashboard': () => {
80
+ try {
81
+ const kpis = metricsCollector?.getKPIs?.() || { kpis: {} };
82
+ const summary = {
83
+ totalTasks: metricsCollector?.getMetricValue?.('total_tasks') || 0,
84
+ patternsLearned: causalityEngine?.getPatternCount?.() || 0,
85
+ skillsGenerated: skillGenerator?.getSkillCount?.() || 0,
86
+ version: '7.0.0'
87
+ };
88
+ return { kpis: { kpis }, summary, version: '7.0.0' };
89
+ } catch (e) {
90
+ return { kpis: { kpis: {} }, summary: {}, error: e.message, version: '7.0.0' };
87
91
  }
88
- .container { max-width: 1200px; margin: 0 auto; }
89
- h1 {
90
- font-size: 2.5rem;
91
- margin-bottom: 0.5rem;
92
- background: linear-gradient(90deg, #fff, #888);
93
- -webkit-background-clip: text;
94
- -webkit-text-fill-color: transparent;
92
+ },
93
+
94
+ // KPIs only
95
+ '/api/kpis': () => {
96
+ try {
97
+ return metricsCollector?.getKPIs?.() || { kpis: {} };
98
+ } catch (e) {
99
+ return { kpis: {}, error: e.message };
95
100
  }
96
- .subtitle { color: #888; margin-bottom: 2rem; }
97
- .stats {
98
- display: grid;
99
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
100
- gap: 1rem;
101
- margin-bottom: 2rem;
101
+ },
102
+
103
+ // Alerts
104
+ '/api/alerts': () => {
105
+ try {
106
+ const kpis = metricsCollector?.getKPIs?.()?.kpis || {};
107
+ const alerts = [];
108
+
109
+ // Generate alerts based on KPI thresholds
110
+ if (kpis.task_success_rate && parseFloat(kpis.task_success_rate.value) < 80) {
111
+ alerts.push({ id: 'low_success', severity: 'warning', message: 'Task success rate below 80%' });
112
+ }
113
+ if (kpis.error_repeat_rate && parseFloat(kpis.error_repeat_rate.value) > 10) {
114
+ alerts.push({ id: 'high_error', severity: 'warning', message: 'Error repeat rate above 10%' });
115
+ }
116
+
117
+ return { alerts, count: alerts.length };
118
+ } catch (e) {
119
+ return { alerts: [], error: e.message };
102
120
  }
103
- .stat-card {
104
- background: rgba(255,255,255,0.05);
105
- border: 1px solid rgba(255,255,255,0.1);
106
- border-radius: 12px;
107
- padding: 1.5rem;
108
- text-align: center;
121
+ },
122
+
123
+ // Skills
124
+ '/api/skills': () => {
125
+ try {
126
+ const skills = skillGenerator?.getAllSkills?.() || [];
127
+ return {
128
+ skills,
129
+ stats: { total: skills.length }
130
+ };
131
+ } catch (e) {
132
+ return { skills: [], stats: { total: 0 }, error: e.message };
109
133
  }
110
- .stat-value { font-size: 2.5rem; font-weight: bold; color: #4ade80; }
111
- .stat-label { color: #888; margin-top: 0.5rem; }
112
- .lessons {
113
- background: rgba(255,255,255,0.03);
114
- border-radius: 12px;
115
- padding: 1.5rem;
134
+ },
135
+
136
+ // A/B Testing
137
+ '/api/ab-testing': () => {
138
+ try {
139
+ const active = abTesting?.getActiveTests?.() || [];
140
+ const completed = abTesting?.getCompletedTests?.() || [];
141
+ return {
142
+ active,
143
+ completed,
144
+ stats: { running: active.length, completed: completed.length }
145
+ };
146
+ } catch (e) {
147
+ return { active: [], completed: [], stats: { running: 0, completed: 0 }, error: e.message };
148
+ }
149
+ },
150
+
151
+ // Reinforcement Loop
152
+ '/api/reinforcement': () => {
153
+ try {
154
+ const stats = reinforcement?.getStats?.() || {};
155
+ return {
156
+ totalRewards: stats.rewards || 0,
157
+ totalPenalties: stats.penalties || 0,
158
+ averageConfidence: stats.avgConfidence || null
159
+ };
160
+ } catch (e) {
161
+ return { totalRewards: 0, totalPenalties: 0, error: e.message };
162
+ }
163
+ },
164
+
165
+ // Patterns
166
+ '/api/patterns': () => {
167
+ try {
168
+ const patterns = causalityEngine?.loadCausalPatterns?.() || [];
169
+ return { total: patterns.length, patterns: patterns.slice(0, 20) };
170
+ } catch (e) {
171
+ return { total: 0, patterns: [], error: e.message };
172
+ }
173
+ },
174
+
175
+ // Summary (legacy support)
176
+ '/api/summary': () => {
177
+ return { status: 'ok', version: '7.0.0', server: 'PikaKit Dashboard Server' };
178
+ }
179
+ };
180
+
181
+ // ============================================================================
182
+ // MIME TYPES
183
+ // ============================================================================
184
+
185
+ const mimeTypes = {
186
+ '.html': 'text/html',
187
+ '.css': 'text/css',
188
+ '.js': 'application/javascript',
189
+ '.json': 'application/json',
190
+ '.png': 'image/png',
191
+ '.jpg': 'image/jpeg',
192
+ '.svg': 'image/svg+xml'
193
+ };
194
+
195
+ // ============================================================================
196
+ // SERVER
197
+ // ============================================================================
198
+
199
+ function createServer(port) {
200
+ const server = http.createServer(async (req, res) => {
201
+ // CORS headers
202
+ res.setHeader('Access-Control-Allow-Origin', '*');
203
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
204
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
205
+
206
+ if (req.method === 'OPTIONS') {
207
+ res.writeHead(204);
208
+ res.end();
209
+ return;
116
210
  }
117
- .lessons h2 { margin-bottom: 1rem; }
118
- .lesson {
119
- padding: 1rem;
120
- border-bottom: 1px solid rgba(255,255,255,0.1);
211
+
212
+ const url = new URL(req.url, `http://localhost:${port}`);
213
+ const pathname = url.pathname;
214
+
215
+ // API routes
216
+ if (api[pathname]) {
217
+ const data = api[pathname](url.searchParams);
218
+ res.writeHead(200, { 'Content-Type': 'application/json' });
219
+ res.end(JSON.stringify(data, null, 2));
220
+ return;
121
221
  }
122
- .lesson:last-child { border-bottom: none; }
123
- .lesson-id {
124
- display: inline-block;
125
- background: rgba(74, 222, 128, 0.2);
126
- color: #4ade80;
127
- padding: 0.2rem 0.5rem;
128
- border-radius: 4px;
129
- font-size: 0.85rem;
130
- margin-right: 0.5rem;
222
+
223
+ // Static files
224
+ let filePath = pathname === '/' ? 'index.html' : pathname.slice(1);
225
+ filePath = path.join(dashboardPath, filePath);
226
+
227
+ if (fs.existsSync(filePath)) {
228
+ const ext = path.extname(filePath);
229
+ const contentType = mimeTypes[ext] || 'application/octet-stream';
230
+ const content = fs.readFileSync(filePath);
231
+ res.writeHead(200, { 'Content-Type': contentType });
232
+ res.end(content);
233
+ return;
131
234
  }
132
- .lesson-message { margin-top: 0.5rem; }
133
- .empty { color: #666; font-style: italic; }
134
- </style>
135
- </head>
136
- <body>
137
- <div class="container">
138
- <h1>⚡ PikaKit Dashboard</h1>
139
- <p class="subtitle">Auto-Learn Knowledge Base • v3.0.1</p>
140
-
141
- <div class="stats">
142
- <div class="stat-card">
143
- <div class="stat-value">${summary.totalLessons}</div>
144
- <div class="stat-label">Total Lessons</div>
145
- </div>
146
- <div class="stat-card">
147
- <div class="stat-value" style="color: #f87171;">${summary.mistakes}</div>
148
- <div class="stat-label">Mistakes</div>
149
- </div>
150
- <div class="stat-card">
151
- <div class="stat-value" style="color: #4ade80;">${summary.improvements}</div>
152
- <div class="stat-label">Improvements</div>
153
- </div>
154
- </div>
155
-
156
- <div class="lessons">
157
- <h2>📚 Lessons Learned</h2>
158
- ${lessons.length === 0
159
- ? '<p class="empty">No lessons learned yet. Start learning from your mistakes!</p>'
160
- : lessons.map(l => `
161
- <div class="lesson">
162
- <span class="lesson-id">${l.id}</span>
163
- <span>${l.severity || l.type || 'INFO'}</span>
164
- <p class="lesson-message">${l.message || l.pattern || 'No description'}</p>
165
- </div>
166
- `).join('')
235
+
236
+ // 404
237
+ res.writeHead(404, { 'Content-Type': 'application/json' });
238
+ res.end(JSON.stringify({ error: 'Not found', path: pathname }));
239
+ });
240
+
241
+ return server;
242
+ }
243
+
244
+ async function startServer(port = 3030) {
245
+ await loadModules();
246
+
247
+ const server = createServer(port);
248
+
249
+ server.listen(port, () => {
250
+ console.log(`
251
+ ${c.bold}${c.cyan}╔════════════════════════════════════════════════════════════╗${c.reset}
252
+ ${c.bold}${c.cyan}║${c.reset} ${c.cyan}║${c.reset}
253
+ ${c.bold}${c.cyan}║${c.reset} 🧠 ${c.bold}PikaKit Dashboard Server v7.0${c.reset} ${c.cyan}║${c.reset}
254
+ ${c.bold}${c.cyan}║${c.reset} ${c.cyan}║${c.reset}
255
+ ${c.bold}${c.cyan}║${c.reset} ${c.green}→${c.reset} Dashboard: ${c.yellow}http://localhost:${port}${c.reset} ${c.cyan}║${c.reset}
256
+ ${c.bold}${c.cyan}║${c.reset} ${c.green}→${c.reset} API Base: ${c.yellow}http://localhost:${port}/api${c.reset} ${c.cyan}║${c.reset}
257
+ ${c.bold}${c.cyan}║${c.reset} ${c.cyan}║${c.reset}
258
+ ${c.bold}${c.cyan}║${c.reset} ${c.gray}Press Ctrl+C to stop${c.reset} ${c.cyan}║${c.reset}
259
+ ${c.bold}${c.cyan}║${c.reset} ${c.cyan}║${c.reset}
260
+ ${c.bold}${c.cyan}╚════════════════════════════════════════════════════════════╝${c.reset}
261
+ `);
262
+ });
263
+
264
+ server.on('error', (e) => {
265
+ if (e.code === 'EADDRINUSE') {
266
+ console.log(`${c.yellow}Port ${port} in use, trying ${port + 1}...${c.reset}`);
267
+ startServer(port + 1);
268
+ } else {
269
+ console.error(`${c.red}Server error:${c.reset}`, e.message);
167
270
  }
168
- </div>
169
- </div>
170
- </body>
171
- </html>`;
271
+ });
272
+
273
+ return server;
172
274
  }
173
275
 
174
- /**
175
- * HTTP Server
176
- */
177
- const server = http.createServer((req, res) => {
178
- const url = req.url;
179
-
180
- // CORS headers
181
- res.setHeader("Access-Control-Allow-Origin", "*");
182
- res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
183
-
184
- if (url === "/" || url === "/dashboard") {
185
- res.writeHead(200, { "Content-Type": "text/html" });
186
- res.end(renderDashboard());
187
- } else if (url === "/api/summary") {
188
- res.writeHead(200, { "Content-Type": "application/json" });
189
- res.end(JSON.stringify(getSummary()));
190
- } else if (url === "/api/lessons") {
191
- res.writeHead(200, { "Content-Type": "application/json" });
192
- const data = loadData("lessons-learned");
193
- res.end(JSON.stringify(data?.lessons || []));
194
- } else {
195
- res.writeHead(404);
196
- res.end("Not Found");
276
+ // CLI handling
277
+ const args = process.argv.slice(2);
278
+
279
+ if (args.includes('--help') || args.includes('-h')) {
280
+ console.log(`
281
+ ${c.bold}PikaKit Dashboard Server v7.0${c.reset}
282
+
283
+ ${c.bold}Usage:${c.reset}
284
+ node dashboard-server.js [options]
285
+
286
+ ${c.bold}Options:${c.reset}
287
+ --port, -p <number> Port to run on (default: 3030)
288
+ --help, -h Show this help
289
+
290
+ ${c.bold}API Endpoints:${c.reset}
291
+ GET /api/dashboard Full dashboard data
292
+ GET /api/kpis KPI metrics only
293
+ GET /api/alerts Active alerts
294
+ GET /api/skills Auto-generated skills
295
+ GET /api/ab-testing A/B test experiments
296
+ GET /api/reinforcement Reinforcement loop stats
297
+ GET /api/patterns Causal patterns
298
+ GET /api/summary Server status
299
+
300
+ ${c.bold}Example:${c.reset}
301
+ node dashboard-server.js --port 3030
302
+ `);
303
+ } else {
304
+ let port = 3030;
305
+ const portIdx = args.findIndex(a => a === '--port' || a === '-p');
306
+ if (portIdx !== -1 && args[portIdx + 1]) {
307
+ port = parseInt(args[portIdx + 1], 10) || 3030;
197
308
  }
198
- });
309
+ startServer(port);
310
+ }
199
311
 
200
- server.listen(PORT, () => {
201
- console.log(`✅ Dashboard server running at http://localhost:${PORT}`);
202
- });
312
+ export { createServer, startServer };
@@ -27,7 +27,7 @@ export const RULES_DIR = path.join(AGENT_DIR, "rules");
27
27
  /** CLI version - read from package.json */
28
28
  export const VERSION = (() => {
29
29
  try { return require("../package.json").version; }
30
- catch { return "3.0.2"; }
30
+ catch { return "3.0.4"; }
31
31
  })();
32
32
 
33
33
  /** Debug mode */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pikakit",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
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>",