mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 21:36:31 +02:00
fix(skills): tell the model where each skill body lives
The system-prompt addendum now tags every skill with a source pointer: '[builtin]' for skills embedded in the zot binary, or the SKILL.md path (HOME collapsed to ~) for user-installed ones. The body still loads on demand through the 'skill' tool by name, so no behaviour change for execution, this is pure disambiguation. Before: - write-zot-extension — Help the user create a new zot extension... After: - write-zot-extension [builtin]: Help the user create a new zot extension... - code-review [~/Library/Application Support/zot/skills/code-review/SKILL.md]: ... Why: built-in skills have no filesystem path because their markdown is embedded in the binary. Without the [builtin] tag the model had no way to distinguish them from user skills, and could mistakenly try to read a nonexistent file. The path pointer for user skills also helps the model cite where guidance came from and reason about trust (builtin vs project-local vs global). Test updated; addendum grew to ~123 tokens for a typical 3-skill setup (code-review, test-fix, write-zot-extension).
This commit is contained in:
parent
1a2ab427fe
commit
5cc54822cd
2 changed files with 43 additions and 8 deletions
|
|
@ -155,24 +155,56 @@ func scanUserSkills(zotHome, cwd, userHome string, seen map[string]*Skill) []err
|
|||
// SystemPromptAddendum returns the text to append to the system
|
||||
// prompt when at least one skill is loaded. Empty string if none.
|
||||
//
|
||||
// Format kept short and explicit so the model reliably calls the
|
||||
// `skill` tool with a name from the list rather than guessing.
|
||||
// The format is deliberately compact: name, one-line description,
|
||||
// and a source pointer telling the model where the full body
|
||||
// lives. Built-in skills show "builtin" since their markdown is
|
||||
// embedded in the zot binary and not on the filesystem; user
|
||||
// skills show their SKILL.md path (shortened with ~ for HOME).
|
||||
//
|
||||
// Loading still goes through the `skill` tool with just the name.
|
||||
// The pointer is there so the model can (a) mention the source
|
||||
// honestly in explanations and (b) distinguish between built-ins
|
||||
// and user-authored instruction sets when reasoning about trust.
|
||||
func SystemPromptAddendum(skills []*Skill) string {
|
||||
if len(skills) == 0 {
|
||||
return ""
|
||||
}
|
||||
home, _ := os.UserHomeDir()
|
||||
var sb strings.Builder
|
||||
sb.WriteString("Available skills (call the `skill` tool with one of these names to load full instructions):\n")
|
||||
sb.WriteString("Available skills (call the `skill` tool with a name from this list to load its full instructions):\n")
|
||||
for _, s := range skills {
|
||||
desc := strings.TrimSpace(s.Description)
|
||||
if desc == "" {
|
||||
desc = "(no description)"
|
||||
}
|
||||
fmt.Fprintf(&sb, "- %s — %s\n", s.Name, desc)
|
||||
pointer := skillSourcePointer(s, home)
|
||||
fmt.Fprintf(&sb, "- %s [%s]: %s\n", s.Name, pointer, desc)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// skillSourcePointer returns a short tag describing where a skill
|
||||
// originates. Built-ins are tagged "builtin" because their markdown
|
||||
// is embedded in the zot binary and not reachable through the
|
||||
// filesystem. User skills are tagged with their SKILL.md path,
|
||||
// collapsed to use ~ for the user home when possible.
|
||||
func skillSourcePointer(s *Skill, home string) string {
|
||||
if s == nil {
|
||||
return "unknown"
|
||||
}
|
||||
if s.Builtin {
|
||||
return "builtin"
|
||||
}
|
||||
p := s.Path
|
||||
if p == "" {
|
||||
return "unknown"
|
||||
}
|
||||
if home != "" && strings.HasPrefix(p, home+string(filepath.Separator)) {
|
||||
return "~" + p[len(home):]
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// FindByName returns the skill with the given name, or nil.
|
||||
func FindByName(skills []*Skill, name string) *Skill {
|
||||
for _, s := range skills {
|
||||
|
|
|
|||
|
|
@ -108,12 +108,15 @@ func TestVisibleSkillsHidesBuiltins(t *testing.T) {
|
|||
|
||||
func TestSystemPromptAddendum(t *testing.T) {
|
||||
skills := []*Skill{
|
||||
{Name: "a", Description: "Do A."},
|
||||
{Name: "b", Description: "Do B."},
|
||||
{Name: "built-a", Description: "Do A.", Builtin: true},
|
||||
{Name: "user-b", Description: "Do B.", Path: "/tmp/skills/user-b/SKILL.md"},
|
||||
}
|
||||
out := SystemPromptAddendum(skills)
|
||||
if want := "- a — Do A.\n- b — Do B.\n"; !contains(out, want) {
|
||||
t.Errorf("addendum missing entries:\n%s", out)
|
||||
if !contains(out, "- built-a [builtin]: Do A.\n") {
|
||||
t.Errorf("builtin entry missing or wrong:\n%s", out)
|
||||
}
|
||||
if !contains(out, "- user-b [/tmp/skills/user-b/SKILL.md]: Do B.\n") {
|
||||
t.Errorf("user entry missing path pointer:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue