@su-record/vibe 2.7.0 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CLAUDE.md +4 -5
  2. package/dist/cli/commands/init.d.ts.map +1 -1
  3. package/dist/cli/commands/init.js +1 -3
  4. package/dist/cli/commands/init.js.map +1 -1
  5. package/dist/cli/commands/update.d.ts.map +1 -1
  6. package/dist/cli/commands/update.js +1 -3
  7. package/dist/cli/commands/update.js.map +1 -1
  8. package/dist/cli/postinstall/fs-utils.d.ts +6 -0
  9. package/dist/cli/postinstall/fs-utils.d.ts.map +1 -1
  10. package/dist/cli/postinstall/fs-utils.js +24 -0
  11. package/dist/cli/postinstall/fs-utils.js.map +1 -1
  12. package/dist/cli/postinstall/index.d.ts +1 -1
  13. package/dist/cli/postinstall/index.d.ts.map +1 -1
  14. package/dist/cli/postinstall/index.js +1 -1
  15. package/dist/cli/postinstall/index.js.map +1 -1
  16. package/dist/cli/postinstall/main.d.ts.map +1 -1
  17. package/dist/cli/postinstall/main.js +4 -1
  18. package/dist/cli/postinstall/main.js.map +1 -1
  19. package/dist/cli/setup/GlobalInstaller.d.ts +0 -4
  20. package/dist/cli/setup/GlobalInstaller.d.ts.map +1 -1
  21. package/dist/cli/setup/GlobalInstaller.js +0 -51
  22. package/dist/cli/setup/GlobalInstaller.js.map +1 -1
  23. package/dist/cli/setup/index.d.ts +2 -2
  24. package/dist/cli/setup/index.d.ts.map +1 -1
  25. package/dist/cli/setup/index.js +2 -2
  26. package/dist/cli/setup/index.js.map +1 -1
  27. package/dist/cli/setup.d.ts +2 -2
  28. package/dist/cli/setup.d.ts.map +1 -1
  29. package/dist/cli/setup.js +2 -2
  30. package/dist/cli/setup.js.map +1 -1
  31. package/hooks/scripts/post-edit.js +18 -48
  32. package/hooks/scripts/prompt-dispatcher.js +0 -25
  33. package/package.json +1 -1
  34. package/hooks/scripts/__tests__/skill-injector.test.js +0 -234
  35. package/hooks/scripts/autonomy-controller.js +0 -101
  36. package/hooks/scripts/code-review.js +0 -22
  37. package/hooks/scripts/complexity.js +0 -22
  38. package/hooks/scripts/compound.js +0 -23
  39. package/hooks/scripts/evolution-engine.js +0 -101
  40. package/hooks/scripts/hud-multiline.js +0 -262
  41. package/hooks/scripts/post-tool-verify.js +0 -210
  42. package/hooks/scripts/recall.js +0 -22
  43. package/hooks/scripts/skill-injector.js +0 -654
  44. package/hooks/scripts/skill-requirements.js +0 -83
