Files
beads/cmd/bd/reset.go
Steve Yegge 2fd1d1fb87 fix: resolve golangci-lint warnings
- Handle ignored errors with explicit _ assignment (errcheck)
- Add #nosec comments for false positive G304/G204 warnings (gosec)
- Fix misspelling: cancelled -> canceled

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 10:07:36 +11:00

231 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/steveyegge/beads/internal/beads"
"github.com/steveyegge/beads/internal/reset"
)
var resetCmd = &cobra.Command{
Use: "reset",
Short: "Reset beads to a clean starting state",
Long: `Reset beads to a clean starting state by clearing .beads/ and reinitializing.
This command is useful when:
- Your beads workspace is in an invalid state after an update
- You want to start fresh with issue tracking
- bd doctor cannot automatically fix problems
RESET MODES:
Soft Reset (default):
- Kills all daemons
- Clears .beads/ directory
- Reinitializes with bd init
- Git history is unchanged
Hard Reset (--hard):
- Same as soft reset, plus:
- Removes .beads/ files from git (git rm)
- Creates a commit removing the old state
- Creates a commit with fresh initialized state
OPTIONS:
--backup Create .beads-backup-{timestamp}/ before clearing
--dry-run Preview what would happen without making changes
--force Skip confirmation prompt
--hard Include git operations (git rm + commit)
--skip-init Don't reinitialize after clearing (leaves .beads/ empty)
--verbose Show detailed progress
EXAMPLES:
bd reset # Reset with confirmation prompt
bd reset --backup # Reset with backup first
bd reset --dry-run # Preview the impact
bd reset --hard # Reset including git history
bd reset --force # Reset without confirmation`,
Run: func(cmd *cobra.Command, _ []string) {
hard, _ := cmd.Flags().GetBool("hard")
force, _ := cmd.Flags().GetBool("force")
backup, _ := cmd.Flags().GetBool("backup")
dryRun, _ := cmd.Flags().GetBool("dry-run")
skipInit, _ := cmd.Flags().GetBool("skip-init")
verbose, _ := cmd.Flags().GetBool("verbose")
// Color helpers
red := color.New(color.FgRed).SprintFunc()
yellow := color.New(color.FgYellow).SprintFunc()
green := color.New(color.FgGreen).SprintFunc()
cyan := color.New(color.FgCyan).SprintFunc()
// Validate state
if err := reset.ValidateState(); err != nil {
fmt.Fprintf(os.Stderr, "%s %v\n", red("Error:"), err)
os.Exit(1)
}
// Get impact summary
impact, err := reset.CountImpact()
if err != nil {
fmt.Fprintf(os.Stderr, "%s Failed to analyze workspace: %v\n", red("Error:"), err)
os.Exit(1)
}
// Show impact summary
fmt.Printf("\n%s Reset Impact Summary\n", yellow("⚠"))
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
totalIssues := impact.IssueCount - impact.TombstoneCount
if totalIssues > 0 {
fmt.Printf(" Issues to delete: %s\n", cyan(fmt.Sprintf("%d", totalIssues)))
fmt.Printf(" - Open: %d\n", impact.OpenCount)
fmt.Printf(" - Closed: %d\n", impact.ClosedCount)
} else {
fmt.Printf(" Issues to delete: %s\n", cyan("0"))
}
if impact.TombstoneCount > 0 {
fmt.Printf(" Tombstones to delete: %s\n", cyan(fmt.Sprintf("%d", impact.TombstoneCount)))
}
if impact.HasUncommitted {
fmt.Printf(" %s Uncommitted changes in .beads/ will be lost\n", yellow("⚠"))
}
fmt.Printf("\n")
// Show what will happen
fmt.Printf("Actions:\n")
if backup {
fmt.Printf(" 1. Create backup (.beads-backup-{timestamp}/)\n")
}
fmt.Printf(" %s. Kill all daemons\n", actionNumber(backup, 1))
if hard {
fmt.Printf(" %s. Remove .beads/ from git index and commit\n", actionNumber(backup, 2))
}
fmt.Printf(" %s. Delete .beads/ directory\n", actionNumber(backup, hardOffset(hard, 2)))
if !skipInit {
fmt.Printf(" %s. Reinitialize workspace (bd init)\n", actionNumber(backup, hardOffset(hard, 3)))
if hard {
fmt.Printf(" %s. Commit fresh state to git\n", actionNumber(backup, hardOffset(hard, 4)))
}
}
fmt.Printf("\n")
// Dry run - stop here
if dryRun {
fmt.Printf("%s This was a dry run. No changes were made.\n\n", cyan(""))
return
}
// Confirmation prompt (unless --force)
if !force {
fmt.Printf("%s This will permanently delete all issues. Continue? [y/N]: ", yellow("Warning:"))
reader := bufio.NewReader(os.Stdin)
response, err := reader.ReadString('\n')
if err != nil {
fmt.Fprintf(os.Stderr, "%s Failed to read response: %v\n", red("Error:"), err)
os.Exit(1)
}
response = strings.TrimSpace(strings.ToLower(response))
if response != "y" && response != "yes" {
fmt.Printf("Reset canceled.\n")
return
}
}
// Execute reset
if verbose {
fmt.Printf("\n%s Starting reset...\n", cyan("→"))
}
opts := reset.ResetOptions{
Hard: hard,
Backup: backup,
DryRun: false, // Already handled above
SkipInit: skipInit,
}
result, err := reset.Reset(opts)
if err != nil {
fmt.Fprintf(os.Stderr, "%s Reset failed: %v\n", red("Error:"), err)
os.Exit(1)
}
// Handle --hard mode: commit fresh state after reinit
if hard && !skipInit {
beadsDir := beads.FindBeadsDir()
if beadsDir != "" {
commitMsg := "Initialize fresh beads workspace\n\nCreated new .beads/ directory after reset."
if err := reset.GitAddAndCommit(beadsDir, commitMsg); err != nil {
fmt.Fprintf(os.Stderr, "%s Failed to commit fresh state: %v\n", yellow("Warning:"), err)
fmt.Fprintf(os.Stderr, "You may need to manually run: git add .beads && git commit -m \"Fresh beads state\"\n")
} else if verbose {
fmt.Printf(" %s Committed fresh state to git\n", green("✓"))
}
}
}
// Show results
fmt.Printf("\n%s Reset complete!\n\n", green("✓"))
if result.BackupPath != "" {
fmt.Printf(" Backup created: %s\n", cyan(result.BackupPath))
}
if result.DaemonsKilled > 0 {
fmt.Printf(" Daemons stopped: %d\n", result.DaemonsKilled)
}
if result.IssuesDeleted > 0 || result.TombstonesDeleted > 0 {
fmt.Printf(" Issues deleted: %d\n", result.IssuesDeleted)
if result.TombstonesDeleted > 0 {
fmt.Printf(" Tombstones deleted: %d\n", result.TombstonesDeleted)
}
}
if !skipInit {
fmt.Printf("\n Workspace reinitialized. Run %s to get started.\n", cyan("bd quickstart"))
} else {
fmt.Printf("\n .beads/ directory has been cleared. Run %s to reinitialize.\n", cyan("bd init"))
}
fmt.Printf("\n")
},
}
// actionNumber returns the step number accounting for backup offset
func actionNumber(hasBackup bool, step int) string {
if hasBackup {
return fmt.Sprintf("%d", step+1)
}
return fmt.Sprintf("%d", step)
}
// hardOffset adjusts step number for --hard mode which adds extra steps
func hardOffset(isHard bool, step int) int {
if isHard {
return step + 1
}
return step
}
func init() {
resetCmd.Flags().Bool("hard", false, "Include git operations (git rm + commit)")
resetCmd.Flags().BoolP("force", "f", false, "Skip confirmation prompt")
resetCmd.Flags().Bool("backup", false, "Create backup before reset")
resetCmd.Flags().Bool("dry-run", false, "Preview what would happen without making changes")
resetCmd.Flags().Bool("skip-init", false, "Don't reinitialize after clearing")
resetCmd.Flags().BoolP("verbose", "v", false, "Show detailed progress")
rootCmd.AddCommand(resetCmd)
}