Skip to content
gentleman-installer logo

Gentleman Installer

gentleman-installer

Installation step patterns for Gentleman.Dots TUI installer. Trigger: When editing installer.go, adding installation steps, or modifying the installation flow.

SKILL.md

Full skill instructions

When to Use

Use this skill when:

  • Adding new installation steps
  • Modifying existing tool installations
  • Working on backup/​restore functionality
  • Implementing non-interactive mode support
  • Adding new OS/​platform support

Critical Patterns

Pattern 1: InstallStep Structure

All steps follow this structure in model.go:

type InstallStep struct {
    ID          string      // Unique identifier: "terminal", "shell", etc.
    Name        string      // Display name: "Install Fish"
    Description string      // Short description
    Status      StepStatus  // Pending, Running, Done, Failed, Skipped
    Progress    float64     // 0.0 - 1.0
    Error       error       // Error if failed
    Interactive bool        // Needs terminal control (sudo, chsh)
}

Pattern 2: Step Registration in SetupInstallSteps

Steps MUST be registered in SetupInstallSteps() in model.go:

func (m *Model) SetupInstallSteps() {
    m.Steps = []InstallStep{}

    // Conditional step based on user choice
    if m.Choices.SomeChoice {
        m.Steps = append(m.Steps, InstallStep{
            ID:          "newstep",
            Name:        "Install Something",
            Description: "Description here",
            Status:      StatusPending,
            Interactive: false, // true if needs sudo/​password
        })
    }
}

Pattern 3: Step Execution in executeStep

All step logic goes in installer.go:

func executeStep(stepID string, m *Model) error {
    switch stepID {
    case "newstep":
        return stepNewStep(m)
    // ... other cases
    default:
        return fmt.Errorf("unknown step: %s", stepID)
    }
}

func stepNewStep(m *Model) error {
    stepID := "newstep"

    SendLog(stepID, "Starting installation...")

    // Check if already installed
    if system.CommandExists("newtool") {
        SendLog(stepID, "Already installed, skipping...")
        return nil
    }

    // Install based on OS
    var result *system.ExecResult
    if m.SystemInfo.IsTermux {
        result = system.RunPkgInstall("newtool", nil, func(line string) {
            SendLog(stepID, line)
        })
    } else {
        result = system.RunBrewWithLogs("install newtool", nil, func(line string) {
            SendLog(stepID, line)
        })
    }

    if result.Error != nil {
        return wrapStepError("newstep", "Install NewTool",
            "Failed to install NewTool",
            result.Error)
    }

    SendLog(stepID, "✓ NewTool installed")
    return nil
}

Pattern 4: Interactive Steps (sudo/​password required)

Mark step as Interactive and use runInteractiveStep:

// In SetupInstallSteps:
m.Steps = append(m.Steps, InstallStep{
    ID:          "interactive_step",
    Name:        "Configure System",
    Description: "Requires password",
    Status:      StatusPending,
    Interactive: true,  // KEY: marks as interactive
})

// In runNextStep (update.go):
if step.Interactive {
    return runInteractiveStep(step.ID, &m)
}

Decision Tree

Adding new tool installation?
├── Add step to SetupInstallSteps() with conditions
├── Add case in executeStep() switch
├── Create step{Name}() function in installer.go
├── Handle all OS variants (Mac, Linux, Arch, Debian, Termux)
├── Use SendLog() for progress updates
└── Return wrapStepError() on failure

Step needs password/​sudo?
├── Set Interactive: true in InstallStep
├── Use system.RunSudo() or system.RunSudoWithLogs()
└── Use tea.ExecProcess for full terminal control

Step should be conditional?
├── Check m.Choices.{option} before appending
├── Check m.SystemInfo for OS-specific logic
└── Use StatusSkipped if conditions not met

Code Examples

Example 1: OS-Specific Installation

func stepInstallTool(m *Model) error {
    stepID := "tool"

    if !system.CommandExists("tool") {
        SendLog(stepID, "Installing tool...")

        var result *system.ExecResult
        switch {
        case m.SystemInfo.IsTermux:
            result = system.RunPkgInstall("tool", nil, logFunc(stepID))
        case m.SystemInfo.OS == system.OSArch:
            result = system.RunSudoWithLogs("pacman -S --noconfirm tool", nil, logFunc(stepID))
        case m.SystemInfo.OS == system.OSMac:
            result = system.RunBrewWithLogs("install tool", nil, logFunc(stepID))
        default: // Debian/​Ubuntu
            result = system.RunBrewWithLogs("install tool", nil, logFunc(stepID))
        }

        if result.Error != nil {
            return wrapStepError("tool", "Install Tool",
                "Failed to install tool",
                result.Error)
        }
    }

    // Copy configuration
    SendLog(stepID, "Copying configuration...")
    homeDir := os.Getenv("HOME")
    if err := system.CopyDir(filepath.Join("Gentleman.Dots", "ToolConfig/​*"),
        filepath.Join(homeDir, ".config/​tool/")); err != nil {
        return wrapStepError("tool", "Install Tool",
            "Failed to copy configuration",
            err)
    }

    SendLog(stepID, "✓ Tool configured")
    return nil
}

func logFunc(stepID string) func(string) {
    return func(line string) {
        SendLog(stepID, line)
    }
}

Example 2: Error Wrapping Pattern

func wrapStepError(stepID, stepName, description string, cause error) error {
    return &StepError{
        StepID:      stepID,
        StepName:    stepName,
        Description: description,
        Cause:       cause,
    }
}

// Usage:
if result.Error != nil {
    return wrapStepError("terminal", "Install Alacritty",
        "Failed to install Alacritty. Check your internet connection.",
        result.Error)
}

Example 3: Config Patching

// Patch config based on user choices
func stepInstallShell(m *Model) error {
    // ... install shell ...

    // Patch config for window manager choice
    configPath := filepath.Join(homeDir, ".config/​fish/​config.fish")
    if err := system.PatchFishForWM(configPath, m.Choices.WindowMgr, m.Choices.InstallNvim); err != nil {
        return wrapStepError("shell", "Install Fish",
            "Failed to configure window manager in shell",
            err)
    }

    return nil
}

Logging Pattern

Always use SendLog for step progress:

SendLog(stepID, "Starting...")           // Start
SendLog(stepID, "Downloading...")        // Progress
SendLog(stepID, "  → file.txt")          // Sub-item
SendLog(stepID, "✓ Step completed")      // Success

Commands

cd installer && go build ./​cmd/​gentleman-installer           # Build
./​gentleman-installer --help                                  # Show help
./​gentleman-installer --non-interactive --shell=fish         # Non-interactive
GENTLEMAN_VERBOSE=1 ./​gentleman-installer --non-interactive  # Verbose logs

Resources

  • Steps: See installer/​internal/​tui/​installer.go for step implementations
  • Model: See installer/​internal/​tui/​model.go for SetupInstallSteps
  • System: See installer/​internal/​system/​exec.go for command execution
  • Non-interactive: See installer/​internal/​tui/​non_interactive.go for CLI mode

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View