p9: expose skills/ directory and add skill tool scripts
Serve /skills/<name>.md via pkg/skills (read-only, refreshes from disk on each read). Add discover_skill.sh and load_skill.sh tool scripts that read from the 9P mount.
This commit is contained in:
parent
944445fd4d
commit
9122f7477e
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"ollie/pkg/agent"
|
"ollie/pkg/agent"
|
||||||
"ollie/pkg/backend"
|
"ollie/pkg/backend"
|
||||||
"ollie/pkg/config"
|
"ollie/pkg/config"
|
||||||
|
"ollie/pkg/skills"
|
||||||
"ollie/pkg/tools"
|
"ollie/pkg/tools"
|
||||||
"ollie/pkg/tools/execute"
|
"ollie/pkg/tools/execute"
|
||||||
"ollie/pkg/tools/file"
|
"ollie/pkg/tools/file"
|
||||||
|
|
@ -199,10 +200,17 @@ func (s *Server) pathType(path string) string {
|
||||||
return "dir"
|
return "dir"
|
||||||
case len(parts) == 1 && parts[0] == "prompts":
|
case len(parts) == 1 && parts[0] == "prompts":
|
||||||
return "dir"
|
return "dir"
|
||||||
|
case len(parts) == 1 && parts[0] == "skills":
|
||||||
|
return "dir"
|
||||||
case len(parts) == 2 && parts[0] == "prompts":
|
case len(parts) == 2 && parts[0] == "prompts":
|
||||||
if _, err := os.Stat(s.promptsDir + "/" + parts[1]); err == nil {
|
if _, err := os.Stat(s.promptsDir + "/" + parts[1]); err == nil {
|
||||||
return "file"
|
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":
|
case len(parts) == 2 && parts[0] == "s":
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
_, ok := s.sessions[parts[1]]
|
_, 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}
|
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)
|
content := s.readFile(path)
|
||||||
var data []byte
|
var data []byte
|
||||||
off := int(fc.Offset)
|
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("ctl", "/ctl", false, 0200))
|
||||||
dirs = append(dirs, makeDir("prompts", "/prompts", true, plan9.DMDIR|0555))
|
dirs = append(dirs, makeDir("prompts", "/prompts", true, plan9.DMDIR|0555))
|
||||||
dirs = append(dirs, makeDir("s", "/s", 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" {
|
} else if path == "/prompts" {
|
||||||
entries, _ := os.ReadDir(s.promptsDir)
|
entries, _ := os.ReadDir(s.promptsDir)
|
||||||
for _, e := range entries {
|
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))
|
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" {
|
} else if path == "/s" {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
for id := range s.sessions {
|
for id := range s.sessions {
|
||||||
|
|
@ -944,6 +977,8 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(path, "/prompts/") {
|
if strings.HasPrefix(path, "/prompts/") {
|
||||||
mode = 0666
|
mode = 0666
|
||||||
|
} else if strings.HasPrefix(path, "/skills/") {
|
||||||
|
mode = 0444
|
||||||
} else {
|
} else {
|
||||||
mode = 0444
|
mode = 0444
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
mkfile
2
mkfile
|
|
@ -6,6 +6,8 @@ build:V:
|
||||||
go build -o $INSTALL_PATH/olliesrv .
|
go build -o $INSTALL_PATH/olliesrv .
|
||||||
|
|
||||||
install:V: build
|
install:V: build
|
||||||
|
mkdir -p $HOME/.local/share/ollie/tools
|
||||||
|
cp -f tools/* $HOME/.local/share/ollie/tools/
|
||||||
|
|
||||||
clean:V:
|
clean:V:
|
||||||
rm -f $INSTALL_PATH/olliesrv
|
rm -f $INSTALL_PATH/olliesrv
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# description: Discover available skills by keyword
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: discover_skill <keyword>" >&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
|
||||||
|
|
@ -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> [skill-name...]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
OLLIE="${OLLIE_9MOUNT:-$HOME/mnt/ollie}"
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
name="${arg##*/}"
|
||||||
|
cat "$OLLIE/skills/${name}.md"
|
||||||
|
done
|
||||||
Reference in New Issue