add-skill-kit 3.2.4 → 3.2.6

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 (78) hide show
  1. package/README.md +179 -119
  2. package/bin/lib/commands/help.js +0 -4
  3. package/bin/lib/commands/install.js +129 -9
  4. package/bin/lib/ui.js +1 -1
  5. package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
  6. package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
  7. package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
  8. package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
  9. package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
  10. package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
  11. package/lib/agent-cli/bin/{ag-smart.js → agent.js} +48 -15
  12. package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
  13. package/lib/agent-cli/dashboard/index.html +538 -0
  14. package/lib/agent-cli/lib/audit.js +2 -2
  15. package/lib/agent-cli/lib/auto-learn.js +8 -8
  16. package/lib/agent-cli/lib/eslint-fix.js +1 -1
  17. package/lib/agent-cli/lib/fix.js +5 -5
  18. package/lib/agent-cli/lib/hooks/install-hooks.js +4 -4
  19. package/lib/agent-cli/lib/hooks/lint-learn.js +4 -4
  20. package/lib/agent-cli/lib/learn.js +10 -10
  21. package/lib/agent-cli/lib/recall.js +1 -1
  22. package/lib/agent-cli/lib/settings.js +24 -0
  23. package/lib/agent-cli/lib/skill-learn.js +2 -2
  24. package/lib/agent-cli/lib/stats.js +3 -3
  25. package/lib/agent-cli/lib/ui/dashboard-ui.js +103 -4
  26. package/lib/agent-cli/lib/ui/index.js +36 -6
  27. package/lib/agent-cli/lib/watcher.js +2 -2
  28. package/lib/agent-cli/package.json +4 -4
  29. package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
  30. package/lib/agent-cli/scripts/dashboard_server.js +224 -0
  31. package/lib/agent-cli/scripts/error_sensor.js +565 -0
  32. package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
  33. package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
  34. package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
  35. package/lib/agent-cli/scripts/rule_sharing.js +374 -0
  36. package/lib/agent-cli/scripts/skill_injector.js +387 -0
  37. package/lib/agent-cli/scripts/success_sensor.js +500 -0
  38. package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
  39. package/lib/agent-cli/services/auto-learn-service.js +247 -0
  40. package/lib/agent-cli/src/MIGRATION.md +418 -0
  41. package/lib/agent-cli/src/README.md +367 -0
  42. package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
  43. package/lib/agent-cli/src/core/evolution/index.js +17 -0
  44. package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
  45. package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
  46. package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
  47. package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
  48. package/lib/agent-cli/src/core/index.js +15 -0
  49. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
  50. package/lib/agent-cli/src/core/learning/index.js +12 -0
  51. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
  52. package/lib/agent-cli/src/core/scanning/index.js +14 -0
  53. package/lib/agent-cli/src/data/index.js +13 -0
  54. package/lib/agent-cli/src/data/repositories/index.js +8 -0
  55. package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
  56. package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
  57. package/lib/agent-cli/src/data/storage/index.js +8 -0
  58. package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
  59. package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
  60. package/lib/agent-cli/src/infrastructure/index.js +13 -0
  61. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
  62. package/lib/agent-cli/src/services/export-service.js +162 -0
  63. package/lib/agent-cli/src/services/index.js +13 -0
  64. package/lib/agent-cli/src/services/learning-service.js +99 -0
  65. package/lib/agent-cli/types/index.d.ts +343 -0
  66. package/lib/agent-cli/utils/benchmark.js +269 -0
  67. package/lib/agent-cli/utils/logger.js +303 -0
  68. package/lib/agent-cli/utils/ml_patterns.js +300 -0
  69. package/lib/agent-cli/utils/recovery.js +312 -0
  70. package/lib/agent-cli/utils/telemetry.js +290 -0
  71. package/lib/agentskillskit-cli/ag-smart.js +15 -15
  72. package/lib/agentskillskit-cli/package.json +3 -3
  73. package/package.json +12 -6
  74. package/lib/agent-cli/lib/auto_preview.py +0 -148
  75. package/lib/agent-cli/lib/checklist.py +0 -222
  76. package/lib/agent-cli/lib/session_manager.py +0 -120
  77. package/lib/agent-cli/lib/verify_all.py +0 -327
  78. /package/bin/{cli.js → kit.js} +0 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * ThresholdChecker - Core Business Logic
