File Coverage

blib/lib/SignalWire/Agents/Skills/SkillRegistry.pm
Criterion Covered Total %
statement 29 29 100.0
branch 6 6 100.0
condition n/a
subroutine 8 8 100.0
pod 0 4 0.0
total 43 47 91.4


line stmt bran cond sub pod time code
1             package SignalWire::Agents::Skills::SkillRegistry;
2             # Copyright (c) 2025 SignalWire
3             # Licensed under the MIT License.
4              
5 19     19   148008 use strict;
  19         53  
  19         798  
6 19     19   108 use warnings;
  19         113  
  19         9499  
7              
8             # Global registry mapping skill name -> class name
9             my %REGISTRY;
10              
11             sub register_skill {
12 54     54 0 9374 my ($class, $skill_name, $skill_class) = @_;
13 54         232 $REGISTRY{$skill_name} = $skill_class;
14             }
15              
16             sub get_factory {
17 185     185 0 3436564 my ($class, $skill_name) = @_;
18              
19             # Return if already registered
20 185 100       884 return $REGISTRY{$skill_name} if exists $REGISTRY{$skill_name};
21              
22             # Attempt to auto-load from Builtin namespace
23 74         215 my $module = 'SignalWire::Agents::Skills::Builtin::' . _camelize($skill_name);
24 74         6699 eval "require $module"; ## no critic
25 74 100       524 if (!$@) {
26             # If the module registered itself, return it
27 71 100       616 return $REGISTRY{$skill_name} if exists $REGISTRY{$skill_name};
28             # Otherwise register it
29 18         50 $REGISTRY{$skill_name} = $module;
30 18         78 return $module;
31             }
32              
33 3         15 return undef;
34             }
35              
36             sub list_skills {
37 5     5 0 9460 my ($class) = @_;
38             # Make sure all builtins are loaded
39 5         50 $class->_load_all_builtins;
40 5         88 return [ sort keys %REGISTRY ];
41             }
42              
43             sub _load_all_builtins {
44 5     5   16 my ($class) = @_;
45 5         54 my @names = qw(
46             api_ninjas_trivia claude_skills datasphere datasphere_serverless
47             datetime google_maps info_gatherer joke math mcp_gateway
48             native_vector_search play_background_file spider swml_transfer
49             weather_api web_search wikipedia_search custom_skills
50             );
51 5         14 for my $name (@names) {
52 90         237 $class->get_factory($name); # triggers auto-load
53             }
54             }
55              
56             sub _camelize {
57 74     74   176 my ($name) = @_;
58             # Convert snake_case to CamelCase: api_ninjas_trivia -> ApiNinjasTrivia
59 74         568 $name =~ s/_(.)/uc($1)/ge;
  66         371  
60 74         328 return ucfirst($name);
61             }
62              
63             sub clear_registry {
64 3     3 0 190926 %REGISTRY = ();
65             }
66              
67             1;