gentleman-programming/gentleman.dots

gentleman-system

System detection and command execution patterns for Gentleman.Dots.

소스 보기

사용 방법

  1. 설치 명령을 복사하고 자신의 에이전트 환경에 설치하기 전에 소스 패키지를 확인하세요.
  2. 필요한 도구, 인증 정보, 지원 환경은 원본 문서에서 확인하세요.
  3. 입력과 원하는 산출물을 설명하고 출처와 결과를 대조하세요.
npx skills add https://github.com/gentleman-programming/gentleman.dots --skill gentleman-system

시작 전 확인

  • 설치에는 모델 구독, API 한도, 파일 접근 권한이 포함되지 않습니다.
  • 별점 수와 설치 수는 활동 지표이며 독립적인 품질·보안 검증이 아닙니다.
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

When to Use

Use this skill when:

  • Adding support for new operating systems
  • Modifying OS detection logic
  • Working with command execution (sudo, brew, pkg)
  • Adding new system checks
  • Implementing backup/restore functionality

Critical Patterns

Pattern 1: OSType Enum

All OS types are defined in detect.go:

go
type OSType int

const (
    OSMac OSType = iota
    OSLinux
    OSArch
    OSDebian    // Debian-based (Debian, Ubuntu)
    OSTermux    // Termux on Android
    OSUnknown
)

Pattern 2: SystemInfo Structure

Detection results in SystemInfo struct:

go
type SystemInfo struct {
    OS        OSType
    OSName    string
    IsWSL     bool
    IsARM     bool
    IsTermux  bool
    HomeDir   string
    HasBrew   bool
    HasPkg    bool    // Termux package manager
    HasXcode  bool
    UserShell string
    Prefix    string  // Termux $PREFIX or empty
}

Pattern 3: OS Detection Priority

Termux is checked FIRST (runs on Linux but is special):

go
func Detect() *SystemInfo {
    info := &SystemInfo{...}

    // Check Termux FIRST
    if isTermux() {
        info.OS = OSTermux
        info.IsTermux = true
        info.HasPkg = checkPkg()
        return info
    }

    // Then check standard OS
    switch runtime.GOOS {
    case "darwin":
        info.OS = OSMac
    case "linux":
        if isArchLinux() {
            info.OS = OSArch
        } else if isDebian() {
            info.OS = OSDebian
        }
    }
    return info
}

Pattern 4: Command Execution Functions

Use the right function for each context:

go
// Basic command (no sudo, no logs)
system.Run("git clone ...", nil)

// With real-time logs
system.RunWithLogs("git clone ...", nil, func(line string) {
    SendLog(stepID, line)
})

// Homebrew commands
system.RunBrewWithLogs("install fish", nil, logFunc)

// Sudo commands (password prompt)
system.RunSudo("apt-get install -y git", nil)
system.RunSudoWithLogs("pacman -S git", nil, logFunc)

// Termux pkg commands (no sudo needed)
system.RunPkgInstall("fish git", nil, logFunc)
system.RunPkgWithLogs("update", nil, logFunc)

Decision Tree

Adding new OS support?
├── Add OSType constant in detect.go
├── Add detection function (isNewOS())
├── Update Detect() with priority order
├── Update SystemInfo if new fields needed
└── Add OS case in installer.go steps

Running a command?
├── Needs sudo? → RunSudo() or RunSudoWithLogs()
├── Needs brew? → RunBrewWithLogs()
├── On Termux? → RunPkgInstall() or RunPkgWithLogs()
├── Needs logs? → RunWithLogs()
└── Simple exec? → Run()

Checking if tool exists?
├── Use CommandExists("toolname")
└── Returns bool

Code Examples

Example 1: Termux Detection

go
func isTermux() bool {
    // Check TERMUX_VERSION environment variable
    if os.Getenv("TERMUX_VERSION") != "" {
        return true
    }
    // Check PREFIX contains termux path
    prefix := os.Getenv("PREFIX")
    if strings.Contains(prefix, "com.termux") {
        return true
    }
    // Check for Termux-specific paths
    if _, err := os.Stat("/data/data/com.termux"); err == nil {
        return true
    }
    return false
}

Example 2: Platform-Specific Execution

go
func installTool(m *Model) error {
    var result *system.ExecResult

    switch {
    case m.SystemInfo.IsTermux:
        // Termux: use pkg (no sudo)
        result = system.RunPkgInstall("tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSArch:
        // Arch: use pacman with sudo
        result = system.RunSudoWithLogs("pacman -S --noconfirm tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSMac:
        // macOS: use Homebrew
        result = system.RunBrewWithLogs("install tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSDebian:
        // Debian/Ubuntu: use Homebrew (installed by us)
        result = system.RunBrewWithLogs("install tool", nil, logFunc)

    default:
        return fmt.Errorf("unsupported OS: %v", m.SystemInfo.OS)
    }

    return result.Error
}

Example 3: Homebrew Prefix Detection

go
func GetBrewPrefix() string {
    if runtime.GOOS == "darwin" {
        // Apple Silicon uses /opt/homebrew
        // Intel uses /usr/local
        if runtime.GOARCH == "arm64" {
            return "/opt/homebrew"
        }
        return "/usr/local"
    }
    return "/home/linuxbrew/.linuxbrew"
}

Example 4: File Operations

go
// Ensure directory exists
if err := system.EnsureDir(filepath.Join(homeDir, ".config/tool")); err != nil {
    return err
}

// Copy single file
if err := system.CopyFile(src, dst); err != nil {
    return err
}

// Copy directory contents
if err := system.CopyDir("Gentleman.Dots/Config/*", destDir+"/"); err != nil {
    return err
}

ExecResult Structure

go
type ExecResult struct {
    Output   string  // stdout
    Stderr   string  // stderr
    ExitCode int     // exit code
    Error    error   // error if any
}

// Usage
result := system.Run("command", nil)
if result.Error != nil {
    // Handle error
}
if result.ExitCode != 0 {
    // Non-zero exit
}

Commands

bash
cd installer && go test ./internal/system/...   # Run system tests
cd installer && go test -run TestDetect         # Test OS detection
cd installer && go test -run TestExec           # Test command execution

Resources

  • Detection: See installer/internal/system/detect.go for OS detection
  • Execution: See installer/internal/system/exec.go for command running
  • Backup: See installer/internal/system/backup.go for backup/restore
  • Tests: See installer/internal/system/*_test.go for patterns

관련 작업