cmd

package
v1.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 15, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

README

Centralized Command & Help Management System

This package provides a unified, context-aware command registry and auto-generated help system for Stapler Squad. It replaces the scattered keybinding definitions with a centralized, maintainable approach.

Architecture Overview

The system consists of several key components:

  1. Command Registry (registry.go) - Central storage for all commands and keybindings
  2. Contexts (contexts.go) - Define different application modes/states
  3. Categories (categories.go) - Organize commands for help display
  4. Commands (commands/) - Individual command implementations
  5. Help Generator (help/generator.go) - Auto-generate help content
  6. State Manager (state/manager.go) - Manage modal contexts and command routing
  7. Migration Bridge (migration.go) - Compatibility layer with existing code

Key Benefits

  • Single Source of Truth: All keybindings and help text in one place
  • Context Awareness: Different keybindings per mode with inheritance
  • Auto-Generated Help: Help screens and status lines generated automatically
  • Conflict Detection: Validate no duplicate keys within contexts
  • Easy Migration: Built-in deprecation and migration path support
  • Type Safety: Strongly typed command IDs and contexts

Quick Integration Example

Here's how to integrate the new system with existing code:

package main

import (
    "stapler-squad/cmd"
    "stapler-squad/cmd/commands" 
    "stapler-squad/cmd/interfaces"
    tea "github.com/charmbracelet/bubbletea"
)

func main() {
    // Get the migration bridge
    bridge := cmd.GetGlobalBridge()
    
    // Configure command handlers
    bridge.Initialize(
        &commands.SessionHandlers{
            OnNewSession: func() (tea.Model, tea.Cmd) {
                // Your existing new session logic
                return yourModel, yourCmd
            },
            OnAttachSession: func() (tea.Model, tea.Cmd) {
                // Your existing attach logic  
                return yourModel, yourCmd
            },
            // ... other handlers
        },
        &commands.GitHandlers{
            OnGitStatus: func() (tea.Model, tea.Cmd) {
                // Show git status overlay
                return yourGitModel, yourGitCmd
            },
            // ... other git handlers
        },
        &commands.NavigationHandlers{
            OnUp: func() (tea.Model, tea.Cmd) {
                // Handle up navigation
                return yourModel, yourCmd
            },
            // ... other navigation handlers
        },
        &commands.OrganizationHandlers{
            OnFilterPaused: func() (tea.Model, tea.Cmd) {
                // Handle filter toggle
                return yourModel, yourCmd
            },
            // ... other organization handlers
        },
        &commands.SystemHandlers{
            OnHelp: func() (tea.Model, tea.Cmd) {
                // Show auto-generated help
                helpContent := bridge.GetContextualHelp()
                return showHelpOverlay(helpContent), nil
            },
            OnQuit: func() (tea.Model, tea.Cmd) {
                return yourModel, tea.Quit
            },
            // ... other system handlers
        },
    )
}

Using in Your Bubble Tea Model

Handle Key Presses

Replace your existing key handling with:

func (m YourModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        bridge := cmd.GetGlobalBridge()
        
        // Try to handle with new command system
        model, teaCmd, err := bridge.HandleKeyString(msg.String())
        if err == nil && model != nil {
            return model, teaCmd
        }
        
        // Fall back to legacy handling if needed
        // ... existing key handling code
    }
    return m, nil
}
Context Management

Switch between different contexts as the user navigates:

// Enter git status mode
bridge := cmd.GetGlobalBridge()
bridge.PushContext(interfaces.ContextGitStatus)

// Exit git status mode  
bridge.PopContext()

// Switch to list context
bridge.SetContext(interfaces.ContextList)
Auto-Generated Status Line

Replace hardcoded status lines with:

func (m YourModel) View() string {
    bridge := cmd.GetGlobalBridge()
    
    // Generate status line for current context
    statusLine := bridge.GetLegacyStatusLine()
    
    return fmt.Sprintf("%s\\n%s", yourContent, statusLine)
}
Auto-Generated Help

Replace manual help screens with:

func showHelpScreen() string {
    bridge := cmd.GetGlobalBridge()
    return bridge.GetContextualHelp()
}

