From ca43de117fb783db1a7d9467120e26a2bdfc6d44 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Tue, 30 Dec 2025 14:58:05 -0500 Subject: [PATCH] skills: add registry reference check and working directory env var - Add check for registry references without digest in loadSkillsFromRefs - Fix IsLocalSkillPath to not treat registry refs as local paths - Inject OLLAMA_WORKING_DIR env var so skill scripts can access the directory where 'ollama run' was called from --- cmd/skills.go | 15 +++++++++++++++ server/skill.go | 7 ++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cmd/skills.go b/cmd/skills.go index 798f51fad..ebbb65536 100644 --- a/cmd/skills.go +++ b/cmd/skills.go @@ -140,6 +140,13 @@ func loadSkillsFromRefs(refs []api.SkillRef) (*skillCatalog, error) { skillDir = path } else if ref.Name != "" { + // Check if this is a local path or a registry reference + if !server.IsLocalSkillPath(ref.Name) { + // Registry reference without a digest - skill needs to be pulled first + // This happens when an agent references a skill that hasn't been bundled + return nil, fmt.Errorf("skill %q is a registry reference but has no digest - the agent may need to be recreated or the skill pulled separately", ref.Name) + } + // Local path - resolve it skillPath := ref.Name if strings.HasPrefix(skillPath, "~") { @@ -486,6 +493,14 @@ func runSkillScript(skillDir, command string) (string, error) { cmd := exec.CommandContext(ctx, "sh", "-c", command) cmd.Dir = absSkillDir + // Inject the current working directory (where ollama run was called from) + // as an environment variable so scripts can reference files in that directory + workingDir, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("failed to get working directory: %w", err) + } + cmd.Env = append(os.Environ(), "OLLAMA_WORKING_DIR="+workingDir) + // Capture both stdout and stderr var stdout, stderr bytes.Buffer cmd.Stdout = &stdout diff --git a/server/skill.go b/server/skill.go index 0e42fabf6..9576e6845 100644 --- a/server/skill.go +++ b/server/skill.go @@ -250,13 +250,14 @@ func CreateSkillLayer(skillDir string) (Layer, error) { } // IsLocalSkillPath checks if a skill reference looks like a local path. +// Local paths are explicitly prefixed with /, ./, ../, or ~. +// Registry references like "skill/calculator:1.0.0" should NOT be treated as local paths. func IsLocalSkillPath(name string) bool { - // Local paths start with /, ./, or contain path separators + // Local paths are explicitly indicated by path prefixes return strings.HasPrefix(name, "/") || strings.HasPrefix(name, "./") || strings.HasPrefix(name, "../") || - strings.HasPrefix(name, "~") || - strings.Contains(name, string(os.PathSeparator)) + strings.HasPrefix(name, "~") } // SkillNamespace is the namespace used for standalone skills in the registry.