diff options
| author | Simen A. W. Olsen <hello@simenandre.no> | 2026-10-25 12:41:28 +0100 |
|---|---|---|
| committer | Simen A. W. Olsen <hello@simenandre.no> | 2026-10-25 12:41:28 +0100 |
| commit | c596038adc7cdbd64988e61fee856d533b58c25d (patch) | |
| tree | a02fde25835416d143ff56b807e640f6a7e9cf82 /main.go | |
| download | skills-c596038adc7cdbd64988e61fee856d533b58c25d.tar.gz skills-c596038adc7cdbd64988e61fee856d533b58c25d.zip | |
Diffstat (limited to 'main.go')
| -rw-r--r-- | main.go | 128 |
1 files changed, 128 insertions, 0 deletions
@@ -0,0 +1,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 + } +} |
