pikakit 3.0.4 → 3.7.1

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 (94) hide show
  1. package/README.md +1 -1
  2. package/bin/lib/commands/install.js +97 -242
  3. package/package.json +2 -3
  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/audit.js +0 -154
  7. package/lib/agent-cli/lib/audit.test.js +0 -100
  8. package/lib/agent-cli/lib/auto-learn.js +0 -319
  9. package/lib/agent-cli/lib/backup.js +0 -138
  10. package/lib/agent-cli/lib/backup.test.js +0 -78
  11. package/lib/agent-cli/lib/cognitive-lesson.js +0 -476
  12. package/lib/agent-cli/lib/completion.js +0 -149
  13. package/lib/agent-cli/lib/config.js +0 -35
  14. package/lib/agent-cli/lib/eslint-fix.js +0 -238
  15. package/lib/agent-cli/lib/evolution-signal.js +0 -215
  16. package/lib/agent-cli/lib/export.js +0 -86
  17. package/lib/agent-cli/lib/export.test.js +0 -65
  18. package/lib/agent-cli/lib/fix.js +0 -337
  19. package/lib/agent-cli/lib/fix.test.js +0 -80
  20. package/lib/agent-cli/lib/gemini-export.js +0 -83
  21. package/lib/agent-cli/lib/generate-registry.js +0 -42
  22. package/lib/agent-cli/lib/hooks/install-hooks.js +0 -152
  23. package/lib/agent-cli/lib/hooks/lint-learn.js +0 -172
  24. package/lib/agent-cli/lib/icons.js +0 -93
  25. package/lib/agent-cli/lib/ignore.js +0 -116
  26. package/lib/agent-cli/lib/ignore.test.js +0 -58
  27. package/lib/agent-cli/lib/init.js +0 -124
  28. package/lib/agent-cli/lib/knowledge-index.js +0 -326
  29. package/lib/agent-cli/lib/knowledge-metrics.js +0 -335
  30. package/lib/agent-cli/lib/knowledge-retention.js +0 -398
  31. package/lib/agent-cli/lib/knowledge-validator.js +0 -312
  32. package/lib/agent-cli/lib/learn.js +0 -255
  33. package/lib/agent-cli/lib/learn.test.js +0 -70
  34. package/lib/agent-cli/lib/proposals.js +0 -199
  35. package/lib/agent-cli/lib/proposals.test.js +0 -56
  36. package/lib/agent-cli/lib/recall.js +0 -835
  37. package/lib/agent-cli/lib/recall.test.js +0 -107
  38. package/lib/agent-cli/lib/selfevolution-bridge.js +0 -167
  39. package/lib/agent-cli/lib/settings.js +0 -203
  40. package/lib/agent-cli/lib/skill-learn.js +0 -296
  41. package/lib/agent-cli/lib/stats.js +0 -132
  42. package/lib/agent-cli/lib/stats.test.js +0 -94
  43. package/lib/agent-cli/lib/types.js +0 -33
  44. package/lib/agent-cli/lib/ui/audit-ui.js +0 -146
  45. package/lib/agent-cli/lib/ui/backup-ui.js +0 -107
  46. package/lib/agent-cli/lib/ui/clack-helpers.js +0 -317
  47. package/lib/agent-cli/lib/ui/common.js +0 -83
  48. package/lib/agent-cli/lib/ui/completion-ui.js +0 -126
  49. package/lib/agent-cli/lib/ui/custom-select.js +0 -69
  50. package/lib/agent-cli/lib/ui/dashboard-ui.js +0 -222
  51. package/lib/agent-cli/lib/ui/evolution-signals-ui.js +0 -107
  52. package/lib/agent-cli/lib/ui/export-ui.js +0 -94
  53. package/lib/agent-cli/lib/ui/fix-all-ui.js +0 -191
  54. package/lib/agent-cli/lib/ui/help-ui.js +0 -49
  55. package/lib/agent-cli/lib/ui/index.js +0 -199
  56. package/lib/agent-cli/lib/ui/init-ui.js +0 -56
  57. package/lib/agent-cli/lib/ui/knowledge-ui.js +0 -55
  58. package/lib/agent-cli/lib/ui/learn-ui.js +0 -706
  59. package/lib/agent-cli/lib/ui/lessons-ui.js +0 -148
  60. package/lib/agent-cli/lib/ui/pretty.js +0 -145
  61. package/lib/agent-cli/lib/ui/proposals-ui.js +0 -99
  62. package/lib/agent-cli/lib/ui/recall-ui.js +0 -342
  63. package/lib/agent-cli/lib/ui/routing-demo.js +0 -79
  64. package/lib/agent-cli/lib/ui/routing-ui.js +0 -325
  65. package/lib/agent-cli/lib/ui/settings-ui.js +0 -381
  66. package/lib/agent-cli/lib/ui/stats-ui.js +0 -123
  67. package/lib/agent-cli/lib/ui/watch-ui.js +0 -236
  68. package/lib/agent-cli/lib/watcher.js +0 -181
  69. package/lib/agent-cli/lib/watcher.test.js +0 -85
  70. package/lib/agent-cli/src/MIGRATION.md +0 -418
  71. package/lib/agent-cli/src/README.md +0 -367
  72. package/lib/agent-cli/src/core/evolution/evolution-signal.js +0 -42
  73. package/lib/agent-cli/src/core/evolution/index.js +0 -17
  74. package/lib/agent-cli/src/core/evolution/review-gate.js +0 -40
  75. package/lib/agent-cli/src/core/evolution/signal-detector.js +0 -137
  76. package/lib/agent-cli/src/core/evolution/signal-queue.js +0 -79
  77. package/lib/agent-cli/src/core/evolution/threshold-checker.js +0 -79
  78. package/lib/agent-cli/src/core/index.js +0 -15
  79. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +0 -282
  80. package/lib/agent-cli/src/core/learning/index.js +0 -12
  81. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +0 -83
  82. package/lib/agent-cli/src/core/scanning/index.js +0 -14
  83. package/lib/agent-cli/src/data/index.js +0 -13
  84. package/lib/agent-cli/src/data/repositories/index.js +0 -8
  85. package/lib/agent-cli/src/data/repositories/lesson-repository.js +0 -130
  86. package/lib/agent-cli/src/data/repositories/signal-repository.js +0 -119
  87. package/lib/agent-cli/src/data/storage/index.js +0 -8
  88. package/lib/agent-cli/src/data/storage/json-storage.js +0 -64
  89. package/lib/agent-cli/src/data/storage/yaml-storage.js +0 -66
  90. package/lib/agent-cli/src/infrastructure/index.js +0 -13
  91. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +0 -232
  92. package/lib/agent-cli/src/services/export-service.js +0 -162
  93. package/lib/agent-cli/src/services/index.js +0 -13
  94. package/lib/agent-cli/src/services/learning-service.js +0 -99
@@ -1,296 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Skill Learn - Auto-update SKILL.md from Hot Patterns
4
- *
5
- * When patterns reach threshold hits, automatically add them
6
- * to the relevant SKILL.md file as learned rules.
7
- *
8
- * This makes the Self-Learning Engine truly "self-learning"!
9
- *
10
- * Usage: agent sync-skills
11
- */
12
-
13
- import fs from "fs";
14
- import path from "path";
15
- import { loadKnowledge, saveKnowledge } from "./recall.js";
16
- import { AGENT_DIR, VERSION } from "./config.js";
17
-
18
- // ============================================================================
19
- // CONFIGURATION
20
- // ============================================================================
21
-
22
- const SYNC_THRESHOLD = 10; // Hits required before syncing to SKILL.md
23
- const SKILLS_DIR = path.join(AGENT_DIR, "skills");
24
-
25
- // Category to skill folder mapping
26
- const CATEGORY_MAP = {
27
- "general": "clean-code",
28
- "security": "vulnerability-scanner",
29
- "code-quality": "clean-code",
30
- "performance": "performance-profiling",
31
- "testing": "testing-patterns",
32
- "react": "react-patterns",
33
- "api": "api-patterns",
34
- "database": "database-design"
35
- };
36
-
37
- // ============================================================================
38
- // SKILL UPDATER
39
- // ============================================================================
40
-
41
- /**
42
- * Find the SKILL.md file for a category
43
- * @param {string} category
44
- * @returns {string|null}
45
- */
46
- function findSkillFile(category) {
47
- const skillFolder = CATEGORY_MAP[category] || CATEGORY_MAP["general"];
48
- const skillPath = path.join(SKILLS_DIR, skillFolder, "SKILL.md");
49
-
50
- if (fs.existsSync(skillPath)) {
51
- return skillPath;
52
- }
53
-
54
- // Fallback to clean-code
55
- const fallback = path.join(SKILLS_DIR, "clean-code", "SKILL.md");
56
- return fs.existsSync(fallback) ? fallback : null;
57
- }
58
-
59
- /**
60
- * Parse SKILL.md structure to find best insertion point
61
- * @param {string} skillPath
62
- * @returns {{ sections: Array<{name: string, start: number, end: number}>, content: string }}
63
- */
64
- function parseSkillStructure(skillPath) {
65
- const content = fs.readFileSync(skillPath, "utf8");
66
- const lines = content.split("\n");
67
- const sections = [];
68
-
69
- let currentSection = null;
70
-
71
- lines.forEach((line, idx) => {
72
- // Detect section headers (## or ###)
73
- const headerMatch = line.match(/^(#{2,3})\s+(.+)/);
74
- if (headerMatch) {
75
- if (currentSection) {
76
- currentSection.end = idx - 1;
77
- sections.push(currentSection);
78
- }
79
- currentSection = {
80
- name: headerMatch[2].trim(),
81
- level: headerMatch[1].length,
82
- start: idx,
83
- end: lines.length - 1
84
- };
85
- }
86
- });
87
-
88
- if (currentSection) {
89
- sections.push(currentSection);
90
- }
91
-
92
- return { sections, content, lines };
93
- }
94
-
95
- /**
96
- * Find best section to insert pattern based on category
97
- * @param {Array} sections
98
- * @param {string} category
99
- * @returns {number} - Line number to insert at
100
- */
101
- function findInsertionPoint(sections, category) {
102
- // Look for "Auto-learned" section first
103
- const autoSection = sections.find(s =>
104
- s.name.toLowerCase().includes("auto-learned") ||
105
- s.name.toLowerCase().includes("learned patterns")
106
- );
107
- if (autoSection) {
108
- return autoSection.end + 1;
109
- }
110
-
111
- // Look for relevant category section
112
- const categoryMap = {
113
- "security": ["security", "vulnerab"],
114
- "performance": ["performance", "optim"],
115
- "code-quality": ["quality", "best practice", "rule"],
116
- "testing": ["test", "spec"]
117
- };
118
-
119
- const keywords = categoryMap[category] || ["rule", "pattern"];
120
- const matchSection = sections.find(s =>
121
- keywords.some(kw => s.name.toLowerCase().includes(kw))
122
- );
123
- if (matchSection) {
124
- return matchSection.end + 1;
125
- }
126
-
127
- // Default: append at end
128
- return -1; // -1 means append
129
- }
130
-
131
- /**
132
- * Generate rule entry for SKILL.md
133
- * @param {object} lesson
134
- * @returns {string}
135
- */
136
- function generateRuleEntry(lesson) {
137
- const date = new Date().toISOString().split("T")[0];
138
- return `
139
- ### 🤖 Auto-learned: ${lesson.id}
140
-
141
- > **Pattern:** \`${lesson.pattern}\`
142
- >
143
- > ${lesson.message}
144
-
145
- - **Severity:** ${lesson.severity}
146
- - **Detected:** ${lesson.hitCount} times
147
- - **Added:** ${date}
148
-
149
- ---
150
- `;
151
- }
152
-
153
- /**
154
- * Check if pattern already exists in SKILL.md
155
- */
156
- function patternExistsInSkill(skillPath, pattern) {
157
- const content = fs.readFileSync(skillPath, "utf8");
158
- // Check multiple formats
159
- return content.includes(`\`${pattern}\``) ||
160
- content.includes(`Pattern:** \`${pattern}\``) ||
161
- content.includes(`"${pattern}"`) ||
162
- content.includes(`'${pattern}'`);
163
- }
164
-
165
- /**
166
- * Insert rule at specific line or append
167
- * @param {string} skillPath
168
- * @param {object} lesson
169
- * @param {string} category
170
- */
171
- function insertRule(skillPath, lesson, category) {
172
- const { sections, content, lines } = parseSkillStructure(skillPath);
173
- const insertPoint = findInsertionPoint(sections, category);
174
- const entry = generateRuleEntry(lesson);
175
-
176
- if (insertPoint === -1 || insertPoint >= lines.length) {
177
- // Append at end
178
- fs.appendFileSync(skillPath, entry, "utf8");
179
- } else {
180
- // Insert at specific line
181
- lines.splice(insertPoint, 0, entry);
182
- fs.writeFileSync(skillPath, lines.join("\n"), "utf8");
183
- }
184
- }
185
-
186
- /**
187
- * Sync hot patterns to SKILL.md files
188
- */
189
- function syncSkills() {
190
- console.log(`\n🔄 Skill Sync v${VERSION}`);
191
- console.log(`📊 Threshold: ${SYNC_THRESHOLD} hits\n`);
192
- console.log("─".repeat(50));
193
-
194
- const db = loadKnowledge();
195
-
196
- if (!db.lessons || db.lessons.length === 0) {
197
- console.log("\nℹ️ No lessons learned yet.");
198
- return { synced: 0, skipped: 0 };
199
- }
200
-
201
- // Find hot patterns
202
- const hotPatterns = db.lessons.filter(l => (l.hitCount || 0) >= SYNC_THRESHOLD && !l.syncedToSkill);
203
-
204
- if (hotPatterns.length === 0) {
205
- console.log("\nℹ️ No patterns have reached the sync threshold yet.");
206
- console.log(` Patterns need ${SYNC_THRESHOLD}+ hits to be synced to SKILL.md\n`);
207
-
208
- // Show progress
209
- const inProgress = db.lessons.filter(l => l.hitCount > 0 && l.hitCount < SYNC_THRESHOLD);
210
- if (inProgress.length > 0) {
211
- console.log("📈 Patterns in progress:\n");
212
- inProgress.forEach(l => {
213
- const progress = Math.round((l.hitCount / SYNC_THRESHOLD) * 100);
214
- const bar = "█".repeat(Math.floor(progress / 10)) + "░".repeat(10 - Math.floor(progress / 10));
215
- console.log(` [${l.id}] ${l.hitCount}/${SYNC_THRESHOLD} hits (${progress}%)`);
216
- console.log(` ${bar}`);
217
- });
218
- }
219
- return { synced: 0, skipped: 0 };
220
- }
221
-
222
- console.log(`\n🔥 Found ${hotPatterns.length} hot pattern(s) ready to sync:\n`);
223
-
224
- let synced = 0;
225
- let skipped = 0;
226
-
227
- hotPatterns.forEach(lesson => {
228
- const category = lesson.category || "general";
229
- const skillPath = findSkillFile(category);
230
-
231
- if (!skillPath) {
232
- console.log(`⚠️ [${lesson.id}] No SKILL.md found for category: ${category}`);
233
- skipped++;
234
- return;
235
- }
236
-
237
- // Check if already synced
238
- if (patternExistsInSkill(skillPath, lesson.pattern)) {
239
- console.log(`⏭️ [${lesson.id}] Already in ${path.basename(path.dirname(skillPath))}/SKILL.md`);
240
- lesson.syncedToSkill = true;
241
- skipped++;
242
- return;
243
- }
244
-
245
- // Append to SKILL.md
246
- const entry = generateRuleEntry(lesson);
247
- fs.appendFileSync(skillPath, entry, "utf8");
248
-
249
- // Mark as synced
250
- lesson.syncedToSkill = true;
251
- lesson.syncedAt = new Date().toISOString();
252
-
253
- console.log(`✅ [${lesson.id}] → ${path.basename(path.dirname(skillPath))}/SKILL.md`);
254
- console.log(` Pattern: /${lesson.pattern}/`);
255
- console.log(` Hits: ${lesson.hitCount}`);
256
- synced++;
257
- });
258
-
259
- // Save updated knowledge base
260
- saveKnowledge(db);
261
-
262
- console.log(`\n${"─".repeat(50)}`);
263
- console.log(`📊 Summary: ${synced} synced, ${skipped} skipped`);
264
-
265
- if (synced > 0) {
266
- console.log(`\n🎉 SKILL.md files updated! The agent will now remember these patterns.`);
267
- }
268
-
269
- return { synced, skipped };
270
- }
271
-
272
- // ============================================================================
273
- // CLI
274
- // ============================================================================
275
-
276
- const args = process.argv.slice(2);
277
-
278
- if (args.includes("--help")) {
279
- console.log(`
280
- 🔄 Skill Sync - Auto-update SKILL.md
281
-
282
- Usage:
283
- agent sync-skills
284
-
285
- When patterns reach ${SYNC_THRESHOLD}+ violations, they are automatically
286
- added to the relevant SKILL.md file as learned rules.
287
-
288
- This makes the Self-Learning Engine truly "self-learning"!
289
-
290
- Options:
291
- --help Show this help
292
- `);
293
- process.exit(0);
294
- }
295
-
296
- syncSkills();
@@ -1,132 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Smart Stats - Knowledge Base Statistics
4
- *
5
- * Display statistics about the knowledge base:
6
- * - Total lessons learned
7
- * - Most triggered patterns
8
- * - Violation trends
9
- *
10
- * Usage: agent stats
11
- */
12
-
13
- import { loadKnowledge } from "./recall.js";
14
- import { VERSION } from "./config.js";
15
- import * as p from "@clack/prompts";
16
- import pc from "picocolors";
17
-
18
- // ============================================================================
19
- // STATS DISPLAY
20
- // ============================================================================
21
-
22
- function displayStats() {
23
- const db = loadKnowledge();
24
-
25
- p.intro(pc.cyan(`📊 PikaKit Knowledge Base v${VERSION}`));
26
-
27
- if (!db.lessons || db.lessons.length === 0) {
28
- p.note("No lessons learned yet.\nUse 'agent learn' to add patterns.", pc.dim("Empty"));
29
- return;
30
- }
31
-
32
- // Summary
33
- const totalHits = db.lessons.reduce((sum, l) => sum + (l.hitCount || 0), 0);
34
- const errorCount = db.lessons.filter(l => l.severity === "ERROR").length;
35
- const warningCount = db.lessons.filter(l => l.severity === "WARNING").length;
36
-
37
- const summaryLines = [
38
- `📚 Total Lessons: ${pc.bold(db.lessons.length)}`,
39
- `🎯 Total Violations: ${pc.bold(totalHits)}`,
40
- `${pc.red("❌")} ERROR patterns: ${errorCount}`,
41
- `${pc.yellow("⚠️")} WARNING patterns: ${warningCount}`
42
- ];
43
- p.note(summaryLines.join("\n"), pc.dim("Summary"));
44
-
45
- // Most triggered
46
- const sorted = [...db.lessons]
47
- .filter(l => l.hitCount > 0)
48
- .sort((a, b) => (b.hitCount || 0) - (a.hitCount || 0))
49
- .slice(0, 5);
50
-
51
- if (sorted.length > 0) {
52
- const triggeredLines = sorted.map((lesson, idx) => {
53
- const bar = "█".repeat(Math.min(15, Math.ceil((lesson.hitCount / (sorted[0].hitCount || 1)) * 15)));
54
- return `${idx + 1}. [${lesson.id}] ${lesson.hitCount} hits\n ${pc.green(bar)}\n ${pc.dim(lesson.message)}`;
55
- });
56
- p.note(triggeredLines.join("\n\n"), pc.dim("🔥 Most Triggered"));
57
- }
58
-
59
- // Auto-escalated
60
- const escalated = db.lessons.filter(l => l.autoEscalated);
61
- if (escalated.length > 0) {
62
- const escalatedLines = escalated.map(l =>
63
- `[${l.id}] → Escalated after ${l.hitCount} violations`
64
- );
65
- p.note(escalatedLines.join("\n"), pc.dim("⚡ Auto-Escalated"));
66
- }
67
-
68
- // Recent activity
69
- const withHits = db.lessons.filter(l => l.lastHit);
70
- if (withHits.length > 0) {
71
- const recent = [...withHits]
72
- .sort((a, b) => new Date(b.lastHit) - new Date(a.lastHit))
73
- .slice(0, 3);
74
-
75
- const recentLines = recent.map(l => {
76
- const timeAgo = getTimeAgo(new Date(l.lastHit));
77
- return `[${l.id}] Last hit: ${timeAgo}`;
78
- });
79
- p.note(recentLines.join("\n"), pc.dim("🕐 Recent Activity"));
80
- }
81
-
82
- // Sources breakdown
83
- const sources = {};
84
- db.lessons.forEach(l => {
85
- const src = l.source || "manual";
86
- sources[src] = (sources[src] || 0) + 1;
87
- });
88
-
89
- const sourceLines = Object.entries(sources).map(([src, count]) => {
90
- const icon = src === "manual" ? "✍️" : src === "eslint" ? "🔧" : "🧪";
91
- return `${icon} ${src}: ${count}`;
92
- });
93
- p.note(sourceLines.join("\n"), pc.dim("📥 Lesson Sources"));
94
-
95
- p.outro(pc.green("Stats complete"));
96
- }
97
-
98
- /**
99
- * Get human-readable time ago string
100
- */
101
- function getTimeAgo(date) {
102
- const seconds = Math.floor((Date.now() - date) / 1000);
103
-
104
- if (seconds < 60) return "just now";
105
- if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
106
- if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
107
- return `${Math.floor(seconds / 86400)} days ago`;
108
- }
109
-
110
- // ============================================================================
111
- // CLI
112
- // ============================================================================
113
-
114
- const args = process.argv.slice(2);
115
-
116
- if (args.includes("--help")) {
117
- console.log(`
118
- 📊 Smart Stats - Knowledge Base Statistics
119
-
120
- Usage:
121
- agent stats
122
-
123
- Shows:
124
- - Total lessons and violations
125
- - Most triggered patterns
126
- - Auto-escalated patterns
127
- - Recent activity
128
- `);
129
- process.exit(0);
130
- }
131
-
132
- displayStats();
@@ -1,94 +0,0 @@
1
- /**
2
- * @fileoverview Tests for stats.js functionality
3
- */
4
- import { describe, it, expect } from "vitest";
5
-
6
- describe("Stats - Statistics Calculation", () => {
7
- it("calculates total hits from lessons", () => {
8
- const lessons = [
9
- { id: "LEARN-001", hitCount: 5 },
10
- { id: "LEARN-002", hitCount: 3 },
11
- { id: "LEARN-003", hitCount: 0 }
12
- ];
13
-
14
- const totalHits = lessons.reduce((sum, l) => sum + (l.hitCount || 0), 0);
15
- expect(totalHits).toBe(8);
16
- });
17
-
18
- it("counts severity types correctly", () => {
19
- const lessons = [
20
- { id: "LEARN-001", severity: "ERROR" },
21
- { id: "LEARN-002", severity: "WARNING" },
22
- { id: "LEARN-003", severity: "ERROR" },
23
- { id: "LEARN-004", severity: "WARNING" }
24
- ];
25
-
26
- const errorCount = lessons.filter(l => l.severity === "ERROR").length;
27
- const warningCount = lessons.filter(l => l.severity === "WARNING").length;
28
-
29
- expect(errorCount).toBe(2);
30
- expect(warningCount).toBe(2);
31
- });
32
-
33
- it("sorts by hit count descending", () => {
34
- const lessons = [
35
- { id: "LEARN-001", hitCount: 3 },
36
- { id: "LEARN-002", hitCount: 10 },
37
- { id: "LEARN-003", hitCount: 5 }
38
- ];
39
-
40
- const sorted = [...lessons].sort((a, b) => (b.hitCount || 0) - (a.hitCount || 0));
41
-
42
- expect(sorted[0].id).toBe("LEARN-002");
43
- expect(sorted[1].id).toBe("LEARN-003");
44
- expect(sorted[2].id).toBe("LEARN-001");
45
- });
46
-
47
- it("filters auto-escalated patterns", () => {
48
- const lessons = [
49
- { id: "LEARN-001", autoEscalated: true },
50
- { id: "LEARN-002", autoEscalated: false },
51
- { id: "LEARN-003", autoEscalated: true }
52
- ];
53
-
54
- const escalated = lessons.filter(l => l.autoEscalated);
55
- expect(escalated.length).toBe(2);
56
- });
57
-
58
- it("groups by source correctly", () => {
59
- const lessons = [
60
- { id: "LEARN-001", source: "manual" },
61
- { id: "LEARN-002", source: "eslint" },
62
- { id: "LEARN-003", source: "manual" },
63
- { id: "LEARN-004", source: "test-failure" }
64
- ];
65
-
66
- const sources = {};
67
- lessons.forEach(l => {
68
- const src = l.source || "manual";
69
- sources[src] = (sources[src] || 0) + 1;
70
- });
71
-
72
- expect(sources["manual"]).toBe(2);
73
- expect(sources["eslint"]).toBe(1);
74
- expect(sources["test-failure"]).toBe(1);
75
- });
76
- });
77
-
78
- describe("Stats - Time Formatting", () => {
79
- it("formats time ago correctly", () => {
80
- const getTimeAgo = (date) => {
81
- const seconds = Math.floor((Date.now() - date) / 1000);
82
- if (seconds < 60) return "just now";
83
- if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
84
- if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
85
- return `${Math.floor(seconds / 86400)} days ago`;
86
- };
87
-
88
- const now = Date.now();
89
- expect(getTimeAgo(now - 30000)).toBe("just now");
90
- expect(getTimeAgo(now - 120000)).toBe("2 minutes ago");
91
- expect(getTimeAgo(now - 7200000)).toBe("2 hours ago");
92
- expect(getTimeAgo(now - 172800000)).toBe("2 days ago");
93
- });
94
- });
@@ -1,33 +0,0 @@
1
- /**
2
- * @fileoverview Type Definitions for agent-skill-kit CLI
3
- */
4
-
5
- /**
6
- * @typedef {Object} Lesson
7
- * @property {string} id - Lesson ID (e.g., LEARN-001)
8
- * @property {string} pattern - Regex pattern to match
9
- * @property {string} message - Explanation message
10
- * @property {'WARNING'|'ERROR'} severity - Severity level
11
- * @property {string} addedAt - ISO timestamp
12
- */
13
-
14
- /**
15
- * @typedef {Object} KnowledgeBase
16
- * @property {Lesson[]} lessons - Array of learned lessons
17
- */
18
-
19
- /**
20
- * @typedef {Object} AuditResult
21
- * @property {string} file - File path
22
- * @property {string[]} violations - List of violations found
23
- * @property {boolean} passed - Whether file passed audit
24
- */
25
-
26
- /**
27
- * @typedef {Object} CLIConfig
28
- * @property {string} agentDir - Path to .agent directory
29
- * @property {string} knowledgePath - Path to lessons file
30
- * @property {string} rulesPath - Path to rules directory
31
- */
32
-
33
- export { };
@@ -1,146 +0,0 @@
1
- /**
2
- * Audit UI - Compliance check with Clack UI
3
- *
4
- * Wrapper for the audit functionality that integrates
5
- * with the main menu instead of spawning a separate process.
6
- */
7
- import * as p from "@clack/prompts";
8
- import pc from "picocolors";
9
- import fs from "fs";
10
- import path from "path";
11
- import { scanDirectory, loadKnowledge, saveKnowledge } from "../recall.js";
12
- import { VERSION, cwd } from "../config.js";
13
-
14
- // ============================================================================
15
- // CONFIGURATION
16
- // ============================================================================
17
-
18
- const SCAN_EXTENSIONS = [".js", ".ts", ".tsx", ".jsx", ".mjs"];
19
-
20
- // ============================================================================
21
- // GOVERNANCE CHECKS
22
- // ============================================================================
23
-
24
- /**
25
- * Check if governance files exist
26
- * @param {string} projectRoot
27
- * @returns {{ passed: boolean, details: string[] }}
28
- */
29
- function checkGovernance(projectRoot) {
30
- const details = [];
31
- let passed = true;
32
-
33
- const governanceFiles = [
34
- { path: path.join(projectRoot, ".agent", "GEMINI.md"), name: "GEMINI.md" },
35
- { path: path.join(projectRoot, ".agent", "ARCHITECTURE.md"), name: "ARCHITECTURE.md" }
36
- ];
37
-
38
- governanceFiles.forEach(file => {
39
- if (fs.existsSync(file.path)) {
40
- details.push(`${pc.green("✓")} ${file.name} found`);
41
- } else {
42
- details.push(`${pc.yellow("⚠")} ${file.name} not found (optional)`);
43
- }
44
- });
45
-
46
- // Check for skills
47
- const skillsDir = path.join(projectRoot, ".agent", "skills");
48
- if (fs.existsSync(skillsDir)) {
49
- try {
50
- const skills = fs.readdirSync(skillsDir).filter(f =>
51
- fs.statSync(path.join(skillsDir, f)).isDirectory()
52
- );
53
- details.push(`${pc.green("✓")} ${skills.length} skill(s) loaded`);
54
- } catch (e) {
55
- details.push(`${pc.dim("○")} Skills directory not accessible`);
56
- }
57
- }
58
-
59
- return { passed, details };
60
- }
61
-
62
- // ============================================================================
63
- // AUDIT UI
64
- // ============================================================================
65
-
66
- /**
67
- * Run audit with Clack UI
68
- */
69
- export async function runAuditUI() {
70
- const projectRoot = cwd;
71
-
72
- p.intro(pc.cyan(`Smart Audit v${VERSION}`));
73
-
74
- const startTime = Date.now();
75
- let hasErrors = false;
76
-
77
- // Phase 1: Memory Recall
78
- const s1 = p.spinner();
79
- s1.start("Phase 1: Memory Recall");
80
-
81
- const db = loadKnowledge();
82
-
83
- if (db.lessons.length === 0) {
84
- s1.stop(pc.dim("Phase 1: No lessons learned yet"));
85
- } else {
86
- const { results } = scanDirectory(projectRoot, db);
87
-
88
- // Count violations
89
- let totalViolations = 0;
90
- let errorCount = 0;
91
- let warningCount = 0;
92
-
93
- results.forEach(result => {
94
- result.violations.forEach(({ lesson, matches }) => {
95
- totalViolations += matches.length;
96
- if (lesson.severity === "ERROR") {
97
- errorCount += matches.length;
98
- hasErrors = true;
99
- } else {
100
- warningCount += matches.length;
101
- }
102
- });
103
- });
104
-
105
- if (totalViolations > 0) {
106
- s1.stop(`Phase 1: Found ${totalViolations} violation(s)`);
107
-
108
- // Show violations summary
109
- if (errorCount > 0 || warningCount > 0) {
110
- p.log.warn(`${pc.red("✖")} ${errorCount} error(s) ${pc.yellow("⚠")} ${warningCount} warning(s)`);
111
- }
112
-
113
- saveKnowledge(db);
114
- } else {
115
- s1.stop(pc.green("Phase 1: No violations found ✓"));
116
- }
117
- }
118
-
119
- // Phase 2: Governance
120
- const s2 = p.spinner();
121
- s2.start("Phase 2: Governance Check");
122
-
123
- const govResult = checkGovernance(projectRoot);
124
- s2.stop("Phase 2: Governance checked");
125
-
126
- p.note(govResult.details.join("\n"), "Governance");
127
-
128
- // Phase 3: Summary
129
- const duration = ((Date.now() - startTime) / 1000).toFixed(2);
130
- const totalHits = db.lessons.reduce((sum, l) => sum + (l.hitCount || 0), 0);
131
-
132
- p.note(
133
- `${pc.dim("Duration:")} ${duration}s\n` +
134
- `${pc.dim("Lessons:")} ${db.lessons.length} in memory\n` +
135
- `${pc.dim("Hits:")} ${totalHits} total`,
136
- "Summary"
137
- );
138
-
139
- if (hasErrors) {
140
- p.outro(pc.red("AUDIT FAILED: Please fix ERROR violations"));
141
- } else {
142
- p.outro(pc.green("AUDIT PASSED: Code is compliant"));
143
- }
144
- }
145
-
146
- export default runAuditUI;