diff --git a/Cargo.lock b/Cargo.lock
index 85a42bec..edbd8005 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -570,6 +570,7 @@ dependencies = [
  "regex",
  "serde",
  "serde_json",
+ "shlex",
  "toml_edit",
  "which",
 ]
@@ -629,6 +630,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
 [[package]]
 name = "strsim"
 version = "0.11.0"
diff --git a/Cargo.toml b/Cargo.toml
index 3e65e00f..8f52ad32 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ notify-debouncer-mini = "0.4.1"
 regex = "1.10.3"
 serde_json = "1.0.114"
 serde = { version = "1.0.197", features = ["derive"] }
+shlex = "1.3.0"
 toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] }
 which = "6.0.1"
 
diff --git a/src/main.rs b/src/main.rs
index 9f45f335..80e5277e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ use clap::{Parser, Subcommand};
 use console::Emoji;
 use notify_debouncer_mini::notify::{self, RecursiveMode};
 use notify_debouncer_mini::{new_debouncer, DebouncedEventKind};
+use shlex::Shlex;
 use std::ffi::OsStr;
 use std::fs;
 use std::io::{self, prelude::*};
@@ -244,47 +245,49 @@ fn main() {
 }
 
 fn spawn_watch_shell(
-    failed_exercise_hint: &Arc<Mutex<Option<String>>>,
+    failed_exercise_hint: Arc<Mutex<Option<String>>>,
     should_quit: Arc<AtomicBool>,
 ) {
-    let failed_exercise_hint = Arc::clone(failed_exercise_hint);
     println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
-    thread::spawn(move || loop {
-        let mut input = String::new();
-        match io::stdin().read_line(&mut input) {
-            Ok(_) => {
-                let input = input.trim();
-                if input == "hint" {
-                    if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
-                        println!("{hint}");
-                    }
-                } else if input == "clear" {
-                    println!("\x1B[2J\x1B[1;1H");
-                } else if input.eq("quit") {
-                    should_quit.store(true, Ordering::SeqCst);
-                    println!("Bye!");
-                } else if input.eq("help") {
-                    println!("Commands available to you in watch mode:");
-                    println!("  hint   - prints the current exercise's hint");
-                    println!("  clear  - clears the screen");
-                    println!("  quit   - quits watch mode");
-                    println!("  !<cmd> - executes a command, like `!rustc --explain E0381`");
-                    println!("  help   - displays this help message");
-                    println!();
-                    println!("Watch mode automatically re-evaluates the current exercise");
-                    println!("when you edit a file's contents.")
-                } else if let Some(cmd) = input.strip_prefix('!') {
-                    let parts: Vec<&str> = cmd.split_whitespace().collect();
-                    if parts.is_empty() {
-                        println!("no command provided");
-                    } else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() {
-                        println!("failed to execute command `{cmd}`: {e}");
-                    }
-                } else {
-                    println!("unknown command: {input}");
-                }
+
+    thread::spawn(move || {
+        let mut input = String::with_capacity(32);
+        let mut stdin = io::stdin().lock();
+
+        loop {
+            // Recycle input buffer.
+            input.clear();
+
+            if let Err(e) = stdin.read_line(&mut input) {
+                println!("error reading command: {e}");
+            }
+
+            let input = input.trim();
+            if input == "hint" {
+                if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
+                    println!("{hint}");
+                }
+            } else if input == "clear" {
+                println!("\x1B[2J\x1B[1;1H");
+            } else if input == "quit" {
+                should_quit.store(true, Ordering::SeqCst);
+                println!("Bye!");
+            } else if input == "help" {
+                println!("{WATCH_MODE_HELP_MESSAGE}");
+            } else if let Some(cmd) = input.strip_prefix('!') {
+                let mut parts = Shlex::new(cmd);
+
+                let Some(program) = parts.next() else {
+                    println!("no command provided");
+                    continue;
+                };
+
+                if let Err(e) = Command::new(program).args(parts).status() {
+                    println!("failed to execute command `{cmd}`: {e}");
+                }
+            } else {
+                println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}");
             }
-            Err(error) => println!("error reading command: {error}"),
         }
     });
 }
@@ -345,7 +348,7 @@ fn watch(
         Ok(_) => return Ok(WatchStatus::Finished),
         Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))),
     };
-    spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit));
+    spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit));
     loop {
         match rx.recv_timeout(Duration::from_secs(1)) {
             Ok(event) => match event {
@@ -462,3 +465,13 @@ const WELCOME: &str = r"       welcome to...
  | |  | |_| \__ \ |_| | | | | | (_| \__ \
  |_|   \__,_|___/\__|_|_|_| |_|\__, |___/
                                |___/";
+
+const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode:
+  hint   - prints the current exercise's hint
+  clear  - clears the screen
+  quit   - quits watch mode
+  !<cmd> - executes a command, like `!rustc --explain E0381`
+  help   - displays this help message
+
+Watch mode automatically re-evaluates the current exercise
+when you edit a file's contents.";