Skip to content

Commit 5e08f89

Browse files
committed
Add backup protection to all user file writes
Extract backup_config_file() from config_writer into a shared utils::backup::backup_file() utility that handles any file extension. Add backup calls before every fs::write to user-owned files across all 12 writer modules: - hook_writer (settings.json, settings.local.json) - claude_settings (settings.json, settings.local.json) - permission_writer (settings.json) - spinner_verb_writer (settings.json) - keybindings_writer (keybindings.json) - memory_writer (CLAUDE.md, CLAUDE.local.md) - agent_memory_writer (agent memory .md files) - skill_writer (SKILL.md, OpenCode agent .md) - subagent_writer (agent .md, OpenCode agent .md) - rule_writer (rule .md files) - command_writer (command .md, OpenCode command .md) - config_writer (now uses shared utility) Every user file now gets a .bak copy before modification, ensuring no data loss on first run or subsequent syncs.
1 parent a1cc0bd commit 5e08f89

15 files changed

Lines changed: 121 additions & 18 deletions

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/src/services/agent_memory_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub fn write_agent_memory(
108108
std::fs::create_dir_all(parent)?;
109109
}
110110

111+
crate::utils::backup::backup_file(&path)?;
111112
std::fs::write(&path, content)?;
112113
read_agent_memory(agent_name, scope, project_path)
113114
}

src-tauri/src/services/claude_settings.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ fn write_settings_file(path: &Path, settings: &Value) -> Result<()> {
111111
if let Some(parent) = path.parent() {
112112
std::fs::create_dir_all(parent)?;
113113
}
114+
crate::utils::backup::backup_file(path)?;
114115
let content = serde_json::to_string_pretty(settings)?;
115116
std::fs::write(path, content)?;
116117
Ok(())

src-tauri/src/services/command_writer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub fn write_command_file(base_path: &Path, command: &Command) -> Result<()> {
5252
std::fs::create_dir_all(&commands_dir)?;
5353

5454
let file_path = commands_dir.join(format!("{}.md", command.name));
55+
crate::utils::backup::backup_file(&file_path)?;
5556
let content = generate_command_markdown(command);
5657
std::fs::write(file_path, content)?;
5758

@@ -146,6 +147,7 @@ pub fn write_command_file_opencode(base_path: &Path, command: &Command) -> Resul
146147
std::fs::create_dir_all(&command_dir)?;
147148

148149
let file_path = command_dir.join(format!("{}.md", command.name));
150+
crate::utils::backup::backup_file(&file_path)?;
149151
let content = generate_command_markdown_opencode(command);
150152
std::fs::write(file_path, content)?;
151153

src-tauri/src/services/config_writer.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,7 @@ use anyhow::Result;
33
use serde_json::{json, Map, Value};
44
use std::path::Path;
55

6-
/// Create a backup of the config file before modifying it
7-
fn backup_config_file(path: &Path) -> Result<()> {
8-
if !path.exists() {
9-
return Ok(());
10-
}
11-
12-
let backup_path = path.with_extension("json.bak");
13-
std::fs::copy(path, &backup_path).map_err(|e| {
14-
anyhow::anyhow!(
15-
"Failed to create backup of {} before writing: {}",
16-
path.display(),
17-
e
18-
)
19-
})?;
20-
21-
Ok(())
22-
}
6+
use crate::utils::backup::backup_file as backup_config_file;
237

248
type McpTuple = (
259
String, // name

src-tauri/src/services/hook_writer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ fn write_settings_file(path: &Path, settings: &Value) -> Result<()> {
129129
std::fs::create_dir_all(parent)?;
130130
}
131131

132+
crate::utils::backup::backup_file(path)?;
133+
132134
let content = serde_json::to_string_pretty(settings)?;
133135
std::fs::write(path, content)?;
134136
Ok(())

src-tauri/src/services/keybindings_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub fn write_keybindings_to_path(path: &Path, kb: &KeybindingsFile) -> Result<()
7777
.collect(),
7878
};
7979

80+
crate::utils::backup::backup_file(path)?;
8081
let content = serde_json::to_string_pretty(&filtered)?;
8182
std::fs::write(path, content)?;
8283
Ok(())

src-tauri/src/services/memory_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ pub fn write_memory_file(
157157
std::fs::create_dir_all(parent)?;
158158
}
159159

160+
crate::utils::backup::backup_file(&path)?;
160161
std::fs::write(&path, content)?;
161162

162163
// Read back the file info to return updated metadata

src-tauri/src/services/permission_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ fn write_settings_file(path: &Path, settings: &Value) -> Result<()> {
4949
if let Some(parent) = path.parent() {
5050
std::fs::create_dir_all(parent)?;
5151
}
52+
crate::utils::backup::backup_file(path)?;
5253
let content = serde_json::to_string_pretty(settings)?;
5354
std::fs::write(path, content)?;
5455
Ok(())

src-tauri/src/services/rule_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub fn write_rule_file(base_path: &Path, rule: &Rule) -> Result<()> {
3030
std::fs::create_dir_all(&rules_dir)?;
3131

3232
let file_path = rules_dir.join(format!("{}.md", rule.name));
33+
crate::utils::backup::backup_file(&file_path)?;
3334
let content = generate_rule_markdown(rule);
3435
std::fs::write(file_path, content)?;
3536

0 commit comments

Comments
 (0)