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
|
package main
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"runtime/debug"
"strings"
"syscall"
"github.com/spf13/cobra"
)
// version is set at build time; `go install` builds fall back to the module
// version.
var version = "dev"
func init() {
if bi, ok := debug.ReadBuildInfo(); ok && version == "dev" && bi.Main.Version != "" && bi.Main.Version != "(devel)" {
version = bi.Main.Version
}
}
const docsURL = "https://git.simenandre.no/skills/about/"
type usageError struct{ error }
type options struct {
manifest string
quiet bool
}
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
err := newRoot().ExecuteContext(ctx)
interrupted := ctx.Err() != nil
stop()
if err == nil {
return
}
fmt.Fprintln(os.Stderr, "skills:", err)
var ue usageError
switch {
case interrupted:
os.Exit(130)
case errors.As(err, &ue), strings.HasPrefix(err.Error(), "unknown command"):
os.Exit(2)
}
os.Exit(1)
}
func newRoot() *cobra.Command {
opts := &options{}
root := &cobra.Command{
Use: "skills",
Short: "Install and update agent skills from git repositories",
Long: `skills installs agent skills (directories holding a SKILL.md) from git
repositories into Claude Code, Codex and OpenCode, and keeps them updated.
What you add is recorded in a manifest, and the exact commit of each source
in a lock file next to it. Commit both to reproduce the same skills on
another machine with ` + "`skills install`" + `.`,
Example: ` # Add every skill in a repository
skills add anthropics/skills
# Add one skill, from a private repository, at a tag
skills add git@example.com:team/skills.git#v1.2.0 --skill deploy
# See what is installed, then move everything to the newest commits
skills list
skills update`,
Version: version,
SilenceUsage: true,
SilenceErrors: true,
}
root.SetFlagErrorFunc(func(_ *cobra.Command, err error) error { return usageError{err} })
root.PersistentFlags().StringVar(&opts.manifest, "manifest", "",
"manifest to use (default $SKILLS_MANIFEST, else $XDG_CONFIG_HOME/skills/skills.json)")
root.PersistentFlags().BoolVarP(&opts.quiet, "quiet", "q", false, "print only errors")
root.SetHelpTemplate(root.HelpTemplate() + "\nDocumentation and source: " + docsURL + "\n")
root.AddCommand(addCmd(opts), removeCmd(opts), updateCmd(opts), installCmd(opts), listCmd(opts))
return root
}
func (o *options) workspace() (*workspace, error) {
path, err := manifestPath(o.manifest)
if err != nil {
return nil, err
}
return openWorkspace(path)
}
func (o *options) printf(format string, a ...any) {
if !o.quiet {
fmt.Printf(format, a...)
}
}
// progressf reports slow work on stderr, only to a person watching.
func (o *options) progressf(format string, a ...any) {
if o.quiet {
return
}
if fi, err := os.Stderr.Stat(); err == nil && fi.Mode()&os.ModeCharDevice != 0 {
fmt.Fprintf(os.Stderr, format, a...)
}
}
func exactArgs(n int) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
if len(args) != n {
return usageError{fmt.Errorf("%s takes %d argument(s), got %d (see `%s --help`)", cmd.Name(), n, len(args), cmd.CommandPath())}
}
return nil
}
}
func minArgs(n int) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
if len(args) < n {
return usageError{fmt.Errorf("%s needs at least %d argument(s) (see `%s --help`)", cmd.Name(), n, cmd.CommandPath())}
}
return nil
}
}
|