@agentforge/skills 0.16.41 → 0.16.43
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/dist/index.cjs +200 -349
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -227
- package/dist/index.d.ts +1 -227
- package/dist/index.js +200 -349
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -444,390 +444,241 @@ function extractBody(content) {
|
|
|
444
444
|
return matter__default.default(content).content.trim();
|
|
445
445
|
}
|
|
446
446
|
var logger3 = core.createLogger("agentforge:skills:registry", { level: core.LogLevel.INFO });
|
|
447
|
+
function discoverSkills(config, skills, rootTrustMap, emit) {
|
|
448
|
+
skills.clear();
|
|
449
|
+
rootTrustMap.clear();
|
|
450
|
+
const scanErrors = [];
|
|
451
|
+
const normalizedRoots = config.skillRoots.map(normalizeRootConfig);
|
|
452
|
+
const plainPaths = normalizedRoots.map((root) => root.path);
|
|
453
|
+
for (const root of normalizedRoots) {
|
|
454
|
+
const resolvedPath = path.resolve(expandHome(root.path));
|
|
455
|
+
rootTrustMap.set(resolvedPath, root.trust);
|
|
456
|
+
}
|
|
457
|
+
const candidates = scanAllSkillRoots(plainPaths);
|
|
458
|
+
let successCount = 0;
|
|
459
|
+
let warningCount = 0;
|
|
460
|
+
for (const candidate of candidates) {
|
|
461
|
+
const result = parseSkillContent(candidate.content, candidate.dirName);
|
|
462
|
+
if (!result.success) {
|
|
463
|
+
warningCount++;
|
|
464
|
+
recordScanError(scanErrors, candidate.skillPath, result.error || "Unknown parse error");
|
|
465
|
+
emit("skill:warning" /* SKILL_WARNING */, {
|
|
466
|
+
skillPath: candidate.skillPath,
|
|
467
|
+
rootPath: candidate.rootPath,
|
|
468
|
+
error: result.error
|
|
469
|
+
});
|
|
470
|
+
logger3.warn("Skipping invalid skill", {
|
|
471
|
+
skillPath: candidate.skillPath,
|
|
472
|
+
...result.error ? { error: result.error } : {}
|
|
473
|
+
});
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const skill = {
|
|
477
|
+
metadata: result.metadata,
|
|
478
|
+
skillPath: candidate.skillPath,
|
|
479
|
+
rootPath: candidate.rootPath,
|
|
480
|
+
trustLevel: rootTrustMap.get(candidate.rootPath) ?? "untrusted"
|
|
481
|
+
};
|
|
482
|
+
if (skills.has(skill.metadata.name)) {
|
|
483
|
+
warningCount++;
|
|
484
|
+
const existing = skills.get(skill.metadata.name);
|
|
485
|
+
const warningMessage = `Duplicate skill name "${skill.metadata.name}" from "${candidate.rootPath}" \u2014 keeping version from "${existing.rootPath}" (first root takes precedence)`;
|
|
486
|
+
recordScanError(scanErrors, candidate.skillPath, warningMessage);
|
|
487
|
+
emit("skill:warning" /* SKILL_WARNING */, {
|
|
488
|
+
skillPath: candidate.skillPath,
|
|
489
|
+
rootPath: candidate.rootPath,
|
|
490
|
+
duplicateOf: existing.skillPath,
|
|
491
|
+
error: warningMessage
|
|
492
|
+
});
|
|
493
|
+
logger3.warn("Duplicate skill name, keeping first", {
|
|
494
|
+
name: skill.metadata.name,
|
|
495
|
+
kept: existing.skillPath,
|
|
496
|
+
skipped: candidate.skillPath
|
|
497
|
+
});
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
skills.set(skill.metadata.name, skill);
|
|
501
|
+
successCount++;
|
|
502
|
+
emit("skill:discovered" /* SKILL_DISCOVERED */, skill);
|
|
503
|
+
logger3.debug("Skill discovered", {
|
|
504
|
+
name: skill.metadata.name,
|
|
505
|
+
description: skill.metadata.description.slice(0, 80),
|
|
506
|
+
skillPath: skill.skillPath
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
logger3.info("Skill registry populated", {
|
|
510
|
+
rootsScanned: config.skillRoots.length,
|
|
511
|
+
skillsDiscovered: successCount,
|
|
512
|
+
warnings: warningCount
|
|
513
|
+
});
|
|
514
|
+
return scanErrors;
|
|
515
|
+
}
|
|
516
|
+
function recordScanError(scanErrors, path, error) {
|
|
517
|
+
scanErrors.push({ path, error });
|
|
518
|
+
}
|
|
519
|
+
var logger4 = core.createLogger("agentforge:skills:registry", { level: core.LogLevel.INFO });
|
|
520
|
+
function addRegistryEventHandler(eventHandlers, event, handler) {
|
|
521
|
+
if (!eventHandlers.has(event)) {
|
|
522
|
+
eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
523
|
+
}
|
|
524
|
+
eventHandlers.get(event).add(handler);
|
|
525
|
+
}
|
|
526
|
+
function removeRegistryEventHandler(eventHandlers, event, handler) {
|
|
527
|
+
const handlers = eventHandlers.get(event);
|
|
528
|
+
if (handlers) {
|
|
529
|
+
handlers.delete(handler);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
function emitRegistryEvent(eventHandlers, event, data) {
|
|
533
|
+
const handlers = eventHandlers.get(event);
|
|
534
|
+
if (!handlers) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
handlers.forEach((handler) => {
|
|
538
|
+
try {
|
|
539
|
+
handler(data);
|
|
540
|
+
} catch (error) {
|
|
541
|
+
logger4.error("Skill event handler error", {
|
|
542
|
+
event,
|
|
543
|
+
error: error instanceof Error ? error.message : String(error),
|
|
544
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
var logger5 = core.createLogger("agentforge:skills:registry", { level: core.LogLevel.INFO });
|
|
550
|
+
function generateSkillPrompt(config, allSkills, totalDiscovered, options) {
|
|
551
|
+
if (!config.enabled) {
|
|
552
|
+
logger5.debug("Skill prompt generation skipped (disabled)", {
|
|
553
|
+
enabled: config.enabled ?? false
|
|
554
|
+
});
|
|
555
|
+
return "";
|
|
556
|
+
}
|
|
557
|
+
let skills = allSkills;
|
|
558
|
+
if (options?.skills && options.skills.length > 0) {
|
|
559
|
+
const requested = new Set(options.skills);
|
|
560
|
+
skills = skills.filter((skill) => requested.has(skill.metadata.name));
|
|
561
|
+
}
|
|
562
|
+
if (config.maxDiscoveredSkills !== void 0 && config.maxDiscoveredSkills >= 0) {
|
|
563
|
+
skills = skills.slice(0, config.maxDiscoveredSkills);
|
|
564
|
+
}
|
|
565
|
+
if (skills.length === 0) {
|
|
566
|
+
logger5.debug("Skill prompt generation produced empty result", {
|
|
567
|
+
totalDiscovered,
|
|
568
|
+
filterApplied: !!(options?.skills && options.skills.length > 0),
|
|
569
|
+
...config.maxDiscoveredSkills !== void 0 ? { maxCap: config.maxDiscoveredSkills } : {}
|
|
570
|
+
});
|
|
571
|
+
return "";
|
|
572
|
+
}
|
|
573
|
+
const xml = `<available_skills>
|
|
574
|
+
${skills.map(renderSkillEntry).join("\n")}
|
|
575
|
+
</available_skills>`;
|
|
576
|
+
const estimatedTokens = Math.ceil(xml.length / 4);
|
|
577
|
+
logger5.info("Skill prompt generated", {
|
|
578
|
+
skillCount: skills.length,
|
|
579
|
+
totalDiscovered,
|
|
580
|
+
filterApplied: !!(options?.skills && options.skills.length > 0),
|
|
581
|
+
...config.maxDiscoveredSkills !== void 0 ? { maxCap: config.maxDiscoveredSkills } : {},
|
|
582
|
+
estimatedTokens,
|
|
583
|
+
xmlLength: xml.length
|
|
584
|
+
});
|
|
585
|
+
return xml;
|
|
586
|
+
}
|
|
587
|
+
function renderSkillEntry(skill) {
|
|
588
|
+
return [
|
|
589
|
+
" <skill>",
|
|
590
|
+
` <name>${escapeXml(skill.metadata.name)}</name>`,
|
|
591
|
+
` <description>${escapeXml(skill.metadata.description)}</description>`,
|
|
592
|
+
` <location>${escapeXml(skill.skillPath)}</location>`,
|
|
593
|
+
" </skill>"
|
|
594
|
+
].join("\n");
|
|
595
|
+
}
|
|
596
|
+
function escapeXml(str) {
|
|
597
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// src/registry-query-api.ts
|
|
601
|
+
function getSkill(skills, name) {
|
|
602
|
+
return skills.get(name);
|
|
603
|
+
}
|
|
604
|
+
function getAllSkills(skills) {
|
|
605
|
+
return Array.from(skills.values());
|
|
606
|
+
}
|
|
607
|
+
function hasSkill(skills, name) {
|
|
608
|
+
return skills.has(name);
|
|
609
|
+
}
|
|
610
|
+
function getSkillCount(skills) {
|
|
611
|
+
return skills.size;
|
|
612
|
+
}
|
|
613
|
+
function getSkillNames(skills) {
|
|
614
|
+
return Array.from(skills.keys());
|
|
615
|
+
}
|
|
616
|
+
function getScanErrors(scanErrors) {
|
|
617
|
+
return scanErrors;
|
|
618
|
+
}
|
|
619
|
+
function getAllowedTools(skills, name) {
|
|
620
|
+
return skills.get(name)?.metadata.allowedTools;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/registry.ts
|
|
447
624
|
var SkillRegistry = class {
|
|
448
625
|
skills = /* @__PURE__ */ new Map();
|
|
449
626
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
450
627
|
config;
|
|
451
628
|
scanErrors = [];
|
|
452
|
-
/** Maps resolved root paths → trust levels for skill trust assignment */
|
|
453
629
|
rootTrustMap = /* @__PURE__ */ new Map();
|
|
454
|
-
/**
|
|
455
|
-
* Create a SkillRegistry and immediately scan configured roots for skills.
|
|
456
|
-
*
|
|
457
|
-
* @param config - Registry configuration with skill root paths
|
|
458
|
-
*
|
|
459
|
-
* @example
|
|
460
|
-
* ```ts
|
|
461
|
-
* const registry = new SkillRegistry({
|
|
462
|
-
* skillRoots: ['.agentskills', '~/.agentskills', './project-skills'],
|
|
463
|
-
* });
|
|
464
|
-
* console.log(`Discovered ${registry.size()} skills`);
|
|
465
|
-
* ```
|
|
466
|
-
*/
|
|
467
630
|
constructor(config) {
|
|
468
631
|
this.config = config;
|
|
469
632
|
this.discover();
|
|
470
633
|
}
|
|
471
|
-
/**
|
|
472
|
-
* Scan all configured roots and populate the registry.
|
|
473
|
-
*
|
|
474
|
-
* Called automatically during construction. Can be called again
|
|
475
|
-
* to re-scan (clears existing skills first).
|
|
476
|
-
*/
|
|
477
634
|
discover() {
|
|
478
|
-
this.skills.
|
|
479
|
-
|
|
480
|
-
this.rootTrustMap.clear();
|
|
481
|
-
const normalizedRoots = this.config.skillRoots.map(normalizeRootConfig);
|
|
482
|
-
const plainPaths = normalizedRoots.map((r) => r.path);
|
|
483
|
-
for (const root of normalizedRoots) {
|
|
484
|
-
const resolvedPath = path.resolve(expandHome(root.path));
|
|
485
|
-
this.rootTrustMap.set(resolvedPath, root.trust);
|
|
486
|
-
}
|
|
487
|
-
const candidates = scanAllSkillRoots(plainPaths);
|
|
488
|
-
let successCount = 0;
|
|
489
|
-
let warningCount = 0;
|
|
490
|
-
for (const candidate of candidates) {
|
|
491
|
-
const result = parseSkillContent(candidate.content, candidate.dirName);
|
|
492
|
-
if (!result.success) {
|
|
493
|
-
warningCount++;
|
|
494
|
-
this.scanErrors.push({
|
|
495
|
-
path: candidate.skillPath,
|
|
496
|
-
error: result.error || "Unknown parse error"
|
|
497
|
-
});
|
|
498
|
-
this.emit("skill:warning" /* SKILL_WARNING */, {
|
|
499
|
-
skillPath: candidate.skillPath,
|
|
500
|
-
rootPath: candidate.rootPath,
|
|
501
|
-
error: result.error
|
|
502
|
-
});
|
|
503
|
-
logger3.warn("Skipping invalid skill", {
|
|
504
|
-
skillPath: candidate.skillPath,
|
|
505
|
-
...result.error ? { error: result.error } : {}
|
|
506
|
-
});
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
const skill = {
|
|
510
|
-
metadata: result.metadata,
|
|
511
|
-
skillPath: candidate.skillPath,
|
|
512
|
-
rootPath: candidate.rootPath,
|
|
513
|
-
trustLevel: this.rootTrustMap.get(candidate.rootPath) ?? "untrusted"
|
|
514
|
-
};
|
|
515
|
-
if (this.skills.has(skill.metadata.name)) {
|
|
516
|
-
const existing = this.skills.get(skill.metadata.name);
|
|
517
|
-
warningCount++;
|
|
518
|
-
const warningMsg = `Duplicate skill name "${skill.metadata.name}" from "${candidate.rootPath}" \u2014 keeping version from "${existing.rootPath}" (first root takes precedence)`;
|
|
519
|
-
this.scanErrors.push({
|
|
520
|
-
path: candidate.skillPath,
|
|
521
|
-
error: warningMsg
|
|
522
|
-
});
|
|
523
|
-
this.emit("skill:warning" /* SKILL_WARNING */, {
|
|
524
|
-
skillPath: candidate.skillPath,
|
|
525
|
-
rootPath: candidate.rootPath,
|
|
526
|
-
duplicateOf: existing.skillPath,
|
|
527
|
-
error: warningMsg
|
|
528
|
-
});
|
|
529
|
-
logger3.warn("Duplicate skill name, keeping first", {
|
|
530
|
-
name: skill.metadata.name,
|
|
531
|
-
kept: existing.skillPath,
|
|
532
|
-
skipped: candidate.skillPath
|
|
533
|
-
});
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
this.skills.set(skill.metadata.name, skill);
|
|
537
|
-
successCount++;
|
|
538
|
-
this.emit("skill:discovered" /* SKILL_DISCOVERED */, skill);
|
|
539
|
-
logger3.debug("Skill discovered", {
|
|
540
|
-
name: skill.metadata.name,
|
|
541
|
-
description: skill.metadata.description.slice(0, 80),
|
|
542
|
-
skillPath: skill.skillPath
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
logger3.info("Skill registry populated", {
|
|
546
|
-
rootsScanned: this.config.skillRoots.length,
|
|
547
|
-
skillsDiscovered: successCount,
|
|
548
|
-
warnings: warningCount
|
|
635
|
+
this.scanErrors = discoverSkills(this.config, this.skills, this.rootTrustMap, (event, data) => {
|
|
636
|
+
this.emit(event, data);
|
|
549
637
|
});
|
|
550
638
|
}
|
|
551
|
-
// ─── Query API (parallel to ToolRegistry) ──────────────────────────────
|
|
552
|
-
/**
|
|
553
|
-
* Get a skill by name.
|
|
554
|
-
*
|
|
555
|
-
* @param name - The skill name
|
|
556
|
-
* @returns The skill, or undefined if not found
|
|
557
|
-
*
|
|
558
|
-
* @example
|
|
559
|
-
* ```ts
|
|
560
|
-
* const skill = registry.get('code-review');
|
|
561
|
-
* if (skill) {
|
|
562
|
-
* console.log(skill.metadata.description);
|
|
563
|
-
* }
|
|
564
|
-
* ```
|
|
565
|
-
*/
|
|
566
639
|
get(name) {
|
|
567
|
-
return this.skills
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* Get all discovered skills.
|
|
571
|
-
*
|
|
572
|
-
* @returns Array of all skills
|
|
573
|
-
*
|
|
574
|
-
* @example
|
|
575
|
-
* ```ts
|
|
576
|
-
* const allSkills = registry.getAll();
|
|
577
|
-
* console.log(`Total skills: ${allSkills.length}`);
|
|
578
|
-
* ```
|
|
579
|
-
*/
|
|
640
|
+
return getSkill(this.skills, name);
|
|
641
|
+
}
|
|
580
642
|
getAll() {
|
|
581
|
-
return
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Check if a skill exists in the registry.
|
|
585
|
-
*
|
|
586
|
-
* @param name - The skill name
|
|
587
|
-
* @returns True if the skill exists
|
|
588
|
-
*
|
|
589
|
-
* @example
|
|
590
|
-
* ```ts
|
|
591
|
-
* if (registry.has('code-review')) {
|
|
592
|
-
* console.log('Skill available!');
|
|
593
|
-
* }
|
|
594
|
-
* ```
|
|
595
|
-
*/
|
|
643
|
+
return getAllSkills(this.skills);
|
|
644
|
+
}
|
|
596
645
|
has(name) {
|
|
597
|
-
return this.skills
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Get the number of discovered skills.
|
|
601
|
-
*
|
|
602
|
-
* @returns Number of skills in the registry
|
|
603
|
-
*
|
|
604
|
-
* @example
|
|
605
|
-
* ```ts
|
|
606
|
-
* console.log(`Registry has ${registry.size()} skills`);
|
|
607
|
-
* ```
|
|
608
|
-
*/
|
|
646
|
+
return hasSkill(this.skills, name);
|
|
647
|
+
}
|
|
609
648
|
size() {
|
|
610
|
-
return this.skills
|
|
649
|
+
return getSkillCount(this.skills);
|
|
611
650
|
}
|
|
612
|
-
/**
|
|
613
|
-
* Get all skill names.
|
|
614
|
-
*
|
|
615
|
-
* @returns Array of skill names
|
|
616
|
-
*/
|
|
617
651
|
getNames() {
|
|
618
|
-
return
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Get errors/warnings from the last scan.
|
|
622
|
-
*
|
|
623
|
-
* Useful for diagnostics and observability.
|
|
624
|
-
*
|
|
625
|
-
* @returns Array of scan errors with paths
|
|
626
|
-
*/
|
|
652
|
+
return getSkillNames(this.skills);
|
|
653
|
+
}
|
|
627
654
|
getScanErrors() {
|
|
628
|
-
return this.scanErrors;
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Check whether untrusted script access is allowed via config override.
|
|
632
|
-
*
|
|
633
|
-
* Used by activation tools to pass the override flag to trust policy checks.
|
|
634
|
-
*
|
|
635
|
-
* @returns True if `allowUntrustedScripts` is set in config
|
|
636
|
-
*/
|
|
655
|
+
return getScanErrors(this.scanErrors);
|
|
656
|
+
}
|
|
637
657
|
getAllowUntrustedScripts() {
|
|
638
658
|
return this.config.allowUntrustedScripts ?? false;
|
|
639
659
|
}
|
|
640
|
-
/**
|
|
641
|
-
* Get the `allowed-tools` list for a skill.
|
|
642
|
-
*
|
|
643
|
-
* Returns the `allowedTools` array from the skill's frontmatter metadata,
|
|
644
|
-
* enabling agents to filter their tool set based on what the skill expects.
|
|
645
|
-
*
|
|
646
|
-
* @param name - The skill name
|
|
647
|
-
* @returns Array of allowed tool names, or undefined if skill not found or field not set
|
|
648
|
-
*
|
|
649
|
-
* @example
|
|
650
|
-
* ```ts
|
|
651
|
-
* const allowed = registry.getAllowedTools('code-review');
|
|
652
|
-
* if (allowed) {
|
|
653
|
-
* const filteredTools = allTools.filter(t => allowed.includes(t.name));
|
|
654
|
-
* }
|
|
655
|
-
* ```
|
|
656
|
-
*/
|
|
657
660
|
getAllowedTools(name) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
// ─── Prompt Generation ─────────────────────────────────────────────────
|
|
662
|
-
/**
|
|
663
|
-
* Generate an `<available_skills>` XML block for system prompt injection.
|
|
664
|
-
*
|
|
665
|
-
* Returns an empty string when:
|
|
666
|
-
* - `config.enabled` is `false` (default) — agents operate with unmodified prompts
|
|
667
|
-
* - No skills match the filter criteria
|
|
668
|
-
*
|
|
669
|
-
* The output composes naturally with `toolRegistry.generatePrompt()` —
|
|
670
|
-
* simply concatenate both into the system prompt.
|
|
671
|
-
*
|
|
672
|
-
* @param options - Optional filtering (subset of skill names)
|
|
673
|
-
* @returns XML string or empty string
|
|
674
|
-
*
|
|
675
|
-
* @example
|
|
676
|
-
* ```ts
|
|
677
|
-
* // All skills
|
|
678
|
-
* const xml = registry.generatePrompt();
|
|
679
|
-
*
|
|
680
|
-
* // Subset for a focused agent
|
|
681
|
-
* const xml = registry.generatePrompt({ skills: ['code-review', 'testing'] });
|
|
682
|
-
*
|
|
683
|
-
* // Compose with tool prompt
|
|
684
|
-
* const systemPrompt = [
|
|
685
|
-
* toolRegistry.generatePrompt(),
|
|
686
|
-
* skillRegistry.generatePrompt(),
|
|
687
|
-
* ].filter(Boolean).join('\n\n');
|
|
688
|
-
* ```
|
|
689
|
-
*/
|
|
661
|
+
return getAllowedTools(this.skills, name);
|
|
662
|
+
}
|
|
690
663
|
generatePrompt(options) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
enabled: this.config.enabled ?? false
|
|
694
|
-
});
|
|
695
|
-
return "";
|
|
696
|
-
}
|
|
697
|
-
let skills = this.getAll();
|
|
698
|
-
if (options?.skills && options.skills.length > 0) {
|
|
699
|
-
const requested = new Set(options.skills);
|
|
700
|
-
skills = skills.filter((s) => requested.has(s.metadata.name));
|
|
701
|
-
}
|
|
702
|
-
if (this.config.maxDiscoveredSkills !== void 0 && this.config.maxDiscoveredSkills >= 0) {
|
|
703
|
-
skills = skills.slice(0, this.config.maxDiscoveredSkills);
|
|
704
|
-
}
|
|
705
|
-
if (skills.length === 0) {
|
|
706
|
-
logger3.debug("Skill prompt generation produced empty result", {
|
|
707
|
-
totalDiscovered: this.size(),
|
|
708
|
-
filterApplied: !!(options?.skills && options.skills.length > 0),
|
|
709
|
-
...this.config.maxDiscoveredSkills !== void 0 ? { maxCap: this.config.maxDiscoveredSkills } : {}
|
|
710
|
-
});
|
|
711
|
-
return "";
|
|
712
|
-
}
|
|
713
|
-
const skillEntries = skills.map((skill) => {
|
|
714
|
-
const lines = [
|
|
715
|
-
" <skill>",
|
|
716
|
-
` <name>${escapeXml(skill.metadata.name)}</name>`,
|
|
717
|
-
` <description>${escapeXml(skill.metadata.description)}</description>`,
|
|
718
|
-
` <location>${escapeXml(skill.skillPath)}</location>`,
|
|
719
|
-
" </skill>"
|
|
720
|
-
];
|
|
721
|
-
return lines.join("\n");
|
|
722
|
-
});
|
|
723
|
-
const xml = `<available_skills>
|
|
724
|
-
${skillEntries.join("\n")}
|
|
725
|
-
</available_skills>`;
|
|
726
|
-
const estimatedTokens = Math.ceil(xml.length / 4);
|
|
727
|
-
logger3.info("Skill prompt generated", {
|
|
728
|
-
skillCount: skills.length,
|
|
729
|
-
totalDiscovered: this.size(),
|
|
730
|
-
filterApplied: !!(options?.skills && options.skills.length > 0),
|
|
731
|
-
...this.config.maxDiscoveredSkills !== void 0 ? { maxCap: this.config.maxDiscoveredSkills } : {},
|
|
732
|
-
estimatedTokens,
|
|
733
|
-
xmlLength: xml.length
|
|
734
|
-
});
|
|
735
|
-
return xml;
|
|
736
|
-
}
|
|
737
|
-
// ─── Event System ──────────────────────────────────────────────────────
|
|
738
|
-
/**
|
|
739
|
-
* Register an event handler.
|
|
740
|
-
*
|
|
741
|
-
* @param event - The event to listen for
|
|
742
|
-
* @param handler - The handler function
|
|
743
|
-
*
|
|
744
|
-
* @example
|
|
745
|
-
* ```ts
|
|
746
|
-
* registry.on(SkillRegistryEvent.SKILL_DISCOVERED, (skill) => {
|
|
747
|
-
* console.log('Found skill:', skill.metadata.name);
|
|
748
|
-
* });
|
|
749
|
-
* ```
|
|
750
|
-
*/
|
|
664
|
+
return generateSkillPrompt(this.config, this.getAll(), this.size(), options);
|
|
665
|
+
}
|
|
751
666
|
on(event, handler) {
|
|
752
|
-
|
|
753
|
-
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
754
|
-
}
|
|
755
|
-
this.eventHandlers.get(event).add(handler);
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* Unregister an event handler.
|
|
759
|
-
*
|
|
760
|
-
* @param event - The event to stop listening for
|
|
761
|
-
* @param handler - The handler function to remove
|
|
762
|
-
*/
|
|
763
|
-
off(event, handler) {
|
|
764
|
-
const handlers = this.eventHandlers.get(event);
|
|
765
|
-
if (handlers) {
|
|
766
|
-
handlers.delete(handler);
|
|
767
|
-
}
|
|
667
|
+
addRegistryEventHandler(this.eventHandlers, event, handler);
|
|
768
668
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
*
|
|
772
|
-
* @param event - The event to emit
|
|
773
|
-
* @param data - The event data
|
|
774
|
-
* @private
|
|
775
|
-
*/
|
|
776
|
-
emit(event, data) {
|
|
777
|
-
const handlers = this.eventHandlers.get(event);
|
|
778
|
-
if (handlers) {
|
|
779
|
-
handlers.forEach((handler) => {
|
|
780
|
-
try {
|
|
781
|
-
handler(data);
|
|
782
|
-
} catch (error) {
|
|
783
|
-
logger3.error("Skill event handler error", {
|
|
784
|
-
event,
|
|
785
|
-
error: error instanceof Error ? error.message : String(error),
|
|
786
|
-
...error instanceof Error && error.stack ? { stack: error.stack } : {}
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
});
|
|
790
|
-
}
|
|
669
|
+
off(event, handler) {
|
|
670
|
+
removeRegistryEventHandler(this.eventHandlers, event, handler);
|
|
791
671
|
}
|
|
792
|
-
/**
|
|
793
|
-
* Emit an event (public API for activation tools).
|
|
794
|
-
*
|
|
795
|
-
* Used by skill activation tools to emit `skill:activated` and
|
|
796
|
-
* `skill:resource-loaded` events through the registry's event system.
|
|
797
|
-
*
|
|
798
|
-
* @param event - The event to emit
|
|
799
|
-
* @param data - The event data
|
|
800
|
-
*/
|
|
801
672
|
emitEvent(event, data) {
|
|
802
673
|
this.emit(event, data);
|
|
803
674
|
}
|
|
804
|
-
// ─── Tool Integration ────────────────────────────────────────────────
|
|
805
|
-
/**
|
|
806
|
-
* Create activation tools pre-wired to this registry instance.
|
|
807
|
-
*
|
|
808
|
-
* Returns `activate-skill` and `read-skill-resource` tools that
|
|
809
|
-
* agents can use to load skill instructions and resources on demand.
|
|
810
|
-
*
|
|
811
|
-
* @returns Array of [activate-skill, read-skill-resource] tools
|
|
812
|
-
*
|
|
813
|
-
* @example
|
|
814
|
-
* ```ts
|
|
815
|
-
* const agent = createReActAgent({
|
|
816
|
-
* model: llm,
|
|
817
|
-
* tools: [
|
|
818
|
-
* ...toolRegistry.toLangChainTools(),
|
|
819
|
-
* ...skillRegistry.toActivationTools(),
|
|
820
|
-
* ],
|
|
821
|
-
* });
|
|
822
|
-
* ```
|
|
823
|
-
*/
|
|
824
675
|
toActivationTools() {
|
|
825
676
|
return createSkillActivationTools(this);
|
|
826
677
|
}
|
|
678
|
+
emit(event, data) {
|
|
679
|
+
emitRegistryEvent(this.eventHandlers, event, data);
|
|
680
|
+
}
|
|
827
681
|
};
|
|
828
|
-
function escapeXml(str) {
|
|
829
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
830
|
-
}
|
|
831
682
|
|
|
832
683
|
exports.SkillRegistry = SkillRegistry;
|
|
833
684
|
exports.SkillRegistryEvent = SkillRegistryEvent;
|