add-skill-kit 3.2.4 → 3.2.5
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.
- package/README.md +1 -1
- package/bin/lib/commands/help.js +0 -4
- package/bin/lib/commands/install.js +90 -9
- package/bin/lib/ui.js +1 -1
- package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
- package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
- package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
- package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
- package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
- package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
- package/lib/agent-cli/bin/{ag-smart.js → agent.js} +48 -15
- package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
- package/lib/agent-cli/dashboard/index.html +538 -0
- package/lib/agent-cli/lib/audit.js +2 -2
- package/lib/agent-cli/lib/auto-learn.js +8 -8
- package/lib/agent-cli/lib/eslint-fix.js +1 -1
- package/lib/agent-cli/lib/fix.js +5 -5
- package/lib/agent-cli/lib/hooks/install-hooks.js +4 -4
- package/lib/agent-cli/lib/hooks/lint-learn.js +4 -4
- package/lib/agent-cli/lib/learn.js +10 -10
- package/lib/agent-cli/lib/recall.js +1 -1
- package/lib/agent-cli/lib/settings.js +24 -0
- package/lib/agent-cli/lib/skill-learn.js +2 -2
- package/lib/agent-cli/lib/stats.js +3 -3
- package/lib/agent-cli/lib/ui/dashboard-ui.js +103 -4
- package/lib/agent-cli/lib/ui/index.js +36 -6
- package/lib/agent-cli/lib/watcher.js +2 -2
- package/lib/agent-cli/package.json +4 -4
- package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
- package/lib/agent-cli/scripts/dashboard_server.js +224 -0
- package/lib/agent-cli/scripts/error_sensor.js +565 -0
- package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
- package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
- package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
- package/lib/agent-cli/scripts/rule_sharing.js +374 -0
- package/lib/agent-cli/scripts/skill_injector.js +387 -0
- package/lib/agent-cli/scripts/success_sensor.js +500 -0
- package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
- package/lib/agent-cli/services/auto-learn-service.js +247 -0
- package/lib/agent-cli/src/MIGRATION.md +418 -0
- package/lib/agent-cli/src/README.md +367 -0
- package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
- package/lib/agent-cli/src/core/evolution/index.js +17 -0
- package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
- package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
- package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
- package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
- package/lib/agent-cli/src/core/index.js +15 -0
- package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
- package/lib/agent-cli/src/core/learning/index.js +12 -0
- package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
- package/lib/agent-cli/src/core/scanning/index.js +14 -0
- package/lib/agent-cli/src/data/index.js +13 -0
- package/lib/agent-cli/src/data/repositories/index.js +8 -0
- package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
- package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
- package/lib/agent-cli/src/data/storage/index.js +8 -0
- package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
- package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
- package/lib/agent-cli/src/infrastructure/index.js +13 -0
- package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
- package/lib/agent-cli/src/services/export-service.js +162 -0
- package/lib/agent-cli/src/services/index.js +13 -0
- package/lib/agent-cli/src/services/learning-service.js +99 -0
- package/lib/agent-cli/types/index.d.ts +343 -0
- package/lib/agent-cli/utils/benchmark.js +269 -0
- package/lib/agent-cli/utils/logger.js +303 -0
- package/lib/agent-cli/utils/ml_patterns.js +300 -0
- package/lib/agent-cli/utils/recovery.js +312 -0
- package/lib/agent-cli/utils/telemetry.js +290 -0
- package/lib/agentskillskit-cli/ag-smart.js +15 -15
- package/lib/agentskillskit-cli/package.json +3 -3
- package/package.json +11 -5
- /package/bin/{cli.js → kit.js} +0 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Agent Skill Kit - New Architecture
|
|
2
|
+
|
|
3
|
+
> **Status:** 🚧 In Progress - Migrating to FAANG-style layered architecture
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains the new **layered architecture** for Agent Skill Kit, following FAANG best practices for maintainability, testability, and scalability.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Architecture Principles
|
|
12
|
+
|
|
13
|
+
### 1. **Layered Architecture** (Clean Architecture)
|
|
14
|
+
```
|
|
15
|
+
┌─────────────────────────────────────┐
|
|
16
|
+
│ Presentation Layer │ (CLI, UI)
|
|
17
|
+
├─────────────────────────────────────┤
|
|
18
|
+
│ Service Layer │ (Orchestration)
|
|
19
|
+
├─────────────────────────────────────┤
|
|
20
|
+
│ Core Domain Layer │ (Business Logic)
|
|
21
|
+
├─────────────────────────────────────┤
|
|
22
|
+
│ Data Layer │ (Persistence)
|
|
23
|
+
├─────────────────────────────────────┤
|
|
24
|
+
│ Infrastructure Layer │ (Config, DI, Logging)
|
|
25
|
+
└─────────────────────────────────────┘
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. **Dependency Rule**
|
|
29
|
+
- **Core** has ZERO external dependencies (pure business logic)
|
|
30
|
+
- **Data** implements interfaces defined in Core
|
|
31
|
+
- **Services** orchestrate Core + Data
|
|
32
|
+
- **Presentation** uses Services
|
|
33
|
+
- **Infrastructure** provides cross-cutting concerns
|
|
34
|
+
|
|
35
|
+
### 3. **Single Responsibility Principle**
|
|
36
|
+
Each class/module has ONE clear purpose:
|
|
37
|
+
- ✅ `EvolutionSignal` - Represents a signal
|
|
38
|
+
- ✅ `ThresholdChecker` - Checks evolution readiness
|
|
39
|
+
- ✅ `SignalRepository` - Persists signals
|
|
40
|
+
- ✅ `SignalDetector` - Orchestrates signal workflow
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Folder Structure
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
src/
|
|
48
|
+
├── core/ # Domain Layer (Pure Business Logic)
|
|
49
|
+
│ ├── learning/
|
|
50
|
+
│ │ ├── lesson-manager.js
|
|
51
|
+
│ │ ├── cognitive-enhancer.js
|
|
52
|
+
│ │ ├── lesson-merger.js
|
|
53
|
+
│ │ └── index.js
|
|
54
|
+
│ ├── evolution/ [✅ COMPLETE]
|
|
55
|
+
│ │ ├── evolution-signal.js
|
|
56
|
+
│ │ ├── threshold-checker.js
|
|
57
|
+
│ │ ├── review-gate.js
|
|
58
|
+
│ │ ├── signal-detector.js
|
|
59
|
+
│ │ └── index.js
|
|
60
|
+
│ ├── scanning/
|
|
61
|
+
│ │ ├── file-scanner.js
|
|
62
|
+
│ │ ├── pattern-matcher.js
|
|
63
|
+
│ │ ├── violation-tracker.js
|
|
64
|
+
│ │ └── index.js
|
|
65
|
+
│ └── index.js
|
|
66
|
+
│
|
|
67
|
+
├── services/ # Application Services (Orchestration)
|
|
68
|
+
│ ├── auto-learning-service.js
|
|
69
|
+
│ ├── stats-service.js
|
|
70
|
+
│ ├── backup-service.js
|
|
71
|
+
│ ├── export-service.js
|
|
72
|
+
│ └── index.js
|
|
73
|
+
│
|
|
74
|
+
├── data/ # Data Access Layer
|
|
75
|
+
│ ├── repositories/ [✅ COMPLETE]
|
|
76
|
+
│ │ ├── signal-repository.js
|
|
77
|
+
│ │ ├── lesson-repository.js
|
|
78
|
+
│ │ ├── settings-repository.js
|
|
79
|
+
│ │ └── index.js
|
|
80
|
+
│ ├── storage/ [✅ COMPLETE]
|
|
81
|
+
│ │ ├── json-storage.js
|
|
82
|
+
│ │ ├── yaml-storage.js
|
|
83
|
+
│ │ ├── file-storage.js
|
|
84
|
+
│ │ └── index.js
|
|
85
|
+
│ └── index.js
|
|
86
|
+
│
|
|
87
|
+
├── presentation/ # Presentation Layer (UI/CLI)
|
|
88
|
+
│ └── cli/
|
|
89
|
+
│ ├── commands/
|
|
90
|
+
│ ├── menus/
|
|
91
|
+
│ └── views/
|
|
92
|
+
│
|
|
93
|
+
├── infrastructure/ # Cross-cutting Concerns
|
|
94
|
+
│ ├── config/
|
|
95
|
+
│ ├── di/ # Dependency Injection
|
|
96
|
+
│ └── logging/
|
|
97
|
+
│
|
|
98
|
+
└── shared/ # Shared Utilities
|
|
99
|
+
├── utils/
|
|
100
|
+
└── types/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Migration Status
|
|
106
|
+
|
|
107
|
+
### ✅ Completed Modules
|
|
108
|
+
|
|
109
|
+
#### Evolution Module (100%)
|
|
110
|
+
- [x] `EvolutionSignal` - Domain model
|
|
111
|
+
- [x] `ThresholdChecker` - Business rules
|
|
112
|
+
- [x] `ReviewGate` - Decision logic
|
|
113
|
+
- [x] `SignalRepository` - Data access
|
|
114
|
+
- [x] `JsonStorage` - Storage adapter
|
|
115
|
+
- [x] `SignalDetector` - Service orchestration
|
|
116
|
+
|
|
117
|
+
**Files:** 7 new files, avg 70 lines each
|
|
118
|
+
**Test Status:** ✅ Passing
|
|
119
|
+
|
|
120
|
+
### 🚧 In Progress
|
|
121
|
+
|
|
122
|
+
#### Learning Module (0%)
|
|
123
|
+
- [ ] `LessonManager`
|
|
124
|
+
- [ ] `CognitiveEnhancer`
|
|
125
|
+
- [ ] `LessonMerger`
|
|
126
|
+
- [ ] `LessonRepository`
|
|
127
|
+
|
|
128
|
+
#### Scanning Module (0%)
|
|
129
|
+
- [ ] `FileScanner`
|
|
130
|
+
- [ ] `PatternMatcher`
|
|
131
|
+
- [ ] `ViolationTracker`
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## How to Use New Architecture
|
|
136
|
+
|
|
137
|
+
### Example: Using SignalDetector
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
// 1. Import components
|
|
141
|
+
import { SignalDetector } from './src/core/evolution/signal-detector.js';
|
|
142
|
+
import { SignalRepository } from './src/data/repositories/signal-repository.js';
|
|
143
|
+
import { JsonStorage } from './src/data/storage/json-storage.js';
|
|
144
|
+
import { KNOWLEDGE_DIR } from './lib/config.js';
|
|
145
|
+
|
|
146
|
+
// 2. Create instances (Dependency Injection)
|
|
147
|
+
const storage = new JsonStorage(KNOWLEDGE_DIR);
|
|
148
|
+
const repository = new SignalRepository(storage);
|
|
149
|
+
const detector = new SignalDetector(repository, { updateThreshold: 10 });
|
|
150
|
+
|
|
151
|
+
// 3. Use async API
|
|
152
|
+
const stats = await detector.getStats();
|
|
153
|
+
console.log(`Pending: ${stats.pending}`);
|
|
154
|
+
|
|
155
|
+
const pending = await detector.getPending();
|
|
156
|
+
pending.forEach(signal => {
|
|
157
|
+
console.log(`${signal.lessonId}: ${signal.reason}`);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// 4. Approve signals
|
|
161
|
+
await detector.approve(signalId);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Testing with Mocks
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
import { SignalDetector } from './src/core/evolution/signal-detector.js';
|
|
168
|
+
|
|
169
|
+
// Create mock repository
|
|
170
|
+
const mockRepo = {
|
|
171
|
+
findAll: async () => [mockSignal1, mockSignal2],
|
|
172
|
+
findPending: async () => [mockSignal1],
|
|
173
|
+
save: async (signal) => signal
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Inject mock
|
|
177
|
+
const detector = new SignalDetector(mockRepo);
|
|
178
|
+
|
|
179
|
+
// Test easily!
|
|
180
|
+
const pending = await detector.getPending();
|
|
181
|
+
expect(pending).toHaveLength(1);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Benefits of New Architecture
|
|
187
|
+
|
|
188
|
+
### Before (Flat Structure)
|
|
189
|
+
```javascript
|
|
190
|
+
// lib/evolution-signal.js (343 lines)
|
|
191
|
+
export class EvolutionSignal { ... }
|
|
192
|
+
export class SignalQueue {
|
|
193
|
+
constructor() {
|
|
194
|
+
this.signals = [];
|
|
195
|
+
this.signalFilePath = path.join(...); // ❌ Tight coupling
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
load() {
|
|
199
|
+
fs.readFileSync(...); // ❌ Direct filesystem access
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Problems:**
|
|
205
|
+
- ❌ SignalQueue mixes business logic + persistence
|
|
206
|
+
- ❌ Hard to test (can't mock filesystem)
|
|
207
|
+
- ❌ Tight coupling
|
|
208
|
+
- ❌ Violates Single Responsibility
|
|
209
|
+
|
|
210
|
+
### After (Layered Architecture)
|
|
211
|
+
```javascript
|
|
212
|
+
// Domain Model (core/evolution/evolution-signal.js)
|
|
213
|
+
export class EvolutionSignal {
|
|
214
|
+
approve() { this.status = 'approved'; } // ✅ Pure business logic
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Repository (data/repositories/signal-repository.js)
|
|
218
|
+
export class SignalRepository {
|
|
219
|
+
constructor(storage) {
|
|
220
|
+
this.storage = storage; // ✅ Dependency injection
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async findAll() {
|
|
224
|
+
return this.storage.read('signals'); // ✅ Uses abstraction
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Storage Adapter (data/storage/json-storage.js)
|
|
229
|
+
export class JsonStorage {
|
|
230
|
+
async read(key) {
|
|
231
|
+
return JSON.parse(fs.readFileSync(...)); // ✅ Isolated I/O
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Service (core/evolution/signal-detector.js)
|
|
236
|
+
export class SignalDetector {
|
|
237
|
+
constructor(repository) {
|
|
238
|
+
this.repository = repository; // ✅ Dependency injection
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async approve(id) {
|
|
242
|
+
const signal = await this.repository.findById(id);
|
|
243
|
+
signal.approve(); // ✅ Uses domain model
|
|
244
|
+
await this.repository.save(signal);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Benefits:**
|
|
250
|
+
- ✅ Single Responsibility (each class has ONE job)
|
|
251
|
+
- ✅ Easy to test (inject mocks)
|
|
252
|
+
- ✅ Loose coupling (swap storage without touching logic)
|
|
253
|
+
- ✅ Clear ownership (know where each responsibility lives)
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Design Patterns Used
|
|
258
|
+
|
|
259
|
+
### 1. Repository Pattern
|
|
260
|
+
**Purpose:** Separate data access from business logic
|
|
261
|
+
|
|
262
|
+
**Implementation:**
|
|
263
|
+
- `SignalRepository` for evolution signals
|
|
264
|
+
- `LessonRepository` for lessons
|
|
265
|
+
- All repositories use storage abstraction
|
|
266
|
+
|
|
267
|
+
### 2. Dependency Injection
|
|
268
|
+
**Purpose:** Enable testability and flexibility
|
|
269
|
+
|
|
270
|
+
**Implementation:**
|
|
271
|
+
```javascript
|
|
272
|
+
class SignalDetector {
|
|
273
|
+
constructor(signalRepository, settings) {
|
|
274
|
+
this.signalRepository = signalRepository; // Injected!
|
|
275
|
+
this.settings = settings;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 3. Strategy Pattern (Storage)
|
|
281
|
+
**Purpose:** Swap storage implementations
|
|
282
|
+
|
|
283
|
+
**Implementation:**
|
|
284
|
+
- `JsonStorage` for JSON files
|
|
285
|
+
- `YamlStorage` for YAML files
|
|
286
|
+
- All implement same interface (`read`, `write`, `delete`)
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Testing Strategy
|
|
291
|
+
|
|
292
|
+
### Unit Tests (Core Layer)
|
|
293
|
+
```javascript
|
|
294
|
+
// Test pure business logic
|
|
295
|
+
describe('ThresholdChecker', () => {
|
|
296
|
+
it('should detect hitCountThreshold', () => {
|
|
297
|
+
const lesson = { hitCount: 10 };
|
|
298
|
+
const result = ThresholdChecker.check(lesson, 10);
|
|
299
|
+
|
|
300
|
+
expect(result.ready).toBe(true);
|
|
301
|
+
expect(result.reason).toBe('hitCountThreshold');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Integration Tests (Service Layer)
|
|
307
|
+
```javascript
|
|
308
|
+
// Test with real repository + mock storage
|
|
309
|
+
describe('SignalDetector', () => {
|
|
310
|
+
it('should queue signal when threshold reached', async () => {
|
|
311
|
+
const mockStorage = {
|
|
312
|
+
read: async () => ({ signals: [] }),
|
|
313
|
+
write: async () => {}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const repository = new SignalRepository(mockStorage);
|
|
317
|
+
const detector = new SignalDetector(repository);
|
|
318
|
+
|
|
319
|
+
const result = await detector.queue('LESSON-1', {
|
|
320
|
+
ready: true,
|
|
321
|
+
reason: 'hitCountThreshold',
|
|
322
|
+
confidence: 0.9
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
expect(result.lessonId).toBe('LESSON-1');
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Migration Guide
|
|
333
|
+
|
|
334
|
+
See [MIGRATION.md](./MIGRATION.md) for detailed migration instructions.
|
|
335
|
+
|
|
336
|
+
**Quick Start:**
|
|
337
|
+
1. New code → Use new architecture
|
|
338
|
+
2. Existing code → Gradually migrate
|
|
339
|
+
3. Backward compatibility maintained in `lib/` folder
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Next Steps
|
|
344
|
+
|
|
345
|
+
1. **Complete Learning Module** - Extract from `cognitive-lesson.js`, `learn.js`
|
|
346
|
+
2. **Complete Scanning Module** - Extract from `recall.js`
|
|
347
|
+
3. **Create DI Container** - Centralize dependency management
|
|
348
|
+
4. **Migrate UI Layer** - Extract to `presentation/cli/`
|
|
349
|
+
5. **Full Test Coverage** - Unit + integration tests
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Contributing
|
|
354
|
+
|
|
355
|
+
When adding new features:
|
|
356
|
+
1. **Core logic** → `src/core/`
|
|
357
|
+
2. **Data access** → `src/data/repositories/`
|
|
358
|
+
3. **Orchestration** → `src/services/`
|
|
359
|
+
4. **UI** → `src/presentation/`
|
|
360
|
+
|
|
361
|
+
**Remember:** Core layer has ZERO dependencies!
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Questions?
|
|
366
|
+
|
|
367
|
+
See [ARCHITECTURE.md](../../../.gemini/antigravity/brain/.../architecture_refactoring_plan.md) for full architecture details.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EvolutionSignal - Domain Model
|
|
3
|
+
*
|
|
4
|
+
* Represents a detected evolution opportunity.
|
|
5
|
+
* Pure domain object with NO external dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class EvolutionSignal {
|
|
9
|
+
constructor(lessonId, reason, confidence, metadata = {}) {
|
|
10
|
+
this.id = `signal_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
11
|
+
this.lessonId = lessonId;
|
|
12
|
+
this.reason = reason; // 'hitCountThreshold', 'patternStable', 'highConfidence'
|
|
13
|
+
this.confidence = confidence; // 0.0 to 1.0
|
|
14
|
+
this.status = 'pending'; // 'pending', 'approved', 'rejected', 'executed'
|
|
15
|
+
this.metadata = metadata; // Additional context
|
|
16
|
+
this.createdAt = Date.now();
|
|
17
|
+
this.resolvedAt = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
approve() {
|
|
21
|
+
this.status = 'approved';
|
|
22
|
+
this.resolvedAt = Date.now();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
reject() {
|
|
26
|
+
this.status = 'rejected';
|
|
27
|
+
this.resolvedAt = Date.now();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
execute() {
|
|
31
|
+
this.status = 'executed';
|
|
32
|
+
this.resolvedAt = Date.now();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isPending() {
|
|
36
|
+
return this.status === 'pending';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isResolved() {
|
|
40
|
+
return this.status !== 'pending';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evolution Module - Core Domain
|
|
3
|
+
*
|
|
4
|
+
* Business logic for evolution signal detection and management.
|
|
5
|
+
*
|
|
6
|
+
* Exported:
|
|
7
|
+
* - SignalDetector: Detect evolution opportunities
|
|
8
|
+
* - SignalQueue: Manage signal lifecycle
|
|
9
|
+
* - ThresholdChecker: Check if lesson ready for evolution
|
|
10
|
+
* - ReviewGate: Determine auto vs manual evolution
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export { SignalDetector } from './signal-detector.js';
|
|
14
|
+
export { SignalQueue } from './signal-queue.js';
|
|
15
|
+
export { ThresholdChecker } from './threshold-checker.js';
|
|
16
|
+
export { ReviewGate } from './review-gate.js';
|
|
17
|
+
export { EvolutionSignal } from './evolution-signal.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReviewGate - Core Business Logic
|
|
3
|
+
*
|
|
4
|
+
* Determines if a signal should auto-evolve or require manual review.
|
|
5
|
+
* Pure function based on signal confidence and settings.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class ReviewGate {
|
|
9
|
+
/**
|
|
10
|
+
* Determine if a signal should auto-evolve or require manual review
|
|
11
|
+
* @param {object} signal - Evolution signal
|
|
12
|
+
* @param {object} settings - { autoUpdating: boolean, confidenceThreshold: number }
|
|
13
|
+
* @returns {{ shouldAuto: boolean, reason: string }}
|
|
14
|
+
*/
|
|
15
|
+
static evaluate(signal, settings = {}) {
|
|
16
|
+
const { autoUpdating = false, confidenceThreshold = 0.8 } = settings;
|
|
17
|
+
|
|
18
|
+
// If auto-updating is disabled, always require review
|
|
19
|
+
if (!autoUpdating) {
|
|
20
|
+
return {
|
|
21
|
+
shouldAuto: false,
|
|
22
|
+
reason: 'autoUpdatingDisabled'
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// If confidence is below threshold, require review
|
|
27
|
+
if (signal.confidence < confidenceThreshold) {
|
|
28
|
+
return {
|
|
29
|
+
shouldAuto: false,
|
|
30
|
+
reason: 'lowConfidence'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// High confidence + auto-updating enabled = auto-evolve
|
|
35
|
+
return {
|
|
36
|
+
shouldAuto: true,
|
|
37
|
+
reason: 'highConfidenceAutoApproved'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SignalDetector - Application Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates evolution signal detection and queuing.
|
|
5
|
+
* Coordinates between core business logic and data layer.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EvolutionSignal } from './evolution-signal.js';
|
|
9
|
+
import { ThresholdChecker } from './threshold-checker.js';
|
|
10
|
+
|
|
11
|
+
export class SignalDetector {
|
|
12
|
+
constructor(signalRepository, settings = {}) {
|
|
13
|
+
this.signalRepository = signalRepository;
|
|
14
|
+
this.settings = settings;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if lesson is ready for evolution
|
|
19
|
+
* @param {object} lesson - Lesson to check
|
|
20
|
+
* @returns {Promise<{ready: boolean, reason?: string, confidence?: number}>}
|
|
21
|
+
*/
|
|
22
|
+
async check(lesson) {
|
|
23
|
+
const threshold = this.settings.updateThreshold || 10;
|
|
24
|
+
return ThresholdChecker.check(lesson, threshold);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Queue an evolution signal
|
|
29
|
+
* @param {string} lessonId
|
|
30
|
+
* @param {object} checkResult - From check()
|
|
31
|
+
* @param {object} metadata - Additional context
|
|
32
|
+
* @returns {Promise<EvolutionSignal|null>}
|
|
33
|
+
*/
|
|
34
|
+
async queue(lessonId, checkResult, metadata = {}) {
|
|
35
|
+
if (!checkResult.ready) return null;
|
|
36
|
+
|
|
37
|
+
// Create signal
|
|
38
|
+
const signal = new EvolutionSignal(
|
|
39
|
+
lessonId,
|
|
40
|
+
checkResult.reason,
|
|
41
|
+
checkResult.confidence,
|
|
42
|
+
metadata
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Check for duplicates
|
|
46
|
+
const existing = await this.signalRepository.findByLesson(lessonId);
|
|
47
|
+
const duplicate = existing.find(s =>
|
|
48
|
+
s.isPending() && s.reason === signal.reason
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (duplicate) {
|
|
52
|
+
// Update confidence if higher
|
|
53
|
+
if (signal.confidence > duplicate.confidence) {
|
|
54
|
+
duplicate.confidence = signal.confidence;
|
|
55
|
+
duplicate.metadata = signal.metadata;
|
|
56
|
+
await this.signalRepository.save(duplicate);
|
|
57
|
+
}
|
|
58
|
+
return duplicate;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Save new signal
|
|
62
|
+
return await this.signalRepository.save(signal);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get all pending signals
|
|
67
|
+
* @returns {Promise<Array<EvolutionSignal>>}
|
|
68
|
+
*/
|
|
69
|
+
async getPending() {
|
|
70
|
+
return await this.signalRepository.findPending();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get signals for a specific lesson
|
|
75
|
+
* @param {string} lessonId
|
|
76
|
+
* @returns {Promise<Array<EvolutionSignal>>}
|
|
77
|
+
*/
|
|
78
|
+
async getByLesson(lessonId) {
|
|
79
|
+
return await this.signalRepository.findByLesson(lessonId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Approve a signal
|
|
84
|
+
* @param {string} signalId
|
|
85
|
+
* @returns {Promise<EvolutionSignal>}
|
|
86
|
+
*/
|
|
87
|
+
async approve(signalId) {
|
|
88
|
+
const signal = await this.signalRepository.findById(signalId);
|
|
89
|
+
if (signal) {
|
|
90
|
+
signal.approve();
|
|
91
|
+
await this.signalRepository.save(signal);
|
|
92
|
+
}
|
|
93
|
+
return signal;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reject a signal
|
|
98
|
+
* @param {string} signalId
|
|
99
|
+
* @returns {Promise<EvolutionSignal>}
|
|
100
|
+
*/
|
|
101
|
+
async reject(signalId) {
|
|
102
|
+
const signal = await this.signalRepository.findById(signalId);
|
|
103
|
+
if (signal) {
|
|
104
|
+
signal.reject();
|
|
105
|
+
await this.signalRepository.save(signal);
|
|
106
|
+
}
|
|
107
|
+
return signal;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Execute a signal
|
|
112
|
+
* @param {string} signalId
|
|
113
|
+
* @returns {Promise<EvolutionSignal>}
|
|
114
|
+
*/
|
|
115
|
+
async execute(signalId) {
|
|
116
|
+
const signal = await this.signalRepository.findById(signalId);
|
|
117
|
+
if (signal) {
|
|
118
|
+
signal.execute();
|
|
119
|
+
await this.signalRepository.save(signal);
|
|
120
|
+
}
|
|
121
|
+
return signal;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get evolution statistics
|
|
126
|
+
* @returns {Promise<{pending: number, approved: number, rejected: number, executed: number}>}
|
|
127
|
+
*/
|
|
128
|
+
async getStats() {
|
|
129
|
+
const all = await this.signalRepository.findAll();
|
|
130
|
+
return {
|
|
131
|
+
pending: all.filter(s => s.status === 'pending').length,
|
|
132
|
+
approved: all.filter(s => s.status === 'approved').length,
|
|
133
|
+
rejected: all.filter(s => s.status === 'rejected').length,
|
|
134
|
+
executed: all.filter(s => s.status === 'executed').length
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal Queue - Service Facade
|
|
3
|
+
*
|
|
4
|
+
* Backward compatible wrapper around SignalDetector.
|
|
5
|
+
* Provides singleton-style access for legacy code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { SignalRepository } from '../data/repositories/signal-repository.js';
|
|
9
|
+
import { JsonStorage } from '../data/storage/json-storage.js';
|
|
10
|
+
import { SignalDetector } from './signal-detector.js';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
|
|
13
|
+
// Lazy initialization
|
|
14
|
+
let _instance = null;
|
|
15
|
+
|
|
16
|
+
async function getInstance() {
|
|
17
|
+
if (!_instance) {
|
|
18
|
+
// Get KNOWLEDGE_DIR from old config
|
|
19
|
+
const { KNOWLEDGE_DIR } = await import('../../lib/config.js');
|
|
20
|
+
|
|
21
|
+
// Create storage and repository
|
|
22
|
+
const storage = new JsonStorage(KNOWLEDGE_DIR);
|
|
23
|
+
const repository = new SignalRepository(storage);
|
|
24
|
+
|
|
25
|
+
// Create detector
|
|
26
|
+
_instance = new SignalDetector(repository);
|
|
27
|
+
}
|
|
28
|
+
return _instance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* SignalQueue - Facade for backward compatibility
|
|
33
|
+
*
|
|
34
|
+
* Provides same interface as old evolution-signal.js
|
|
35
|
+
*/
|
|
36
|
+
export class SignalQueue {
|
|
37
|
+
static async add(signal) {
|
|
38
|
+
const detector = await getInstance();
|
|
39
|
+
return detector.queue(signal.lessonId, {
|
|
40
|
+
ready: true,
|
|
41
|
+
reason: signal.reason,
|
|
42
|
+
confidence: signal.confidence
|
|
43
|
+
}, signal.metadata);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static async getPending() {
|
|
47
|
+
const detector = await getInstance();
|
|
48
|
+
return detector.getPending();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static async getByLesson(lessonId) {
|
|
52
|
+
const detector = await getInstance();
|
|
53
|
+
return detector.getByLesson(lessonId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static async approve(signalId) {
|
|
57
|
+
const detector = await getInstance();
|
|
58
|
+
return detector.approve(signalId);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static async reject(signalId) {
|
|
62
|
+
const detector = await getInstance();
|
|
63
|
+
return detector.reject(signalId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static async execute(signalId) {
|
|
67
|
+
const detector = await getInstance();
|
|
68
|
+
return detector.execute(signalId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static async cleanup() {
|
|
72
|
+
const detector = await getInstance();
|
|
73
|
+
const repository = detector.signalRepository;
|
|
74
|
+
await repository.cleanup();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Export singleton instance for compatibility
|
|
79
|
+
export const signalQueue = SignalQueue;
|