mirror of
https://github.com/lukaszraczylo/harness-sync.git
synced 2026-10-12 00:45:22 +00:00
fix: pre-select all in init prompt, preserve agent paths with slashes, fix rollback off-by-one
This commit is contained in:
+37
-4
@@ -49,7 +49,11 @@ func NewInit(reg *adapter.Registry) *cobra.Command {
|
||||
var picked []string
|
||||
switch {
|
||||
case len(from) > 0:
|
||||
picked = from
|
||||
for _, f := range from {
|
||||
if f != "" {
|
||||
picked = append(picked, f)
|
||||
}
|
||||
}
|
||||
case noPrompt:
|
||||
picked = candidates
|
||||
default:
|
||||
@@ -58,6 +62,14 @@ func NewInit(reg *adapter.Registry) *cobra.Command {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(picked) == 0 {
|
||||
return fmt.Errorf("no harnesses selected; rerun with --no-prompt or --from <name,name>")
|
||||
}
|
||||
for _, name := range picked {
|
||||
if _, ok := reg.Get(name); !ok {
|
||||
return fmt.Errorf("unknown adapter %q (try `harness-sync adapter list`)", name)
|
||||
}
|
||||
}
|
||||
return runImport(cmd, reg, r, home, picked)
|
||||
},
|
||||
}
|
||||
@@ -134,7 +146,10 @@ func runImport(cmd *cobra.Command, reg *adapter.Registry, root, home string, pic
|
||||
if err := repo.Commit(fmt.Sprintf("import from %s", strings.Join(picked, ", "))); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "imported from %v into %s\n", picked, root)
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(),
|
||||
"imported from %v into %s\n skills: %d\n agents: %d\n mcp servers: %d\n instructions: %d source(s)\n",
|
||||
picked, root, len(bundle.Skills), len(bundle.Agents), len(bundle.MCP.Servers), len(instructions),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -183,7 +198,11 @@ func writeCanonical(root string, b *canonical.Bundle, picked []string) error {
|
||||
}
|
||||
|
||||
for _, s := range b.Skills {
|
||||
path := filepath.Join(root, "skills", s.Name, "SKILL.md")
|
||||
rel := s.Path
|
||||
if rel == "" {
|
||||
rel = filepath.Join(safeFilename(s.Name), "SKILL.md")
|
||||
}
|
||||
path := filepath.Join(root, "skills", rel)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -192,7 +211,14 @@ func writeCanonical(root string, b *canonical.Bundle, picked []string) error {
|
||||
}
|
||||
}
|
||||
for _, a := range b.Agents {
|
||||
path := filepath.Join(root, "agents", a.Name+".md")
|
||||
rel := a.Path
|
||||
if rel == "" {
|
||||
rel = safeFilename(a.Name) + ".md"
|
||||
}
|
||||
path := filepath.Join(root, "agents", rel)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(a.Body), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -205,3 +231,10 @@ func writeCanonical(root string, b *canonical.Bundle, picked []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// safeFilename strips path separators and other characters that would break
|
||||
// when used as a basename.
|
||||
func safeFilename(s string) string {
|
||||
r := strings.NewReplacer("/", "-", "\\", "-", ":", "-", "\x00", "")
|
||||
return r.Replace(s)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,12 @@ func (r *Repo) IsRepo() bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Revert undoes the last N commits with `git revert --no-edit HEAD~N..HEAD`.
|
||||
// n must be >= 1.
|
||||
func (r *Repo) Revert(n int) error {
|
||||
_, err := r.run("revert", "--no-edit", fmt.Sprintf("HEAD~%d..HEAD", n-1))
|
||||
if n < 1 {
|
||||
return fmt.Errorf("revert: n must be >= 1, got %d", n)
|
||||
}
|
||||
_, err := r.run("revert", "--no-edit", fmt.Sprintf("HEAD~%d..HEAD", n))
|
||||
return err
|
||||
}
|
||||
|
||||
+11
-2
@@ -44,19 +44,28 @@ func MultiSelect(title string, choices []string, opts ...Option) ([]string, erro
|
||||
}
|
||||
return o.nonInteractive, nil
|
||||
}
|
||||
var picked []string
|
||||
// Pre-select every choice. Users deselect what they don't want; hitting
|
||||
// enter without toggling thus accepts all (the common case) instead of
|
||||
// silently producing an empty result.
|
||||
picked := make([]string, len(choices))
|
||||
copy(picked, choices)
|
||||
|
||||
opts2 := make([]huh.Option[string], 0, len(choices))
|
||||
for _, c := range choices {
|
||||
opts2 = append(opts2, huh.NewOption(c, c))
|
||||
opts2 = append(opts2, huh.NewOption(c, c).Selected(true))
|
||||
}
|
||||
form := huh.NewForm(huh.NewGroup(
|
||||
huh.NewMultiSelect[string]().
|
||||
Title(title).
|
||||
Description("space to toggle · enter to confirm · all pre-selected").
|
||||
Options(opts2...).
|
||||
Value(&picked),
|
||||
))
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(picked) == 0 {
|
||||
return nil, fmt.Errorf("no items selected")
|
||||
}
|
||||
return picked, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user