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.
- package/README.md +1 -1
- package/bin/lib/commands/install.js +119 -242
- package/package.json +3 -4
- package/lib/agent-cli/bin/agent.js +0 -187
- package/lib/agent-cli/dashboard/dashboard_server.js +0 -312
- package/lib/agent-cli/lib/ab-testing.js +0 -364
- package/lib/agent-cli/lib/audit.js +0 -154
- package/lib/agent-cli/lib/audit.test.js +0 -100
- package/lib/agent-cli/lib/auto-learn.js +0 -319
- package/lib/agent-cli/lib/backup.js +0 -138
- package/lib/agent-cli/lib/backup.test.js +0 -78
- package/lib/agent-cli/lib/causality-engine.js +0 -331
- package/lib/agent-cli/lib/cognitive-lesson.js +0 -476
- package/lib/agent-cli/lib/completion.js +0 -149
- package/lib/agent-cli/lib/config.js +0 -35
- package/lib/agent-cli/lib/dashboard-data.js +0 -380
- package/lib/agent-cli/lib/eslint-fix.js +0 -238
- package/lib/agent-cli/lib/evolution-signal.js +0 -215
- package/lib/agent-cli/lib/export.js +0 -86
- package/lib/agent-cli/lib/export.test.js +0 -65
- package/lib/agent-cli/lib/fix.js +0 -337
- package/lib/agent-cli/lib/fix.test.js +0 -80
- package/lib/agent-cli/lib/gemini-export.js +0 -83
- package/lib/agent-cli/lib/generate-registry.js +0 -42
- package/lib/agent-cli/lib/hooks/install-hooks.js +0 -152
- package/lib/agent-cli/lib/hooks/lint-learn.js +0 -172
- package/lib/agent-cli/lib/icons.js +0 -93
- package/lib/agent-cli/lib/ignore.js +0 -116
- package/lib/agent-cli/lib/ignore.test.js +0 -58
- package/lib/agent-cli/lib/init.js +0 -124
- package/lib/agent-cli/lib/knowledge-index.js +0 -326
- package/lib/agent-cli/lib/knowledge-metrics.js +0 -335
- package/lib/agent-cli/lib/knowledge-retention.js +0 -398
- package/lib/agent-cli/lib/knowledge-validator.js +0 -312
- package/lib/agent-cli/lib/learn.js +0 -255
- package/lib/agent-cli/lib/learn.test.js +0 -70
- package/lib/agent-cli/lib/metrics-collector.js +0 -410
- package/lib/agent-cli/lib/proposals.js +0 -199
- package/lib/agent-cli/lib/proposals.test.js +0 -56
- package/lib/agent-cli/lib/recall.js +0 -835
- package/lib/agent-cli/lib/recall.test.js +0 -107
- package/lib/agent-cli/lib/reinforcement.js +0 -299
- package/lib/agent-cli/lib/selfevolution-bridge.js +0 -167
- package/lib/agent-cli/lib/settings.js +0 -203
- package/lib/agent-cli/lib/skill-generator.js +0 -379
- package/lib/agent-cli/lib/skill-learn.js +0 -296
- package/lib/agent-cli/lib/stats.js +0 -132
- package/lib/agent-cli/lib/stats.test.js +0 -94
- package/lib/agent-cli/lib/types.js +0 -33
- package/lib/agent-cli/lib/ui/audit-ui.js +0 -146
- package/lib/agent-cli/lib/ui/backup-ui.js +0 -107
- package/lib/agent-cli/lib/ui/clack-helpers.js +0 -317
- package/lib/agent-cli/lib/ui/common.js +0 -83
- package/lib/agent-cli/lib/ui/completion-ui.js +0 -126
- package/lib/agent-cli/lib/ui/custom-select.js +0 -69
- package/lib/agent-cli/lib/ui/dashboard-ui.js +0 -222
- package/lib/agent-cli/lib/ui/evolution-signals-ui.js +0 -107
- package/lib/agent-cli/lib/ui/export-ui.js +0 -94
- package/lib/agent-cli/lib/ui/fix-all-ui.js +0 -191
- package/lib/agent-cli/lib/ui/help-ui.js +0 -49
- package/lib/agent-cli/lib/ui/index.js +0 -199
- package/lib/agent-cli/lib/ui/init-ui.js +0 -56
- package/lib/agent-cli/lib/ui/knowledge-ui.js +0 -55
- package/lib/agent-cli/lib/ui/learn-ui.js +0 -706
- package/lib/agent-cli/lib/ui/lessons-ui.js +0 -148
- package/lib/agent-cli/lib/ui/pretty.js +0 -145
- package/lib/agent-cli/lib/ui/proposals-ui.js +0 -99
- package/lib/agent-cli/lib/ui/recall-ui.js +0 -342
- package/lib/agent-cli/lib/ui/routing-demo.js +0 -79
- package/lib/agent-cli/lib/ui/routing-ui.js +0 -325
- package/lib/agent-cli/lib/ui/settings-ui.js +0 -381
- package/lib/agent-cli/lib/ui/stats-ui.js +0 -123
- package/lib/agent-cli/lib/ui/watch-ui.js +0 -236
- package/lib/agent-cli/lib/watcher.js +0 -181
- package/lib/agent-cli/lib/watcher.test.js +0 -85
- package/lib/agent-cli/src/MIGRATION.md +0 -418
- package/lib/agent-cli/src/README.md +0 -367
- package/lib/agent-cli/src/core/evolution/evolution-signal.js +0 -42
- package/lib/agent-cli/src/core/evolution/index.js +0 -17
- package/lib/agent-cli/src/core/evolution/review-gate.js +0 -40
- package/lib/agent-cli/src/core/evolution/signal-detector.js +0 -137
- package/lib/agent-cli/src/core/evolution/signal-queue.js +0 -79
- package/lib/agent-cli/src/core/evolution/threshold-checker.js +0 -79
- package/lib/agent-cli/src/core/index.js +0 -15
- package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +0 -282
- package/lib/agent-cli/src/core/learning/index.js +0 -12
- package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +0 -83
- package/lib/agent-cli/src/core/scanning/index.js +0 -14
- package/lib/agent-cli/src/data/index.js +0 -13
- package/lib/agent-cli/src/data/repositories/index.js +0 -8
- package/lib/agent-cli/src/data/repositories/lesson-repository.js +0 -130
- package/lib/agent-cli/src/data/repositories/signal-repository.js +0 -119
- package/lib/agent-cli/src/data/storage/index.js +0 -8
- package/lib/agent-cli/src/data/storage/json-storage.js +0 -64
- package/lib/agent-cli/src/data/storage/yaml-storage.js +0 -66
- package/lib/agent-cli/src/infrastructure/index.js +0 -13
- package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +0 -232
- package/lib/agent-cli/src/services/export-service.js +0 -162
- package/lib/agent-cli/src/services/index.js +0 -13
- package/lib/agent-cli/src/services/learning-service.js +0 -99
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A/B Testing v7.0 - Pattern Experiment Framework
|
|
3
|
-
*
|
|
4
|
-
* Manages A/B tests for comparing pattern effectiveness.
|
|
5
|
-
* Tracks experiment results and determines winners.
|
|
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 METRICS_DIR = path.join(projectRoot, '.agent', 'metrics');
|
|
31
|
-
const AB_TESTS_FILE = path.join(METRICS_DIR, 'ab-tests.json');
|
|
32
|
-
|
|
33
|
-
// ============================================================================
|
|
34
|
-
// DATA STRUCTURES
|
|
35
|
-
// ============================================================================
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* A/B Test structure:
|
|
39
|
-
* {
|
|
40
|
-
* id: string,
|
|
41
|
-
* name: string,
|
|
42
|
-
* description: string,
|
|
43
|
-
* variantA: { id: string, patternId: string, description: string },
|
|
44
|
-
* variantB: { id: string, patternId: string, description: string },
|
|
45
|
-
* status: 'running' | 'completed' | 'paused',
|
|
46
|
-
* startDate: string,
|
|
47
|
-
* endDate: string | null,
|
|
48
|
-
* results: {
|
|
49
|
-
* variantA: { impressions: number, successes: number },
|
|
50
|
-
* variantB: { impressions: number, successes: number }
|
|
51
|
-
* },
|
|
52
|
-
* winner: 'A' | 'B' | null,
|
|
53
|
-
* confidence: number
|
|
54
|
-
* }
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// DATA LOADERS
|
|
59
|
-
// ============================================================================
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Ensure metrics directory exists
|
|
63
|
-
*/
|
|
64
|
-
function ensureMetricsDir() {
|
|
65
|
-
if (!fs.existsSync(METRICS_DIR)) {
|
|
66
|
-
fs.mkdirSync(METRICS_DIR, { recursive: true });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Load A/B tests data
|
|
72
|
-
*/
|
|
73
|
-
function loadABTestsData() {
|
|
74
|
-
if (fs.existsSync(AB_TESTS_FILE)) {
|
|
75
|
-
try {
|
|
76
|
-
return JSON.parse(fs.readFileSync(AB_TESTS_FILE, 'utf8'));
|
|
77
|
-
} catch (e) {
|
|
78
|
-
return getDefaultData();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return getDefaultData();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Save A/B tests data
|
|
86
|
-
*/
|
|
87
|
-
function saveABTestsData(data) {
|
|
88
|
-
ensureMetricsDir();
|
|
89
|
-
data.lastUpdated = new Date().toISOString();
|
|
90
|
-
fs.writeFileSync(AB_TESTS_FILE, JSON.stringify(data, null, 2));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Get default data structure
|
|
95
|
-
*/
|
|
96
|
-
function getDefaultData() {
|
|
97
|
-
return {
|
|
98
|
-
tests: [],
|
|
99
|
-
lastUpdated: new Date().toISOString(),
|
|
100
|
-
totalCompleted: 0
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ============================================================================
|
|
105
|
-
// TEST MANAGEMENT
|
|
106
|
-
// ============================================================================
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Create a new A/B test
|
|
110
|
-
*/
|
|
111
|
-
export function createTest(name, description, variantA, variantB) {
|
|
112
|
-
const data = loadABTestsData();
|
|
113
|
-
|
|
114
|
-
const test = {
|
|
115
|
-
id: `test-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
116
|
-
name,
|
|
117
|
-
description,
|
|
118
|
-
variantA: {
|
|
119
|
-
id: 'A',
|
|
120
|
-
...variantA
|
|
121
|
-
},
|
|
122
|
-
variantB: {
|
|
123
|
-
id: 'B',
|
|
124
|
-
...variantB
|
|
125
|
-
},
|
|
126
|
-
status: 'running',
|
|
127
|
-
startDate: new Date().toISOString(),
|
|
128
|
-
endDate: null,
|
|
129
|
-
results: {
|
|
130
|
-
variantA: { impressions: 0, successes: 0 },
|
|
131
|
-
variantB: { impressions: 0, successes: 0 }
|
|
132
|
-
},
|
|
133
|
-
winner: null,
|
|
134
|
-
confidence: 0
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
data.tests.push(test);
|
|
138
|
-
saveABTestsData(data);
|
|
139
|
-
|
|
140
|
-
return test;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Record an impression for a variant
|
|
145
|
-
*/
|
|
146
|
-
export function recordImpression(testId, variant, success = false) {
|
|
147
|
-
const data = loadABTestsData();
|
|
148
|
-
const test = data.tests.find(t => t.id === testId);
|
|
149
|
-
|
|
150
|
-
if (!test || test.status !== 'running') {
|
|
151
|
-
return { success: false, error: 'Test not found or not running' };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const variantKey = variant.toUpperCase() === 'A' ? 'variantA' : 'variantB';
|
|
155
|
-
test.results[variantKey].impressions += 1;
|
|
156
|
-
|
|
157
|
-
if (success) {
|
|
158
|
-
test.results[variantKey].successes += 1;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Recalculate confidence
|
|
162
|
-
updateTestConfidence(test);
|
|
163
|
-
|
|
164
|
-
// Auto-complete if enough data
|
|
165
|
-
const minImpressions = 30;
|
|
166
|
-
if (test.results.variantA.impressions >= minImpressions &&
|
|
167
|
-
test.results.variantB.impressions >= minImpressions &&
|
|
168
|
-
test.confidence >= 0.95) {
|
|
169
|
-
completeTest(testId);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
saveABTestsData(data);
|
|
173
|
-
|
|
174
|
-
return { success: true, test };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Update test confidence using simple statistical calculation
|
|
179
|
-
*/
|
|
180
|
-
function updateTestConfidence(test) {
|
|
181
|
-
const rA = test.results.variantA;
|
|
182
|
-
const rB = test.results.variantB;
|
|
183
|
-
|
|
184
|
-
if (rA.impressions === 0 || rB.impressions === 0) {
|
|
185
|
-
test.confidence = 0;
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const successRateA = rA.successes / rA.impressions;
|
|
190
|
-
const successRateB = rB.successes / rB.impressions;
|
|
191
|
-
|
|
192
|
-
// Simple confidence calculation based on sample size and difference
|
|
193
|
-
const n = Math.min(rA.impressions, rB.impressions);
|
|
194
|
-
const diff = Math.abs(successRateA - successRateB);
|
|
195
|
-
|
|
196
|
-
// Higher sample size and larger difference = higher confidence
|
|
197
|
-
test.confidence = Math.min(
|
|
198
|
-
(n / 50) * (diff / 0.2), // Scale by sample size and effect size
|
|
199
|
-
0.99
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
// Determine preliminary winner
|
|
203
|
-
if (test.confidence >= 0.8) {
|
|
204
|
-
test.winner = successRateA > successRateB ? 'A' : 'B';
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Complete a test
|
|
210
|
-
*/
|
|
211
|
-
export function completeTest(testId) {
|
|
212
|
-
const data = loadABTestsData();
|
|
213
|
-
const test = data.tests.find(t => t.id === testId);
|
|
214
|
-
|
|
215
|
-
if (!test) {
|
|
216
|
-
return { success: false, error: 'Test not found' };
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
test.status = 'completed';
|
|
220
|
-
test.endDate = new Date().toISOString();
|
|
221
|
-
|
|
222
|
-
// Determine final winner
|
|
223
|
-
const rA = test.results.variantA;
|
|
224
|
-
const rB = test.results.variantB;
|
|
225
|
-
|
|
226
|
-
if (rA.impressions > 0 && rB.impressions > 0) {
|
|
227
|
-
const successRateA = rA.successes / rA.impressions;
|
|
228
|
-
const successRateB = rB.successes / rB.impressions;
|
|
229
|
-
test.winner = successRateA >= successRateB ? 'A' : 'B';
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
data.totalCompleted = (data.totalCompleted || 0) + 1;
|
|
233
|
-
saveABTestsData(data);
|
|
234
|
-
|
|
235
|
-
return { success: true, test };
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Pause a test
|
|
240
|
-
*/
|
|
241
|
-
export function pauseTest(testId) {
|
|
242
|
-
const data = loadABTestsData();
|
|
243
|
-
const test = data.tests.find(t => t.id === testId);
|
|
244
|
-
|
|
245
|
-
if (!test) {
|
|
246
|
-
return { success: false, error: 'Test not found' };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
test.status = 'paused';
|
|
250
|
-
saveABTestsData(data);
|
|
251
|
-
|
|
252
|
-
return { success: true, test };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Resume a test
|
|
257
|
-
*/
|
|
258
|
-
export function resumeTest(testId) {
|
|
259
|
-
const data = loadABTestsData();
|
|
260
|
-
const test = data.tests.find(t => t.id === testId);
|
|
261
|
-
|
|
262
|
-
if (!test || test.status === 'completed') {
|
|
263
|
-
return { success: false, error: 'Test not found or already completed' };
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
test.status = 'running';
|
|
267
|
-
saveABTestsData(data);
|
|
268
|
-
|
|
269
|
-
return { success: true, test };
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ============================================================================
|
|
273
|
-
// QUERIES
|
|
274
|
-
// ============================================================================
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Get all active tests
|
|
278
|
-
*/
|
|
279
|
-
export function getActiveTests() {
|
|
280
|
-
const data = loadABTestsData();
|
|
281
|
-
return data.tests.filter(t => t.status === 'running');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Get completed tests
|
|
286
|
-
*/
|
|
287
|
-
export function getCompletedTests() {
|
|
288
|
-
const data = loadABTestsData();
|
|
289
|
-
return data.tests.filter(t => t.status === 'completed');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Get all tests
|
|
294
|
-
*/
|
|
295
|
-
export function getAllTests() {
|
|
296
|
-
const data = loadABTestsData();
|
|
297
|
-
return data.tests;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Get test by ID
|
|
302
|
-
*/
|
|
303
|
-
export function getTest(testId) {
|
|
304
|
-
const data = loadABTestsData();
|
|
305
|
-
return data.tests.find(t => t.id === testId);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Get A/B testing statistics
|
|
310
|
-
*/
|
|
311
|
-
export function getABTestStats() {
|
|
312
|
-
const data = loadABTestsData();
|
|
313
|
-
const active = data.tests.filter(t => t.status === 'running');
|
|
314
|
-
const completed = data.tests.filter(t => t.status === 'completed');
|
|
315
|
-
|
|
316
|
-
// Calculate win rate for variant A vs B
|
|
317
|
-
let aWins = 0;
|
|
318
|
-
let bWins = 0;
|
|
319
|
-
|
|
320
|
-
for (const test of completed) {
|
|
321
|
-
if (test.winner === 'A') aWins++;
|
|
322
|
-
else if (test.winner === 'B') bWins++;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
running: active.length,
|
|
327
|
-
completed: completed.length,
|
|
328
|
-
total: data.tests.length,
|
|
329
|
-
variantAWinRate: completed.length > 0 ? Math.round((aWins / completed.length) * 100) : 0,
|
|
330
|
-
variantBWinRate: completed.length > 0 ? Math.round((bWins / completed.length) * 100) : 0
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Select variant for a test (randomized)
|
|
336
|
-
*/
|
|
337
|
-
export function selectVariant(testId) {
|
|
338
|
-
const test = getTest(testId);
|
|
339
|
-
|
|
340
|
-
if (!test || test.status !== 'running') {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Simple 50/50 split
|
|
345
|
-
return Math.random() < 0.5 ? 'A' : 'B';
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// ============================================================================
|
|
349
|
-
// EXPORTS
|
|
350
|
-
// ============================================================================
|
|
351
|
-
|
|
352
|
-
export default {
|
|
353
|
-
createTest,
|
|
354
|
-
recordImpression,
|
|
355
|
-
completeTest,
|
|
356
|
-
pauseTest,
|
|
357
|
-
resumeTest,
|
|
358
|
-
getActiveTests,
|
|
359
|
-
getCompletedTests,
|
|
360
|
-
getAllTests,
|
|
361
|
-
getTest,
|
|
362
|
-
getABTestStats,
|
|
363
|
-
selectVariant
|
|
364
|
-
};
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Smart Audit Script (Production-Ready)
|
|
4
|
-
*
|
|
5
|
-
* The "Judge" - Orchestrates all compliance checks:
|
|
6
|
-
* 1. Memory Recall (Past Mistakes)
|
|
7
|
-
* 2. Constitution Checks (Governance)
|
|
8
|
-
* 3. Real-time Analysis
|
|
9
|
-
*
|
|
10
|
-
* Usage: agent audit [directory]
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import fs from "fs";
|
|
14
|
-
import path from "path";
|
|
15
|
-
import { fileURLToPath } from "url";
|
|
16
|
-
import { scanDirectory, loadKnowledge, saveKnowledge, printResults } from "./recall.js";
|
|
17
|
-
import { AGENT_DIR, VERSION } from "./config.js";
|
|
18
|
-
import * as p from "@clack/prompts";
|
|
19
|
-
import pc from "picocolors";
|
|
20
|
-
|
|
21
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
|
|
23
|
-
// ============================================================================
|
|
24
|
-
// AUDIT CONFIGURATION
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
|
-
const SCAN_EXTENSIONS = [".js", ".ts", ".tsx", ".jsx", ".mjs"];
|
|
28
|
-
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// GOVERNANCE CHECKS
|
|
31
|
-
// ============================================================================
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Check if governance files exist
|
|
35
|
-
* @param {string} projectRoot
|
|
36
|
-
* @returns {{ passed: boolean, details: string[] }}
|
|
37
|
-
*/
|
|
38
|
-
function checkGovernance(projectRoot) {
|
|
39
|
-
const details = [];
|
|
40
|
-
let passed = true;
|
|
41
|
-
|
|
42
|
-
const governanceFiles = [
|
|
43
|
-
{ path: path.join(projectRoot, ".agent", "GEMINI.md"), name: "GEMINI.md" },
|
|
44
|
-
{ path: path.join(projectRoot, ".agent", "ARCHITECTURE.md"), name: "ARCHITECTURE.md" }
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
governanceFiles.forEach(file => {
|
|
48
|
-
if (fs.existsSync(file.path)) {
|
|
49
|
-
details.push(`${pc.green("✓")} ${file.name} found`);
|
|
50
|
-
} else {
|
|
51
|
-
details.push(`${pc.yellow("⚠")} ${file.name} not found (optional)`);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Check for skills
|
|
56
|
-
const skillsDir = path.join(projectRoot, ".agent", "skills");
|
|
57
|
-
if (fs.existsSync(skillsDir)) {
|
|
58
|
-
const skills = fs.readdirSync(skillsDir).filter(f =>
|
|
59
|
-
fs.statSync(path.join(skillsDir, f)).isDirectory()
|
|
60
|
-
);
|
|
61
|
-
details.push(`${pc.green("✓")} ${skills.length} skill(s) loaded`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { passed, details };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ============================================================================
|
|
68
|
-
// MAIN AUDIT
|
|
69
|
-
// ============================================================================
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Run full audit on a project
|
|
73
|
-
* @param {string} projectRoot
|
|
74
|
-
*/
|
|
75
|
-
async function runAudit(projectRoot) {
|
|
76
|
-
p.intro(pc.cyan(`⚖️ SMART AUDIT v${VERSION}`));
|
|
77
|
-
|
|
78
|
-
let exitCode = 0;
|
|
79
|
-
const startTime = Date.now();
|
|
80
|
-
|
|
81
|
-
// Phase 1: Memory Recall
|
|
82
|
-
const s1 = p.spinner();
|
|
83
|
-
s1.start("Phase 1: Memory Recall");
|
|
84
|
-
|
|
85
|
-
const db = loadKnowledge();
|
|
86
|
-
|
|
87
|
-
if (db.lessons.length === 0) {
|
|
88
|
-
s1.stop("Phase 1: No lessons learned yet");
|
|
89
|
-
} else {
|
|
90
|
-
const { results } = scanDirectory(projectRoot, db, SCAN_EXTENSIONS);
|
|
91
|
-
|
|
92
|
-
if (results.length > 0) {
|
|
93
|
-
const stats = printResults(results);
|
|
94
|
-
saveKnowledge(db);
|
|
95
|
-
|
|
96
|
-
if (stats.errors > 0) {
|
|
97
|
-
exitCode = 1;
|
|
98
|
-
}
|
|
99
|
-
s1.stop(`Phase 1: Found ${stats.total} violation(s)`);
|
|
100
|
-
} else {
|
|
101
|
-
s1.stop("Phase 1: No violations found");
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Phase 2: Governance
|
|
106
|
-
const s2 = p.spinner();
|
|
107
|
-
s2.start("Phase 2: Governance Check");
|
|
108
|
-
|
|
109
|
-
const govResult = checkGovernance(projectRoot);
|
|
110
|
-
s2.stop("Phase 2: Governance checked");
|
|
111
|
-
|
|
112
|
-
p.note(govResult.details.join("\n"), pc.dim("Governance"));
|
|
113
|
-
|
|
114
|
-
// Phase 3: Summary
|
|
115
|
-
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
116
|
-
const totalHits = db.lessons.reduce((sum, l) => sum + (l.hitCount || 0), 0);
|
|
117
|
-
|
|
118
|
-
const summaryLines = [
|
|
119
|
-
`⏱️ Completed in ${duration}s`,
|
|
120
|
-
`📊 Lessons in memory: ${db.lessons.length}`,
|
|
121
|
-
`🎯 Total pattern hits: ${totalHits}`
|
|
122
|
-
];
|
|
123
|
-
p.note(summaryLines.join("\n"), pc.dim("Summary"));
|
|
124
|
-
|
|
125
|
-
if (exitCode === 0) {
|
|
126
|
-
p.outro(pc.green("✅ AUDIT PASSED: Code is smart and compliant"));
|
|
127
|
-
} else {
|
|
128
|
-
p.outro(pc.red("❌ AUDIT FAILED: Please fix ERROR violations"));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
process.exit(exitCode);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ============================================================================
|
|
135
|
-
// CLI
|
|
136
|
-
// ============================================================================
|
|
137
|
-
|
|
138
|
-
const args = process.argv.slice(2);
|
|
139
|
-
const projectRoot = args[0] || process.cwd();
|
|
140
|
-
|
|
141
|
-
if (args.includes("--help")) {
|
|
142
|
-
console.log(`
|
|
143
|
-
⚖️ Smart Audit - Compliance Checker
|
|
144
|
-
|
|
145
|
-
Usage:
|
|
146
|
-
agent audit [directory]
|
|
147
|
-
|
|
148
|
-
Options:
|
|
149
|
-
--help Show this help
|
|
150
|
-
`);
|
|
151
|
-
process.exit(0);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
runAudit(projectRoot);
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Tests for audit.js functionality
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
5
|
-
import fs from "fs";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import os from "os";
|
|
8
|
-
|
|
9
|
-
const TEST_DIR = path.join(os.tmpdir(), "agent-skill-kit-audit-test");
|
|
10
|
-
const AGENT_DIR = path.join(TEST_DIR, ".agent");
|
|
11
|
-
|
|
12
|
-
describe("Audit - Governance Check", () => {
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
fs.mkdirSync(AGENT_DIR, { recursive: true });
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("detects GEMINI.md presence", () => {
|
|
22
|
-
const geminiPath = path.join(AGENT_DIR, "GEMINI.md");
|
|
23
|
-
|
|
24
|
-
// Before creation
|
|
25
|
-
expect(fs.existsSync(geminiPath)).toBe(false);
|
|
26
|
-
|
|
27
|
-
// After creation
|
|
28
|
-
fs.writeFileSync(geminiPath, "# Test", "utf8");
|
|
29
|
-
expect(fs.existsSync(geminiPath)).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("detects ARCHITECTURE.md presence", () => {
|
|
33
|
-
const archPath = path.join(AGENT_DIR, "ARCHITECTURE.md");
|
|
34
|
-
fs.writeFileSync(archPath, "# Architecture", "utf8");
|
|
35
|
-
|
|
36
|
-
expect(fs.existsSync(archPath)).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("counts skills in skills directory", () => {
|
|
40
|
-
const skillsDir = path.join(AGENT_DIR, "skills");
|
|
41
|
-
fs.mkdirSync(skillsDir, { recursive: true });
|
|
42
|
-
|
|
43
|
-
// Create mock skills
|
|
44
|
-
fs.mkdirSync(path.join(skillsDir, "skill-1"));
|
|
45
|
-
fs.mkdirSync(path.join(skillsDir, "skill-2"));
|
|
46
|
-
|
|
47
|
-
const skills = fs.readdirSync(skillsDir).filter(f =>
|
|
48
|
-
fs.statSync(path.join(skillsDir, f)).isDirectory()
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
expect(skills.length).toBe(2);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe("Audit - Compliance Results", () => {
|
|
56
|
-
it("calculates pass/fail status correctly", () => {
|
|
57
|
-
const results = {
|
|
58
|
-
errors: 0,
|
|
59
|
-
warnings: 2,
|
|
60
|
-
total: 2
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Errors = 0 means pass
|
|
64
|
-
expect(results.errors === 0).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("fails when errors exist", () => {
|
|
68
|
-
const results = {
|
|
69
|
-
errors: 1,
|
|
70
|
-
warnings: 0,
|
|
71
|
-
total: 1
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
expect(results.errors > 0).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("calculates total correctly", () => {
|
|
78
|
-
const violations = [
|
|
79
|
-
{ lesson: { severity: "ERROR" }, matches: [1, 2] },
|
|
80
|
-
{ lesson: { severity: "WARNING" }, matches: [1] }
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
let total = 0;
|
|
84
|
-
let errors = 0;
|
|
85
|
-
let warnings = 0;
|
|
86
|
-
|
|
87
|
-
violations.forEach(v => {
|
|
88
|
-
total += v.matches.length;
|
|
89
|
-
if (v.lesson.severity === "ERROR") {
|
|
90
|
-
errors += v.matches.length;
|
|
91
|
-
} else {
|
|
92
|
-
warnings += v.matches.length;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
expect(total).toBe(3);
|
|
97
|
-
expect(errors).toBe(2);
|
|
98
|
-
expect(warnings).toBe(1);
|
|
99
|
-
});
|
|
100
|
-
});
|