Available Contexts

  • ContextGlobal - Commands available everywhere
  • ContextList - Session list view
  • ContextGitStatus - Git status interface (fugitive-style)
  • ContextHelp - Help screen
  • ContextPrompt - Text input
  • ContextSearch - Search mode
  • ContextConfirm - Confirmation dialogs

Adding New Commands

To add a new command:

  1. Create the handler function:
func MyNewCommand(ctx *interfaces.CommandContext) error {
    // Your command logic here
    return nil
}
  1. Register the command:
registry := cmd.GetGlobalRegistry()
registry.Register(&cmd.Command{
    ID:          "my.new_command",
    Name:        "My New Command", 
    Description: "Does something useful",
    Category:    cmd.CategorySession,
    Handler:     MyNewCommand,
    Contexts:    []cmd.ContextID{cmd.ContextList},
}).BindKey("x")

Migration Strategy

The system provides full backward compatibility:

  1. Legacy Key Mapping: Old keys.KeyName constants are automatically mapped
  2. Gradual Migration: Can coexist with existing key handling
  3. Deprecation Support: Mark old commands as deprecated with alternatives
  4. Conflict Detection: Validates no duplicate keybindings

Validation

The system includes built-in validation:

bridge := cmd.GetGlobalBridge()
issues := bridge.ValidateSetup()
for _, issue := range issues {
    log.Warn("Command system issue: %s", issue)
}

File Organization

cmd/
├── registry.go          # Core registry system
├── contexts.go          # Context definitions  
├── categories.go        # Command categories
├── init.go              # Command registration
├── migration.go         # Legacy compatibility
├── interfaces/          # Type definitions
│   ├── types.go        # Basic types
│   └── registry.go     # Registry interfaces
├── commands/            # Command implementations
│   ├── session.go      # Session management
│   ├── git.go          # Git integration
│   ├── navigation.go   # Navigation commands
│   ├── organization.go # Filtering/organization  
│   └── system.go       # System commands
├── help/               # Auto-generated help
│   └── generator.go    # Help content generation
├── state/              # State management
│   └── manager.go      # Modal context management
└── README.md           # This documentation

This architecture eliminates the scattered keybinding definitions and provides a single, maintainable system for managing all commands and help text.

Documentation

Index

Constants

View Source
const (
	CategoryView = interfaces.CategoryView
	CategoryPTY  = interfaces.CategoryPTY
)

Import category constants from interfaces

Variables

CategoryOrder defines the display order for help screens

Functions

func CheckCommandPermission

func CheckCommandPermission(cmdID CommandID, perms session.InstancePermissions) bool

CheckCommandPermission checks if the given permissions allow executing the command

func InitializeCommands

func InitializeCommands(registry *CommandRegistry) error

InitializeCommands sets up all standard commands in the registry

func InitializeContexts

func InitializeContexts(registry *CommandRegistry) error

InitializeContexts sets up the context hierarchy

Types

type Bridge

type Bridge struct {
	// contains filtered or unexported fields
}

Bridge provides compatibility between old and new command systems

func GetGlobalBridge

func GetGlobalBridge() *Bridge

GetGlobalBridge returns the global bridge instance

func NewBridge

func NewBridge() *Bridge

NewBridge creates a new migration bridge

func (*Bridge) DetectKeyConflicts

func (b *Bridge) DetectKeyConflicts() []string

DetectKeyConflicts checks for duplicate key bindings within the current context

func (*Bridge) GetAvailableKeys

func (b *Bridge) GetAvailableKeys() map[string]string

GetAvailableKeys returns all keys available in the current context

func (*Bridge) GetAvailableKeysForInstance

func (b *Bridge) GetAvailableKeysForInstance(instance interfaces.Instance) map[string]string

GetAvailableKeysForInstance returns keys available based on instance permissions This filters commands to only show what the user is allowed to execute for the given instance

func (*Bridge) GetCommandForKey

func (b *Bridge) GetCommandForKey(key string) *Command

GetCommandForKey returns the command bound to a key

func (*Bridge) GetContextualHelp

func (b *Bridge) GetContextualHelp() string

GetContextualHelp generates help for current context

func (*Bridge) GetCurrentContext

