Files
agent-lib/internal/cli/vendor.go
T
m3ta-chiron 876ff1c6b9 feat(vendor): select command — manage include/exclude selection after initial add
vendor select <name> --add/--remove/--mode all changes what a source
contributes without advancing the pinned rev: ids are validated against
the pinned upstream (typos abort listing available ids), collisions
re-run, renames from the lockfile are honored, and the external area is
rewritten via the same staging-swap as update. include mode grows and
shrinks the include list; all mode --remove moves ids onto exclude;
--mode all resets to everything. Failed selects mutate nothing.

Closes beads: agent-lib-cd3
2026-08-23 15:13:50 +02:00

111 lines
3.8 KiB
Go

package cli
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/m3tam3re/agent-lib/internal/vendor"
)
func newVendorCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "vendor",
Short: "Curate external sources into this work repository",
}
cmd.AddCommand(newVendorAddCmd())
cmd.AddCommand(newVendorSelectCmd())
cmd.AddCommand(newVendorListCmd())
cmd.AddCommand(newVendorInspectCmd())
cmd.AddCommand(newVendorUpdateCmd())
cmd.AddCommand(newVendorDiffCmd())
cmd.AddCommand(newVendorRemoveCmd())
return cmd
}
func newVendorAddCmd() *cobra.Command {
var ref string
var include []string
var renames []string
cmd := &cobra.Command{
Use: "add <name> <url>",
Short: "Vendor a new external git source",
Long: "Clones the source (in-process, via go-git), discovers skills, commands,\n" +
"agents and MCP fragments, materializes the selection under external/<name>/\n" +
"and pins URL, ref, revision, selection and renames in the lockfile.\n" +
"Default selection is everything; --include switches to an explicit list.\n" +
"Collisions abort with a hard error; resolve them with --rename upstream=deployed.",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
renameMap, err := vendor.ParseRenames(renames)
if err != nil {
return err
}
opts := vendor.AddOptions{
Name: args[0],
URL: args[1],
Ref: ref,
Include: splitList(include),
Renames: renameMap,
}
if err := vendor.Add(".", opts, cmd.OutOrStdout()); err != nil {
return fmt.Errorf("vendor add: %w", err)
}
return nil
},
}
cmd.Flags().StringVar(&ref, "ref", "", "branch or tag to track (default: the source's default branch)")
cmd.Flags().StringSliceVar(&include, "include", nil, "comma-separated item ids to vendor (switches selection to include mode)")
cmd.Flags().StringSliceVar(&renames, "rename", nil, "deployed-name override upstream-id=deployed-name (repeatable)")
return cmd
}
func splitList(in []string) []string {
var out []string
for _, s := range in {
for _, part := range strings.Split(s, ",") {
if t := strings.TrimSpace(part); t != "" {
out = append(out, t)
}
}
}
return out
}
func newVendorSelectCmd() *cobra.Command {
var add, remove []string
var mode string
cmd := &cobra.Command{
Use: "select <name>",
Short: "Change a source's selection without advancing the pinned revision",
Long: "Manages include/exclude selection after the initial vendor add.\n" +
"Include mode: --add appends ids (validated against the pinned upstream),\n" +
"--remove drops ids and deletes their folders. All mode: --remove moves ids\n" +
"onto the exclude list; --add is an error (everything is selected already).\n" +
"--mode all resets to everything: include/exclude lists are cleared and the\n" +
"full pinned upstream is re-materialized. Collisions re-run on every change;\n" +
"renames from the lockfile are honored. The revision never moves — that is\n" +
"vendor update's job.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if mode != "" && mode != "all" {
return fmt.Errorf("--mode accepts only \"all\" (got %q)", mode)
}
opts := vendor.SelectOptions{
Add: splitList(add),
Remove: splitList(remove),
ModeAll: mode == "all",
}
if _, err := vendor.Select(".", args[0], opts, cmd.OutOrStdout()); err != nil {
return fmt.Errorf("vendor select: %w", err)
}
return nil
},
}
cmd.Flags().StringSliceVar(&add, "add", nil, "comma-separated upstream ids to add to the selection")
cmd.Flags().StringSliceVar(&remove, "remove", nil, "comma-separated upstream ids to remove from the selection")
cmd.Flags().StringVar(&mode, "mode", "", "switch selection mode (only \"all\": clear include/exclude, take everything)")
return cmd
}