Skip to content

Commit cad9ff4

Browse files
committed
feat(cli): add migrate para command with --apply and --undo
1 parent 0e9b8bc commit cad9ff4

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

src/main.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ enum Command {
158158
#[command(subcommand)]
159159
action: WriteAction,
160160
},
161+
162+
/// Migrate vault structure.
163+
Migrate {
164+
#[command(subcommand)]
165+
action: MigrateAction,
166+
},
161167
}
162168

163169
#[derive(Subcommand, Debug)]
@@ -307,6 +313,19 @@ enum ModelsAction {
307313
Info { name: String },
308314
}
309315

316+
#[derive(Subcommand, Debug)]
317+
enum MigrateAction {
318+
/// Classify notes and generate PARA migration preview.
319+
Para {
320+
/// Apply a previously generated preview.
321+
#[arg(long)]
322+
apply: bool,
323+
/// Undo the last migration.
324+
#[arg(long, conflicts_with = "apply")]
325+
undo: bool,
326+
},
327+
}
328+
310329
/// Prompt user to enable intelligence, download models if yes.
311330
fn prompt_intelligence(data_dir: &std::path::Path) -> Result<bool> {
312331
eprint!(
@@ -1421,6 +1440,58 @@ async fn main() -> Result<()> {
14211440
}
14221441
}
14231442

1443+
Command::Migrate { action } => {
1444+
let data_dir = Config::data_dir()?;
1445+
if !index_exists(&data_dir) {
1446+
eprintln!("No index found. Run 'engraph index <path>' first.");
1447+
std::process::exit(1);
1448+
}
1449+
let db_path = data_dir.join("engraph.db");
1450+
let store = store::Store::open(&db_path)?;
1451+
let vault_path_str = store.get_meta("vault_path")?.expect("no vault path in index");
1452+
let vault_path = PathBuf::from(&vault_path_str);
1453+
let profile = Config::load_vault_profile().ok().flatten();
1454+
1455+
match action {
1456+
MigrateAction::Para { apply, undo } => {
1457+
if undo {
1458+
let result = engraph::migrate::undo_last(&store, &vault_path)?;
1459+
println!("Migration {} undone: {} files restored", result.migration_id, result.restored);
1460+
if !result.errors.is_empty() {
1461+
eprintln!("Errors:");
1462+
for e in &result.errors { eprintln!(" {}", e); }
1463+
}
1464+
} else if apply {
1465+
let preview = engraph::migrate::load_preview(&data_dir)?;
1466+
let result = engraph::migrate::apply_preview(&preview, &store, &vault_path)?;
1467+
println!("Migration {} applied: {} files moved", result.migration_id, result.moved);
1468+
if !result.errors.is_empty() {
1469+
eprintln!("Errors:");
1470+
for e in &result.errors { eprintln!(" {}", e); }
1471+
}
1472+
} else {
1473+
// Generate preview
1474+
println!("Scanning vault for PARA classification...");
1475+
let preview = engraph::migrate::generate_preview(
1476+
&store, &vault_path, profile.as_ref(),
1477+
)?;
1478+
engraph::migrate::save_preview(&preview, &data_dir)?;
1479+
println!();
1480+
println!("Preview generated:");
1481+
println!(" Files to move: {}", preview.files.len());
1482+
println!(" Uncertain: {}", preview.uncertain.len());
1483+
println!(" Skipped: {}", preview.skipped);
1484+
println!();
1485+
println!("Preview saved to:");
1486+
println!(" {}", data_dir.join("migration-preview.md").display());
1487+
println!(" {}", data_dir.join("migration-preview.json").display());
1488+
println!();
1489+
println!("Review the preview, then run: engraph migrate para --apply");
1490+
}
1491+
}
1492+
}
1493+
}
1494+
14241495
Command::Models { action } => {
14251496
let defaults = engraph::llm::ModelDefaults::default();
14261497
match action {

0 commit comments

Comments
 (0)