refactor(cmd): remove automatic .bak backup creation
All checks were successful
CI / Test (push) Successful in 35s
CI / Lint (push) Successful in 25s
CI / Build (push) Successful in 19s

Eliminate .bak file backups from edit, docs, lint, testgen, and agent commands to simplify safety features, relying on previews and confirmations instead. Update README, architecture docs, troubleshooting, and TODOs to reflect changes. Adjust tests to remove backup assertions.
This commit is contained in:
Greg Gauthier 2026-03-03 20:44:39 +00:00
parent 7881ac2446
commit 81fd65b14d
10 changed files with 13 additions and 78 deletions

View File

@ -89,7 +89,6 @@ grokkit edit utils.go "add detailed docstrings to all exported functions"
``` ```
**Safety features:** **Safety features:**
- Creates `.bak` backup before any changes
- Shows preview with diff-style output - Shows preview with diff-style output
- Requires confirmation before applying - Requires confirmation before applying
- Uses silent streaming (no console spam) - Uses silent streaming (no console spam)
@ -189,7 +188,6 @@ grokkit docs app.py -m grok-4
| Shell | Shell comments (`# function: desc, # Args: ...`) | | Shell | Shell comments (`# function: desc, # Args: ...`) |
**Safety features:** **Safety features:**
- Creates `.bak` backup before any changes
- Shows first 50 lines of documented code as preview - Shows first 50 lines of documented code as preview
- Requires confirmation (unless `--auto-apply`) - Requires confirmation (unless `--auto-apply`)
@ -202,7 +200,7 @@ grokkit docs app.py -m grok-4
- Python: Pytest with `@parametrize`. - Python: Pytest with `@parametrize`.
- C: Check framework suites. - C: Check framework suites.
- C++: Google Test `EXPECT_*`. - C++: Google Test `EXPECT_*`.
- Boosts coverage; safe preview/backup. - Boosts coverage; safe preview.
**CLI examples**: **CLI examples**:
```bash ```bash
@ -213,7 +211,6 @@ grokkit testgen foo.c bar.cpp
**Safety features**: **Safety features**:
- Lang detection via `internal/linter`. - Lang detection via `internal/linter`.
- Creates `test_*.bak` backups.
- Unified diff preview. - Unified diff preview.
- Y/N (--yes auto). - Y/N (--yes auto).
@ -253,7 +250,6 @@ grokkit lint script.rb -m grok-4
- **Shell** (shellcheck) - **Shell** (shellcheck)
**Safety features:** **Safety features:**
- Creates `.bak` backup before changes
- Shows preview of fixes - Shows preview of fixes
- Verifies fixes by re-running linter - Verifies fixes by re-running linter
- Requires confirmation (unless `--auto-fix`) - Requires confirmation (unless `--auto-fix`)

View File

