pikakit 3.0.2 → 3.0.3

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,344 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Auto-Learn Dashboard Server
3
+ * Dashboard Server v6.0 - AutoLearn Precision Learning Engine
4
4
  *
5
- * Simple HTTP server to display lessons, stats, and knowledge base.
6
- * Bundled with pikakit for standalone usage.
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
7
16
  */
8
17
 
9
- import http from "http";
10
- import fs from "fs";
11
- import path from "path";
12
- import { fileURLToPath } from "url";
18
+ import fs from 'fs';
19
+ import path from 'path';
20
+ import http from 'http';
21
+ import { fileURLToPath } from 'url';
13
22
 
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
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';
15
30
 
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;
31
+ const __filename = fileURLToPath(import.meta.url);
32
+ const __dirname = path.dirname(__filename);
20
33
 
21
- // Paths
22
- const AGENT_DIR = path.join(process.cwd(), ".agent");
23
- const KNOWLEDGE_DIR = path.join(AGENT_DIR, "knowledge");
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
+ };
24
44
 
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");
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
+ },
31
91
 
32
- try {
33
- if (fs.existsSync(jsonPath)) {
34
- return JSON.parse(fs.readFileSync(jsonPath, "utf8"));
92
+ // Trends over time
93
+ '/api/trends': () => {
94
+ try {
95
+ return getKeyTrends();
96
+ } catch (e) {
97
+ return { error: e.message };
35
98
  }
36
- if (fs.existsSync(yamlPath)) {
37
- // Simple YAML parsing for basic structures
38
- const content = fs.readFileSync(yamlPath, "utf8");
39
- return { raw: content };
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 };
40
185
  }
41
- } catch (e) {
42
- console.error(`Error loading ${filename}:`, e.message);
43
186
  }
44
- return null;
45
- }
187
+ };
46
188
 
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
- };
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;
63
268
  }
64
269
 
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;
87
- }
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;
95
- }
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;
102
- }
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;
109
- }
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;
116
- }
117
- .lessons h2 { margin-bottom: 1rem; }
118
- .lesson {
119
- padding: 1rem;
120
- border-bottom: 1px solid rgba(255,255,255,0.1);
121
- }
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;
131
- }
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('')
167
- }
168
- </div>
169
- </div>
170
- </body>
171
- </html>`;
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;
172
304
  }
173
305
 
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");
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);
197
314
  }
198
- });
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
+ }
199
343
 
200
- server.listen(PORT, () => {
201
- console.log(`✅ Dashboard server running at http://localhost:${PORT}`);
202
- });
344
+ 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.3"; }
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.3",
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>",