Skip to content

Commit 17ac3ad

Browse files
committed
feat: add uninstall command
1 parent 107c242 commit 17ac3ad

3 files changed

Lines changed: 96 additions & 0 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ For scripting or power users:
100100
| `opencode-sync doctor` | Diagnose issues |
101101
| `opencode-sync config` | Manage configuration |
102102
| `opencode-sync key` | Manage encryption keys |
103+
| `opencode-sync uninstall` | Uninstall opencode-sync |
104+
105+
## Uninstalling
106+
107+
```bash
108+
opencode-sync uninstall
109+
```
110+
111+
This will:
112+
- Remove the binary (may require sudo)
113+
- Optionally remove config (`~/.config/opencode-sync/`) and data (`~/.local/share/opencode-sync/`)
114+
- Your OpenCode configurations are **not affected**
103115

104116
## Requirements
105117

internal/cli/commands.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,21 @@ Examples:
269269
},
270270
}
271271

272+
var uninstallCmd = &cobra.Command{
273+
Use: "uninstall",
274+
Short: "Uninstall opencode-sync",
275+
Long: `Remove opencode-sync and optionally its data.
276+
277+
This will:
278+
- Remove the opencode-sync binary (may require sudo)
279+
- Optionally remove config and sync data
280+
281+
Your OpenCode configurations are NOT affected.`,
282+
RunE: func(cmd *cobra.Command, args []string) error {
283+
return runUninstall()
284+
},
285+
}
286+
272287
func init() {
273288
// Add config subcommands
274289
configCmd.AddCommand(configShowCmd)
@@ -1278,3 +1293,71 @@ func runGitCommand(dir string, args ...string) error {
12781293
cmd.Stderr = os.Stderr
12791294
return cmd.Run()
12801295
}
1296+
1297+
func runUninstall() error {
1298+
ui.Warn("This will uninstall opencode-sync from your system.")
1299+
fmt.Println()
1300+
1301+
p, err := paths.Get()
1302+
if err != nil {
1303+
return fmt.Errorf("failed to get paths: %w", err)
1304+
}
1305+
1306+
binaryPath, err := os.Executable()
1307+
if err != nil {
1308+
binaryPath = "opencode-sync (location unknown)"
1309+
}
1310+
1311+
fmt.Println("The following will be removed:")
1312+
fmt.Printf(" Binary: %s\n", binaryPath)
1313+
fmt.Printf(" Config: %s\n", p.ConfigDir)
1314+
fmt.Printf(" Data: %s\n", p.DataDir)
1315+
fmt.Println()
1316+
ui.Info("Your OpenCode configurations will NOT be affected.")
1317+
fmt.Println()
1318+
1319+
confirmed, err := ui.Confirm("Proceed with uninstall?", "This cannot be undone")
1320+
if err != nil {
1321+
return err
1322+
}
1323+
if !confirmed {
1324+
ui.Info("Uninstall cancelled")
1325+
return nil
1326+
}
1327+
1328+
removeData, err := ui.Confirm("Also remove config and sync data?", "Includes encryption key and local repo")
1329+
if err != nil {
1330+
return err
1331+
}
1332+
1333+
if removeData {
1334+
if err := os.RemoveAll(p.ConfigDir); err != nil {
1335+
ui.Warn(fmt.Sprintf("Failed to remove config dir: %v", err))
1336+
} else {
1337+
ui.Success(fmt.Sprintf("Removed: %s", p.ConfigDir))
1338+
}
1339+
1340+
if err := os.RemoveAll(p.DataDir); err != nil {
1341+
ui.Warn(fmt.Sprintf("Failed to remove data dir: %v", err))
1342+
} else {
1343+
ui.Success(fmt.Sprintf("Removed: %s", p.DataDir))
1344+
}
1345+
}
1346+
1347+
if binaryPath != "" && binaryPath != "opencode-sync (location unknown)" {
1348+
if err := os.Remove(binaryPath); err != nil {
1349+
if os.IsPermission(err) {
1350+
ui.Warn("Cannot remove binary (permission denied). Run with sudo or remove manually:")
1351+
fmt.Printf(" sudo rm %s\n", binaryPath)
1352+
} else {
1353+
ui.Warn(fmt.Sprintf("Failed to remove binary: %v", err))
1354+
}
1355+
} else {
1356+
ui.Success(fmt.Sprintf("Removed: %s", binaryPath))
1357+
}
1358+
}
1359+
1360+
fmt.Println()
1361+
ui.Success("Uninstall complete!")
1362+
return nil
1363+
}

internal/cli/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func init() {
8080
rootCmd.AddCommand(configCmd)
8181
rootCmd.AddCommand(keyCmd)
8282
rootCmd.AddCommand(rebindCmd)
83+
rootCmd.AddCommand(uninstallCmd)
8384
}
8485

8586
// runSetupWizard runs the first-time setup wizard

0 commit comments

Comments
 (0)