@ -79,9 +79,6 @@ var agentCmd = &cobra.Command{
continue continue
} }
backupPath := file + ".bak"
_ = os.WriteFile(backupPath, original, 0644)
messages := []map[string]string{ messages := []map[string]string{
{"role": "system", "content": "You are an expert programmer. Remove all unnecessary comments including last modified timestamps and ownership comments. Return clean, complete file content with no explanations, markdown, diffs, or extra text."}, {"role": "system", "content": "You are an expert programmer. Remove all unnecessary comments including last modified timestamps and ownership comments. Return clean, complete file content with no explanations, markdown, diffs, or extra text."},
{"role": "user", "content": fmt.Sprintf("File: %s\n\nOriginal content:\n%s\n\nTask: %s", filepath.Base(file), original, instruction)}, {"role": "user", "content": fmt.Sprintf("File: %s\n\nOriginal content:\n%s\n\nTask: %s", filepath.Base(file), original, instruction)},
@ -107,7 +104,7 @@ var agentCmd = &cobra.Command{
if answer == "a" { if answer == "a" {
applyAll = true applyAll = true
} else if answer != "y" { } else if answer != "y" {
color.Yellow("Skipped %s (backup kept)", file) color.Yellow("Skipped %s", file)
continue continue
} }
} }

View File

@ -32,7 +32,6 @@ Supported doc styles:
Shell Shell comments (# function: ...) Shell Shell comments (# function: ...)
Safety features: Safety features:
- Creates .bak backup before modifying files
- Shows preview of changes before applying - Shows preview of changes before applying
- Requires confirmation unless --auto-apply is used`, - Requires confirmation unless --auto-apply is used`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
@ -124,15 +123,6 @@ func processDocsFile(client grok.AIClient, model, filePath string) {
} }
} }
// Create backup
backupPath := filePath + ".bak"
if err := os.WriteFile(backupPath, originalContent, 0644); err != nil {
logger.Error("failed to create backup", "file", filePath, "error", err)
color.Red("❌ Failed to create backup: %v", err)
return
}
logger.Info("backup created", "backup", backupPath)
if err := os.WriteFile(filePath, []byte(documented), 0644); err != nil { if err := os.WriteFile(filePath, []byte(documented), 0644); err != nil {
logger.Error("failed to write documented file", "file", filePath, "error", err) logger.Error("failed to write documented file", "file", filePath, "error", err)
color.Red("❌ Failed to write file: %v", err) color.Red("❌ Failed to write file: %v", err)
@ -141,7 +131,6 @@ func processDocsFile(client grok.AIClient, model, filePath string) {
logger.Info("documentation applied successfully", "file", filePath) logger.Info("documentation applied successfully", "file", filePath)
color.Green("✅ Documentation applied: %s", filePath) color.Green("✅ Documentation applied: %s", filePath)
color.Cyan("💾 Original saved to: %s", backupPath)
} }
func buildDocsMessages(language, code string) []map[string]string { func buildDocsMessages(language, code string) []map[string]string {

View File

@ -15,7 +15,7 @@ import (
var editCmd = &cobra.Command{ var editCmd = &cobra.Command{
Use: "edit FILE INSTRUCTION", Use: "edit FILE INSTRUCTION",
Short: "Edit a file in-place with Grok (safe preview + backup)", Short: "Edit a file in-place with Grok (safe preview)",
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
filePath := args[0] filePath := args[0]
@ -44,15 +44,6 @@ var editCmd = &cobra.Command{
logger.Debug("file read successfully", "file", filePath, "size_bytes", len(original)) logger.Debug("file read successfully", "file", filePath, "size_bytes", len(original))
cleanedOriginal := removeLastModifiedComments(string(original)) cleanedOriginal := removeLastModifiedComments(string(original))
backupPath := filePath + ".bak"
logger.Debug("creating backup", "backup_path", backupPath)
if err := os.WriteFile(backupPath, original, 0644); err != nil {
logger.Error("failed to create backup", "backup_path", backupPath, "error", err)
color.Red("Failed to create backup: %v", err)
os.Exit(1)
}
logger.Info("backup created", "backup_path", backupPath)
client := grok.NewClient() client := grok.NewClient()
messages := []map[string]string{ messages := []map[string]string{
{"role": "system", "content": "You are an expert programmer. Remove all unnecessary comments including last modified timestamps and ownership comments. Return only the cleaned code with no explanations, no markdown, no extra text."}, {"role": "system", "content": "You are an expert programmer. Remove all unnecessary comments including last modified timestamps and ownership comments. Return only the cleaned code with no explanations, no markdown, no extra text."},
@ -73,11 +64,11 @@ var editCmd = &cobra.Command{
var confirm string var confirm string
if _, err := fmt.Scanln(&confirm); err != nil { if _, err := fmt.Scanln(&confirm); err != nil {
color.Red("Failed to read input: %v", err) color.Red("Failed to read input: %v", err)
color.Yellow("Changes discarded. Backup saved as %s", backupPath) color.Yellow("Changes discarded.")
return return
} }
if confirm != "y" && confirm != "Y" { if confirm != "y" && confirm != "Y" {
color.Yellow("Changes discarded. Backup saved as %s", backupPath) color.Yellow("Changes discarded.")
return return
} }
@ -89,10 +80,9 @@ var editCmd = &cobra.Command{
} }
logger.Info("changes applied successfully", logger.Info("changes applied successfully",
"file", filePath, "file", filePath,
"backup", backupPath,
"original_size", len(original), "original_size", len(original),
"new_size", len(newContent)) "new_size", len(newContent))
color.Green("✅ Applied successfully! Backup: %s", backupPath) color.Green("✅ Applied successfully!")
}, },
} }

View File

@ -132,16 +132,6 @@ func runLint(cmd *cobra.Command, args []string) {
fixedCode := grok.CleanCodeResponse(response) fixedCode := grok.CleanCodeResponse(response)
logger.Info("received AI fixes", "file", absPath, "fixed_size", len(fixedCode)) logger.Info("received AI fixes", "file", absPath, "fixed_size", len(fixedCode))
// Create backup
backupPath := absPath + ".bak"
if err := os.WriteFile(backupPath, originalContent, 0644); err != nil {
logger.Error("failed to create backup", "file", absPath, "error", err)
color.Red("❌ Failed to create backup: %v", err)
return
}
logger.Info("backup created", "backup", backupPath)
color.Green("💾 Backup created: %s", backupPath)
// Show preview if not auto-fix // Show preview if not auto-fix
if !autoFix { if !autoFix {
fmt.Println() fmt.Println()
@ -167,14 +157,14 @@ func runLint(cmd *cobra.Command, args []string) {
var response string var response string
if _, err := fmt.Scanln(&response); err != nil { if _, err := fmt.Scanln(&response); err != nil {
logger.Info("failed to read user input", "file", absPath, "error", err) logger.Info("failed to read user input", "file", absPath, "error", err)
color.Yellow("❌ Cancelled. Backup preserved at: %s", backupPath) color.Yellow("❌ Cancelled. No changes made.")
return return
} }
response = strings.ToLower(strings.TrimSpace(response)) response = strings.ToLower(strings.TrimSpace(response))
if response != "y" && response != "yes" { if response != "y" && response != "yes" {
logger.Info("user cancelled fix application", "file", absPath) logger.Info("user cancelled fix application", "file", absPath)
color.Yellow("❌ Cancelled. Backup preserved at: %s", backupPath) color.Yellow("❌ Cancelled. No changes made.")
return return
} }
} }
@ -188,7 +178,6 @@ func runLint(cmd *cobra.Command, args []string) {
logger.Info("fixes applied successfully", "file", absPath) logger.Info("fixes applied successfully", "file", absPath)
color.Green("✅ Fixes applied successfully!") color.Green("✅ Fixes applied successfully!")
color.Cyan("💾 Original saved to: %s", backupPath)
// Optionally run linter again to verify // Optionally run linter again to verify
color.Cyan("\n🔍 Re-running linter to verify fixes...") color.Cyan("\n🔍 Re-running linter to verify fixes...")

View File

@ -386,7 +386,6 @@ func TestProcessDocsFilePreviewAndCancel(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { _ = os.Remove(f.Name()) }() defer func() { _ = os.Remove(f.Name()) }()
defer func() { _ = os.Remove(f.Name() + ".bak") }()
mock := &mockStreamer{response: "package main\n\n// Foo does nothing.\nfunc Foo() {}\n"} mock := &mockStreamer{response: "package main\n\n// Foo does nothing.\nfunc Foo() {}\n"}
@ -426,7 +425,6 @@ func TestProcessDocsFileAutoApply(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { _ = os.Remove(f.Name()) }() defer func() { _ = os.Remove(f.Name()) }()
defer func() { _ = os.Remove(f.Name() + ".bak") }()
// CleanCodeResponse will trim the trailing newline from the AI response. // CleanCodeResponse will trim the trailing newline from the AI response.
aiResponse := "package main\n\n// Bar does nothing.\nfunc Bar() {}\n" aiResponse := "package main\n\n// Bar does nothing.\nfunc Bar() {}\n"
@ -447,11 +445,6 @@ func TestProcessDocsFileAutoApply(t *testing.T) {
if string(content) != documented { if string(content) != documented {
t.Errorf("file content = %q, want %q", string(content), documented) t.Errorf("file content = %q, want %q", string(content), documented)
} }
// Verify backup was created with original content.
backup, _ := os.ReadFile(f.Name() + ".bak")
if string(backup) != original {
t.Errorf("backup content = %q, want %q", string(backup), original)
}
} }
func TestRunDocs(t *testing.T) { func TestRunDocs(t *testing.T) {

View File

@ -92,7 +92,6 @@ func processTestgenFile(client *grok.Client, filePath, lang, systemPrompt, model
testPath := getTestFilePath(filePath, lang) testPath := getTestFilePath(filePath, lang)
// Handle existing test file // Handle existing test file
var origTest []byte
testExists := true testExists := true
testInfo, err := os.Stat(testPath) testInfo, err := os.Stat(testPath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -101,20 +100,6 @@ func processTestgenFile(client *grok.Client, filePath, lang, systemPrompt, model
return fmt.Errorf("stat test file: %w", err) return fmt.Errorf("stat test file: %w", err)
} else if testInfo.IsDir() { } else if testInfo.IsDir() {
return fmt.Errorf("test path is dir: %s", testPath) return fmt.Errorf("test path is dir: %s", testPath)
} else {
origTest, err = os.ReadFile(testPath)
if err != nil {
return fmt.Errorf("read existing test: %w", err)
}
}
// Backup existing test
backupPath := testPath + ".bak"
if testExists {
if err := os.WriteFile(backupPath, origTest, 0644); err != nil {
return fmt.Errorf("backup test file: %w", err)
}
color.Yellow("💾 Backup: %s", backupPath)
} }
// Generate tests // Generate tests
@ -167,7 +152,7 @@ func processTestgenFile(client *grok.Client, filePath, lang, systemPrompt, model
} }
confirm = strings.TrimSpace(strings.ToLower(confirm)) confirm = strings.TrimSpace(strings.ToLower(confirm))
if confirm != "y" && confirm != "yes" { if confirm != "y" && confirm != "yes" {
color.Yellow("⏭️ Skipped %s (backup: %s)", testPath, backupPath) color.Yellow("⏭️ Skipped %s", testPath)
return nil return nil
} }
} }

View File

@ -334,7 +334,6 @@ User: grokkit edit file.go "instruction"
editCmd.Run() editCmd.Run()
├─→ Validate file exists ├─→ Validate file exists
├─→ Read original content ├─→ Read original content
├─→ Create backup (.bak)
├─→ Clean "Last modified" comments ├─→ Clean "Last modified" comments
grok.Client.StreamSilent() # Get AI response grok.Client.StreamSilent() # Get AI response
@ -347,8 +346,7 @@ Display preview # Show diff-style output
Prompt user (y/n) Prompt user (y/n)
Write file (if confirmed) Write file (if confirmed)
├─→ logger.Info() # Log changes └─→ logger.Info() # Log changes
└─→ Keep backup
``` ```
### Commit Command Flow ### Commit Command Flow
@ -394,8 +392,6 @@ grok.Client.StreamSilent() # Get fixed code
grok.CleanCodeResponse() # Remove markdown grok.CleanCodeResponse() # Remove markdown
Create backup (.bak)
(if not --auto-fix) → Show preview + prompt (if not --auto-fix) → Show preview + prompt
Write fixed file Write fixed file

View File

@ -581,8 +581,8 @@ After applying AI fixes, re-running the linter still reports issues.
**Solution:** **Solution:**
```bash ```bash
# Some issues may require manual intervention # Some issues may require manual intervention
# Check the backup file to compare # Use git diff to compare changes
diff file.py file.py.bak git diff file.py
# Apply fixes iteratively # Apply fixes iteratively
grokkit lint file.py # Apply fixes grokkit lint file.py # Apply fixes

View File

@ -31,7 +31,7 @@ grokkit chat --agent # or new subcommand: grokkit agent-chat
- Safe-by-default workflow: - Safe-by-default workflow:
- Always preview changes (diff style, same as current `edit`/`scaffold`) - Always preview changes (diff style, same as current `edit`/`scaffold`)
- Require explicit confirmation (`y/n` or `--yes`) - Require explicit confirmation (`y/n` or `--yes`)
- Create `.bak` backups automatically - Preview and confirm all changes before application
- Ability to chain actions naturally in conversation: - Ability to chain actions naturally in conversation:
- “The scaffold tests are flaky on Windows paths — fix it and add a test case.” - “The scaffold tests are flaky on Windows paths — fix it and add a test case.”
- “Refactor the context harvester to use rg, then run make test.” - “Refactor the context harvester to use rg, then run make test.”