feat: vendor add end-to-end with deterministic lockfile v2
- go-git in-process clone, default-branch/tag resolution, optional token auth - web-tree URL normalization (github/gitlab) with implied discovery root - discovery of skills/commands/agents/mcp, lenient frontmatter with warnings - selection: all by default, --include with missing-entry hard errors - external/<source>/<type>/ materialization preserving exec bits - lockfile v2: deterministic JSON, pinned url/ref/rev, inventory, warnings - offline black-box e2e suite against local fixture git repositories
This commit is contained in:
@@ -5,6 +5,7 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -20,6 +21,7 @@ func NewRootCmd() *cobra.Command {
|
||||
SilenceErrors: true,
|
||||
}
|
||||
root.AddCommand(newVersionCmd())
|
||||
root.AddCommand(newVendorCmd())
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -40,5 +42,9 @@ func printVersion(w io.Writer) error {
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
return NewRootCmd().Execute()
|
||||
if err := NewRootCmd().Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
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())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newVendorAddCmd() *cobra.Command {
|
||||
var ref string
|
||||
var include []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.",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts := vendor.AddOptions{
|
||||
Name: args[0],
|
||||
URL: args[1],
|
||||
Ref: ref,
|
||||
Include: splitList(include),
|
||||
}
|
||||
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)")
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user