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:
2026-08-22 21:52:30 +02:00
parent 2a6498dae1
commit 2c7200380b
16 changed files with 1648 additions and 2 deletions
+7 -1
View File
@@ -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
}
+60
View File
@@ -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
}