commit 981dc3d2ff4b1ce4dbe5933c5416bbba72991327 Author: e Date: Tue Jan 21 16:59:43 2025 -0500 Init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b92f8ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Binaries +upm +*.exe + +# IDE specific files +.idea/ +.vscode/ +*.swp +*.swo + +# OS specific files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cb557e2 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY: build clean install + +build: + go build -o upm + +clean: + rm -f upm + +install: build + sudo mv upm /usr/local/bin/ + +test: + go test ./... + +deps: + go mod tidy \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..14bfe8f --- /dev/null +++ b/README.md @@ -0,0 +1,145 @@ +# Universal Package Manager (UPM) + +A unified command-line interface for managing packages across different Linux distributions. UPM provides a consistent interface for package management operations regardless of the underlying package manager (apt, dnf, pacman, etc.). + +## Features + +- ๐Ÿ”„ Universal syntax across package managers +- ๐Ÿ“ฆ Support for multiple package managers: + - apt (Debian/Ubuntu) + - dnf/yum (RHEL/Fedora) + - pacman (Arch) + - zypper (openSUSE) + - apk (Alpine) + - xbps (Void Linux) + - emerge (Gentoo) +- ๐Ÿš€ Support for universal package managers: + - snap + - flatpak +- ๐Ÿ” Version-specific package installation +- ๐Ÿ“‹ Repository management +- ๐Ÿงน Cache cleaning +- ๐Ÿ”— Dependency handling + +## Installation + +### Prerequisites + +- Go 1.22 or newer +- Git + +### Building from Source + +1. Clone the repository and enter the directory: + + git clone https://git.supernets.org/e/managerofmanagers + cd upm + +2. Install dependencies: + + go mod download + +3. Build the binary: + + go build -o upm + +4. (Optional) Install system-wide: + + sudo mv upm /usr/local/bin/ + +## Usage + +### Basic Commands + +Install packages: + upm install nginx + upm install nginx@1.18.0 # Install specific version + +Remove packages: + upm remove nginx + +Search for packages: + upm search nginx + +Update package lists: + upm update + +Upgrade installed packages: + upm upgrade + +List installed packages: + upm list + +Show package information: + upm info nginx + +### Repository Management + +Add a repository: + upm repo add https://repo.example.com + +Remove a repository: + upm repo remove example-repo + +List repositories: + upm repo list + +### Maintenance + +Clean package cache: + upm clean + +Remove unused dependencies: + upm autoremove + +### Dependency Management + +Check dependencies: + upm check-deps + +Check dependencies for specific package: + upm check-deps nginx + +Show package dependencies: + upm show-deps nginx + +### Using Universal Package Managers + +Install using snap: + upm -u snap install firefox + +Install using flatpak: + upm -u flatpak install org.mozilla.firefox + +## Project Structure + +upm/ +โ”œโ”€โ”€ main.go # Main application entry point +โ”œโ”€โ”€ pkg_manager/ +โ”‚ โ””โ”€โ”€ pkg_manager.go # Core package manager implementation +โ”œโ”€โ”€ go.mod +โ”œโ”€โ”€ go.sum +โ””โ”€โ”€ README.md + +## Dependencies + +Required Go packages: +- github.com/fatih/color - For colorized terminal output +- github.com/briandowns/spinner - For progress indicators + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Acknowledgments + +- Thanks to all the Linux distribution maintainers for their package managers +- The Go community for the excellent standard library and third-party packages + +## Support + +If you encounter any issues or have questions, please file an issue on the GitHub repository. \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ddf6b49 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module git.supernets.org/e/managerofmanagers + +go 1.22 + +require ( + github.com/briandowns/spinner v1.23.0 + github.com/fatih/color v1.16.0 +) + +require ( + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fb4411b --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= +github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/main.go b/main.go new file mode 100644 index 0000000..f881404 --- /dev/null +++ b/main.go @@ -0,0 +1,277 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strings" + "time" + + "github.com/fatih/color" + "github.com/briandowns/spinner" + "git.supernets.org/e/managerofmanagers/pkg_manager" +) + +var ( + // Color definitions + success = color.New(color.FgGreen, color.Bold) + warning = color.New(color.FgYellow, color.Bold) + error = color.New(color.FgRed, color.Bold) + info = color.New(color.FgCyan) + header = color.New(color.FgMagenta, color.Bold) + command = color.New(color.FgBlue, color.Bold) + // Command line flags + verbose bool +) + +func showSpinner(message string) func() { + s := spinner.New(spinner.CharSets[14], 100*time.Millisecond) + s.Suffix = " " + message + s.Color("cyan") + s.Start() + return func() { + s.Stop() + } +} + +func main() { + // Initialize universal package managers + pkg_manager.InitializeUniversalManagers() + + if len(os.Args) < 2 { + printUsage() + os.Exit(1) + } + + // Check for flags + var universalPM string + flag.StringVar(&universalPM, "u", "", "Use universal package manager (snap/flatpak)") + flag.BoolVar(&verbose, "v", false, "Show verbose output") + flag.Parse() + + if universalPM != "" { + if mgr, ok := pkg_manager.GetUniversalManager(universalPM); ok { + handleCommand(&mgr.Manager, os.Args[1:]) + return + } + fmt.Fprintf(os.Stderr, "Universal package manager %s not available\n", universalPM) + os.Exit(1) + } + + pm, err := pkg_manager.DetectPackageManager() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + handleCommand(pm, os.Args[1:]) +} + +func handleCommand(pm *pkg_manager.Manager, args []string) { + // Set output mode based on verbose flag + if verbose { + pkg_manager.SetOutputMode(pkg_manager.OutputVerbose) + } else { + pkg_manager.SetOutputMode(pkg_manager.OutputQuiet) + } + + if len(args) == 0 { + printUsage() + os.Exit(1) + } + + command := args[0] + cmdArgs := args[1:] + + switch command { + case "install": + if len(cmdArgs) == 0 { + error.Fprintln(os.Stderr, "Error: no packages specified") + os.Exit(1) + } + stop := showSpinner("Installing packages...") + err := pm.Install(cmdArgs...) + stop() + if err != nil { + error.Fprintf(os.Stderr, "Failed to install packages: %v\n", err) + os.Exit(1) + } + success.Printf("โœ“ Successfully installed: %s\n", strings.Join(cmdArgs, ", ")) + + case "remove": + if len(cmdArgs) == 0 { + error.Fprintln(os.Stderr, "Error: no packages specified") + os.Exit(1) + } + stop := showSpinner("Removing packages...") + err := pm.Remove(cmdArgs...) + stop() + if err != nil { + error.Fprintf(os.Stderr, "Failed to remove packages: %v\n", err) + os.Exit(1) + } + success.Printf("โœ“ Successfully removed: %s\n", strings.Join(cmdArgs, ", ")) + + case "search": + if len(cmdArgs) == 0 { + fmt.Fprintln(os.Stderr, "Error: search term required") + os.Exit(1) + } + if err := pm.Search(strings.Join(cmdArgs, " ")); err != nil { + fmt.Fprintf(os.Stderr, "Search failed: %v\n", err) + os.Exit(1) + } + + case "update": + if err := pm.Update(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to update package lists: %v\n", err) + os.Exit(1) + } + + case "upgrade": + if err := pm.Upgrade(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to upgrade packages: %v\n", err) + os.Exit(1) + } + + case "list": + if err := pm.ListInstalled(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to list packages: %v\n", err) + os.Exit(1) + } + + case "info": + if len(cmdArgs) == 0 { + fmt.Fprintln(os.Stderr, "Error: package name required") + os.Exit(1) + } + if err := pm.ShowInfo(cmdArgs[0]); err != nil { + fmt.Fprintf(os.Stderr, "Failed to show package info: %v\n", err) + os.Exit(1) + } + + case "repo": + if len(cmdArgs) < 1 { + fmt.Fprintln(os.Stderr, "Error: repo subcommand required (add/remove/list)") + os.Exit(1) + } + switch cmdArgs[0] { + case "add": + if len(cmdArgs) < 2 { + fmt.Fprintln(os.Stderr, "Error: repository URL required") + os.Exit(1) + } + if err := pm.AddRepository(cmdArgs[1]); err != nil { + fmt.Fprintf(os.Stderr, "Failed to add repository: %v\n", err) + os.Exit(1) + } + case "remove": + if len(cmdArgs) < 2 { + fmt.Fprintln(os.Stderr, "Error: repository name required") + os.Exit(1) + } + if err := pm.RemoveRepository(cmdArgs[1]); err != nil { + fmt.Fprintf(os.Stderr, "Failed to remove repository: %v\n", err) + os.Exit(1) + } + case "list": + if err := pm.ListRepositories(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to list repositories: %v\n", err) + os.Exit(1) + } + default: + fmt.Fprintf(os.Stderr, "Unknown repo subcommand: %s\n", cmdArgs[0]) + printUsage() + os.Exit(1) + } + + case "clean": + if err := pm.CleanCache(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to clean cache: %v\n", err) + os.Exit(1) + } + + case "autoremove": + if err := pm.AutoRemove(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to remove unused dependencies: %v\n", err) + os.Exit(1) + } + + case "check-deps": + pkg := "" + if len(cmdArgs) > 0 { + pkg = cmdArgs[0] + } + if err := pm.CheckDependencies(pkg); err != nil { + fmt.Fprintf(os.Stderr, "Dependency check failed: %v\n", err) + os.Exit(1) + } + + case "show-deps": + if len(cmdArgs) == 0 { + fmt.Fprintln(os.Stderr, "Error: package name required") + os.Exit(1) + } + if err := pm.ShowDependencies(cmdArgs[0]); err != nil { + fmt.Fprintf(os.Stderr, "Failed to show dependencies: %v\n", err) + os.Exit(1) + } + + default: + fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) + printUsage() + os.Exit(1) + } +} + +func printUsage() { + header.Println("\n๐Ÿ“ฆ Universal Package Manager") + fmt.Println("\nUsage: upm [-u snap|flatpak] [-v] [arguments]") + + info.Println("\nCommands:") + command.Print(" install ") + fmt.Println(" [package2[@version]...] - Install packages") + command.Print(" remove ") + fmt.Println(" [package2...] - Remove packages") + command.Print(" search ") + fmt.Println(" - Search for packages") + command.Print(" update ") + fmt.Println(" - Update package lists") + command.Print(" upgrade ") + fmt.Println(" - Upgrade installed packages") + command.Print(" list ") + fmt.Println(" - List installed packages") + command.Print(" info ") + fmt.Println(" - Show package information") + + info.Println("\nRepository Management:") + command.Print(" repo add ") + fmt.Println(" - Add repository") + command.Print(" repo remove ") + fmt.Println(" - Remove repository") + command.Print(" repo list ") + fmt.Println(" - List repositories") + + info.Println("\nMaintenance:") + command.Print(" clean ") + fmt.Println(" - Clean package cache") + command.Print(" autoremove ") + fmt.Println(" - Remove unused dependencies") + + info.Println("\nDependency Management:") + command.Print(" check-deps ") + fmt.Println("[package] - Check dependencies") + command.Print(" show-deps ") + fmt.Println(" - Show package dependencies") + + info.Println("\nFlags:") + fmt.Println(" -u string Use universal package manager (snap/flatpak)") + fmt.Println(" -v Show verbose output") + + warning.Println("\nExamples:") + fmt.Println(" upm install nginx@1.18.0") + fmt.Println(" upm -u snap install firefox") + fmt.Println(" upm repo add https://repo.example.com") + fmt.Println(" upm show-deps nginx") + fmt.Println() +} \ No newline at end of file diff --git a/pkg_manager/pkg_manager.go b/pkg_manager/pkg_manager.go new file mode 100644 index 0000000..37bec1f --- /dev/null +++ b/pkg_manager/pkg_manager.go @@ -0,0 +1,388 @@ +package pkg_manager + +import ( + "fmt" + "os" + "os/exec" + "strings" +) + +// PackageManager interface defines common operations +type PackageManager interface { + Install(packages ...string) error + Remove(packages ...string) error + Search(term string) error + Update() error + Upgrade() error + ListInstalled() error + ShowInfo(pkg string) error + AddRepository(repo string) error + RemoveRepository(repo string) error + ListRepositories() error + CleanCache() error + AutoRemove() error + CheckDependencies(pkg string) error + ShowDependencies(pkg string) error +} + +// Manager represents a concrete package manager +type Manager struct { + command string + installCmd []string + removeCmd []string + searchCmd []string + updateCmd []string + upgradeCmd []string + listCmd []string + infoCmd []string + addRepoCmd []string + removeRepoCmd []string + listRepoCmd []string + cleanCacheCmd []string + autoRemoveCmd []string + checkDependsCmd []string + showDependsCmd []string + needsSudo bool + versionSep string + cacheDir string +} + +// UniversalManager represents universal package managers like snap/flatpak +type UniversalManager struct { + Manager + isAvailable bool +} + +// PackageVersion represents a package with optional version +type PackageVersion struct { + Name string + Version string +} + +var universalManagers = map[string]*UniversalManager{ + "snap": { + Manager: Manager{ + command: "snap", + installCmd: []string{"install"}, + removeCmd: []string{"remove"}, + searchCmd: []string{"find"}, + updateCmd: []string{"refresh"}, + upgradeCmd: []string{"refresh"}, + needsSudo: true, + }, + }, + "flatpak": { + Manager: Manager{ + command: "flatpak", + installCmd: []string{"install", "-y"}, + removeCmd: []string{"uninstall", "-y"}, + searchCmd: []string{"search"}, + updateCmd: []string{"update", "-y"}, + upgradeCmd: []string{"update", "-y"}, + needsSudo: false, + }, + }, +} + +var managers = map[string]Manager{ + "apt": { + command: "apt", + installCmd: []string{"install", "-y"}, + removeCmd: []string{"remove", "-y"}, + searchCmd: []string{"search"}, + updateCmd: []string{"update"}, + upgradeCmd: []string{"upgrade", "-y"}, + listCmd: []string{"list", "--installed"}, + infoCmd: []string{"show"}, + addRepoCmd: []string{"add-apt-repository", "-y"}, + removeRepoCmd: []string{"add-apt-repository", "-r", "-y"}, + listRepoCmd: []string{"sources"}, + cleanCacheCmd: []string{"clean"}, + autoRemoveCmd: []string{"autoremove", "-y"}, + checkDependsCmd: []string{"check"}, + showDependsCmd: []string{"depends"}, + needsSudo: true, + versionSep: "=", + cacheDir: "/var/cache/apt", + }, + "dnf": { + command: "dnf", + installCmd: []string{"install", "-y"}, + removeCmd: []string{"remove", "-y"}, + searchCmd: []string{"search"}, + updateCmd: []string{"check-update"}, + upgradeCmd: []string{"upgrade", "-y"}, + listCmd: []string{"list", "installed"}, + infoCmd: []string{"info"}, + addRepoCmd: []string{"config-manager", "--add-repo"}, + removeRepoCmd: []string{"config-manager", "--disable"}, + listRepoCmd: []string{"repolist"}, + cleanCacheCmd: []string{"clean", "all"}, + autoRemoveCmd: []string{"autoremove", "-y"}, + checkDependsCmd: []string{"check-dependencies"}, + showDependsCmd: []string{"repoquery", "--requires"}, + needsSudo: true, + versionSep: "-", + cacheDir: "/var/cache/dnf", + }, + "yum": { + command: "yum", + installCmd: []string{"install", "-y"}, + removeCmd: []string{"remove", "-y"}, + searchCmd: []string{"search"}, + updateCmd: []string{"check-update"}, + upgradeCmd: []string{"upgrade", "-y"}, + needsSudo: true, + versionSep: "-", + cacheDir: "/var/cache/yum", + }, + "pacman": { + command: "pacman", + installCmd: []string{"-S", "--noconfirm"}, + removeCmd: []string{"-R", "--noconfirm"}, + searchCmd: []string{"-Ss"}, + updateCmd: []string{"-Sy"}, + upgradeCmd: []string{"-Syu", "--noconfirm"}, + needsSudo: true, + versionSep: "=", + cacheDir: "/var/cache/pacman", + }, + "zypper": { + command: "zypper", + installCmd: []string{"install", "-y"}, + removeCmd: []string{"remove", "-y"}, + searchCmd: []string{"search"}, + updateCmd: []string{"refresh"}, + upgradeCmd: []string{"update", "-y"}, + needsSudo: true, + versionSep: "=", + cacheDir: "/var/cache/zypp", + }, + "apk": { + command: "apk", + installCmd: []string{"add"}, + removeCmd: []string{"del"}, + searchCmd: []string{"search"}, + updateCmd: []string{"update"}, + upgradeCmd: []string{"upgrade"}, + needsSudo: true, + versionSep: "=", + cacheDir: "/var/cache/apk", + }, + "xbps": { + command: "xbps-install", + installCmd: []string{"-y"}, + removeCmd: []string{"-R"}, + searchCmd: []string{"-Rs"}, + updateCmd: []string{"-S"}, + upgradeCmd: []string{"-Su"}, + listCmd: []string{"-l"}, + infoCmd: []string{"-S"}, + addRepoCmd: []string{"-R", "add"}, + removeRepoCmd: []string{"-R", "remove"}, + listRepoCmd: []string{"-R", "list"}, + cleanCacheCmd: []string{"-Op"}, + autoRemoveCmd: []string{"-Oo"}, + checkDependsCmd: []string{"-C"}, + showDependsCmd: []string{"-x"}, + needsSudo: true, + versionSep: "-", + cacheDir: "/var/cache/xbps", + }, + "emerge": { + command: "emerge", + installCmd: []string{"--ask", "n"}, + removeCmd: []string{"--unmerge", "--ask", "n"}, + searchCmd: []string{"--search"}, + updateCmd: []string{"--sync"}, + upgradeCmd: []string{"--update", "--deep", "--ask", "n", "@world"}, + listCmd: []string{"--list-sets"}, + infoCmd: []string{"--info"}, + addRepoCmd: []string{"--repos-conf", "add"}, + removeRepoCmd: []string{"--repos-conf", "remove"}, + listRepoCmd: []string{"--repos-conf", "list"}, + cleanCacheCmd: []string{"--depclean"}, + autoRemoveCmd: []string{"--depclean"}, + checkDependsCmd: []string{"--depclean", "-p"}, + showDependsCmd: []string{"--deps"}, + needsSudo: true, + versionSep: "-", + cacheDir: "/var/cache/portage", + }, +} + +// ParsePackageVersion parses package name and version +func ParsePackageVersion(pkg string) PackageVersion { + parts := strings.Split(pkg, "@") + if len(parts) == 2 { + return PackageVersion{ + Name: parts[0], + Version: parts[1], + } + } + return PackageVersion{Name: pkg} +} + +// formatPackageWithVersion formats package with version using manager's separator +func (m *Manager) formatPackageWithVersion(pv PackageVersion) string { + if pv.Version == "" { + return pv.Name + } + return fmt.Sprintf("%s%s%s", pv.Name, m.versionSep, pv.Version) +} + +// Install packages with version support +func (m *Manager) Install(packages ...string) error { + if len(packages) == 0 { + return fmt.Errorf("no packages specified") + } + + formattedPkgs := make([]string, len(packages)) + for i, pkg := range packages { + pv := ParsePackageVersion(pkg) + formattedPkgs[i] = m.formatPackageWithVersion(pv) + } + + return m.execCommand(m.installCmd, formattedPkgs...) +} + +// InitializeUniversalManagers checks which universal package managers are available +func InitializeUniversalManagers() { + for _, mgr := range universalManagers { + _, err := exec.LookPath(mgr.command) + mgr.isAvailable = err == nil + } +} + +// GetUniversalManager returns a universal package manager if available +func GetUniversalManager(name string) (*UniversalManager, bool) { + if mgr, exists := universalManagers[name]; exists && mgr.isAvailable { + return mgr, true + } + return nil, false +} + +type OutputMode int + +const ( + OutputQuiet OutputMode = iota + OutputVerbose +) + +var outputMode = OutputQuiet + +// SetOutputMode sets the output verbosity +func SetOutputMode(mode OutputMode) { + outputMode = mode +} + +func (m *Manager) execCommand(baseCmd []string, args ...string) error { + cmdArgs := append(baseCmd, args...) + + var cmd *exec.Cmd + if m.needsSudo && os.Geteuid() != 0 { + // Clear the line to prevent spinner interference with sudo prompt + fmt.Print("\r\033[K") + cmdArgs = append([]string{m.command}, cmdArgs...) + cmd = exec.Command("sudo", cmdArgs...) + } else { + cmd = exec.Command(m.command, cmdArgs...) + } + + // Always capture output + if outputMode == OutputVerbose { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } else { + // Capture but don't display output in quiet mode + cmd.Stdout = nil + cmd.Stderr = nil + } + cmd.Stdin = os.Stdin + + return cmd.Run() +} + +// Remove packages +func (m *Manager) Remove(packages ...string) error { + if len(packages) == 0 { + return fmt.Errorf("no packages specified") + } + return m.execCommand(m.removeCmd, packages...) +} + +// Search for packages +func (m *Manager) Search(term string) error { + if term == "" { + return fmt.Errorf("no search term specified") + } + return m.execCommand(m.searchCmd, term) +} + +// Update package lists +func (m *Manager) Update() error { + return m.execCommand(m.updateCmd) +} + +// Upgrade installed packages +func (m *Manager) Upgrade() error { + return m.execCommand(m.upgradeCmd) +} + +// ListInstalled lists all installed packages +func (m *Manager) ListInstalled() error { + return m.execCommand(m.listCmd) +} + +// ShowInfo shows detailed information about a package +func (m *Manager) ShowInfo(pkg string) error { + return m.execCommand(m.infoCmd, pkg) +} + +// AddRepository adds a new repository +func (m *Manager) AddRepository(repo string) error { + return m.execCommand(m.addRepoCmd, repo) +} + +// RemoveRepository removes a repository +func (m *Manager) RemoveRepository(repo string) error { + return m.execCommand(m.removeRepoCmd, repo) +} + +// ListRepositories lists all configured repositories +func (m *Manager) ListRepositories() error { + return m.execCommand(m.listRepoCmd) +} + +// CleanCache cleans the package manager cache +func (m *Manager) CleanCache() error { + return m.execCommand(m.cleanCacheCmd) +} + +// AutoRemove removes unused dependencies +func (m *Manager) AutoRemove() error { + return m.execCommand(m.autoRemoveCmd) +} + +// CheckDependencies checks for dependency issues +func (m *Manager) CheckDependencies(pkg string) error { + if pkg == "" { + return m.execCommand(m.checkDependsCmd) + } + return m.execCommand(m.checkDependsCmd, pkg) +} + +// ShowDependencies shows dependencies for a package +func (m *Manager) ShowDependencies(pkg string) error { + return m.execCommand(m.showDependsCmd, pkg) +} + +// DetectPackageManager identifies the system's package manager +func DetectPackageManager() (*Manager, error) { + for _, mgr := range managers { + if _, err := exec.LookPath(mgr.command); err == nil { + return &mgr, nil + } + } + return nil, fmt.Errorf("no supported package manager found") +} \ No newline at end of file