forked from benitogf/detritus
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugins.go
More file actions
129 lines (120 loc) · 4.36 KB
/
Copy pathplugins.go
File metadata and controls
129 lines (120 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
)
// pluginCommandsDir is the Claude/Codex plugin's slash-command directory. Its
// shims are generated from docs/flows/ so they can never drift to a deleted
// doc name. Regenerate with `detritus --plugin-commands`;
// TestPluginCommandsMatchFlows guards it.
const pluginCommandsDir = "plugins/detritus/commands"
// pluginCommandGeneratedMarker identifies a shim this generator owns, so the
// prune step never deletes a hand-authored command file.
const pluginCommandGeneratedMarker = "<!-- detritus-generated-command -->"
// extractFrontmatterField returns the first `field: value` from a doc's YAML
// frontmatter, or "" if absent.
func extractFrontmatterField(content, field string) string {
prefix := field + ":"
for _, line := range strings.Split(content, "\n") {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, prefix) {
return strings.TrimSpace(strings.TrimPrefix(trimmed, prefix))
}
}
return ""
}
// pluginCommandContent renders one plugin command shim for a flows/ doc. It
// carries the doc's description and (when present) its argument-hint, and
// routes to the canonical doc name via kb_get.
func pluginCommandContent(name, desc, hint string) string {
var b strings.Builder
b.WriteString("---\n")
fmt.Fprintf(&b, "description: %s\n", desc)
if hint != "" {
fmt.Fprintf(&b, "argument-hint: %s\n", hint)
}
b.WriteString("---\n\n")
b.WriteString(pluginCommandGeneratedMarker + "\n\n")
b.WriteString("The user invoked this command with: $ARGUMENTS\n\n")
fmt.Fprintf(&b, "Call the detritus MCP tool `kb_get` with `name=\"%s\"` and follow the returned guidance.\n", name)
return b.String()
}
// pluginCommandFiles returns the generated shim filename → content for every
// doc under docs/flows/. The single source the writer and the drift test share.
func pluginCommandFiles() map[string]string {
out := map[string]string{}
_ = fs.WalkDir(docsFS, "docs/flows", func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() || !strings.HasSuffix(path, ".md") {
return nil
}
name := strings.TrimSuffix(strings.TrimPrefix(path, "docs/"), ".md")
content, _ := fs.ReadFile(docsFS, path)
desc := extractDescription(string(content))
hint := extractFrontmatterField(string(content), "argument-hint")
out[aliasForDoc(name)+".md"] = pluginCommandContent(name, desc, hint)
return nil
})
return out
}
// writePluginCommands regenerates the plugin command shims from docs/flows/,
// pruning generated shims whose backing doc was removed. Invoked by
// `detritus --plugin-commands`.
func writePluginCommands(dir string) error {
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
want := pluginCommandFiles()
for fname, content := range want {
if err := os.WriteFile(filepath.Join(dir, fname), []byte(content), 0o644); err != nil {
return err
}
}
entries, _ := os.ReadDir(dir)
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".md") || want[e.Name()] != "" {
continue
}
data, err := os.ReadFile(filepath.Join(dir, e.Name()))
if err == nil && strings.Contains(string(data), pluginCommandGeneratedMarker) {
os.Remove(filepath.Join(dir, e.Name()))
}
}
return nil
}
// writeOpenCodeCommands installs flow commands without replacing user-owned
// commands that share a name in OpenCode's global command directory.
func writeOpenCodeCommands(dir string) error {
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
want := pluginCommandFiles()
for fname, content := range want {
path := filepath.Join(dir, fname)
if existing, err := os.ReadFile(path); err == nil && !isGeneratedOpenCodeCommand(string(existing), content) {
continue
}
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
return err
}
}
entries, _ := os.ReadDir(dir)
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".md") || want[e.Name()] != "" {
continue
}
data, err := os.ReadFile(filepath.Join(dir, e.Name()))
if err == nil && strings.Contains(string(data), pluginCommandGeneratedMarker) {
os.Remove(filepath.Join(dir, e.Name()))
}
}
return nil
}
func isGeneratedOpenCodeCommand(existing, generated string) bool {
if strings.Contains(existing, pluginCommandGeneratedMarker) {
return true
}
return existing == strings.Replace(generated, pluginCommandGeneratedMarker+"\n\n", "", 1)
}