func (b *Bridge) GetCurrentContext() ContextID

GetCurrentContext returns the current context

func (*Bridge) GetKeyCategories

func (b *Bridge) GetKeyCategories() map[string][]string

GetKeyCategories returns keys organized by category for dynamic help generation Uses per-context caching to avoid expensive registry lookups on every help display

func (*Bridge) GetLegacyStatusLine

func (b *Bridge) GetLegacyStatusLine() string

GetLegacyStatusLine generates status line compatible with old menu system

func (*Bridge) GetRegistry

func (b *Bridge) GetRegistry() *CommandRegistry

GetRegistry returns the command registry

func (*Bridge) HandleKeyString

func (b *Bridge) HandleKeyString(key string) (tea.Model, tea.Cmd, error)

HandleKeyString processes a key string through the new command system

func (*Bridge) HandleLegacyKey

func (b *Bridge) HandleLegacyKey(keyName interface{}) (tea.Model, tea.Cmd, error)

HandleLegacyKey is disabled since legacy keys package has been removed

func (*Bridge) Initialize

func (b *Bridge) Initialize(
	sessionHandlers *commands.SessionHandlers,
	gitHandlers *commands.GitHandlers,
	navigationHandlers *commands.NavigationHandlers,
	organizationHandlers *commands.OrganizationHandlers,
	systemHandlers *commands.SystemHandlers,
)

Initialize sets up the bridge with handler callbacks

func (*Bridge) IsKeyBound

func (b *Bridge) IsKeyBound(key string) bool

IsKeyBound checks if a key is bound to any command in current context

func (*Bridge) PopContext

func (b *Bridge) PopContext() ContextID

PopContext removes the top context from the stack

func (*Bridge) PushContext

func (b *Bridge) PushContext(contextID ContextID)

PushContext adds a context to the stack (for modal operations)

func (*Bridge) ReloadConfig

func (b *Bridge) ReloadConfig()

ReloadConfig refreshes the configuration from disk

func (*Bridge) SetContext

func (b *Bridge) SetContext(contextID ContextID)

SetContext switches to a different application context

func (*Bridge) ValidateAllContexts

func (b *Bridge) ValidateAllContexts() map[string][]string

ValidateAllContexts checks for key conflicts across all contexts

func (*Bridge) ValidateSetup

func (b *Bridge) ValidateSetup() []string

ValidateSetup checks if the bridge is properly configured

type Category

type Category = interfaces.Category
const (
	CategorySession      Category = "Session Management"
	CategoryGit          Category = "Git Integration"
	CategoryVC           Category = "Version Control" // VC tab operations (Git/Jujutsu)
	CategoryNavigation   Category = "Navigation"
	CategoryOrganization Category = "Organization"
	CategorySystem       Category = "System"
	CategoryLegacy       Category = "Legacy"
	CategorySpecial      Category = "Special" // Hidden from main help
)

Standard command categories for organizing help display

type Command

type Command struct {
	ID          CommandID
	Name        string
	Description string
	Category    Category
	Handler     CommandHandler
	Contexts    []ContextID

	// Optional fields
	Aliases       []string
	Deprecated    *DeprecationInfo
	Prerequisites []CommandID
	// contains filtered or unexported fields
}

Command represents a user action that can be triggered by keybindings

func FilterCommandsByPermissions

func FilterCommandsByPermissions(commands []*Command, perms session.InstancePermissions) []*Command

FilterCommandsByPermissions filters a list of commands based on instance permissions

type CommandBuilder

type CommandBuilder struct {
	// contains filtered or unexported fields
}

CommandBuilder provides a fluent interface for configuring commands

func (*CommandBuilder) BindKey

func (cb *CommandBuilder) BindKey(key string) *CommandBuilder

BindKey binds a single key to the command in all its contexts

func (*CommandBuilder) BindKeyInContext

func (cb *CommandBuilder) BindKeyInContext(key string, contexts ...ContextID) *CommandBuilder

BindKeyInContext binds a key to the command only in specific contexts

func (*CommandBuilder) BindKeys

func (cb *CommandBuilder) BindKeys(keys ...string) *CommandBuilder

BindKeys binds multiple keys to the command in all its contexts