3
+ *
4
+ * Determines if a lesson is ready for evolution based on:
5
+ * - Hit count threshold
6
+ * - Cognitive confidence
7
+ * - Pattern stability
8
+ *
9
+ * Pure function with NO side effects.
10
+ */
11
+
12
+ export class ThresholdChecker {
13
+ /**
14
+ * Check if a lesson is ready for evolution
15
+ * @param {object} lesson - Cognitive lesson with hitCount, confidence, etc.
16
+ * @param {number} threshold - Hit count threshold from settings
17
+ * @returns {{ ready: boolean, reason?: string, confidence?: number }}
18
+ */
19
+ static check(lesson, threshold = 10) {
20
+ const hitCount = lesson.hitCount || 0;
21
+ const cognitiveConfidence = lesson.cognitive?.confidence || 0.3;
22
+
23
+ // Rule 1: Hit count threshold reached
24
+ if (hitCount >= threshold) {
25
+ return {
26
+ ready: true,
27
+ reason: 'hitCountThreshold',
28
+ confidence: Math.min(0.9, 0.7 + (hitCount - threshold) * 0.02)
29
+ };
30
+ }
31
+
32
+ // Rule 2: High cognitive confidence even with lower hits
33
+ if (cognitiveConfidence >= 0.85 && hitCount >= threshold * 0.5) {
34
+ return {
35
+ ready: true,
36
+ reason: 'highConfidence',
37
+ confidence: cognitiveConfidence
38
+ };
39
+ }
40
+
41
+ // Rule 3: Pattern stability (if we have violation history)
42
+ if (lesson.metadata?.stabilityScore && lesson.metadata.stabilityScore > 0.9) {
43
+ return {
44
+ ready: true,
45
+ reason: 'patternStable',
46
+ confidence: lesson.metadata.stabilityScore
47
+ };
48
+ }
49
+
50
+ // Not ready for evolution
51
+ return { ready: false };
52
+ }
53
+
54
+ /**
55
+ * Calculate pattern stability from violation history
56
+ * @param {Array} violationHistory - Array of { timestamp, file, line }
57
+ * @returns {number} - Stability score 0.0 to 1.0
58
+ */
59
+ static calculateStability(violationHistory = []) {
60
+ if (violationHistory.length < 3) return 0.0;
61
+
62
+ // Check if violations are consistent (same files, similar patterns)
63
+ const uniqueFiles = new Set(violationHistory.map(v => v.file)).size;
64
+ const totalViolations = violationHistory.length;
65
+
66
+ // High stability = violations come from same files (not random)
67
+ const fileConsistency = 1 - (uniqueFiles / totalViolations);
68
+
69
+ // Time-based stability: violations spread over time (not one-off)
70
+ const timestamps = violationHistory.map(v => v.timestamp).sort();
71
+ const timeSpan = timestamps[timestamps.length - 1] - timestamps[0];
72
+ const daySpan = timeSpan / (1000 * 60 * 60 * 24);
73
+
74
+ const timeStability = daySpan > 1 ? Math.min(1.0, daySpan / 7) : 0.2;
75
+
76
+ // Combined stability
77
+ return (fileConsistency * 0.6 + timeStability * 0.4);
78
+ }
79
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Core - Domain Layer
3
+ *
4
+ * Pure business logic with ZERO external dependencies.
5
+ * This layer defines the business rules and domain models.
6
+ *
7
+ * Exported modules:
8
+ * - learning: Lesson management, cognitive enhancements, lesson merging
9
+ * - evolution: Signal detection, threshold checking, review gate
10
+ * - scanning: File scanning, pattern matching, violation tracking
11
+ */
12
+
13
+ export * from './learning/index.js';
14
+ export * from './evolution/index.js';
15
+ export * from './scanning/index.js';
@@ -0,0 +1,282 @@
1
+ /**
2
+ * CognitiveEnhancer - Core Business Logic
3
+ *
4
+ * Transforms raw lessons into cognitive lessons with intelligence layers:
5
+ * 1. Intent Inference - detect purpose
6
+ * 2. Pattern Classification - categorize by type
7
+ * 3. Maturity Calculation - confidence & state
8
+ * 4. Evolution Analysis - gap detection
9
+ *
10
+ * Pure business logic with NO external dependencies.
11
+ */
12
+
13
+ // ============================================================================
14
+ // INTENT INFERENCE
15
+ // ============================================================================
16
+
17
+ const INTENT_PATTERNS = {
18
+ 'safe-rebranding': {
19
+ keywords: ['rebrand', 'rename', 'file-safety'],
20
+ goal: 'Rename files/entities without data loss or breaking changes',
21
+ category: 'file-operations',
22
+ },
23
+ 'cli-ux-consistency': {
24
+ keywords: ['cli', 'ux', 'menu', 'navigation', 'clack'],
25
+ goal: 'Provide consistent, intuitive CLI user experience',
26
+ category: 'user-experience',
27
+ },
28
+ 'error-prevention': {
29
+ keywords: ['validation', 'error', 'check', 'verify'],
30
+ goal: 'Prevent runtime errors through proactive validation',
31
+ category: 'reliability',
32
+ },
33
+ 'code-quality': {
34
+ keywords: ['import', 'architecture', 'quality'],
35
+ goal: 'Maintain clean, maintainable code structure',
36
+ category: 'maintainability',
37
+ },
38
+ };
39
+
40
+ export class CognitiveEnhancer {
41
+ /**
42
+ * Infer lesson intent from tags
43
+ * @param {Array<string>} tags
44
+ * @returns {{id: string, goal: string, strength: number, category: string}}
45
+ */
46
+ static inferIntent(tags) {
47
+ if (!tags || tags.length === 0) {
48
+ return {
49
+ id: 'unknown',
50
+ goal: 'General code quality improvement',
51
+ strength: 0.3,
52
+ category: 'general',
53
+ };
54
+ }
55
+
56
+ let bestMatch = null;
57
+ let bestScore = 0;
58
+
59
+ for (const [intentId, pattern] of Object.entries(INTENT_PATTERNS)) {
60
+ const matchCount = tags.filter(tag =>
61
+ pattern.keywords.some(kw => tag.toLowerCase().includes(kw.toLowerCase()))
62
+ ).length;
63
+
64
+ const score = matchCount / tags.length; // 0-1 confidence
65
+
66
+ if (score > bestScore) {
67
+ bestScore = score;
68
+ bestMatch = {
69
+ id: intentId,
70
+ goal: pattern.goal,
71
+ strength: score,
72
+ category: pattern.category,
73
+ };
74
+ }
75
+ }
76
+
77
+ return bestMatch || {
78
+ id: 'unknown',
79
+ goal: 'General code quality improvement',
80
+ strength: 0.3,
81
+ category: 'general',
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Calculate lesson maturity metrics
87
+ * @param {Array} mistakes
88
+ * @param {Array} improvements
89
+ * @returns {{state: string, confidence: number, indicators: object, coverage: string, recommendation: string}}
90
+ */
91
+ static calculateMaturity(mistakes, improvements) {
92
+ const m = mistakes.length;
93
+ const i = improvements.length;
94
+
95
+ // State determination
96
+ let state;
97
+ if (i === 0 && m > 0) state = 'RAW'; // 🟥 Only mistakes
98
+ else if (i > 0 && m === 0) state = 'IDEAL'; // 🟦 Only improvements (rare)
99
+ else if (i >= m) state = 'MATURE'; // 🟩 Balanced or improvement-heavy
100
+ else state = 'LEARNING'; // 🟨 More mistakes than solutions
101
+
102
+ // Multi-factor confidence
103
+ const balanceScore = i / (m + i) || 0;
104
+ const evidenceScore = this.calculateEvidenceScore(mistakes, improvements);
105
+ const recencyScore = this.calculateRecencyScore(mistakes, improvements);
106
+
107
+ const confidence = (
108
+ balanceScore * 0.5 + // Balance is most important
109
+ evidenceScore * 0.3 + // Evidence validates
110
+ recencyScore * 0.2 // Fresh is better
111
+ );
112
+
113
+ return {
114
+ state,
115
+ confidence: Math.round(confidence * 100) / 100,
116
+ indicators: {
117
+ balance: Math.round(balanceScore * 100) / 100,
118
+ evidence: Math.round(evidenceScore * 100) / 100,
119
+ recency: Math.round(recencyScore * 100) / 100,
120
+ },
121
+ coverage: `${m} mistakes / ${i} improvements`,
122
+ recommendation: this.getRecommendation(state, confidence),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Calculate evidence score based on hit counts
128
+ */
129
+ static calculateEvidenceScore(mistakes, improvements) {
130
+ const totalMistakeHits = mistakes.reduce((sum, m) => sum + (m.hitCount || 0), 0);
131
+ const totalImprovementHits = improvements.reduce((sum, i) => sum + (i.appliedCount || 0), 0);
132
+ const totalHits = totalMistakeHits + totalImprovementHits;
133
+
134
+ if (totalHits === 0) return 0.1; // No evidence
135
+ if (totalHits < 5) return 0.3; // Weak
136
+ if (totalHits < 20) return 0.6; // Moderate
137
+ return 0.9; // Strong
138
+ }
139
+
140
+ /**
141
+ * Calculate recency score
142
+ */
143
+ static calculateRecencyScore(mistakes, improvements) {
144
+ const now = Date.now();
145
+
146
+ const allDates = [
147
+ ...mistakes.map(m => new Date(m.lastHit || m.added).getTime()),
148
+ ...improvements.map(i => new Date(i.lastApplied || i.added).getTime()),
149
+ ];
150
+
151
+ if (allDates.length === 0) return 0.5;
152
+
153
+ const lastUpdate = Math.max(...allDates);
154
+ const daysSinceUpdate = (now - lastUpdate) / (1000 * 60 * 60 * 24);
155
+
156
+ if (daysSinceUpdate < 7) return 1.0; // This week
157
+ if (daysSinceUpdate < 30) return 0.8; // This month
158
+ if (daysSinceUpdate < 90) return 0.5; // This quarter
159
+ return 0.3; // Stale
160
+ }
161
+
162
+ /**
163
+ * Get recommendation based on state and confidence
164
+ */
165
+ static getRecommendation(state, confidence) {
166
+ if (state === 'RAW') {
167
+ return 'URGENT: Discover best practices for this area';
168
+ }
169
+ if (state === 'LEARNING' && confidence < 0.5) {
170
+ return 'Needs more improvements to balance anti-patterns';
171
+ }
172
+ if (state === 'MATURE' && confidence > 0.8) {
173
+ return 'Stable - can be trusted for skill injection';
174
+ }
175
+ if (state === 'IDEAL') {
176
+ return 'Perfect - all best practices, no anti-patterns';
177
+ }
178
+ return 'Continue learning';
179
+ }
180
+
181
+ /**
182
+ * Analyze evolution needs and gaps
183
+ * @param {Array} mistakes
184
+ * @param {Array} improvements
185
+ * @param {object} intent
186
+ * @returns {{signals: Array, missingAreas: Array, nextAction: string}}
187
+ */
188
+ static analyzeEvolution(mistakes, improvements, intent) {
189
+ const signals = [];
190
+ const missingAreas = [];
191
+
192
+ // Signal 1: Many mistakes, few solutions
193
+ if (mistakes.length > improvements.length * 2) {
194
+ signals.push({
195
+ type: 'SUGGEST_IMPROVEMENT_DISCOVERY',
196
+ priority: 'HIGH',
197
+ reason: `${mistakes.length} anti-patterns but only ${improvements.length} solution(s)`,
198
+ });
199
+ }
200
+
201
+ // Signal 2: Uncovered tags
202
+ const mistakeTags = new Set(mistakes.flatMap(m => m.tags || []));
203
+ const improvementTags = new Set(improvements.flatMap(i => i.tags || []));
204
+ const uncoveredTags = [...mistakeTags].filter(tag => !improvementTags.has(tag));
205
+
206
+ uncoveredTags.forEach(tag => {
207
+ missingAreas.push({
208
+ area: tag,
209
+ reason: 'Anti-patterns detected but no best practice documented',
210
+ mistakeCount: mistakes.filter(m => m.tags && m.tags.includes(tag)).length,
211
+ });
212
+ });
213
+
214
+ // Signal 3: High-hit mistakes without solution
215
+ mistakes.forEach(m => {
216
+ if ((m.hitCount || 0) > 10 && !this.hasRelatedImprovement(m, improvements)) {
217
+ signals.push({
218
+ type: 'HOT_MISTAKE_NEEDS_SOLUTION',
219
+ priority: 'CRITICAL',
220
+ mistake: m.id,
221
+ reason: `${m.title} hit ${m.hitCount} times but no solution documented`,
222
+ });
223
+
224
+ missingAreas.push({
225
+ area: `solution-for-${m.id}`,
226
+ reason: 'Frequently violated anti-pattern needs best practice',
227
+ hitCount: m.hitCount,
228
+ });
229
+ }
230
+ });
231
+
232
+ // Signal 4: Intent under-served
233
+ if (intent && intent.strength > 0.7) {
234
+ const coverageScore = improvements.length / (mistakes.length + improvements.length);
235
+ if (coverageScore < 0.5) {
236
+ signals.push({
237
+ type: 'INTENT_UNDER_SERVED',
238
+ priority: 'MEDIUM',
239
+ reason: `Intent "${intent.goal}" is clear but solutions are scarce`,
240
+ });
241
+ }
242
+ }
243
+
244
+ return {
245
+ signals,
246
+ missingAreas,
247
+ nextAction: this.determineNextAction(signals),
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Check if mistake has related improvement
253
+ */
254
+ static hasRelatedImprovement(mistake, improvements) {
255
+ return improvements.some(i =>
256
+ i.tags && mistake.tags && i.tags.some(tag => mistake.tags.includes(tag))
257
+ );
258
+ }
259
+
260
+ /**
261
+ * Determine next action from signals
262
+ */
263
+ static determineNextAction(signals) {
264
+ if (signals.some(s => s.priority === 'CRITICAL')) {
265
+ return 'Document solution for high-frequency violations immediately';
266
+ }
267
+ if (signals.some(s => s.type === 'SUGGEST_IMPROVEMENT_DISCOVERY')) {
268
+ return 'Research and document best practices in underserved areas';
269
+ }
270
+ return 'Continue normal learning';
271
+ }
272
+
273
+ /**
274
+ * Format tag as title
275
+ */
276
+ static formatTagAsTitle(tag) {
277
+ return tag
278
+ .split('-')
279
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
280
+ .join(' ');
281
+ }
282
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Learning Module - Core Domain
3
+ *
4
+ * Business logic for lesson management and cognitive enhancements.
5
+ *
6
+ * Exported:
7
+ * - CognitiveEnhancer: Transform raw lessons to cognitive lessons
8
+ * - LessonSynthesizer: Group and synthesize lessons
9
+ */
10
+
11
+ export { CognitiveEnhancer } from './cognitive-enhancer.js';
12
+ export { LessonSynthesizer } from './lesson-synthesizer.js';
@@ -0,0 +1,83 @@
1
+ /**
2
+ * LessonSynthesizer - Core Business Logic
3
+ *
4
+ * Synthesizes cognitive lessons from raw mistakes and improvements.
5
+ * Groups by tag, applies cognitive enhancements, and sorts by maturity.
6
+ *
7
+ * Pure function - no side effects.
8
+ */
9
+
10
+ import { CognitiveEnhancer } from './cognitive-enhancer.js';
11
+
12
+ export class LessonSynthesizer {
13
+ /**
14
+ * Synthesize cognitive lessons from mistakes and improvements
15
+ * @param {Array} mistakes - Raw mistake data
16
+ * @param {Array} improvements - Raw improvement data
17
+ * @returns {Array} Cognitive lesson units
18
+ */
19
+ static synthesize(mistakes, improvements) {
20
+ // Group by tag
21
+ const groups = new Map();
22
+
23
+ // Add mistakes to groups
24
+ mistakes.forEach(m => {
25
+ const tags = m.tags || ['uncategorized'];
26
+ tags.forEach(tag => {
27
+ if (!groups.has(tag)) {
28
+ groups.set(tag, { mistakes: [], improvements: [] });
29
+ }
30
+ groups.get(tag).mistakes.push(m);
31
+ });
32
+ });
33
+
34
+ // Add improvements to groups
35
+ improvements.forEach(i => {
36
+ const tags = i.tags || ['uncategorized'];
37
+ tags.forEach(tag => {
38
+ if (!groups.has(tag)) {
39
+ groups.set(tag, { mistakes: [], improvements: [] });
40
+ }
41
+ groups.get(tag).improvements.push(i);
42
+ });
43
+ });
44
+
45
+ // Build Cognitive Lessons
46
+ const lessons = [];
47
+ let lessonId = 1;
48
+
49
+ groups.forEach((group, tag) => {
50
+ const allTags = [tag, ...group.mistakes.flatMap(m => m.tags || [])];
51
+ const intent = CognitiveEnhancer.inferIntent(allTags);
52
+ const maturity = CognitiveEnhancer.calculateMaturity(group.mistakes, group.improvements);
53
+ const evolution = CognitiveEnhancer.analyzeEvolution(group.mistakes, group.improvements, intent);
54
+
55
+ lessons.push({
56
+ id: `LESSON-${String(lessonId++).padStart(3, '0')}`,
57
+ title: CognitiveEnhancer.formatTagAsTitle(tag),
58
+ tag,
59
+ intent,
60
+ mistakes: group.mistakes,
61
+ improvements: group.improvements,
62
+ maturity,
63
+ evolution,
64
+ });
65
+ });
66
+
67
+ // Sort by maturity (MATURE first, RAW last)
68
+ return this.sortByMaturity(lessons);
69
+ }
70
+
71
+ /**
72
+ * Sort lessons by maturity state and confidence
73
+ */
74
+ static sortByMaturity(lessons) {
75
+ const stateOrder = { 'IDEAL': 0, 'MATURE': 1, 'LEARNING': 2, 'RAW': 3 };
76
+
77
+ return lessons.sort((a, b) => {
78
+ const stateCompare = stateOrder[a.maturity.state] - stateOrder[b.maturity.state];
79
+ if (stateCompare !== 0) return stateCompare;
80
+ return b.maturity.confidence - a.maturity.confidence; // Higher confidence first
81
+ });
82
+ }
83
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Scanning Module - Core Domain
3
+ *
4
+ * Business logic for code scanning and violation detection.
5
+ *
6
+ * Exported:
7
+ * - FileScanner: Scan files for pattern violations
8
+ * - PatternMatcher: Match patterns against code
9
+ * - ViolationTracker: Track violation statistics
10
+ */
11
+
12
+ export { FileScanner } from './file-scanner.js';
13
+ export { PatternMatcher } from './pattern-matcher.js';
14
+ export { ViolationTracker } from './violation-tracker.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Data Layer
3
+ *
4
+ * Data access layer with repositories and storage adapters.
5
+ * Implements persistence interfaces defined in core.
6
+ *
7
+ * Exported:
8
+ * - Repositories: LessonRepository, SignalRepository, SettingsRepository
9
+ * - Storage: FileStorage, YamlStorage, JsonStorage
10
+ */
11
+
12
+ export * from './repositories/index.js';
13
+ export * from './storage/index.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Repositories - Data Access Layer
3
+ *
4
+ * Repository pattern for domain objects.
5
+ */
6
+
7
+ export { SignalRepository } from './signal-repository.js';
8
+ export { LessonRepository } from './lesson-repository.js';
@@ -0,0 +1,130 @@
1
+ /**
2
+ * LessonRepository - Data Access Layer
3
+ *
4
+ * Manages persistence of mistakes and improvements.
5
+ * Provides CRUD operations with YAML storage.
6
+ */
7
+
8
+ export class LessonRepository {
9
+ constructor(storage) {
10
+ this.storage = storage;
11
+ }
12
+
13
+ /**
14
+ * Load mistakes from storage
15
+ * @returns {Promise<{version: number, mistakes: Array}>}
16
+ */
17
+ async loadMistakes() {
18
+ try {
19
+ const data = await this.storage.read('mistakes');
20
+ return data || { version: 4.0, mistakes: [] };
21
+ } catch (error) {
22
+ console.error('Error loading mistakes:', error.message);
23
+ return { version: 4.0, mistakes: [] };
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Load improvements from storage
29
+ * @returns {Promise<{version: number, improvements: Array}>}
30
+ */
31
+ async loadImprovements() {
32
+ try {
33
+ const data = await this.storage.read('improvements');
34
+ return data || { version: 4.0, improvements: [] };
35
+ } catch (error) {
36
+ console.error('Error loading improvements:', error.message);
37
+ return { version: 4.0, improvements: [] };
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Save mistakes to storage
43
+ * @param {object} data - {version, mistakes}
44
+ */
45
+ async saveMistakes(data) {
46
+ await this.storage.write('mistakes', data);
47
+ }
48
+
49
+ /**
50
+ * Save improvements to storage
51
+ * @param {object} data - {version, improvements}
52
+ */
53
+ async saveImprovements(data) {
54
+ await this.storage.write('improvements', data);
55
+ }
56
+
57
+ /**
58
+ * Load legacy lessons.yaml (v1-v3 format)
59
+ * @returns {Promise<{version: number, lessons: Array}>}
60
+ */
61
+ async loadLegacyLessons() {
62
+ try {
63
+ const data = await this.storage.read('lessons');
64
+ if (!data) {
65
+ // Initialize if doesn't exist
66
+ const initial = { lessons: [], version: 1 };
67
+ await this.storage.write('lessons', initial);
68
+ return initial;
69
+ }
70
+ return data;
71
+ } catch (error) {
72
+ console.error('Error loading legacy lessons:', error.message);
73
+ return { lessons: [], version: 1 };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Save legacy lessons.yaml
79
+ * @param {object} data - {version, lessons}
80
+ */
81
+ async saveLegacyLessons(data) {
82
+ await this.storage.write('lessons', data);
83
+ }
84
+
85
+ /**
86
+ * Add a lesson to legacy format
87
+ * @param {object} lesson - Lesson object
88
+ */
89
+ async addLegacyLesson(lesson) {
90
+ const db = await this.loadLegacyLessons();
91
+ db.lessons.push(lesson);
92
+ await this.saveLegacyLessons(db);
93
+ return lesson;
94
+ }
95
+
96
+ /**
97
+ * Remove a lesson from legacy format
98
+ * @param {string} lessonId
99
+ */
100
+ async removeLegacyLesson(lessonId) {
101
+ const db = await this.loadLegacyLessons();
102
+ const idx = db.lessons.findIndex(l => l.id === lessonId.toUpperCase());
103
+
104
+ if (idx === -1) {
105
+ throw new Error(`Lesson not found: ${lessonId}`);
106
+ }
107
+
108
+ const removed = db.lessons.splice(idx, 1)[0];
109
+ await this.saveLegacyLessons(db);
110
+ return removed;
111
+ }
112
+
113
+ /**
114
+ * Find lesson by ID in legacy format
115
+ * @param {string} lessonId
116
+ */
117
+ async findLegacyLessonById(lessonId) {
118
+ const db = await this.loadLegacyLessons();
119
+ return db.lessons.find(l => l.id === lessonId.toUpperCase());
120
+ }
121
+
122
+ /**
123
+ * Find lessons by category in legacy format
124
+ * @param {string} category
125
+ */
126
+ async findLegacyLessonsByCategory(category) {
127
+ const db = await this.loadLegacyLessons();
128
+ return db.lessons.filter(l => l.category === category);
129
+ }
130
+ }