diff --git a/internal/p9/server.go b/internal/p9/server.go index 28d0d83..7b4a275 100644 --- a/internal/p9/server.go +++ b/internal/p9/server.go @@ -33,6 +33,7 @@ import ( "ollie/pkg/agent" "ollie/pkg/backend" "ollie/pkg/config" + "ollie/pkg/skills" "ollie/pkg/tools" "ollie/pkg/tools/execute" "ollie/pkg/tools/file" @@ -199,10 +200,17 @@ func (s *Server) pathType(path string) string { return "dir" case len(parts) == 1 && parts[0] == "prompts": return "dir" + case len(parts) == 1 && parts[0] == "skills": + return "dir" case len(parts) == 2 && parts[0] == "prompts": if _, err := os.Stat(s.promptsDir + "/" + parts[1]); err == nil { return "file" } + case len(parts) == 2 && parts[0] == "skills": + name := strings.TrimSuffix(parts[1], ".md") + if _, err := skills.Read(name); err == nil { + return "file" + } case len(parts) == 2 && parts[0] == "s": s.mu.RLock() _, ok := s.sessions[parts[1]] @@ -367,6 +375,25 @@ func (s *Server) read(cs *connState, fc *plan9.Fcall) *plan9.Fcall { return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: uint32(len(data)), Data: data} } + // Skill files are served via pkg/skills (reads from disk each time). + if strings.HasPrefix(path, "/skills/") { + name := strings.TrimSuffix(pathBase(path), ".md") + content, err := skills.Read(name) + if err != nil { + return errFcall(fc, err.Error()) + } + var data []byte + off := int(fc.Offset) + if off < len(content) { + end := off + int(fc.Count) + if end > len(content) { + end = len(content) + } + data = content[off:end] + } + return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: uint32(len(data)), Data: data} + } + content := s.readFile(path) var data []byte off := int(fc.Offset) @@ -849,6 +876,7 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte { dirs = append(dirs, makeDir("ctl", "/ctl", false, 0200)) dirs = append(dirs, makeDir("prompts", "/prompts", true, plan9.DMDIR|0555)) dirs = append(dirs, makeDir("s", "/s", true, plan9.DMDIR|0555)) + dirs = append(dirs, makeDir("skills", "/skills", true, plan9.DMDIR|0555)) } else if path == "/prompts" { entries, _ := os.ReadDir(s.promptsDir) for _, e := range entries { @@ -856,6 +884,11 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte { dirs = append(dirs, makeDir(e.Name(), "/prompts/"+e.Name(), false, 0666)) } } + } else if path == "/skills" { + for _, m := range skills.List() { + name := m.Name + ".md" + dirs = append(dirs, makeDir(name, "/skills/"+name, false, 0444)) + } } else if path == "/s" { s.mu.RLock() for id := range s.sessions { @@ -944,6 +977,8 @@ func (s *Server) makeStat(path string) plan9.Dir { default: if strings.HasPrefix(path, "/prompts/") { mode = 0666 + } else if strings.HasPrefix(path, "/skills/") { + mode = 0444 } else { mode = 0444 } diff --git a/mkfile b/mkfile index 0228b6c..c6c9092 100644 --- a/mkfile +++ b/mkfile @@ -6,6 +6,8 @@ build:V: go build -o $INSTALL_PATH/olliesrv . install:V: build + mkdir -p $HOME/.local/share/ollie/tools + cp -f tools/* $HOME/.local/share/ollie/tools/ clean:V: rm -f $INSTALL_PATH/olliesrv diff --git a/tools/discover_skill.sh b/tools/discover_skill.sh new file mode 100755 index 0000000..94bd94b --- /dev/null +++ b/tools/discover_skill.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# description: Discover available skills by keyword +set -euo pipefail + +if [ $# -ne 1 ]; then + echo "Usage: discover_skill " >&2 + exit 1 +fi + +OLLIE="${OLLIE_9MOUNT:-$HOME/mnt/ollie}" +keyword="$1" +results="" + +for file in "$OLLIE/skills"/*.md; do + [ -f "$file" ] || continue + name=$(basename "$file" .md) + if grep -qi "$keyword" "$file"; then + desc=$(grep -m1 '^description:' "$file" | sed 's/^description: *//') + results="${results:+$results +}$name $desc" + fi +done + +if [ -n "$results" ]; then + echo "$results" | sort -u +else + echo "No skills found matching: $keyword" +fi diff --git a/tools/load_skill.sh b/tools/load_skill.sh new file mode 100755 index 0000000..eb78df4 --- /dev/null +++ b/tools/load_skill.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# description: Load a skill's SKILL.md by name +set -euo pipefail + +if [ $# -lt 1 ]; then + echo "Usage: load_skill [skill-name...]" >&2 + exit 1 +fi + +OLLIE="${OLLIE_9MOUNT:-$HOME/mnt/ollie}" + +for arg in "$@"; do + name="${arg##*/}" + cat "$OLLIE/skills/${name}.md" +done