adding config list command with renderer (#6741)

* adding list command with renderer

* showing empty strings for empty values
This commit is contained in:
Toshii
2025-10-09 19:23:03 -07:00
committed by GitHub
parent e65590c9a0
commit 1704df14e1
3 changed files with 272 additions and 1 deletions
+52 -1
View File
@@ -1,6 +1,7 @@
package cli
import (
"context"
"fmt"
"github.com/cline/cli/pkg/cli/config"
@@ -9,6 +10,45 @@ import (
"github.com/spf13/cobra"
)
var configManager *config.Manager
func ensureConfigManager(ctx context.Context, address string) error {
if configManager == nil || (address != "" && configManager.GetCurrentInstance() != address) {
var err error
var instanceAddress string
if address != "" {
// Ensure instance exists at the specified address
if err := ensureInstanceAtAddress(ctx, address); err != nil {
return fmt.Errorf("failed to ensure instance at address %s: %w", address, err)
}
configManager, err = config.NewManager(ctx, address)
instanceAddress = address
} else {
// Ensure default instance exists
if err := global.EnsureDefaultInstance(ctx); err != nil {
return fmt.Errorf("failed to ensure default instance: %w", err)
}
configManager, err = config.NewManager(ctx, "")
if err == nil {
instanceAddress = configManager.GetCurrentInstance()
}
}
if err != nil {
return fmt.Errorf("failed to create config manager: %w", err)
}
// Always set the instance we're using as the default
registry := global.Clients.GetRegistry()
if err := registry.SetDefaultInstance(instanceAddress); err != nil {
// Log warning but don't fail - this is not critical
fmt.Printf("Warning: failed to set default instance: %v\n", err)
}
}
return nil
}
func NewConfigCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "config",
@@ -42,16 +82,27 @@ func newConfigGetCommand() *cobra.Command {
}
func newConfigListCommand() *cobra.Command {
var address string
cmd := &cobra.Command{
Use: "list",
Aliases: []string{"l"},
Short: "List all configuration settings",
Long: `List all configuration settings from the Cline instance.`,
RunE: func(cmd *cobra.Command, args []string) error {
return nil
ctx := cmd.Context()
// Ensure config manager
if err := ensureConfigManager(ctx, address); err != nil {
return err
}
// List settings
return configManager.ListSettings(ctx)
},
}
cmd.Flags().StringVar(&address, "address", "", "specific Cline instance address to use")
return cmd
}
+67
View File
@@ -2,6 +2,7 @@ package config
import (
"context"
"encoding/json"
"fmt"
"github.com/cline/cli/pkg/cli/global"
@@ -40,6 +41,11 @@ func NewManager(ctx context.Context, address string) (*Manager, error) {
}, nil
}
// GetCurrentInstance returns the address of the current instance
func (m *Manager) GetCurrentInstance() string {
return m.clientAddress
}
func (m *Manager) UpdateSettings(ctx context.Context, settings *cline.Settings, secrets *cline.Secrets) error {
request := &cline.UpdateSettingsRequestCli{
Metadata: &cline.Metadata{},
@@ -57,3 +63,64 @@ func (m *Manager) UpdateSettings(ctx context.Context, settings *cline.Settings,
fmt.Printf("Instance: %s\n", m.clientAddress)
return nil
}
func (m *Manager) GetState(ctx context.Context) (map[string]interface{}, error) {
state, err := m.client.State.GetLatestState(ctx, &cline.EmptyRequest{})
if err != nil {
return nil, fmt.Errorf("failed to get state: %w", err)
}
var stateData map[string]interface{}
if err := json.Unmarshal([]byte(state.StateJson), &stateData); err != nil {
return nil, fmt.Errorf("failed to parse state: %w", err)
}
return stateData, nil
}
func (m *Manager) ListSettings(ctx context.Context) error {
// Get state
stateData, err := m.GetState(ctx)
if err != nil {
return err
}
// Subset of fields we will print the values for
settingsFields := []string{
"apiConfiguration",
"telemetrySetting",
"planActSeparateModelsSetting",
"enableCheckpointsSetting",
"mcpMarketplaceEnabled",
"shellIntegrationTimeout",
"terminalReuseEnabled",
"mcpResponsesCollapsed",
"mcpDisplayMode",
"terminalOutputLineLimit",
"mode",
"preferredLanguage",
"openaiReasoningEffort",
"strictPlanModeEnabled",
"focusChainSettings",
"useAutoCondense",
"customPrompt",
"browserSettings",
"defaultTerminalProfile",
"yoloModeToggled",
"dictationSettings",
"autoCondenseThreshold",
"autoApprovalSettings",
}
// Render each field using the renderer
for _, field := range settingsFields {
if value, ok := stateData[field]; ok {
if err := RenderField(field, value); err != nil {
fmt.Printf("Error rendering %s: %v\n", field, err)
}
fmt.Println()
}
}
return nil
}
+153
View File
@@ -0,0 +1,153 @@
package config
import (
"fmt"
)
// formatValue formats a value for display, handling empty strings
func formatValue(val interface{}) string {
// Handle empty strings specifically
if str, ok := val.(string); ok && str == "" {
return "''"
}
return fmt.Sprintf("%v", val)
}
// RenderField renders a single config field with proper formatting
func RenderField(key string, value interface{}) error {
switch key {
// Nested objects - render with header + nested fields
case "apiConfiguration":
return renderApiConfiguration(value)
case "browserSettings":
return renderBrowserSettings(value)
case "focusChainSettings":
return renderFocusChainSettings(value)
case "dictationSettings":
return renderDictationSettings(value)
case "autoApprovalSettings":
return renderAutoApprovalSettings(value)
// Simple values - just print key: value
case "mode", "telemetrySetting", "preferredLanguage", "customPrompt",
"defaultTerminalProfile", "mcpDisplayMode", "openaiReasoningEffort",
"planActSeparateModelsSetting", "enableCheckpointsSetting",
"mcpMarketplaceEnabled", "terminalReuseEnabled",
"mcpResponsesCollapsed", "strictPlanModeEnabled",
"useAutoCondense", "yoloModeToggled", "shellIntegrationTimeout",
"terminalOutputLineLimit", "autoCondenseThreshold":
fmt.Printf("%s: %s\n", key, formatValue(value))
return nil
default:
return fmt.Errorf("unknown config field: %s", key)
}
}
// renderApiConfiguration renders the API configuration object
func renderApiConfiguration(value interface{}) error {
fmt.Println("apiConfiguration:")
configMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid apiConfiguration format")
}
// Print each field directly
for key, val := range configMap {
fmt.Printf(" %s: %s\n", key, formatValue(val))
}
return nil
}
// renderBrowserSettings renders browser settings
func renderBrowserSettings(value interface{}) error {
fmt.Println("browserSettings:")
settingsMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid browserSettings format")
}
// Handle nested viewport if present
if viewport, ok := settingsMap["viewport"].(map[string]interface{}); ok {
fmt.Println(" viewport:")
for key, val := range viewport {
fmt.Printf(" %s: %s\n", key, formatValue(val))
}
}
// Print other fields
for key, val := range settingsMap {
if key != "viewport" {
fmt.Printf(" %s: %s\n", key, formatValue(val))
}
}
return nil
}
// renderFocusChainSettings renders focus chain settings
func renderFocusChainSettings(value interface{}) error {
fmt.Println("focusChainSettings:")
settingsMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid focusChainSettings format")
}
for key, val := range settingsMap {
fmt.Printf(" %s: %s\n", key, formatValue(val))
}
return nil
}
// renderDictationSettings renders dictation settings
func renderDictationSettings(value interface{}) error {
fmt.Println("dictationSettings:")
settingsMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid dictationSettings format")
}
for key, val := range settingsMap {
fmt.Printf(" %s: %s\n", key, formatValue(val))
}
return nil
}
// renderAutoApprovalSettings renders auto approval settings
func renderAutoApprovalSettings(value interface{}) error {
fmt.Println("autoApprovalSettings:")
settingsMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid autoApprovalSettings format")
}
// Print top-level fields (skip version, handle actions specially)
for key, val := range settingsMap {
if key == "version" {
continue // Skip version
}
if key == "actions" {
// Handle nested actions with double indentation
fmt.Println(" actions:")
if actionsMap, ok := val.(map[string]interface{}); ok {
for actionKey, actionVal := range actionsMap {
fmt.Printf(" %s: %s\n", actionKey, formatValue(actionVal))
}
}
} else {
// Print other fields normally (enabled, maxRequests, enableNotifications, favorites)
fmt.Printf(" %s: %s\n", key, formatValue(val))
}
}
return nil
}