Core features: - Microkernel architecture with Actor model - Session management with JSONL persistence - Tool system (5 built-in tools) - Skill system with SKILL.md parsing - Sandbox security execution - Ollama integration with gemma4:e4b - Prompt-based tool calling (compatible with native function calling) - REPL interface 11 packages, all tests passing
57 lines
2.0 KiB
Go
57 lines
2.0 KiB
Go
// Package skill provides the Skill definition and management system.
|
|
//
|
|
// Skills are composable capabilities loaded from ~/.agents/skills/.
|
|
// Each skill has a SKILL.md manifest with YAML frontmatter and optional
|
|
// scripts in a scripts/ subdirectory. Skills can be discovered and
|
|
// invoked by trigger keywords.
|
|
package skill
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Skill represents a composable capability loaded from the skills directory.
|
|
//
|
|
// Each Skill is defined by a SKILL.md file with YAML frontmatter containing
|
|
// metadata (name, description, triggers) and optional executable scripts
|
|
// in a scripts/ subdirectory.
|
|
type Skill struct {
|
|
// Name is the unique identifier for this skill (e.g., "dev-browser").
|
|
Name string `yaml:"name"`
|
|
// Description is a human-readable explanation of what this skill does.
|
|
Description string `yaml:"description"`
|
|
// Triggers are keywords that activate this skill from natural language.
|
|
Triggers []string `yaml:"triggers"`
|
|
// Scripts is the list of script file names in the scripts/ directory.
|
|
Scripts []string `yaml:"-"`
|
|
// ScriptsDir is the absolute path to the scripts/ directory.
|
|
ScriptsDir string `yaml:"-"`
|
|
// Body is the markdown content after the YAML frontmatter.
|
|
Body string `yaml:"-"`
|
|
// Path is the absolute path to the SKILL.md file.
|
|
Path string `yaml:"-"`
|
|
}
|
|
|
|
// MatchTrigger checks if the given query matches any of the skill's triggers.
|
|
// Matching is case-insensitive and supports partial matches.
|
|
func (s *Skill) MatchTrigger(query string) bool {
|
|
query = strings.ToLower(query)
|
|
for _, trigger := range s.Triggers {
|
|
if strings.Contains(strings.ToLower(query), strings.ToLower(trigger)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// String returns a human-readable representation of the skill.
|
|
func (s *Skill) String() string {
|
|
return fmt.Sprintf("Skill{Name: %q, Triggers: %v, Scripts: %d}", s.Name, s.Triggers, len(s.Scripts))
|
|
}
|
|
|
|
// HasScripts returns true if the skill has at least one script.
|
|
func (s *Skill) HasScripts() bool {
|
|
return len(s.Scripts) > 0
|
|
}
|