package main import ( "context" "fmt" "os" "path/filepath" "strings" "github.com/fatih/color" "github.com/spf13/cobra" "github.com/steveyegge/beads/internal/storage/sqlite" ) var initCmd = &cobra.Command{ Use: "init", Short: "Initialize bd in the current directory", Long: `Initialize bd in the current directory by creating a .beads/ directory and database file. Optionally specify a custom issue prefix.`, Run: func(cmd *cobra.Command, args []string) { prefix, _ := cmd.Flags().GetString("prefix") quiet, _ := cmd.Flags().GetBool("quiet") if prefix == "" { // Auto-detect from directory name cwd, err := os.Getwd() if err != nil { fmt.Fprintf(os.Stderr, "Error: failed to get current directory: %v\n", err) os.Exit(1) } prefix = filepath.Base(cwd) } // Normalize prefix: strip trailing hyphens // The hyphen is added automatically during ID generation prefix = strings.TrimRight(prefix, "-") // Create .beads directory beadsDir := ".beads" if err := os.MkdirAll(beadsDir, 0750); err != nil { fmt.Fprintf(os.Stderr, "Error: failed to create %s directory: %v\n", beadsDir, err) os.Exit(1) } // Create .gitignore in .beads directory gitignorePath := filepath.Join(beadsDir, ".gitignore") gitignoreContent := `# SQLite databases *.db *.db-journal *.db-wal *.db-shm # Daemon runtime files daemon.log daemon.pid bd.sock # Legacy database files db.sqlite bd.db # Keep JSONL exports (source of truth for git) !*.jsonl ` if err := os.WriteFile(gitignorePath, []byte(gitignoreContent), 0644); err != nil { fmt.Fprintf(os.Stderr, "Warning: failed to create .gitignore: %v\n", err) // Non-fatal - continue anyway } // Create database dbPath := filepath.Join(beadsDir, prefix+".db") store, err := sqlite.New(dbPath) if err != nil { fmt.Fprintf(os.Stderr, "Error: failed to create database: %v\n", err) os.Exit(1) } // Set the issue prefix in config ctx := context.Background() if err := store.SetConfig(ctx, "issue_prefix", prefix); err != nil { fmt.Fprintf(os.Stderr, "Error: failed to set issue prefix: %v\n", err) _ = store.Close() os.Exit(1) } // Store the bd version in metadata (for version mismatch detection) if err := store.SetMetadata(ctx, "bd_version", Version); err != nil { fmt.Fprintf(os.Stderr, "Warning: failed to store version metadata: %v\n", err) // Non-fatal - continue anyway } // Check if git has existing issues to import (fresh clone scenario) issueCount, jsonlPath := checkGitForIssues() if issueCount > 0 { if !quiet { fmt.Fprintf(os.Stderr, "\nāœ“ Database initialized. Found %d issues in git, importing...\n", issueCount) } if err := importFromGit(ctx, dbPath, store, jsonlPath); err != nil { if !quiet { fmt.Fprintf(os.Stderr, "Warning: auto-import failed: %v\n", err) fmt.Fprintf(os.Stderr, "Try manually: git show HEAD:%s | bd import -i /dev/stdin\n", jsonlPath) } // Non-fatal - continue with empty database } else if !quiet { fmt.Fprintf(os.Stderr, "āœ“ Successfully imported %d issues from git.\n\n", issueCount) } } if err := store.Close(); err != nil { fmt.Fprintf(os.Stderr, "Warning: failed to close database: %v\n", err) } // Skip output if quiet mode if quiet { return } green := color.New(color.FgGreen).SprintFunc() cyan := color.New(color.FgCyan).SprintFunc() fmt.Printf("\n%s bd initialized successfully!\n\n", green("āœ“")) fmt.Printf(" Database: %s\n", cyan(dbPath)) fmt.Printf(" Issue prefix: %s\n", cyan(prefix)) fmt.Printf(" Issues will be named: %s\n\n", cyan(prefix+"-1, "+prefix+"-2, ...")) fmt.Printf("Run %s to get started.\n\n", cyan("bd quickstart")) }, } func init() { initCmd.Flags().StringP("prefix", "p", "", "Issue prefix (default: current directory name)") initCmd.Flags().BoolP("quiet", "q", false, "Suppress output (quiet mode)") rootCmd.AddCommand(initCmd) }