func (*CommandBuilder) DeprecateKey

func (cb *CommandBuilder) DeprecateKey(key, message string) *CommandBuilder

DeprecateKey marks a specific key binding as deprecated

func (*CommandBuilder) SetAlternative

func (cb *CommandBuilder) SetAlternative(altID CommandID) *CommandBuilder

SetAlternative sets the alternative command for deprecated commands

type CommandHandler

type CommandHandler func(ctx *interfaces.CommandContext) error

CommandHandler is the function signature for command implementations

type CommandID

type CommandID = interfaces.CommandID

Use types from interfaces package to avoid duplication

type CommandRegistry

type CommandRegistry struct {
	// contains filtered or unexported fields
}

CommandRegistry is the central registry for all commands and keybindings

func GetCommandRegistry

func GetCommandRegistry() *CommandRegistry

GetCommandRegistry returns the global command registry

func GetGlobalRegistry

func GetGlobalRegistry() *CommandRegistry

GetGlobalRegistry returns the initialized global registry

func NewCommandRegistry

func NewCommandRegistry() *CommandRegistry

NewCommandRegistry creates a new command registry

func (*CommandRegistry) DetectConflicts

func (r *CommandRegistry) DetectConflicts() []KeyConflict

DetectConflicts finds keybinding conflicts within contexts

func (*CommandRegistry) GetAllCommands

func (r *CommandRegistry) GetAllCommands() map[CommandID]*Command

GetAllCommands returns all registered commands

func (*CommandRegistry) GetCommand

func (r *CommandRegistry) GetCommand(id CommandID) (*Command, bool)

GetCommand retrieves a command by ID

func (*CommandRegistry) GetCommandsForContext

func (r *CommandRegistry) GetCommandsForContext(contextID ContextID) []*Command

GetCommandsForContext returns all commands available in a context (including inherited)

func (*CommandRegistry) GetContext

func (r *CommandRegistry) GetContext(id ContextID) (*Context, bool)

GetContext retrieves a context by ID

func (*CommandRegistry) GetKeysForCommand

func (r *CommandRegistry) GetKeysForCommand(cmdID CommandID) []string

GetKeysForCommand returns all keys bound to a command

func (*CommandRegistry) Register

func (r *CommandRegistry) Register(cmd *Command) *CommandBuilder

Register adds a command to the registry and returns a builder for further configuration

func (*CommandRegistry) RegisterContext

func (r *CommandRegistry) RegisterContext(ctx *Context) error

RegisterContext adds a new context to the registry

func (*CommandRegistry) ResolveCommand

func (r *CommandRegistry) ResolveCommand(contextID ContextID, key string) *Command

ResolveCommand finds the command bound to a key in a given context

func (*CommandRegistry) String

func (r *CommandRegistry) String() string

String returns a debug string representation of the registry

type Context

type Context struct {
	ID          ContextID
	Name        string
	Parent      *ContextID
	Description string
}

Context represents an application mode or state

type ContextID

type ContextID = interfaces.ContextID
const (
	ContextGlobal    ContextID = "global"
	ContextList      ContextID = "list"
	ContextPTYList   ContextID = "pty-list"
	ContextGitStatus ContextID = "git-status"
	ContextVCTab     ContextID = "vc-tab"
	ContextHelp      ContextID = "help"
	ContextPrompt    ContextID = "prompt"
	ContextSearch    ContextID = "search"
	ContextConfirm   ContextID = "confirm"
)

Standard application contexts

type DeprecationInfo

type DeprecationInfo struct {
	Message     string
	Alternative CommandID
	RemoveIn    string
}

DeprecationInfo tracks deprecated commands and their alternatives

type KeyConflict

type KeyConflict struct {
	Key      string
	Context  ContextID
	Commands []CommandID
}

KeyConflict represents a keybinding conflict within a context

Directories

Path Synopsis
claude-mux is a PTY multiplexer that enables bidirectional terminal access from multiple sources (e.g., IntelliJ terminal + claude-squad web UI).
claude-mux is a PTY multiplexer that enables bidirectional terminal access from multiple sources (e.g., IntelliJ terminal + claude-squad web UI).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL