diff --git a/build/scripts/test.toml b/build/scripts/test.toml
index 9f98bfd79059cdc8bc63e9db37761e9cac6965b1..376ec85a388d43831b776151eb5dab061bae6023 100644
--- a/build/scripts/test.toml
+++ b/build/scripts/test.toml
@@ -6,10 +6,10 @@ dep2 = { script = "sleep 2", hide = true }
 
 # other tests
 hidden = { script = "echo hidden", hide = true }
-debug = { info = "debug env", path="src", script = ["echo %{env.BOOL}", "echo %{env.STRING}", "echo %{arg.1}", "echo %{dir.current}", "echo %{dir.home}", "echo %{env.TYPE}", "echo %{env.ARR}", "echo %{os.platform}", "echo %{os.arch}"] }
+debug = { info = "debug env", path="maid", script = ["echo %{env.BOOL}", "echo %{env.STRING}", "echo %{arg.1}", "echo %{dir.current}", "echo %{dir.home}", "echo %{env.TYPE}", "echo %{env.ARR}", "echo %{os.platform}", "echo %{os.arch}"] }
 broken = { info = "bad task", script = ["this_does_not_exist"] }
 
 # exit types
 exit = { script = "maid 'exit %{arg.1}'" }
-"exit bad" = { script = ["exit_test 0", "exit_test 1", "exit_test 2"], hide = true }
-"exit good" = { script = ["exit_test 2", "exit_test 1", "exit_test 0"], hide = true }
\ No newline at end of file
+"exit bad" = { script = ["bash -c 'exit 0'", "bash -c 'exit 1'", "bash -c 'exit 2'"], hide = true }
+"exit good" = { script = ["bash -c 'exit 2'", "bash -c 'exit 1'", "bash -c 'exit 0'"], hide = true }
\ No newline at end of file
diff --git a/maid/client/globals.rs b/maid/client/globals.rs
index ccf5ab88ffc1d1d0850e4be4a052e6121731104f..fab42cd0cf5d7f2b67f734ec69eebb8e486f0511 100644
--- a/maid/client/globals.rs
+++ b/maid/client/globals.rs
@@ -1,6 +1,6 @@
 use global_placeholders::init;
 
 pub fn init() {
-    init!("maid.cache_dir", ".maid/cache/{}/target");
     init!("maid.temp_dir", ".maid/temp");
+    init!("maid.cache_dir", ".maid/cache/{}/target");
 }
diff --git a/maid/client/main.rs b/maid/client/main.rs
index 4b5fc25320b157b4350eee4acc7144dcc7385c70..f7b66a263414deb65fda9bb51b2ca152f6f7183f 100644
--- a/maid/client/main.rs
+++ b/maid/client/main.rs
@@ -54,7 +54,7 @@ struct Cli {
     list: bool,
     
     /// Watch for changes in specified path 
-    #[arg(short, long)]
+    #[arg(short = 'W', long)]
     watch: Option<String>,
     
     /// View Maid health (server health if enabled)
@@ -83,14 +83,16 @@ enum System {
     Update,
     /// Return the Maidfile in json
     Json,
-    /// Hydrate json output with environment
-    HydrateJson,
+    /// Hydrate json with environment fields
+    JsonHydrated,
 }
 
 #[derive(ValueEnum, Clone)]
 enum Project {
-    /// Get Project Info
+    /// Retrieve project metadata
     Info,
+    /// Display current defined environment
+    Env,
 }
 
 fn main() {
@@ -119,6 +121,7 @@ fn main() {
     if let Some(project) = cli.project {
         return match project {
             Project::Info => cli::info(&cli.path), // add more info
+            Project::Env => {}, // print env from maidfile
         };
     }
     
@@ -126,7 +129,7 @@ fn main() {
         return match system {
             System::Update => cli::butler::update(), // add real update checker
             System::Json => cli::tasks::json(&cli.path, &cli.task, false),
-            System::HydrateJson => cli::tasks::json(&cli.path, &cli.task, true),
+            System::JsonHydrated => cli::tasks::json(&cli.path, &cli.task, true),
         };
     }
     
diff --git a/maid/client/shell.rs b/maid/client/shell.rs
index 02428372da37be9947a077c558b3c2b6db4a3eaa..59de9547188c3209f711b53f7e1e2d7de786b54b 100644
--- a/maid/client/shell.rs
+++ b/maid/client/shell.rs
@@ -1,19 +1,28 @@
-use core::mem;
+use std::mem;
 use std::fmt::{Display, Formatter, Result as FmtResult};
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub struct ParseError;
+pub enum ParseError {
+    UnterminatedQuote,
+    DanglingBackslash,
+    InvalidEscape(char),
+}
 
 impl Display for ParseError {
-    fn fmt(&self, f: &mut Formatter) -> FmtResult {
-        f.write_str("missing closing quote")
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        match self {
+            ParseError::UnterminatedQuote => f.write_str("unterminated quote"),
+            ParseError::DanglingBackslash => f.write_str("dangling backslash at end of input"),
+            ParseError::InvalidEscape(c) => write!(f, "invalid escape sequence '\\{}' in double-quoted string", c),
+        }
     }
 }
 
 impl std::error::Error for ParseError {}
 
+#[derive(Debug)]
 enum State {
-    Delimiter,
+    Delimiter,     
     Backslash,
     Unquoted,
     UnquotedBackslash,
@@ -22,107 +31,154 @@ enum State {
     DoubleQuotedBackslash,
 }
 
+
 pub(crate) trait IntoArgs {
     fn try_into_args(&self) -> Result<Vec<String>, ParseError>;
 }
 
 impl<S: std::ops::Deref<Target = str>> IntoArgs for S {
     fn try_into_args(&self) -> Result<Vec<String>, ParseError> {
+        let mut parser = ArgumentParser::new();
+        parser.parse(self)
+    }
+}
+
+struct ArgumentParser {
+    words: Vec<String>,
+    current_word: String,
+    state: State,
+}
+
+impl ArgumentParser {
+    fn new() -> Self {
+        Self {
+            words: Vec::new(),
+            current_word: String::new(),
+            state: State::Delimiter,
+        }
+    }
+
+    fn parse(&mut self, input: &str) -> Result<Vec<String>, ParseError> {
         use State::*;
 
-        let mut words = Vec::new();
-        let mut word = String::new();
-        let mut chars = self.chars();
-        let mut state = Delimiter;
-
-        loop {
-            let c = chars.next();
-            state = match state {
-                Delimiter => match c {
-                    None => break,
-                    Some('\'') => SingleQuoted,
-                    Some('\"') => DoubleQuoted,
-                    Some('\\') => Backslash,
-                    Some('\t') | Some(' ') | Some('\n') => Delimiter,
-                    Some(c) => {
-                        word.push(c);
-                        Unquoted
-                    }
-                },
-                Backslash => match c {
-                    None => {
-                        word.push('\\');
-                        words.push(mem::take(&mut word));
-                        break;
-                    }
-                    Some('\n') => Delimiter,
-                    Some(c) => {
-                        word.push(c);
-                        Unquoted
-                    }
-                },
-                Unquoted => match c {
-                    None => {
-                        words.push(mem::take(&mut word));
-                        break;
-                    }
-                    Some('\'') => SingleQuoted,
-                    Some('\"') => DoubleQuoted,
-                    Some('\\') => UnquotedBackslash,
-                    Some('\t') | Some(' ') | Some('\n') => {
-                        words.push(mem::take(&mut word));
-                        Delimiter
-                    }
-                    Some(c) => {
-                        word.push(c);
-                        Unquoted
-                    }
-                },
-                UnquotedBackslash => match c {
-                    None => {
-                        word.push('\\');
-                        words.push(mem::take(&mut word));
-                        break;
-                    }
-                    Some('\n') => Unquoted,
-                    Some(c) => {
-                        word.push(c);
-                        Unquoted
-                    }
-                },
-                SingleQuoted => match c {
-                    None => return Err(ParseError),
-                    Some('\'') => Unquoted,
-                    Some(c) => {
-                        word.push(c);
-                        SingleQuoted
-                    }
-                },
-                DoubleQuoted => match c {
-                    None => return Err(ParseError),
-                    Some('\"') => Unquoted,
-                    Some('\\') => DoubleQuotedBackslash,
-                    Some(c) => {
-                        word.push(c);
-                        DoubleQuoted
-                    }
-                },
-                DoubleQuotedBackslash => match c {
-                    None => return Err(ParseError),
-                    Some('\n') => DoubleQuoted,
-                    Some(c @ '$') | Some(c @ '`') | Some(c @ '"') | Some(c @ '\\') => {
-                        word.push(c);
-                        DoubleQuoted
-                    }
-                    Some(c) => {
-                        word.push('\\');
-                        word.push(c);
-                        DoubleQuoted
-                    }
-                },
+        for c in input.chars() {
+            self.state = match self.state {
+                Delimiter => self.handle_delimiter(c)?,
+                Backslash => self.handle_backslash(c)?,
+                Unquoted => self.handle_unquoted(c)?,
+                UnquotedBackslash => self.handle_unquoted_backslash(c)?,
+                SingleQuoted => self.handle_single_quoted(c)?,
+                DoubleQuoted => self.handle_double_quoted(c)?,
+                DoubleQuotedBackslash => self.handle_double_quoted_backslash(c)?,
+            };
+        }
+
+        self.handle_end_of_input()?;
+        Ok(mem::take(&mut self.words))
+    }
+
+    fn push_word(&mut self) {
+        if !self.current_word.is_empty() {
+            self.words.push(mem::take(&mut self.current_word));
+        }
+    }
+
+    fn handle_delimiter(&mut self, c: char) -> Result<State, ParseError> {
+        Ok(match c {
+            '\'' => State::SingleQuoted,
+            '"' => State::DoubleQuoted,
+            '\\' => State::Backslash,
+            '\t' | ' ' | '\n' => State::Delimiter,
+            c => {
+                self.current_word.push(c);
+                State::Unquoted
+            }
+        })
+    }
+
+    fn handle_backslash(&mut self, c: char) -> Result<State, ParseError> {
+        Ok(match c {
+            '\n' => State::Delimiter,
+            c => {
+                self.current_word.push(c);
+                State::Unquoted
             }
+        })
+    }
+
+    fn handle_unquoted(&mut self, c: char) -> Result<State, ParseError> {
+        Ok(match c {
+            '\'' => State::SingleQuoted,
+            '"' => State::DoubleQuoted,
+            '\\' => State::UnquotedBackslash,
+            '\t' | ' ' | '\n' => {
+                self.push_word();
+                State::Delimiter
+            }
+            c => {
+                self.current_word.push(c);
+                State::Unquoted
+            }
+        })
+    }
+
+    fn handle_unquoted_backslash(&mut self, c: char) -> Result<State, ParseError> {
+        Ok(match c {
+            '\n' => State::Unquoted,
+            c => {
+                self.current_word.push(c);
+                State::Unquoted
+            }
+        })
+    }
+
+    fn handle_single_quoted(&mut self, c: char) -> Result<State, ParseError> {
+        Ok(match c {
+            '\'' => State::Unquoted,
+            c => {
+                self.current_word.push(c);
+                State::SingleQuoted
+            }
+        })
+    }
+
+    fn handle_double_quoted(&mut self, c: char) -> Result<State, ParseError> {
+        Ok(match c {
+            '"' => State::Unquoted,
+            '\\' => State::DoubleQuotedBackslash,
+            c => {
+                self.current_word.push(c);
+                State::DoubleQuoted
+            }
+        })
+    }
+
+    fn handle_double_quoted_backslash(&mut self, c: char) -> Result<State, ParseError> {
+        match c {
+            '\n' => Ok(State::DoubleQuoted),
+            '$' | '`' | '"' | '\\' => {
+                self.current_word.push(c);
+                Ok(State::DoubleQuoted)
+            }
+            c => Err(ParseError::InvalidEscape(c))
         }
+    }
 
-        Ok(words)
+    fn handle_end_of_input(&mut self) -> Result<(), ParseError> {
+        match self.state {
+            State::SingleQuoted | State::DoubleQuoted =>
+                Err(ParseError::UnterminatedQuote),
+            State::DoubleQuotedBackslash =>
+                Err(ParseError::DanglingBackslash),
+            State::Backslash | State::UnquotedBackslash => {
+                self.current_word.push('\\');
+                self.push_word();
+                Ok(())
+            }
+            _ => {
+                self.push_word();
+                Ok(())
+            }
+        }
     }
 }