xiaohongshu
xiaohongshu
gentleman-installer
Installation step patterns for Gentleman.Dots TUI installer. Trigger: When editing installer.go, adding installation steps, or modifying the installation flow.
Full skill instructions
Use this skill when:
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)
}
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
})
}
}
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
}
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)
}
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
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)
}
}
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)
}
// 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
}
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
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
installer/internal/tui/installer.go for step implementationsinstaller/internal/tui/model.go for SetupInstallStepsinstaller/internal/system/exec.go for command executioninstaller/internal/tui/non_interactive.go for CLI modexiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
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...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
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...
Display project status, active tracks, and next actions
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.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience