diff --git a/Cargo.lock b/Cargo.lock
index 0375887f3..b797b1fd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -275,6 +275,19 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af"
 
+[[package]]
+name = "console"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "unicode-width 0.1.14",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "content_inspector"
 version = "0.2.4"
@@ -406,6 +419,19 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "dialoguer"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
+dependencies = [
+ "console",
+ "shell-words",
+ "tempfile",
+ "thiserror",
+ "zeroize",
+]
+
 [[package]]
 name = "dictgen"
 version = "0.2.9"
@@ -477,6 +503,12 @@ version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.34"
@@ -730,6 +762,12 @@ dependencies = [
  "static_assertions",
 ]
 
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
 [[package]]
 name = "libc"
 version = "0.2.149"
@@ -1117,6 +1155,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "shell-words"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+
 [[package]]
 name = "shlex"
 version = "1.2.0"
@@ -1351,6 +1395,7 @@ dependencies = [
  "content_inspector",
  "derive_more",
  "derive_setters",
+ "dialoguer",
  "difflib",
  "divan",
  "encoding_rs",
@@ -1378,7 +1423,7 @@ dependencies = [
  "unic-emoji-char",
  "unicase",
  "unicode-segmentation",
- "unicode-width",
+ "unicode-width 0.2.0",
  "varcon-core",
 ]
 
@@ -1473,6 +1518,12 @@ version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
 
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
 [[package]]
 name = "unicode-width"
 version = "0.2.0"
@@ -1600,6 +1651,15 @@ dependencies = [
  "windows-targets 0.48.5",
 ]
 
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.59.0"
@@ -1758,3 +1818,9 @@ dependencies = [
  "quote",
  "syn",
 ]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
diff --git a/crates/typos-cli/Cargo.toml b/crates/typos-cli/Cargo.toml
index 65d58c330..392b8ef5b 100644
--- a/crates/typos-cli/Cargo.toml
+++ b/crates/typos-cli/Cargo.toml
@@ -77,6 +77,7 @@ colorchoice-clap = "1.0.3"
 serde_regex = "1.1.0"
 regex = "1.10.4"
 encoding_rs = "0.8.34"
+dialoguer = "0.11.0"
 
 [dev-dependencies]
 assert_fs = "1.1"
diff --git a/crates/typos-cli/src/bin/typos-cli/args.rs b/crates/typos-cli/src/bin/typos-cli/args.rs
index 3541533cf..d716b2d1f 100644
--- a/crates/typos-cli/src/bin/typos-cli/args.rs
+++ b/crates/typos-cli/src/bin/typos-cli/args.rs
@@ -67,6 +67,10 @@ pub(crate) struct Args {
     #[arg(long, short = 'w', group = "mode", help_heading = "Mode")]
     pub(crate) write_changes: bool,
 
+    /// Prompt for each suggested correction whether to write the fix
+    #[arg(long, short = 'a', group = "mode", help_heading = "Mode")]
+    pub(crate) write_ask: bool,
+
     /// Debug: Print each file that would be spellchecked.
     #[arg(long, group = "mode", help_heading = "Mode")]
     pub(crate) files: bool,
diff --git a/crates/typos-cli/src/bin/typos-cli/main.rs b/crates/typos-cli/src/bin/typos-cli/main.rs
index 9dd8773d4..ff75b7d2d 100644
--- a/crates/typos-cli/src/bin/typos-cli/main.rs
+++ b/crates/typos-cli/src/bin/typos-cli/main.rs
@@ -289,6 +289,8 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult {
             &typos_cli::file::Identifiers
         } else if args.words {
             &typos_cli::file::Words
+        } else if args.write_ask {
+            &typos_cli::file::AskFixTypos
         } else if args.write_changes {
             &typos_cli::file::FixTypos
         } else if args.diff {
diff --git a/crates/typos-cli/src/file.rs b/crates/typos-cli/src/file.rs
index c7f377c71..86cb1273e 100644
--- a/crates/typos-cli/src/file.rs
+++ b/crates/typos-cli/src/file.rs
@@ -1,4 +1,6 @@
+use anyhow::Result;
 use bstr::ByteSlice;
+use dialoguer::{Confirm, Select};
 use std::io::Read;
 use std::io::Write;
 
@@ -127,12 +129,85 @@ impl FileChecker for FixTypos {
                     }
                 }
                 if !fixes.is_empty() {
-                    let file_name = file_name.to_owned().into_bytes();
-                    let new_name = fix_buffer(file_name, fixes.into_iter());
-                    let new_name =
-                        String::from_utf8(new_name).expect("corrections are valid utf-8");
-                    let new_path = path.with_file_name(new_name);
-                    std::fs::rename(path, new_path)?;
+                    fix_file_name(path, &file_name.to_owned(), fixes)?;
+                }
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct AskFixTypos;
+
+impl FileChecker for AskFixTypos {
+    fn check_file(
+        &self,
+        path: &std::path::Path,
+        explicit: bool,
+        policy: &crate::policy::Policy<'_, '_, '_>,
+        reporter: &dyn report::Report,
+    ) -> Result<(), std::io::Error> {
+        if policy.check_files {
+            let (buffer, content_type) = read_file(path, reporter)?;
+            let bc = buffer.clone();
+            if !explicit && !policy.binary && content_type.is_binary() {
+                let msg = report::BinaryFile { path };
+                reporter.report(msg.into())?;
+            } else {
+                let mut fixes = Vec::new();
+
+                let mut accum_line_num = AccumulateLineNum::new();
+                for typo in check_bytes(&bc, policy) {
+                    let line_num = accum_line_num.line_num(&buffer, typo.byte_offset);
+                    let (line, line_offset) = extract_line(&buffer, typo.byte_offset);
+                    let msg = report::Typo {
+                        context: Some(report::FileContext { path, line_num }.into()),
+                        buffer: std::borrow::Cow::Borrowed(line),
+                        byte_offset: line_offset,
+                        typo: typo.typo.as_ref(),
+                        corrections: typo.corrections.clone(),
+                    };
+                    reporter.report(msg.into())?;
+
+                    if let Some(correction_index) = select_fix(&typo) {
+                        fixes.push((typo, correction_index));
+                    }
+
+                    println!("\n");
+                }
+
+                if !fixes.is_empty() || path == std::path::Path::new("-") {
+                    let buffer = fix_buffer_with_correction_index(buffer, fixes.into_iter());
+                    write_file(path, content_type, buffer, reporter)?;
+                }
+            }
+        }
+
+        if policy.check_filenames {
+            if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
+                let mut fixes = Vec::new();
+
+                for typo in check_str(file_name, policy) {
+                    let msg = report::Typo {
+                        context: Some(report::PathContext { path }.into()),
+                        buffer: std::borrow::Cow::Borrowed(file_name.as_bytes()),
+                        byte_offset: typo.byte_offset,
+                        typo: typo.typo.as_ref(),
+                        corrections: typo.corrections.clone(),
+                    };
+                    reporter.report(msg.into())?;
+
+                    if let Some(correction_index) = select_fix(&typo) {
+                        fixes.push((typo, correction_index));
+                    }
+
+                    println!("\n");
+                }
+
+                if !fixes.is_empty() {
+                    fix_file_name_with_correction_index(path, &file_name, fixes)?;
                 }
             }
         }
@@ -650,6 +725,40 @@ fn is_fixable(typo: &typos::Typo<'_>) -> bool {
     extract_fix(typo).is_some()
 }
 
+fn fix_buffer_with_correction_index<'a>(
+    mut buffer: Vec<u8>,
+    typos: impl Iterator<Item = (typos::Typo<'a>, usize)>,
+) -> Vec<u8> {
+    let mut offset = 0isize;
+    for (typo, correction_index) in typos {
+        let fix = match &typo.corrections {
+            typos::Status::Corrections(c) => Some(c[correction_index].as_ref()),
+            _ => None,
+        }
+        .expect("Caller provided invalid fix index");
+        let start = ((typo.byte_offset as isize) + offset) as usize;
+        let end = start + typo.typo.len();
+
+        buffer.splice(start..end, fix.as_bytes().iter().copied());
+
+        offset += (fix.len() as isize) - (typo.typo.len() as isize);
+    }
+    buffer
+}
+
+fn fix_file_name_with_correction_index<'a>(
+    path: &std::path::Path,
+    file_name: &'a str,
+    fixes: Vec<(typos::Typo<'a>, usize)>,
+) -> Result<(), std::io::Error> {
+    let file_name = file_name.to_owned().into_bytes();
+    let new_name = fix_buffer_with_correction_index(file_name, fixes.into_iter());
+    let new_name = String::from_utf8(new_name).expect("corrections are valid utf-8");
+    let new_path = path.with_file_name(new_name);
+    std::fs::rename(path, new_path)?;
+    Ok(())
+}
+
 fn fix_buffer(mut buffer: Vec<u8>, typos: impl Iterator<Item = typos::Typo<'static>>) -> Vec<u8> {
     let mut offset = 0isize;
     for typo in typos {
@@ -664,6 +773,56 @@ fn fix_buffer(mut buffer: Vec<u8>, typos: impl Iterator<Item = typos::Typo<'stat
     buffer
 }
 
+fn fix_file_name<'a>(
+    path: &std::path::Path,
+    file_name: &'a str,
+    fixes: Vec<typos::Typo<'static>>,
+) -> Result<(), std::io::Error> {
+    let file_name = file_name.to_owned().into_bytes();
+    let new_name = fix_buffer(file_name, fixes.into_iter());
+    let new_name = String::from_utf8(new_name).expect("corrections are valid utf-8");
+    let new_path = path.with_file_name(new_name);
+    std::fs::rename(path, new_path)?;
+    Ok(())
+}
+
+fn select_fix(typo: &typos::Typo<'_>) -> Option<usize> {
+    if is_fixable(&typo) {
+        let confirmation = Confirm::new()
+            .with_prompt("Do you want to apply the fix suggested above?")
+            .default(true)
+            .show_default(true)
+            .interact()
+            .unwrap();
+
+        if confirmation {
+            return Some(0);
+        }
+    } else {
+        let mut items = match &typo.corrections {
+            typos::Status::Corrections(c) => c,
+            _ => return None,
+        }
+        .clone();
+        items.insert(0, std::borrow::Cow::from("None (skip)"));
+
+        let selection = Select::new()
+            .with_prompt("Please choose one of the following suggestions")
+            .items(&items)
+            .default(0)
+            .interact()
+            .unwrap();
+
+        if selection == 0 {
+            return None;
+        }
+
+        return Some(selection - 1);
+    }
+
+    None
+}
+
 pub fn walk_path(
     walk: ignore::Walk,
     checks: &dyn FileChecker,