Add config agent commands and config-cli docs
- config agent defaults: get [key] / set key value for agents.defaults - config agent list: list agents in agents.list - config agent add/remove/update: manage agents by id with optional flags - docs/config-cli.md: English usage for model_list and agent CLI Made-with: Cursor
This commit is contained in:
parent
59e196e3ee
commit
5b637adaec
9 changed files with 729 additions and 1 deletions
83
cmd/picoclaw/internal/configcmd/agent_add.go
Normal file
83
cmd/picoclaw/internal/configcmd/agent_add.go
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAgentAddCommand() *cobra.Command {
|
||||||
|
var (
|
||||||
|
name string
|
||||||
|
model string
|
||||||
|
workspace string
|
||||||
|
defaultAgent bool
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "add <id>",
|
||||||
|
Short: "Add an agent to agents.list",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
return runAgentAdd(args[0], name, model, workspace, defaultAgent)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVar(&name, "name", "", "Display name")
|
||||||
|
cmd.Flags().StringVar(&model, "model", "", "Model name (from model_list)")
|
||||||
|
cmd.Flags().StringVar(&workspace, "workspace", "", "Workspace path")
|
||||||
|
cmd.Flags().BoolVar(&defaultAgent, "default", false, "Set as default agent")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgentAdd(id, name, model, workspace string, defaultAgent bool) error {
|
||||||
|
cfg, err := internal.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("config not found; run: picoclaw onboard")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive prompt when TTY and missing fields
|
||||||
|
if IsTTY() {
|
||||||
|
if name == "" {
|
||||||
|
name, _ = Prompt("Name (display): ")
|
||||||
|
}
|
||||||
|
if model == "" {
|
||||||
|
model, _ = Prompt("Model (model_name from model_list): ")
|
||||||
|
}
|
||||||
|
if workspace == "" {
|
||||||
|
workspace, _ = Prompt("Workspace: ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := config.AgentConfig{
|
||||||
|
ID: id,
|
||||||
|
Default: defaultAgent,
|
||||||
|
Name: name,
|
||||||
|
Workspace: workspace,
|
||||||
|
}
|
||||||
|
if model != "" {
|
||||||
|
entry.Model = &config.AgentModelConfig{Primary: model}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Agents.List = append(cfg.Agents.List, entry)
|
||||||
|
|
||||||
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
|
return fmt.Errorf("saving config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Added agent %q to agents.list.\n", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
18
cmd/picoclaw/internal/configcmd/agent_defaults.go
Normal file
18
cmd/picoclaw/internal/configcmd/agent_defaults.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAgentDefaultsCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "defaults",
|
||||||
|
Short: "Get or set agents.defaults",
|
||||||
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
|
return cmd.Help()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.AddCommand(newAgentDefaultsGetCommand())
|
||||||
|
cmd.AddCommand(newAgentDefaultsSetCommand())
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
104
cmd/picoclaw/internal/configcmd/agent_defaults_get.go
Normal file
104
cmd/picoclaw/internal/configcmd/agent_defaults_get.go
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// agentDefaultsKeys in display order for get (all).
|
||||||
|
var agentDefaultsKeys = []string{
|
||||||
|
"workspace", "restrict_to_workspace", "provider", "model_name", "model",
|
||||||
|
"model_fallbacks", "image_model", "image_model_fallbacks",
|
||||||
|
"max_tokens", "temperature", "max_tool_iterations",
|
||||||
|
}
|
||||||
|
|
||||||
|
var agentDefaultsKeySet map[string]bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
agentDefaultsKeySet = make(map[string]bool, len(agentDefaultsKeys))
|
||||||
|
for _, k := range agentDefaultsKeys {
|
||||||
|
agentDefaultsKeySet[k] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAgentDefaultsGetCommand() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "get [key]",
|
||||||
|
Short: "Get agents.defaults (all fields or one key)",
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
|
RunE: runAgentDefaultsGet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgentDefaultsGet(_ *cobra.Command, args []string) error {
|
||||||
|
cfg, err := internal.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("config not found; run: picoclaw onboard")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &cfg.Agents.Defaults
|
||||||
|
|
||||||
|
if len(args) == 0 {
|
||||||
|
for _, key := range agentDefaultsKeys {
|
||||||
|
value := agentDefaultsGetValue(d, key)
|
||||||
|
fmt.Printf("%s: %s\n", key, value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
key := args[0]
|
||||||
|
if !agentDefaultsKeySet[key] {
|
||||||
|
return fmt.Errorf("invalid key %q; allowed: %s", key, strings.Join(agentDefaultsKeys, ", "))
|
||||||
|
}
|
||||||
|
fmt.Println(agentDefaultsGetValue(d, key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func agentDefaultsGetValue(d *config.AgentDefaults, key string) string {
|
||||||
|
switch key {
|
||||||
|
case "workspace":
|
||||||
|
return d.Workspace
|
||||||
|
case "restrict_to_workspace":
|
||||||
|
if d.RestrictToWorkspace {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
return "false"
|
||||||
|
case "provider":
|
||||||
|
return d.Provider
|
||||||
|
case "model_name":
|
||||||
|
return d.ModelName
|
||||||
|
case "model":
|
||||||
|
return d.Model
|
||||||
|
case "model_fallbacks":
|
||||||
|
return strings.Join(d.ModelFallbacks, ",")
|
||||||
|
case "image_model":
|
||||||
|
return d.ImageModel
|
||||||
|
case "image_model_fallbacks":
|
||||||
|
return strings.Join(d.ImageModelFallbacks, ",")
|
||||||
|
case "max_tokens":
|
||||||
|
return fmt.Sprintf("%d", d.MaxTokens)
|
||||||
|
case "temperature":
|
||||||
|
if d.Temperature == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%g", *d.Temperature)
|
||||||
|
case "max_tool_iterations":
|
||||||
|
return fmt.Sprintf("%d", d.MaxToolIterations)
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
120
cmd/picoclaw/internal/configcmd/agent_defaults_set.go
Normal file
120
cmd/picoclaw/internal/configcmd/agent_defaults_set.go
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAgentDefaultsSetCommand() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "set <key> <value>",
|
||||||
|
Short: "Set a single field in agents.defaults",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
RunE: runAgentDefaultsSet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgentDefaultsSet(_ *cobra.Command, args []string) error {
|
||||||
|
cfg, err := internal.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("config not found; run: picoclaw onboard")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, value := args[0], args[1]
|
||||||
|
if !agentDefaultsKeySet[key] {
|
||||||
|
return fmt.Errorf("invalid key %q; allowed: %s", key, strings.Join(agentDefaultsKeys, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &cfg.Agents.Defaults
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "workspace":
|
||||||
|
d.Workspace = value
|
||||||
|
case "restrict_to_workspace":
|
||||||
|
b, err := parseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("restrict_to_workspace: %w", err)
|
||||||
|
}
|
||||||
|
d.RestrictToWorkspace = b
|
||||||
|
case "provider":
|
||||||
|
d.Provider = value
|
||||||
|
case "model_name":
|
||||||
|
d.ModelName = value
|
||||||
|
case "model":
|
||||||
|
d.Model = value
|
||||||
|
case "model_fallbacks":
|
||||||
|
if value == "" {
|
||||||
|
d.ModelFallbacks = nil
|
||||||
|
} else {
|
||||||
|
d.ModelFallbacks = strings.Split(value, ",")
|
||||||
|
for i := range d.ModelFallbacks {
|
||||||
|
d.ModelFallbacks[i] = strings.TrimSpace(d.ModelFallbacks[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "image_model":
|
||||||
|
d.ImageModel = value
|
||||||
|
case "image_model_fallbacks":
|
||||||
|
if value == "" {
|
||||||
|
d.ImageModelFallbacks = nil
|
||||||
|
} else {
|
||||||
|
d.ImageModelFallbacks = strings.Split(value, ",")
|
||||||
|
for i := range d.ImageModelFallbacks {
|
||||||
|
d.ImageModelFallbacks[i] = strings.TrimSpace(d.ImageModelFallbacks[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "max_tokens":
|
||||||
|
n, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("max_tokens: %w", err)
|
||||||
|
}
|
||||||
|
d.MaxTokens = n
|
||||||
|
case "temperature":
|
||||||
|
if value == "" {
|
||||||
|
d.Temperature = nil
|
||||||
|
} else {
|
||||||
|
f, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("temperature: %w", err)
|
||||||
|
}
|
||||||
|
d.Temperature = &f
|
||||||
|
}
|
||||||
|
case "max_tool_iterations":
|
||||||
|
n, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("max_tool_iterations: %w", err)
|
||||||
|
}
|
||||||
|
d.MaxToolIterations = n
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
|
return fmt.Errorf("saving config: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Set agents.defaults %s.\n", key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBool(s string) (bool, error) {
|
||||||
|
switch strings.ToLower(s) {
|
||||||
|
case "true", "1", "yes":
|
||||||
|
return true, nil
|
||||||
|
case "false", "0", "no":
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("expected true/false, got %q", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
69
cmd/picoclaw/internal/configcmd/agent_list.go
Normal file
69
cmd/picoclaw/internal/configcmd/agent_list.go
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAgentListCommand() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List all agents in agents.list",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
RunE: runAgentList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgentList(_ *cobra.Command, _ []string) error {
|
||||||
|
cfg, err := internal.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
fmt.Println("No config file found. Run: picoclaw onboard")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.Agents.List) == 0 {
|
||||||
|
fmt.Println("agents.list is empty.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%-20s %-25s %-20s %s\n", "ID", "NAME", "MODEL", "WORKSPACE")
|
||||||
|
fmt.Println(strings.Repeat("-", 85))
|
||||||
|
|
||||||
|
for _, a := range cfg.Agents.List {
|
||||||
|
model := ""
|
||||||
|
if a.Model != nil && a.Model.Primary != "" {
|
||||||
|
model = a.Model.Primary
|
||||||
|
}
|
||||||
|
if len(model) > 18 {
|
||||||
|
model = model[:15] + "..."
|
||||||
|
}
|
||||||
|
name := a.Name
|
||||||
|
if len(name) > 23 {
|
||||||
|
name = name[:20] + "..."
|
||||||
|
}
|
||||||
|
id := a.ID
|
||||||
|
if len(id) > 18 {
|
||||||
|
id = id[:15] + "..."
|
||||||
|
}
|
||||||
|
ws := a.Workspace
|
||||||
|
if len(ws) > 35 {
|
||||||
|
ws = ws[:32] + "..."
|
||||||
|
}
|
||||||
|
fmt.Printf("%-20s %-25s %-20s %s\n", id, name, model, ws)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
56
cmd/picoclaw/internal/configcmd/agent_remove.go
Normal file
56
cmd/picoclaw/internal/configcmd/agent_remove.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAgentRemoveCommand() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "remove <id>",
|
||||||
|
Short: "Remove an agent from agents.list by id",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
return runAgentRemove(args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgentRemove(id string) error {
|
||||||
|
cfg, err := internal.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("config not found; run: picoclaw onboard")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var kept []config.AgentConfig
|
||||||
|
for _, a := range cfg.Agents.List {
|
||||||
|
if a.ID != id {
|
||||||
|
kept = append(kept, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(kept) == len(cfg.Agents.List) {
|
||||||
|
return fmt.Errorf("no agent with id %q", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Agents.List = kept
|
||||||
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
|
return fmt.Errorf("saving config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Removed agent %q from agents.list.\n", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
87
cmd/picoclaw/internal/configcmd/agent_update.go
Normal file
87
cmd/picoclaw/internal/configcmd/agent_update.go
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
package configcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAgentUpdateCommand() *cobra.Command {
|
||||||
|
var (
|
||||||
|
name string
|
||||||
|
model string
|
||||||
|
workspace string
|
||||||
|
defaultAgent bool
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "update <id>",
|
||||||
|
Short: "Update the first matching agent in agents.list",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
return runAgentUpdate(args[0], name, model, workspace, defaultAgent)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVar(&name, "name", "", "Display name")
|
||||||
|
cmd.Flags().StringVar(&model, "model", "", "Model name (from model_list)")
|
||||||
|
cmd.Flags().StringVar(&workspace, "workspace", "", "Workspace path")
|
||||||
|
cmd.Flags().BoolVar(&defaultAgent, "default", false, "Set as default agent")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgentUpdate(id, name, model, workspace string, defaultAgent bool) error {
|
||||||
|
cfg, err := internal.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("config not found; run: picoclaw onboard")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := -1
|
||||||
|
for i := range cfg.Agents.List {
|
||||||
|
if cfg.Agents.List[i].ID == id {
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if idx < 0 {
|
||||||
|
return fmt.Errorf("no agent with id %q", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := &cfg.Agents.List[idx]
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
entry.Name = name
|
||||||
|
}
|
||||||
|
if model != "" {
|
||||||
|
if entry.Model == nil {
|
||||||
|
entry.Model = &config.AgentModelConfig{}
|
||||||
|
}
|
||||||
|
entry.Model.Primary = model
|
||||||
|
}
|
||||||
|
if workspace != "" {
|
||||||
|
entry.Workspace = workspace
|
||||||
|
}
|
||||||
|
if defaultAgent {
|
||||||
|
entry.Default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
|
return fmt.Errorf("saving config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Updated agent %q in agents.list.\n", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -7,13 +7,14 @@ import (
|
||||||
func NewConfigCommand() *cobra.Command {
|
func NewConfigCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "config",
|
Use: "config",
|
||||||
Short: "Manage configuration (model_list)",
|
Short: "Manage configuration (model_list, agents)",
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
return cmd.Help()
|
return cmd.Help()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(newModelListCommand())
|
cmd.AddCommand(newModelListCommand())
|
||||||
|
cmd.AddCommand(newAgentCommand())
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,3 +37,20 @@ func newModelListCommand() *cobra.Command {
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newAgentCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "agent",
|
||||||
|
Short: "Manage agents (defaults, list, add, remove, update)",
|
||||||
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
|
return cmd.Help()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(newAgentDefaultsCommand())
|
||||||
|
cmd.AddCommand(newAgentListCommand())
|
||||||
|
cmd.AddCommand(newAgentAddCommand())
|
||||||
|
cmd.AddCommand(newAgentRemoveCommand())
|
||||||
|
cmd.AddCommand(newAgentUpdateCommand())
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
|
||||||
173
docs/config-cli.md
Normal file
173
docs/config-cli.md
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
# Config CLI Usage
|
||||||
|
|
||||||
|
Use `picoclaw config` to manage `model_list` and `agents` without editing `config.json` directly.
|
||||||
|
|
||||||
|
Default config path: `~/.picoclaw/config.json`. If the file does not exist, some commands will prompt you to run `picoclaw onboard` first.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. model_list
|
||||||
|
|
||||||
|
### list — List all models
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list list
|
||||||
|
```
|
||||||
|
|
||||||
|
Prints the current `model_list` in a table (MODEL_NAME, MODEL, API_BASE, AUTH, etc.). Shows a message when the list is empty or the config file is missing.
|
||||||
|
|
||||||
|
### get — Inspect a single model’s config
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show all fields for that model (sensitive fields are masked)
|
||||||
|
picoclaw config model_list get <model_name>
|
||||||
|
|
||||||
|
# Show only one key’s value (for scripting; not masked)
|
||||||
|
picoclaw config model_list get <model_name> <key>
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported keys (snake_case, matching JSON):
|
||||||
|
`model_name`, `model`, `api_base`, `api_key`, `proxy`, `auth_method`, `connect_mode`, `workspace`, `token_url`, `client_id`, `client_secret`, `max_tokens_field`, `rpm`.
|
||||||
|
|
||||||
|
### set — Set a single field
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list set <model_name> <key> <value>
|
||||||
|
```
|
||||||
|
|
||||||
|
Sets the given key for the **first** entry whose `model_name` matches, then writes the config.
|
||||||
|
`rpm` is an integer; all other keys are strings. The key must be one of the supported keys above.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list set gpt4 api_base https://api.openai.com/v1
|
||||||
|
picoclaw config model_list set gpt4 rpm 60
|
||||||
|
```
|
||||||
|
|
||||||
|
### add — Add a model
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Specify everything with flags
|
||||||
|
picoclaw config model_list add --model-name qwen-turbo --model litellm/qwen-turbo \
|
||||||
|
--api-base https://litellm.example.com \
|
||||||
|
--token-url https://keycloak.example.com/realms/xxx/protocol/openid-connect/token \
|
||||||
|
--client-id ai-bot --client-secret xxx
|
||||||
|
|
||||||
|
# Interactive: pass model_name only or omit; in a TTY you’ll be prompted for the rest
|
||||||
|
picoclaw config model_list add qwen-turbo
|
||||||
|
picoclaw config model_list add
|
||||||
|
```
|
||||||
|
|
||||||
|
Common flags:
|
||||||
|
`--model-name`, `--model`, `--api-base`, `--api-key`, `--proxy`, `--auth-method`, `--max-tokens-field`, `--token-url`, `--client-id`, `--client-secret`.
|
||||||
|
For `litellm/...` protocol you must provide `api_base`, `token_url`, `client_id`, and `client_secret`; in a TTY, missing values are prompted interactively.
|
||||||
|
|
||||||
|
### remove — Remove model(s)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list remove <model_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Removes entries by `model_name`. If there are multiple entries with the same name (e.g. round-robin), all are removed by default. Use `--first` to remove only the first match:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list remove loadbalanced-gpt4 --first
|
||||||
|
```
|
||||||
|
|
||||||
|
### update — Update a model
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list update <model_name> [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
Updates the **first** entry matching `model_name`; only the fields corresponding to the given flags are changed.
|
||||||
|
Flags are the same as for add: `--model`, `--api-base`, `--api-key`, `--token-url`, `--client-id`, `--client-secret`, `--proxy`, `--auth-method`, `--max-tokens-field`, etc.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config model_list update qwen-turbo --api-base https://new.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. agent
|
||||||
|
|
||||||
|
### defaults — Global defaults (agents.defaults)
|
||||||
|
|
||||||
|
**get**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Print all agents.defaults fields
|
||||||
|
picoclaw config agent defaults get
|
||||||
|
|
||||||
|
# Print a single key
|
||||||
|
picoclaw config agent defaults get model_name
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported keys:
|
||||||
|
`workspace`, `restrict_to_workspace`, `provider`, `model_name`, `model`, `model_fallbacks`, `image_model`, `image_model_fallbacks`, `max_tokens`, `temperature`, `max_tool_iterations`.
|
||||||
|
|
||||||
|
**set**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config agent defaults set <key> <value>
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config agent defaults set model_name gpt4
|
||||||
|
picoclaw config agent defaults set max_tokens 8192
|
||||||
|
picoclaw config agent defaults set restrict_to_workspace true
|
||||||
|
picoclaw config agent defaults set model_fallbacks "gpt4,claude"
|
||||||
|
```
|
||||||
|
|
||||||
|
`restrict_to_workspace` is true/false; `max_tokens` and `max_tool_iterations` are integers; `temperature` is a float; `model_fallbacks` and `image_model_fallbacks` are comma-separated strings.
|
||||||
|
|
||||||
|
### list — List all agents
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config agent list
|
||||||
|
```
|
||||||
|
|
||||||
|
Prints `agents.list` in a table (ID, NAME, MODEL, WORKSPACE). Shows a message when the list is empty.
|
||||||
|
|
||||||
|
### add — Add an agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config agent add <id> [--name "Display name"] [--model gpt4] [--workspace ~/ws] [--default]
|
||||||
|
```
|
||||||
|
|
||||||
|
`id` is required; `--name`, `--model`, and `--workspace` are optional. In a TTY, missing values are prompted. `--default` sets this agent as the default.
|
||||||
|
|
||||||
|
### remove — Remove an agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config agent remove <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Removes the agent with the given id from `agents.list` and saves the config.
|
||||||
|
|
||||||
|
### update — Update an agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
picoclaw config agent update <id> [--name "New name"] [--model gpt4] [--workspace ~/ws] [--default]
|
||||||
|
```
|
||||||
|
|
||||||
|
Only the provided fields are updated; others are left unchanged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Quick reference
|
||||||
|
|
||||||
|
| Purpose | Command |
|
||||||
|
|----------------------|--------|
|
||||||
|
| List models | `picoclaw config model_list list` |
|
||||||
|
| Get/set one model | `picoclaw config model_list get/set <model_name> [key] [value]` |
|
||||||
|
| Add/remove/update models | `picoclaw config model_list add/remove/update ...` |
|
||||||
|
| Agent defaults | `picoclaw config agent defaults get [key]` / `set <key> <value>` |
|
||||||
|
| List/add/remove/update agents | `picoclaw config agent list/add/remove/update ...` |
|
||||||
|
|
||||||
|
For more options and descriptions, run `picoclaw config --help`, `picoclaw config model_list --help`, and `picoclaw config agent --help`.
|
||||||
Loading…
Add table
Reference in a new issue