@@ -1,654 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Skill Injector - Progressive Disclosure
4
- * Trigger-based skill injection on UserPromptSubmit
5
- *
6
- * Tier 1 - Metadata: Always loaded (name, description, triggers)
7
- * Tier 2 - Body: Loaded on trigger match
8
- * Tier 3 - Resources: Listed but not loaded (scripts/, references/, assets/)
9
- */
10
-
11
- import fs from 'fs';
12
- import path from 'path';
13
- import os from 'os';
14
- import { VIBE_PATH, PROJECT_DIR } from './utils.js';
15
- import { checkBinaryExists, checkPlatform, getInstallHint } from './skill-requirements.js';
16
-
17
- // Skill storage locations
18
- const USER_SKILLS_DIR = path.join(os.homedir(), '.claude', 'vibe', 'skills');
19
- const PROJECT_SKILLS_DIR = path.join(PROJECT_DIR, '.claude', 'vibe', 'skills');
20
-
21
- // Session cache to prevent re-injection
22
- const SESSION_CACHE = new Set();
23
-
24
- // Metadata cache (per-session, disk-backed)
25
- let METADATA_CACHE = null;
26
-
27
- // Eligibility cache (per-session)
28
- const ELIGIBILITY_CACHE = new Map();
29
-
30
- // Max skills per injection
31
- const MAX_SKILLS_PER_INJECTION = 5;
32
-
33
- // Token estimation: 1 token ≈ 4 chars
34
- const CHARS_PER_TOKEN = 4;
35
-
36
- // ============================================
37
- // Tier 1: Metadata Loading
38
- // ============================================
39
-
40
- /**
41
- * Parse skill frontmatter only (no body)
42
- */
43
- function loadSkillMetadataOnly(skillPath) {
44
- try {
45
- const content = fs.readFileSync(skillPath, 'utf8');
46
- if (!content.startsWith('---')) return null;
47
-
48
- const endIndex = content.indexOf('---', 3);
49
- if (endIndex === -1) return null;
50
-
51
- const frontmatter = content.slice(3, endIndex).trim();
52
- const metadata = {};
53
-
54
- for (const line of frontmatter.split('\n')) {
55
- const colonIndex = line.indexOf(':');
56
- if (colonIndex === -1) continue;
57
-
58
- const key = line.slice(0, colonIndex).trim();
59
- let value = line.slice(colonIndex + 1).trim();
60
-
61
- // Parse arrays
62
- if (value.startsWith('[') && value.endsWith(']')) {
63
- value = value.slice(1, -1).split(',').map(v => v.trim().replace(/^["']|["']$/g, ''));
64
- } else if (value.startsWith('{')) {
65
- // Parse simple objects (install hints)
66
- try {
67
- value = JSON.parse(value);
68
- } catch {
69
- // Keep as string
70
- }
71
- } else if (value.startsWith('"') || value.startsWith("'")) {
72
- value = value.slice(1, -1);
73
- } else if (/^\d+$/.test(value)) {
74
- value = parseInt(value, 10);
75
- }
76
-
77
- metadata[key] = value;
78
- }
79
-
80
- return metadata;
81
- } catch {
82
- return null;
83
- }
84
- }
85
-
86
- /**
87
- * Load skill body only (no frontmatter)
88
- */
89
- function loadSkillBody(skillPath) {
90
- try {
91
- const content = fs.readFileSync(skillPath, 'utf8');
92
- if (!content.startsWith('---')) return content;
93
-
94
- const endIndex = content.indexOf('---', 3);
95
- if (endIndex === -1) return content;
96
-
97
- return content.slice(endIndex + 3).trim();
98
- } catch {
99
- return '';
100
- }
101
- }
102
-
103
- // ============================================
104
- // Section-Level Progressive Disclosure
105
- // ============================================
106
-
107
- /**
108
- * Parse skill body into sections (split by ## headings)
109
- * Returns array of { name, content } objects
110
- */
111
- function parseBodySections(body) {
112
- const sections = [];
113
- const lines = body.split('\n');
114
- let currentSection = null;
115
- let currentLines = [];
116
-
117
- for (const line of lines) {
118
- if (line.startsWith('## ')) {
119
- if (currentSection !== null) {
120
- sections.push({ name: currentSection, content: currentLines.join('\n').trim() });
121
- }
122
- currentSection = line.slice(3).trim();
123
- currentLines = [line];
124
- } else {
125
- currentLines.push(line);
126
- }
127
- }
128
-
129
- if (currentSection !== null) {
130
- sections.push({ name: currentSection, content: currentLines.join('\n').trim() });
131
- } else if (currentLines.length > 0) {
132
- sections.push({ name: '_intro', content: currentLines.join('\n').trim() });
133
- }
134
-
135
- return sections;
136
- }
137
-
138
- /**
139
- * Load skill body with section-level filtering.
140
- * If metadata has `sections` array, only load matching sections.
141
- * Otherwise, load full body (backward compatible).
142
- */
143
- function loadSkillBodyWithSections(skillPath, metadata, prompt) {
144
- const body = loadSkillBody(skillPath);
145
- if (!body) return { body: '', loadedSections: [], availableSections: [] };
146
-
147
- const sectionsMeta = metadata.sections;
148
- if (!sectionsMeta || !Array.isArray(sectionsMeta) || sectionsMeta.length === 0) {
149
- return { body, loadedSections: [], availableSections: [] };
150
- }
151
-
152
- const parsedSections = parseBodySections(body);
153
- const loadedSections = [];
154
- const skippedSections = [];
155
-
156
- // Always include intro content (before first ## heading)
157
- let resultParts = [];
158
- const introSection = parsedSections.find(s => s.name === '_intro');
159
- if (introSection) {
160
- resultParts.push(introSection.content);
161
- }
162
-
163
- // Score each section defined in frontmatter
164
- const sectionScores = sectionsMeta.map(secMeta => {
165
- const triggers = secMeta.triggers;
166
- const score = (triggers && triggers.length > 0) ? scoreSkillMatch(triggers, prompt) : -1;
167
- const parsed = parsedSections.find(p =>
168
- p.name.toLowerCase().includes(secMeta.name.toLowerCase()) ||
169
- secMeta.name.toLowerCase().includes(p.name.toLowerCase())
170
- );
171
- return { meta: secMeta, score, parsed };
172
- });
173
-
174
- // Include sections with score > 0, or sections without triggers (always load)
175
- for (const { meta, score, parsed } of sectionScores) {
176
- if (!parsed) continue;
177
-
178
- if (score > 0 || score === -1) {
179
- resultParts.push(parsed.content);
180
- loadedSections.push(meta.name);
181
- } else {
182
- skippedSections.push(meta.name);
183
- }
184
- }
185
-
186
- // Include body sections NOT tracked in frontmatter (untracked always loaded)
187
- for (const parsed of parsedSections) {
188
- if (parsed.name === '_intro') continue;
189
- const isTracked = sectionsMeta.some(s =>
190
- parsed.name.toLowerCase().includes(s.name.toLowerCase()) ||
191
- s.name.toLowerCase().includes(parsed.name.toLowerCase())
192
- );
193
- if (!isTracked) {
194
- resultParts.push(parsed.content);
195
- }
196
- }
197
-
198
- return {
199
- body: resultParts.join('\n\n').trim(),
200
- loadedSections,
201
- availableSections: skippedSections,
202
- };
203
- }
204
-
205
- /**
206
- * List skill resources (scripts/, references/, assets/)
207
- */
208
- function listSkillResources(skillDir) {
209
- const resources = [];
210
- const subDirs = ['scripts', 'references', 'assets'];
211
-
212
- for (const sub of subDirs) {
213
- const subPath = path.join(skillDir, sub);
214
- if (!fs.existsSync(subPath)) continue;
215
-
216
- try {
217
- const files = fs.readdirSync(subPath).filter(f => !f.startsWith('.'));
218
- for (const file of files) {
219
- resources.push(`${sub}/${file}`);
220
- }
221
- } catch {
222
- // Skip unreadable dirs
223
- }
224
- }
225
-
226
- return resources;
227
- }
228
-
229
- /**
230
- * Get disk cache path
231
- */
232
- function getSkillsCachePath() {
233
- const cacheDir = path.join(PROJECT_DIR, '.claude', 'vibe');
234
- return path.join(cacheDir, '.skills-cache.json');
235
- }
236
-
237
- /**
238
- * Load all skill metadata with disk cache
239
- */
240
- function loadAllMetadata() {
241
- if (METADATA_CACHE) return METADATA_CACHE;
242
-
243
- const cachePath = getSkillsCachePath();
244
- let diskCache = {};
245
-
246
- // Load disk cache
247
- try {
248
- if (fs.existsSync(cachePath)) {
249
- diskCache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
250
- }
251
- } catch {
252
- diskCache = {};
253
- }
254
-
255
- const allMetadata = [];
256
- const skillFiles = findSkillFiles();
257
- let cacheUpdated = false;
258
-
259
- for (const { path: skillPath, scope } of skillFiles) {
260
- try {
261
- const stat = fs.statSync(skillPath);
262
- const mtimeMs = stat.mtimeMs;
263
- const cached = diskCache[skillPath];
264
-
265
- if (cached && cached.mtimeMs === mtimeMs) {
266
- allMetadata.push({ ...cached.metadata, _path: skillPath, _scope: scope });
267
- } else {
268
- const metadata = loadSkillMetadataOnly(skillPath);
269
- if (metadata) {
270
- allMetadata.push({ ...metadata, _path: skillPath, _scope: scope });
271
- diskCache[skillPath] = { mtimeMs, metadata };
272
- cacheUpdated = true;
273
- }
274
- }
275
- } catch {
276
- // Skip unreadable
277
- }
278
- }
279
-
280
- // Save disk cache if updated
281
- if (cacheUpdated) {
282
- try {
283
- const cacheDir = path.dirname(cachePath);
284
- if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
285
- fs.writeFileSync(cachePath, JSON.stringify(diskCache, null, 2));
286
- } catch {
287
- // Cache write failure is non-fatal
288
- }
289
- }
290
-
291
- METADATA_CACHE = allMetadata;
292
- return allMetadata;
293
- }
294
-
295
- // ============================================
296
- // Skill Eligibility
297
- // ============================================
298
-
299
- /**
300
- * Check if a skill is eligible on this platform/environment
301
- */
302
- function checkSkillEligibility(metadata) {
303
- const cacheKey = metadata._path || metadata.name;
304
- if (ELIGIBILITY_CACHE.has(cacheKey)) {
305
- return ELIGIBILITY_CACHE.get(cacheKey);
306
- }
307
-
308
- // 1. Platform check
309
- if (metadata.os && Array.isArray(metadata.os)) {
310
- if (!checkPlatform(metadata.os)) {
311
- const result = {
312
- eligible: false,
313
- reason: `Platform ${process.platform} not supported (requires: ${metadata.os.join(', ')})`,
314
- };
315
- ELIGIBILITY_CACHE.set(cacheKey, result);
316
- return result;
317
- }
318
- }
319
-
320
- // 2. Binary requirements check
321
- if (metadata.requires && Array.isArray(metadata.requires)) {
322
- const missing = metadata.requires.filter(b => !checkBinaryExists(b));
323
- if (missing.length > 0) {
324
- const installHint = metadata.install
325
- ? getInstallHint(metadata.install, process.platform)
326
- : null;
327
- const result = {
328
- eligible: false,
329
- reason: `Missing: ${missing.join(', ')}`,
330
- installHint,
331
- };
332
- ELIGIBILITY_CACHE.set(cacheKey, result);
333
- return result;
334
- }
335
- }
336
-
337
- const result = { eligible: true };
338
- ELIGIBILITY_CACHE.set(cacheKey, result);
339
- return result;
340
- }
341
-
342
- // ============================================
343
- // Skill Matching & Scoring
344
- // ============================================
345
-
346
- /**
347
- * Find all skill files (including auto/ subdirectories)
348
- * Ignores .disabled files
349
- */
350
- function findSkillFiles() {
351
- const skills = [];
352
-
353
- // Helper: scan directory for .md files, skip .disabled
354
- function scanDir(dir, scope) {
355
- if (!fs.existsSync(dir)) return;
356
- try {
357
- const files = fs.readdirSync(dir)
358
- .filter(f => f.endsWith('.md') && !f.endsWith('.disabled'))
359
- .map(f => ({ path: path.join(dir, f), scope }));
360
- skills.push(...files);
361
- } catch { /* skip unreadable */ }
362
- }
363
-
364
- // Project skills (higher priority)
365
- scanDir(PROJECT_SKILLS_DIR, 'project');
366
-
367
- // Project auto-generated skills
368
- scanDir(path.join(PROJECT_SKILLS_DIR, 'auto'), 'project-auto');
369
-
370
- // User skills
371
- scanDir(USER_SKILLS_DIR, 'user');
372
-
373
- // User auto-generated skills
374
- scanDir(path.join(USER_SKILLS_DIR, 'auto'), 'user-auto');
375
-
376
- return skills;
377
- }
378
-
379
- /**
380
- * Score skill match against prompt
381
- */
382
- function escapeRegex(str) {
383
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
384
- }
385
-
386
- function scoreSkillMatch(triggers, prompt) {
387
- if (!triggers || triggers.length === 0) return 0;
388
-
389
- const lowerPrompt = prompt.toLowerCase();
390
- let score = 0;
391
-
392
- for (const trigger of triggers) {
393
- const lowerTrigger = trigger.toLowerCase();
394
- if (lowerPrompt.includes(lowerTrigger)) {
395
- score += 10;
396
- const regex = new RegExp(`\\b${escapeRegex(lowerTrigger)}\\b`, 'i');
397
- if (regex.test(prompt)) {
398
- score += 5;
399
- }
400
- }
401
- }
402
-
403
- return score;
404
- }
405
-
406
- /**
407
- * Find matching skills for prompt (with eligibility check)
408
- */
409
- function findMatchingSkills(prompt) {
410
- const allMetadata = loadAllMetadata();
411
- const matches = [];
412
- const skippedSkills = [];
413
-
414
- for (const meta of allMetadata) {
415
- const skillPath = meta._path;
416
- const scope = meta._scope;
417
-
418
- // Check session cache
419
- if (SESSION_CACHE.has(skillPath)) continue;
420
-
421
- // Check eligibility
422
- const eligibility = checkSkillEligibility(meta);
423
- if (!eligibility.eligible) {
424
- skippedSkills.push({
425
- name: meta.name || path.basename(skillPath, '.md'),
426
- reason: eligibility.reason,
427
- installHint: eligibility.installHint,
428
- });
429
- continue;
430
- }
431
-
432
- const triggers = meta.triggers || [];
433
- const score = scoreSkillMatch(triggers, prompt);
434
-
435
- if (score > 0) {
436
- // Tier 2: Load body on match (with section-level filtering)
437
- const { body: sectionBody, loadedSections, availableSections } =
438
- loadSkillBodyWithSections(skillPath, meta, prompt);
439
- let body = sectionBody;
440
-
441
- // Apply maxBodyTokens truncation
442
- if (meta.maxBodyTokens && typeof meta.maxBodyTokens === 'number') {
443
- const maxChars = meta.maxBodyTokens * CHARS_PER_TOKEN;
444
- if (body.length > maxChars) {
445
- body = body.slice(0, maxChars) + '\n\n<!-- [truncated: body exceeded maxBodyTokens] -->';
446
- }
447
- }
448
-
449
- // Tier 3: List resources
450
- const skillDir = path.dirname(skillPath);
451
- const resources = listSkillResources(skillDir);
452
-
453
- matches.push({
454
- path: skillPath,
455
- name: meta.name || path.basename(skillPath, '.md'),
456
- description: meta.description || '',
457
- score,
458
- scope,
459
- body,
460
- resources,
461
- metadata: meta,
462
- loadedSections,
463
- availableSections,
464
- });
465
- }
466
- }
467
-
468
- // Sort by score (descending) and scope (project > user)
469
- matches.sort((a, b) => {
470
- if (a.scope !== b.scope) {
471
- return a.scope === 'project' ? -1 : 1;
472
- }
473
- return b.score - a.score;
474
- });
475
-
476
- return {
477
- triggered: matches.slice(0, MAX_SKILLS_PER_INJECTION),
478
- skipped: skippedSkills,
479
- allMetadata,
480
- };
481
- }
482
-
483
- // ============================================
484
- // Output Formatting (Progressive Disclosure)
485
- // ============================================
486
-
487
- /**
488
- * Format skill injection with progressive disclosure
489
- */
490
- function formatSkillInjection(result) {
491
- const { triggered, skipped, allMetadata } = result;
492
- if (triggered.length === 0 && allMetadata.length === 0) return '';
493
-
494
- const lines = [];
495
- lines.push('<mnemosyne>');
496
-
497
- // Tier 1: Available Skills (metadata only)
498
- const nonTriggered = allMetadata.filter(
499
- m => !triggered.some(t => t.path === m._path) && !SESSION_CACHE.has(m._path)
500
- );
501
-
502
- if (nonTriggered.length > 0) {
503
- lines.push('## Available Skills (metadata only)\n');
504
- for (const meta of nonTriggered) {
505
- const name = meta.name || 'unnamed';
506
- const desc = meta.description || '';
507
- const triggers = Array.isArray(meta.triggers) ? meta.triggers.join(', ') : '';
508
- lines.push(`- ${name}: ${desc}${triggers ? ` (triggers: ${triggers})` : ''}`);
509
- }
510
- lines.push('');
511
- }
512
-
513
- // Tier 2+3: Triggered Skills (full body + resource listing)
514
- if (triggered.length > 0) {
515
- lines.push('## Triggered Skills (full body)\n');
516
-
517
- for (const skill of triggered) {
518
- lines.push(`### ${skill.name} (${skill.scope})`);
519
- lines.push('');
520
- lines.push(skill.body);
521
- lines.push('');
522
-
523
- // Section-level disclosure: show available (not loaded) sections
524
- if (skill.availableSections && skill.availableSections.length > 0) {
525
- lines.push(`<!-- More sections available: ${skill.availableSections.map(s => `"${s}"`).join(', ')} -->`);
526
- lines.push('');
527
- }
528
-
529
- // Tier 3: Resource listing
530
- if (skill.resources.length > 0) {
531
- lines.push(`<!-- Resources available: ${skill.resources.join(', ')} -->`);
532
- lines.push('');
533
- }
534
-
535
- // Mark as injected
536
- SESSION_CACHE.add(skill.path);
537
- }
538
- }
539
-
540
- // Skipped skills (ineligible)
541
- for (const skip of skipped) {
542
- const hint = skip.installHint ? `. Install: ${skip.installHint}` : '';
543
- lines.push(`<!-- Skill "${skip.name}" skipped: ${skip.reason}${hint} -->`);
544
- }
545
-
546
- lines.push('</mnemosyne>');
547
-
548
- return lines.join('\n');
549
- }
550
-
551
- // ============================================
552
- // Exports (for testing)
553
- // ============================================
554
- export {
555
- loadSkillMetadataOnly,
556
- loadSkillBody,
557
- loadSkillBodyWithSections,
558
- parseBodySections,
559
- listSkillResources,
560
- checkSkillEligibility,
561
- findMatchingSkills,
562
- formatSkillInjection,
563
- scoreSkillMatch,
564
- parseSkillFrontmatter,
565
- };
566
-
567
- /**
568
- * Parse skill frontmatter (kept for backward compatibility)
569
- */
570
- function parseSkillFrontmatter(content) {
571
- if (!content.startsWith('---')) return null;
572
-
573
- const endIndex = content.indexOf('---', 3);
574
- if (endIndex === -1) return null;
575
-
576
- const frontmatter = content.slice(3, endIndex).trim();
577
- const template = content.slice(endIndex + 3).trim();
578
-
579
- const metadata = {};
580
- for (const line of frontmatter.split('\n')) {
581
- const colonIndex = line.indexOf(':');
582
- if (colonIndex === -1) continue;
583
-
584
- const key = line.slice(0, colonIndex).trim();
585
- let value = line.slice(colonIndex + 1).trim();
586
-
587
- if (value.startsWith('[') && value.endsWith(']')) {
588
- value = value.slice(1, -1).split(',').map(v => v.trim().replace(/^["']|["']$/g, ''));
589
- } else if (value.startsWith('"') || value.startsWith("'")) {
590
- value = value.slice(1, -1);
591
- }
592
-
593
- metadata[key] = value;
594
- }
595
-
596
- return { metadata, template };
597
- }
598
-
599
- // ============================================
600
- // Main Execution (only when run directly)
601
- // ============================================
602
-
603
- const isMainModule = process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, '/'));
604
-
605
- if (isMainModule) {
606
- const prompt = process.argv.slice(2).join(' ') || process.env.USER_PROMPT || '';
607
-
608
- if (!prompt) {
609
- process.exit(0);
610
- }
611
-
612
- const result = findMatchingSkills(prompt);
613
-
614
- if (result.triggered.length > 0 || result.allMetadata.length > 0) {
615
- const injection = formatSkillInjection(result);
616
- if (injection) {
617
- console.log(injection);
618
- }
619
-
620
- // Track usage of auto-generated skills (fire-and-forget)
621
- const autoTriggered = result.triggered.filter(
622
- s => s.metadata && (s.metadata.generated === true || s.metadata.generated === 'true')
623
- );
624
- if (autoTriggered.length > 0) {
625
- setImmediate(async () => {
626
- try {
627
- const { getLibBaseUrl } = await import('./utils.js');
628
- const LIB_BASE = getLibBaseUrl();
629
- const [memMod, trackerMod, regMod] = await Promise.all([
630
- import(`${LIB_BASE}memory/MemoryStorage.js`),
631
- import(`${LIB_BASE}evolution/UsageTracker.js`),
632
- import(`${LIB_BASE}evolution/GenerationRegistry.js`),
633
- ]);
634
- const storage = new memMod.MemoryStorage(PROJECT_DIR);
635
- const registry = new regMod.GenerationRegistry(storage);
636
- const tracker = new trackerMod.UsageTracker(storage);
637
-
638
- for (const skill of autoTriggered) {
639
- const insightId = skill.metadata.insightId;
640
- if (insightId) {
641
- const gen = registry.getByName(skill.name);
642
- if (gen) {
643
- tracker.recordUsage(gen.id, process.env.SESSION_ID, prompt.slice(0, 200));
644
- }
645
- }
646
- }
647
- storage.close();
648
- } catch (e) {
649
- process.stderr.write(`[Evolution] Usage tracking error: ${e.message}\n`);
650
- }
651
- });
652
- }
653
- }
654
- }