gentleman-installer
Provides patterns for managing installation steps in the Gentleman.Dots TUI.
Install
mkdir -p .claude/skills/gentleman-installer && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4718" && unzip -o skill.zip -d .claude/skills/gentleman-installer && rm skill.zipInstalls to .claude/skills/gentleman-installer
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Installation step patterns for Gentleman.Dots TUI installer. Trigger: When editing installer.go, adding installation steps, or modifying the installation flow.Key capabilities
- →Define installation step structures
- →Register installation steps in the TUI model
- →Execute OS-specific installation logic
- →Handle interactive steps requiring sudo
- →Wrap and report installation errors
How it works
It uses a structured pattern for defining, registering, and executing installation steps within the TUI, supporting conditional logic and OS-specific commands.
Inputs & outputs
When to use gentleman-installer
- →Add a new tool installation step
- →Modify installation flow patterns
- →Implement non-interactive installer modes
About this skill
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.gofor step implementations - Model: See
installer/internal/tui/model.gofor SetupInstallSteps - System: See
installer/internal/system/exec.gofor command execution - Non-interactive: See
installer/internal/tui/non_interactive.gofor CLI mode
When not to use it
- →When not working on the Gentleman.Dots installer
Prerequisites
Limitations
- →Requires manual implementation of OS-specific variants
- →Interactive steps require specific TUI handling
How it compares
It enforces a standardized TUI-based installation pattern with built-in error handling and logging rather than ad-hoc shell scripts.
Compared to similar skills
gentleman-installer side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| gentleman-installer (this skill) | 1 | 7mo | Review | Intermediate |
| upgrading-golang | 1 | 5mo | Review | Intermediate |
| gentleman-system | 1 | 7mo | Review | Intermediate |
| agent-module-architecture | 2 | 3mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by Gentleman-Programming
View all by Gentleman-Programming →You might also like
upgrading-golang
chainloop-dev
Upgrades Go version across the entire Chainloop codebase including source files, Docker images, CI/CD workflows, and documentation. Use when the user mentions upgrading Go, golang version, or updating Go compiler version.
gentleman-system
Gentleman-Programming
System detection and command execution patterns for Gentleman.Dots. Trigger: When editing files in installer/internal/system/, adding OS support, or modifying command execution.
agent-module-architecture
TencentBlueKing
Agent 构建机模块架构指南(Go 语言),涵盖 Agent 启动流程、心跳机制、任务领取执行、升级更新、与 Dispatch 交互。当用户开发 Agent 功能、修改心跳逻辑、处理任务执行或实现 Agent 升级时使用。
go-agent-development
TencentBlueKing
Go Agent 开发指南,涵盖 Agent 架构设计、心跳机制、任务执行、日志上报、升级流程、与 Dispatch 模块交互。当用户开发构建机 Agent、实现任务执行逻辑、处理 Agent 通信或进行 Go 语言开发时使用。
goto-remote-command
chinglinwen
Use this skill in the goto/goterm project when an agent needs to execute remote commands over SSH with the goto CLI, debug batch remote execution, preserve raw stdout/stderr/exit status, or update remote-command docs, tests, or behavior.
opencode-cli
SpillwaveSolutions
This skill should be used when configuring or using the OpenCode CLI for headless LLM automation. Use when the user asks to "configure opencode", "use opencode cli", "set up opencode", "opencode run command", "opencode model selection", "opencode providers", "opencode vertex ai", "opencode mcp servers", "opencode ollama", "opencode local models", "opencode deepseek", "opencode kimi", "opencode mistral", "fallback cli tool", or "headless llm cli". Covers command syntax, provider configuration, Vertex AI setup, MCP servers, local models, cloud providers, and subprocess integration patterns.