pikakit 3.0.5 → 3.7.2

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 (100) hide show
  1. package/README.md +1 -1
  2. package/bin/lib/commands/install.js +119 -242
  3. package/package.json +3 -4
  4. package/lib/agent-cli/bin/agent.js +0 -187
  5. package/lib/agent-cli/dashboard/dashboard_server.js +0 -312
  6. package/lib/agent-cli/lib/ab-testing.js +0 -364
  7. package/lib/agent-cli/lib/audit.js +0 -154
  8. package/lib/agent-cli/lib/audit.test.js +0 -100
  9. package/lib/agent-cli/lib/auto-learn.js +0 -319
  10. package/lib/agent-cli/lib/backup.js +0 -138
  11. package/lib/agent-cli/lib/backup.test.js +0 -78
  12. package/lib/agent-cli/lib/causality-engine.js +0 -331
  13. package/lib/agent-cli/lib/cognitive-lesson.js +0 -476
  14. package/lib/agent-cli/lib/completion.js +0 -149
  15. package/lib/agent-cli/lib/config.js +0 -35
  16. package/lib/agent-cli/lib/dashboard-data.js +0 -380
  17. package/lib/agent-cli/lib/eslint-fix.js +0 -238
  18. package/lib/agent-cli/lib/evolution-signal.js +0 -215
  19. package/lib/agent-cli/lib/export.js +0 -86
  20. package/lib/agent-cli/lib/export.test.js +0 -65
  21. package/lib/agent-cli/lib/fix.js +0 -337
  22. package/lib/agent-cli/lib/fix.test.js +0 -80
  23. package/lib/agent-cli/lib/gemini-export.js +0 -83
  24. package/lib/agent-cli/lib/generate-registry.js +0 -42
  25. package/lib/agent-cli/lib/hooks/install-hooks.js +0 -152
  26. package/lib/agent-cli/lib/hooks/lint-learn.js +0 -172
  27. package/lib/agent-cli/lib/icons.js +0 -93
  28. package/lib/agent-cli/lib/ignore.js +0 -116
  29. package/lib/agent-cli/lib/ignore.test.js +0 -58
  30. package/lib/agent-cli/lib/init.js +0 -124
  31. package/lib/agent-cli/lib/knowledge-index.js +0 -326
  32. package/lib/agent-cli/lib/knowledge-metrics.js +0 -335
  33. package/lib/agent-cli/lib/knowledge-retention.js +0 -398
  34. package/lib/agent-cli/lib/knowledge-validator.js +0 -312
  35. package/lib/agent-cli/lib/learn.js +0 -255
  36. package/lib/agent-cli/lib/learn.test.js +0 -70
  37. package/lib/agent-cli/lib/metrics-collector.js +0 -410
  38. package/lib/agent-cli/lib/proposals.js +0 -199
  39. package/lib/agent-cli/lib/proposals.test.js +0 -56
  40. package/lib/agent-cli/lib/recall.js +0 -835
  41. package/lib/agent-cli/lib/recall.test.js +0 -107
  42. package/lib/agent-cli/lib/reinforcement.js +0 -299
  43. package/lib/agent-cli/lib/selfevolution-bridge.js +0 -167
  44. package/lib/agent-cli/lib/settings.js +0 -203
  45. package/lib/agent-cli/lib/skill-generator.js +0 -379
  46. package/lib/agent-cli/lib/skill-learn.js +0 -296
  47. package/lib/agent-cli/lib/stats.js +0 -132
  48. package/lib/agent-cli/lib/stats.test.js +0 -94
  49. package/lib/agent-cli/lib/types.js +0 -33
  50. package/lib/agent-cli/lib/ui/audit-ui.js +0 -146
  51. package/lib/agent-cli/lib/ui/backup-ui.js +0 -107
  52. package/lib/agent-cli/lib/ui/clack-helpers.js +0 -317
  53. package/lib/agent-cli/lib/ui/common.js +0 -83
  54. package/lib/agent-cli/lib/ui/completion-ui.js +0 -126
  55. package/lib/agent-cli/lib/ui/custom-select.js +0 -69
  56. package/lib/agent-cli/lib/ui/dashboard-ui.js +0 -222
  57. package/lib/agent-cli/lib/ui/evolution-signals-ui.js +0 -107
  58. package/lib/agent-cli/lib/ui/export-ui.js +0 -94
  59. package/lib/agent-cli/lib/ui/fix-all-ui.js +0 -191
  60. package/lib/agent-cli/lib/ui/help-ui.js +0 -49
  61. package/lib/agent-cli/lib/ui/index.js +0 -199
  62. package/lib/agent-cli/lib/ui/init-ui.js +0 -56
  63. package/lib/agent-cli/lib/ui/knowledge-ui.js +0 -55
  64. package/lib/agent-cli/lib/ui/learn-ui.js +0 -706
  65. package/lib/agent-cli/lib/ui/lessons-ui.js +0 -148
  66. package/lib/agent-cli/lib/ui/pretty.js +0 -145
  67. package/lib/agent-cli/lib/ui/proposals-ui.js +0 -99
  68. package/lib/agent-cli/lib/ui/recall-ui.js +0 -342
  69. package/lib/agent-cli/lib/ui/routing-demo.js +0 -79
  70. package/lib/agent-cli/lib/ui/routing-ui.js +0 -325
  71. package/lib/agent-cli/lib/ui/settings-ui.js +0 -381
  72. package/lib/agent-cli/lib/ui/stats-ui.js +0 -123
  73. package/lib/agent-cli/lib/ui/watch-ui.js +0 -236
  74. package/lib/agent-cli/lib/watcher.js +0 -181
  75. package/lib/agent-cli/lib/watcher.test.js +0 -85
  76. package/lib/agent-cli/src/MIGRATION.md +0 -418
  77. package/lib/agent-cli/src/README.md +0 -367
  78. package/lib/agent-cli/src/core/evolution/evolution-signal.js +0 -42
  79. package/lib/agent-cli/src/core/evolution/index.js +0 -17
  80. package/lib/agent-cli/src/core/evolution/review-gate.js +0 -40
  81. package/lib/agent-cli/src/core/evolution/signal-detector.js +0 -137
  82. package/lib/agent-cli/src/core/evolution/signal-queue.js +0 -79
  83. package/lib/agent-cli/src/core/evolution/threshold-checker.js +0 -79
  84. package/lib/agent-cli/src/core/index.js +0 -15
  85. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +0 -282
  86. package/lib/agent-cli/src/core/learning/index.js +0 -12
  87. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +0 -83
  88. package/lib/agent-cli/src/core/scanning/index.js +0 -14
  89. package/lib/agent-cli/src/data/index.js +0 -13
  90. package/lib/agent-cli/src/data/repositories/index.js +0 -8
  91. package/lib/agent-cli/src/data/repositories/lesson-repository.js +0 -130
  92. package/lib/agent-cli/src/data/repositories/signal-repository.js +0 -119
  93. package/lib/agent-cli/src/data/storage/index.js +0 -8
  94. package/lib/agent-cli/src/data/storage/json-storage.js +0 -64
  95. package/lib/agent-cli/src/data/storage/yaml-storage.js +0 -66
  96. package/lib/agent-cli/src/infrastructure/index.js +0 -13
  97. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +0 -232
  98. package/lib/agent-cli/src/services/export-service.js +0 -162
  99. package/lib/agent-cli/src/services/index.js +0 -13
  100. package/lib/agent-cli/src/services/learning-service.js +0 -99
@@ -1,410 +0,0 @@
1
- /**
2
- * Metrics Collector v7.0 - PikaKit Learning Metrics
3
- *
4
- * Collects and manages 18 KPIs for the Dashboard.
5
- * Reads data from .agent/knowledge/ and .agent/metrics/
6
- *
7
- * @version 7.0.0
8
- * @author PikaKit
9
- */
10
-
11
- import fs from 'fs';
12
- import path from 'path';
13
- import { fileURLToPath } from 'url';
14
-
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = path.dirname(__filename);
17
-
18
- // Find project root
19
- function findProjectRoot() {
20
- let dir = process.cwd();
21
- while (dir !== path.dirname(dir)) {
22
- if (fs.existsSync(path.join(dir, '.agent'))) return dir;
23
- if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
24
- dir = path.dirname(dir);
25
- }
26
- return process.cwd();
27
- }
28
-
29
- const projectRoot = findProjectRoot();
30
- const KNOWLEDGE_DIR = path.join(projectRoot, '.agent', 'knowledge');
31
- const METRICS_DIR = path.join(projectRoot, '.agent', 'metrics');
32
-
33
- // Ensure metrics directory exists
34
- function ensureMetricsDir() {
35
- if (!fs.existsSync(METRICS_DIR)) {
36
- fs.mkdirSync(METRICS_DIR, { recursive: true });
37
- }
38
- }
39
-
40
- // ============================================================================
41
- // DATA LOADERS
42
- // ============================================================================
43
-
44
- /**
45
- * Load lessons from knowledge base
46
- */
47
- function loadLessons() {
48
- const paths = [
49
- path.join(KNOWLEDGE_DIR, 'lessons-learned.json'),
50
- path.join(KNOWLEDGE_DIR, 'lessons-learned.yaml')
51
- ];
52
-
53
- for (const filePath of paths) {
54
- if (fs.existsSync(filePath)) {
55
- try {
56
- const content = fs.readFileSync(filePath, 'utf8');
57
- if (filePath.endsWith('.json')) {
58
- return JSON.parse(content).lessons || [];
59
- } else {
60
- // Simple YAML parsing for lessons array
61
- const lessons = [];
62
- const lines = content.split('\n');
63
- let currentLesson = null;
64
-
65
- for (const line of lines) {
66
- if (line.startsWith('- id:')) {
67
- if (currentLesson) lessons.push(currentLesson);
68
- currentLesson = { id: line.split(':')[1]?.trim() };
69
- } else if (currentLesson && line.includes(':')) {
70
- const [key, ...valueParts] = line.trim().split(':');
71
- currentLesson[key.trim()] = valueParts.join(':').trim();
72
- }
73
- }
74
- if (currentLesson) lessons.push(currentLesson);
75
- return lessons;
76
- }
77
- } catch (e) {
78
- console.error('Error loading lessons:', e.message);
79
- }
80
- }
81
- }
82
- return [];
83
- }
84
-
85
- /**
86
- * Load metrics history
87
- */
88
- function loadMetricsHistory() {
89
- const historyPath = path.join(METRICS_DIR, 'history.json');
90
- if (fs.existsSync(historyPath)) {
91
- try {
92
- return JSON.parse(fs.readFileSync(historyPath, 'utf8'));
93
- } catch (e) {
94
- return { entries: [], lastUpdated: null };
95
- }
96
- }
97
- return { entries: [], lastUpdated: null };
98
- }
99
-
100
- /**
101
- * Save metrics history
102
- */
103
- function saveMetricsHistory(history) {
104
- ensureMetricsDir();
105
- const historyPath = path.join(METRICS_DIR, 'history.json');
106
- history.lastUpdated = new Date().toISOString();
107
- fs.writeFileSync(historyPath, JSON.stringify(history, null, 2));
108
- }
109
-
110
- /**
111
- * Load current metrics snapshot
112
- */
113
- function loadCurrentMetrics() {
114
- const metricsPath = path.join(METRICS_DIR, 'current.json');
115
- if (fs.existsSync(metricsPath)) {
116
- try {
117
- return JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
118
- } catch (e) {
119
- return getDefaultMetrics();
120
- }
121
- }
122
- return getDefaultMetrics();
123
- }
124
-
125
- /**
126
- * Save current metrics
127
- */
128
- function saveCurrentMetrics(metrics) {
129
- ensureMetricsDir();
130
- const metricsPath = path.join(METRICS_DIR, 'current.json');
131
- metrics.timestamp = new Date().toISOString();
132
- fs.writeFileSync(metricsPath, JSON.stringify(metrics, null, 2));
133
- }
134
-
135
- /**
136
- * Get default metrics structure
137
- */
138
- function getDefaultMetrics() {
139
- return {
140
- // Task metrics
141
- total_tasks: 0,
142
- successful_tasks: 0,
143
- failed_tasks: 0,
144
-
145
- // Learning metrics
146
- patterns_learned: 0,
147
- skills_generated: 0,
148
- lessons_count: 0,
149
-
150
- // Performance metrics
151
- task_success_rate: 0,
152
- error_repeat_rate: 0,
153
- first_time_success_rate: 0,
154
- skill_effectiveness: 0,
155
-
156
- // Reinforcement metrics
157
- total_rewards: 0,
158
- total_penalties: 0,
159
- avg_confidence: 0,
160
-
161
- // A/B Testing metrics
162
- active_tests: 0,
163
- completed_tests: 0,
164
-
165
- // Timestamps
166
- timestamp: new Date().toISOString(),
167
- period_start: new Date().toISOString()
168
- };
169
- }
170
-
171
- // ============================================================================
172
- // KPI CALCULATIONS
173
- // ============================================================================
174
-
175
- /**
176
- * Calculate all KPIs
177
- */
178
- function calculateKPIs() {
179
- const lessons = loadLessons();
180
- const currentMetrics = loadCurrentMetrics();
181
-
182
- // Count patterns from lessons
183
- const patternsLearned = lessons.length;
184
-
185
- // Calculate rates
186
- const totalTasks = currentMetrics.total_tasks || 0;
187
- const successfulTasks = currentMetrics.successful_tasks || 0;
188
- const failedTasks = currentMetrics.failed_tasks || 0;
189
-
190
- const taskSuccessRate = totalTasks > 0
191
- ? Math.round((successfulTasks / totalTasks) * 100)
192
- : 0;
193
-
194
- // Error repeat rate (lessons with severity > 1 occurrence)
195
- const repeatedErrors = lessons.filter(l =>
196
- (l.occurrences && parseInt(l.occurrences) > 1) ||
197
- (l.severity === 'high' || l.severity === 'critical')
198
- ).length;
199
- const errorRepeatRate = lessons.length > 0
200
- ? Math.round((repeatedErrors / lessons.length) * 100)
201
- : 0;
202
-
203
- // First-time success (tasks without retries)
204
- const firstTimeSuccess = currentMetrics.first_time_success_rate || 0;
205
-
206
- // Build KPIs object
207
- return {
208
- kpis: {
209
- task_success_rate: {
210
- value: `${taskSuccessRate}%`,
211
- trend: taskSuccessRate >= 80 ? 'up' : 'down',
212
- status: taskSuccessRate >= 80 ? 'good' : taskSuccessRate >= 50 ? 'warning' : 'bad'
213
- },
214
- error_repeat_rate: {
215
- value: `${errorRepeatRate}%`,
216
- trend: errorRepeatRate <= 10 ? 'down' : 'up',
217
- status: errorRepeatRate <= 10 ? 'good' : errorRepeatRate <= 30 ? 'warning' : 'bad',
218
- hint: 'Lower is better'
219
- },
220
- first_time_success: {
221
- value: `${firstTimeSuccess}%`,
222
- trend: firstTimeSuccess >= 70 ? 'up' : 'down',
223
- status: firstTimeSuccess >= 70 ? 'good' : 'warning',
224
- hint: 'No retries needed'
225
- },
226
- skill_effectiveness: {
227
- value: `${currentMetrics.skill_effectiveness || 0}%`,
228
- trend: 'stable',
229
- status: 'neutral',
230
- hint: 'Skills that help'
231
- },
232
- total_tasks: {
233
- value: totalTasks,
234
- icon: '📊'
235
- },
236
- patterns_learned: {
237
- value: patternsLearned,
238
- icon: '🧩'
239
- },
240
- skills_generated: {
241
- value: currentMetrics.skills_generated || 0,
242
- icon: '⚙️'
243
- },
244
- ab_tests_active: {
245
- value: currentMetrics.active_tests || 0,
246
- icon: '🧪'
247
- }
248
- },
249
- summary: {
250
- totalTasks,
251
- patternsLearned,
252
- skillsGenerated: currentMetrics.skills_generated || 0,
253
- lessonsCount: lessons.length
254
- },
255
- timestamp: new Date().toISOString()
256
- };
257
- }
258
-
259
- /**
260
- * Get KPIs for dashboard
261
- */
262
- export function getKPIs() {
263
- return calculateKPIs();
264
- }
265
-
266
- /**
267
- * Get dashboard data (alias)
268
- */
269
- export function getDashboardData() {
270
- return calculateKPIs();
271
- }
272
-
273
- /**
274
- * Get summary stats
275
- */
276
- export function getSummary() {
277
- const kpis = calculateKPIs();
278
- return {
279
- ...kpis.summary,
280
- version: '7.0.0',
281
- status: 'ok'
282
- };
283
- }
284
-
285
- /**
286
- * Get specific metric value
287
- */
288
- export function getMetricValue(metricName) {
289
- const currentMetrics = loadCurrentMetrics();
290
- return currentMetrics[metricName] ?? null;
291
- }
292
-
293
- /**
294
- * Get metric history for charts
295
- */
296
- export function getMetricHistory(metricName, limit = 168) {
297
- const history = loadMetricsHistory();
298
- const entries = history.entries || [];
299
-
300
- // Filter entries for specific metric
301
- const metricHistory = entries
302
- .filter(e => e[metricName] !== undefined)
303
- .slice(-limit)
304
- .map(e => ({
305
- timestamp: e.timestamp,
306
- value: e[metricName]
307
- }));
308
-
309
- return metricHistory;
310
- }
311
-
312
- // ============================================================================
313
- // METRIC UPDATES
314
- // ============================================================================
315
-
316
- /**
317
- * Record a task completion
318
- */
319
- export function recordTask(success, firstTime = true) {
320
- const metrics = loadCurrentMetrics();
321
-
322
- metrics.total_tasks = (metrics.total_tasks || 0) + 1;
323
-
324
- if (success) {
325
- metrics.successful_tasks = (metrics.successful_tasks || 0) + 1;
326
- if (firstTime) {
327
- const total = metrics.total_tasks;
328
- const firstTimeCount = Math.round((metrics.first_time_success_rate || 0) * (total - 1) / 100) + 1;
329
- metrics.first_time_success_rate = Math.round((firstTimeCount / total) * 100);
330
- }
331
- } else {
332
- metrics.failed_tasks = (metrics.failed_tasks || 0) + 1;
333
- }
334
-
335
- // Recalculate success rate
336
- metrics.task_success_rate = Math.round(
337
- (metrics.successful_tasks / metrics.total_tasks) * 100
338
- );
339
-
340
- saveCurrentMetrics(metrics);
341
- recordHistoryEntry(metrics);
342
-
343
- return metrics;
344
- }
345
-
346
- /**
347
- * Record a pattern learned
348
- */
349
- export function recordPatternLearned() {
350
- const metrics = loadCurrentMetrics();
351
- metrics.patterns_learned = (metrics.patterns_learned || 0) + 1;
352
- saveCurrentMetrics(metrics);
353
- return metrics;
354
- }
355
-
356
- /**
357
- * Record a skill generated
358
- */
359
- export function recordSkillGenerated() {
360
- const metrics = loadCurrentMetrics();
361
- metrics.skills_generated = (metrics.skills_generated || 0) + 1;
362
- saveCurrentMetrics(metrics);
363
- return metrics;
364
- }
365
-
366
- /**
367
- * Record history entry (hourly snapshot)
368
- */
369
- function recordHistoryEntry(metrics) {
370
- const history = loadMetricsHistory();
371
- const now = new Date();
372
-
373
- // Only record once per hour
374
- const lastEntry = history.entries[history.entries.length - 1];
375
- if (lastEntry) {
376
- const lastTime = new Date(lastEntry.timestamp);
377
- const hoursDiff = (now - lastTime) / (1000 * 60 * 60);
378
- if (hoursDiff < 1) return;
379
- }
380
-
381
- history.entries.push({
382
- timestamp: now.toISOString(),
383
- task_success_rate: metrics.task_success_rate || 0,
384
- error_repeat_rate: metrics.error_repeat_rate || 0,
385
- patterns_learned: metrics.patterns_learned || 0,
386
- total_tasks: metrics.total_tasks || 0
387
- });
388
-
389
- // Keep last 7 days (168 hours)
390
- if (history.entries.length > 168) {
391
- history.entries = history.entries.slice(-168);
392
- }
393
-
394
- saveMetricsHistory(history);
395
- }
396
-
397
- // ============================================================================
398
- // EXPORTS
399
- // ============================================================================
400
-
401
- export default {
402
- getKPIs,
403
- getDashboardData,
404
- getSummary,
405
- getMetricValue,
406
- getMetricHistory,
407
- recordTask,
408
- recordPatternLearned,
409
- recordSkillGenerated
410
- };
@@ -1,199 +0,0 @@
1
- /**
2
- * @fileoverview Proposal Generator for Auto-Updating Flow
3
- * Generates markdown instructions for AI agents to update skills
4
- */
5
-
6
- import fs from "fs";
7
- import path from "path";
8
- import yaml from "js-yaml";
9
- import { KNOWLEDGE_DIR, LESSONS_PATH, AGENT_DIR } from "./config.js";
10
- import { loadSettings } from "./settings.js";
11
- import { loadKnowledge } from "./recall.js";
12
-
13
- /** Proposals directory */
14
- const PROPOSALS_DIR = path.join(KNOWLEDGE_DIR, "proposals");
15
-
16
- /** Dismissed proposals file */
17
- const DISMISSED_FILE = path.join(PROPOSALS_DIR, "dismissed.yaml");
18
-
19
- /**
20
- * Get lessons that qualify for proposals (hitCount >= threshold)
21
- * @returns {Array<{ lesson: object, proposalPath: string }>}
22
- */
23
- export function getQualifyingLessons() {
24
- const settings = loadSettings();
25
- const threshold = settings.updateThreshold || 5;
26
- const db = loadKnowledge();
27
-
28
- if (!db.lessons) return [];
29
-
30
- const dismissed = loadDismissed();
31
-
32
- return db.lessons
33
- .filter(l => (l.hitCount || 0) >= threshold && !dismissed.has(l.id))
34
- .map(lesson => ({
35
- lesson,
36
- proposalPath: path.join(PROPOSALS_DIR, `${lesson.id}.md`)
37
- }));
38
- }
39
-
40
- /**
41
- * Load dismissed proposal IDs
42
- * @returns {Set<string>}
43
- */
44
- function loadDismissed() {
45
- try {
46
- if (fs.existsSync(DISMISSED_FILE)) {
47
- const data = yaml.load(fs.readFileSync(DISMISSED_FILE, "utf8")) || {};
48
- return new Set(data.dismissed || []);
49
- }
50
- } catch (e) { }
51
- return new Set();
52
- }
53
-
54
- /**
55
- * Dismiss a proposal
56
- * @param {string} lessonId
57
- */
58
- export function dismissProposal(lessonId) {
59
- fs.mkdirSync(PROPOSALS_DIR, { recursive: true });
60
- const dismissed = loadDismissed();
61
- dismissed.add(lessonId);
62
- fs.writeFileSync(DISMISSED_FILE, yaml.dump({ dismissed: [...dismissed] }), "utf8");
63
- }
64
-
65
- /**
66
- * Generate proposal markdown for AI agent
67
- * @param {object} lesson - Lesson object
68
- * @returns {string} Markdown content
69
- */
70
- export function generateProposalMarkdown(lesson) {
71
- const skillHint = guessSkillFromLesson(lesson);
72
-
73
- return `# 🤖 Skill Update Proposal
74
-
75
- ## Pattern Detected
76
- | Field | Value |
77
- |-------|-------|
78
- | **ID** | \`${lesson.id}\` |
79
- | **Hit Count** | ${lesson.hitCount || 0} |
80
- | **Severity** | ${lesson.severity || "WARNING"} |
81
- | **Last Hit** | ${lesson.lastHit || "N/A"} |
82
-
83
- ## Message
84
- > ${lesson.message}
85
-
86
- ## Pattern (Regex)
87
- \`\`\`
88
- ${lesson.pattern}
89
- \`\`\`
90
-
91
- ---
92
-
93
- ## 📝 Suggested Action for AI Agent
94
-
95
- Please update the relevant skill file to include this rule:
96
-
97
- ### Target File
98
- \`${skillHint}\`
99
-
100
- ### Add to Rules Section
101
- \`\`\`markdown
102
- ### ${lesson.message}
103
- - Pattern: \`${lesson.pattern}\`
104
- - Severity: ${lesson.severity || "WARNING"}
105
- - Auto-learned from violations
106
- \`\`\`
107
-
108
- ---
109
-
110
- ## How to Apply
111
-
112
- 1. **Copy this entire proposal**
113
- 2. **Paste to your AI coding agent** (Claude, Gemini, etc.)
114
- 3. **Ask:** "Update the skill file with this rule"
115
-
116
- The AI agent will modify the appropriate \`.agent/skills/*/SKILL.md\` file.
117
-
118
- ---
119
-
120
- *Generated by PikaKit v2.1.0*
121
- `;
122
- }
123
-
124
- /**
125
- * Guess which skill file this lesson relates to
126
- * @param {object} lesson
127
- * @returns {string}
128
- */
129
- function guessSkillFromLesson(lesson) {
130
- const pattern = (lesson.pattern || "").toLowerCase();
131
- const message = (lesson.message || "").toLowerCase();
132
- const combined = pattern + " " + message;
133
-
134
- // Frontend patterns
135
- if (combined.includes("react") || combined.includes("component") || combined.includes("hook") || combined.includes("usestate") || combined.includes("useeffect")) {
136
- return ".agent/skills/react-patterns/SKILL.md";
137
- }
138
- if (combined.includes("next") || combined.includes("getserverside") || combined.includes("getstaticprops") || combined.includes("app router")) {
139
- return ".agent/skills/nextjs-best-practices/SKILL.md";
140
- }
141
- if (combined.includes("css") || combined.includes("style") || combined.includes("tailwind") || combined.includes("classname")) {
142
- return ".agent/skills/frontend-design/SKILL.md";
143
- }
144
-
145
- // Backend patterns
146
- if (combined.includes("api") || combined.includes("fetch") || combined.includes("http") || combined.includes("endpoint") || combined.includes("rest")) {
147
- return ".agent/skills/api-patterns/SKILL.md";
148
- }
149
- if (combined.includes("database") || combined.includes("sql") || combined.includes("prisma") || combined.includes("query") || combined.includes("schema")) {
150
- return ".agent/skills/database-design/SKILL.md";
151
- }
152
- if (combined.includes("node") || combined.includes("express") || combined.includes("server") || combined.includes("middleware")) {
153
- return ".agent/skills/nodejs-best-practices/SKILL.md";
154
- }
155
-
156
- // Testing patterns
157
- if (combined.includes("test") || combined.includes("mock") || combined.includes("jest") || combined.includes("vitest") || combined.includes("expect")) {
158
- return ".agent/skills/testing-patterns/SKILL.md";
159
- }
160
-
161
- // TypeScript patterns
162
- if (combined.includes("typescript") || combined.includes("type") || combined.includes("interface") || combined.includes("generic") || combined.includes("any")) {
163
- return ".agent/skills/typescript-expert/SKILL.md";
164
- }
165
-
166
- // Security patterns
167
- if (combined.includes("security") || combined.includes("auth") || combined.includes("password") || combined.includes("token") || combined.includes("xss") || combined.includes("injection")) {
168
- return ".agent/skills/vulnerability-scanner/SKILL.md";
169
- }
170
-
171
- // Performance patterns
172
- if (combined.includes("performance") || combined.includes("memo") || combined.includes("lazy") || combined.includes("cache") || combined.includes("optimize")) {
173
- return ".agent/skills/performance-profiling/SKILL.md";
174
- }
175
-
176
- // Git patterns
177
- if (combined.includes("commit") || combined.includes("branch") || combined.includes("merge") || combined.includes("git")) {
178
- return ".agent/skills/git-conventions/SKILL.md";
179
- }
180
-
181
- // Default to clean-code for general patterns
182
- return ".agent/skills/clean-code/SKILL.md";
183
- }
184
-
185
- /**
186
- * Count pending proposals
187
- * @returns {number}
188
- */
189
- export function countPendingProposals() {
190
- return getQualifyingLessons().length;
191
- }
192
-
193
- export default {
194
- getQualifyingLessons,
195
- dismissProposal,
196
- generateProposalMarkdown,
197
- countPendingProposals,
198
- PROPOSALS_DIR
199
- };
@@ -1,56 +0,0 @@
1
- /**
2
- * @fileoverview Tests for proposals module
3
- */
4
-
5
- import { describe, it, expect } from "vitest";
6
-
7
- // Test proposal markdown generation without mocking
8
- describe("proposals", () => {
9
- describe("generateProposalMarkdown format", () => {
10
- it("creates markdown with required sections", () => {
11
- const lesson = {
12
- id: "LEARN-001",
13
- pattern: "console\\.log",
14
- message: "No console.log in production",
15
- hitCount: 5,
16
- severity: "ERROR"
17
- };
18
-
19
- // Simulate markdown generation
20
- const md = `# 🤖 Skill Update Proposal\n\n## Pattern Detected\n| Field | Value |\n|-------|-------|\n| **ID** | \`${lesson.id}\` |\n| **Hit Count** | ${lesson.hitCount || 0} |\n\n## Message\n> ${lesson.message}`;
21
-
22
- expect(md).toContain("LEARN-001");
23
- expect(md).toContain("No console.log");
24
- expect(md).toContain("5");
25
- });
26
-
27
- it("includes AI agent instructions", () => {
28
- const md = `## How to Apply\n1. **Copy this entire proposal**\n2. **Paste to your AI coding agent**`;
29
-
30
- expect(md).toContain("Copy");
31
- expect(md).toContain("Paste");
32
- expect(md).toContain("AI");
33
- });
34
- });
35
-
36
- describe("proposal threshold", () => {
37
- it("default threshold is 5", () => {
38
- const defaultThreshold = 5;
39
- expect(defaultThreshold).toBe(5);
40
- });
41
-
42
- it("lesson qualifies when hitCount >= threshold", () => {
43
- const threshold = 5;
44
- const lessons = [
45
- { id: "L1", hitCount: 5 },
46
- { id: "L2", hitCount: 3 },
47
- { id: "L3", hitCount: 10 }
48
- ];
49
-
50
- const qualifying = lessons.filter(l => l.hitCount >= threshold);
51
- expect(qualifying).toHaveLength(2);
52
- expect(qualifying.map(l => l.id)).toContain("L1");
53
- expect(qualifying.map(l => l.id)).toContain("L3");
54
- });
55
- });
56
- });