diff --git a/src/lib/builtins/functions.rs b/src/lib/builtins/functions.rs
index 7b59e4354cfbeec33eaa6759b6653a314f1f2056..db6943705cf7fa95ea7b31de29b5a1e53882de1b 100644
--- a/src/lib/builtins/functions.rs
+++ b/src/lib/builtins/functions.rs
@@ -1,7 +1,7 @@
 use crate::shell::{status::*, variables::Variables};
 use std::io::{self, Write};
 
-fn print_functions(vars: &Variables) {
+pub fn print_functions(vars: &Variables) -> i32 {
     let stdout = io::stdout();
     let stdout = &mut stdout.lock();
     let _ = writeln!(stdout, "# Functions");
@@ -13,9 +13,5 @@ fn print_functions(vars: &Variables) {
             let _ = writeln!(stdout, "    {}", fn_name);
         }
     }
-}
-
-pub(crate) fn fn_(vars: &mut Variables) -> i32 {
-    print_functions(vars);
     SUCCESS
 }
diff --git a/src/lib/builtins/job_control.rs b/src/lib/builtins/job_control.rs
index 1489ff69090817b6eace04592f1d9a68224c727b..0024175f54650f1b02bd9b8569f707cfb81be0ad 100644
--- a/src/lib/builtins/job_control.rs
+++ b/src/lib/builtins/job_control.rs
@@ -1,12 +1,7 @@
 //! Contains the `jobs`, `disown`, `bg`, and `fg` commands that manage job
 //! control in the shell.
 
-use crate::shell::{
-    job_control::{JobControl, ProcessState},
-    signals,
-    status::*,
-    Shell,
-};
+use crate::shell::{signals, status::*, ProcessState, Shell};
 use small;
 use smallvec::SmallVec;
 
@@ -144,7 +139,7 @@ pub(crate) fn fg(shell: &mut Shell, args: &[small::String]) -> i32 {
 /// Resumes a stopped background process, if it was stopped.
 pub(crate) fn bg(shell: &mut Shell, args: &[small::String]) -> i32 {
     fn bg_job(shell: &mut Shell, njob: u32) -> bool {
-        if let Some(job) = shell.background.lock().unwrap().iter_mut().nth(njob as usize) {
+        if let Some(job) = shell.background.lock().unwrap().iter().nth(njob as usize) {
             match job.state {
                 ProcessState::Running => {
                     eprintln!("ion: bg: job {} is already running", njob);
diff --git a/src/lib/builtins/man_pages.rs b/src/lib/builtins/man_pages.rs
index 50eb176086e02374b0fdf6eb4156ec788deaed2a..5c8bf09b901c836b5b1938a9e5b6b7d11b4cf387 100644
--- a/src/lib/builtins/man_pages.rs
+++ b/src/lib/builtins/man_pages.rs
@@ -1,6 +1,6 @@
 use small;
 
-pub(crate) fn check_help(args: &[small::String], man_page: &'static str) -> bool {
+pub fn check_help(args: &[small::String], man_page: &'static str) -> bool {
     for arg in args {
         if arg == "-h" || arg == "--help" {
             println!("{}", man_page);
@@ -10,7 +10,7 @@ pub(crate) fn check_help(args: &[small::String], man_page: &'static str) -> bool
     false
 }
 
-pub(crate) const MAN_STATUS: &str = r#"NAME
+pub const MAN_STATUS: &str = r#"NAME
     status - Evaluates the current runtime status
 
 SYNOPSIS
@@ -27,7 +27,7 @@ OPTIONS
     -f
         prints the filename of the currently running script or else stdio. Also --current-filename."#;
 
-pub(crate) const MAN_CD: &str = r#"NAME
+pub const MAN_CD: &str = r#"NAME
     cd - Change directory.
 
 SYNOPSIS
@@ -39,7 +39,7 @@ DESCRIPTION
     With arguments cd changes the working directory to the directory you provided.
 "#;
 
-pub(crate) const MAN_BOOL: &str = r#"NAME
+pub const MAN_BOOL: &str = r#"NAME
     bool - Returns true if the value given to it is equal to '1' or 'true'.
 
 SYNOPSIS
@@ -48,7 +48,7 @@ SYNOPSIS
 DESCRIPTION
     Returns true if the value given to it is equal to '1' or 'true'."#;
 
-pub(crate) const MAN_IS: &str = r#"NAME
+pub const MAN_IS: &str = r#"NAME
     is - Checks if two arguments are the same
 
 SYNOPSIS
@@ -61,7 +61,7 @@ OPTIONS
     not
         returns 0 if the two arguments are not equal."#;
 
-pub(crate) const MAN_ISATTY: &str = r#"
+pub const MAN_ISATTY: &str = r#"
     isatty - Checks if argument is a file descriptor
 
 SYNOPSIS
@@ -70,7 +70,7 @@ SYNOPSIS
 DESCRIPTION
     Returns 0 exit status if the supplied file descriptor is a tty."#;
 
-pub(crate) const MAN_DIRS: &str = r#"NAME
+pub const MAN_DIRS: &str = r#"NAME
     dirs - prints the directory stack
 
 SYNOPSIS
@@ -79,7 +79,7 @@ SYNOPSIS
 DESCRIPTION
     dirs prints the current directory stack."#;
 
-pub(crate) const MAN_PUSHD: &str = r#"NAME
+pub const MAN_PUSHD: &str = r#"NAME
     pushd - push a directory to the directory stack
 
 SYNOPSIS
@@ -88,7 +88,7 @@ SYNOPSIS
 DESCRIPTION
     pushd pushes a directory to the directory stack."#;
 
-pub(crate) const MAN_POPD: &str = r#"NAME
+pub const MAN_POPD: &str = r#"NAME
     popd - shift through the directory stack
 
 SYNOPSIS
@@ -98,7 +98,7 @@ DESCRIPTION
     popd removes the top directory from the directory stack and changes the working directory to the new top directory.
     pushd adds directories to the stack."#;
 
-// pub(crate) const MAN_FN: &str = r#"NAME
+// pub const MAN_FN: &str = r#"NAME
 // fn - print a list of all functions or create a function
 //
 // SYNOPSIS
@@ -126,7 +126,7 @@ DESCRIPTION
 // example 1
 //"#;
 
-pub(crate) const MAN_READ: &str = r#"NAME
+pub const MAN_READ: &str = r#"NAME
     read - read a line of input into some variables
 
 SYNOPSIS
@@ -135,7 +135,7 @@ SYNOPSIS
 DESCRIPTION
     For each variable reads from standard input and stores the results in the variable."#;
 
-pub(crate) const MAN_DROP: &str = r#"NAME
+pub const MAN_DROP: &str = r#"NAME
     drop - delete some variables or arrays
 
 SYNOPSIS
@@ -149,7 +149,7 @@ OPTIONS
     -a
         Instead of deleting variables deletes arrays."#;
 
-pub(crate) const MAN_SET: &str = r#"NAME
+pub const MAN_SET: &str = r#"NAME
     set - Set or unset values of shell options and positional parameters.
 
 SYNOPSIS
@@ -172,7 +172,7 @@ OPTIONS
     -   Following arguments will be set as positional arguments in the shell.
         If no arguments are suppled, arguments will not be unset."#;
 
-pub(crate) const MAN_EQ: &str = r#"NAME
+pub const MAN_EQ: &str = r#"NAME
     eq - Checks if two arguments are the same
 
 SYNOPSIS
@@ -185,7 +185,7 @@ OPTIONS
     not
         returns 0 if the two arguments are not equal."#;
 
-pub(crate) const MAN_EVAL: &str = r#"NAME
+pub const MAN_EVAL: &str = r#"NAME
     eval - evaluates the specified commands
 
 SYNOPSIS
@@ -195,7 +195,7 @@ DESCRIPTION
     eval evaluates the given arguments as a command. If more than one argument is given,
     all arguments are joined using a space as a separator."#;
 
-pub(crate) const MAN_EXEC: &str = r#"NAME
+pub const MAN_EXEC: &str = r#"NAME
     exec - Replace the shell with the given command.
 
 SYNOPSIS
@@ -209,7 +209,7 @@ DESCRIPTION
 OPTIONS
     -c  Execute command with an empty environment."#;
 
-pub(crate) const MAN_HISTORY: &str = r#"NAME
+pub const MAN_HISTORY: &str = r#"NAME
     history - print command history
 
 SYNOPSIS
@@ -218,7 +218,7 @@ SYNOPSIS
 DESCRIPTION
     Prints the command history."#;
 
-pub(crate) const MAN_SOURCE: &str = r#"NAME
+pub const MAN_SOURCE: &str = r#"NAME
     source - evaluates given file
 
 SYNOPSIS
@@ -228,7 +228,7 @@ DESCRIPTION
     Evaluates the commands in a specified file in the current shell. All changes in shell
     variables will affect the current shell because of this."#;
 
-pub(crate) const MAN_ECHO: &str = r#"NAME
+pub const MAN_ECHO: &str = r#"NAME
     echo - display a line of text
 
 SYNOPSIS
@@ -258,7 +258,7 @@ OPTIONS
         \t  horizontal tab (HT)
         \v  vertical tab (VT)"#;
 
-pub(crate) const MAN_RANDOM: &str = r#"NAME
+pub const MAN_RANDOM: &str = r#"NAME
     random - generate a random number
 
 SYNOPSIS
@@ -270,7 +270,7 @@ DESCRIPTION
     The range depends on what arguments you pass. If no arguments are given the range is [0, 32767].
     If two arguments are given the range is [START, END]."#;
 
-pub(crate) const MAN_TRUE: &str = r#"NAME
+pub const MAN_TRUE: &str = r#"NAME
     true - does nothing successfully
 
 SYNOPSIS
@@ -279,7 +279,7 @@ SYNOPSIS
 DESCRIPTION
     Sets the exit status to 0."#;
 
-pub(crate) const MAN_FALSE: &str = r#"NAME
+pub const MAN_FALSE: &str = r#"NAME
     false - does nothing unsuccessfully
 
 SYNOPSIS
@@ -288,7 +288,7 @@ SYNOPSIS
 DESCRIPTION
     Sets the exit status to 1."#;
 
-pub(crate) const MAN_JOBS: &str = r#"NAME
+pub const MAN_JOBS: &str = r#"NAME
     jobs - list all jobs running in the background
 
 SYNOPSIS
@@ -297,7 +297,7 @@ SYNOPSIS
 DESCRIPTION
     Prints a list of all jobs running in the background."#;
 
-pub(crate) const MAN_BG: &str = r#"NAME
+pub const MAN_BG: &str = r#"NAME
     bg - sends jobs to background
 
 SYNOPSIS
@@ -306,7 +306,7 @@ SYNOPSIS
 DESCRIPTION
     bg sends the job to the background resuming it if it has stopped."#;
 
-pub(crate) const MAN_FG: &str = r#"NAME
+pub const MAN_FG: &str = r#"NAME
     fg - bring job to foreground
 
 SYNOPSIS
@@ -315,7 +315,7 @@ SYNOPSIS
 DESCRIPTION
     fg brings the specified job to foreground resuming it if it has stopped."#;
 
-pub(crate) const MAN_SUSPEND: &str = r#"NAME
+pub const MAN_SUSPEND: &str = r#"NAME
     suspend - suspend the current shell
 
 SYNOPSIS
@@ -325,7 +325,7 @@ DESCRIPTION
     Suspends the current shell by sending it the SIGTSTP signal,
     returning to the parent process. It can be resumed by sending it SIGCONT."#;
 
-pub(crate) const MAN_DISOWN: &str = r#"NAME
+pub const MAN_DISOWN: &str = r#"NAME
     disown - Disown processes
 
 SYNOPSIS
@@ -339,7 +339,7 @@ OPTIONS
     -h  Specifies that each job supplied will not receive the SIGHUP signal when the shell receives a SIGHUP.
     -a  If no job IDs were supplied, remove all jobs from the background process list."#;
 
-pub(crate) const MAN_EXIT: &str = r#"NAME
+pub const MAN_EXIT: &str = r#"NAME
     exit - exit the shell
 
 SYNOPSIS
@@ -348,7 +348,7 @@ SYNOPSIS
 DESCRIPTION
     Makes ion exit. The exit status will be that of the last command executed."#;
 
-pub(crate) const MAN_MATCHES: &str = r#"NAME
+pub const MAN_MATCHES: &str = r#"NAME
     matches - checks if the second argument contains any portion of the first.
 
 SYNOPSIS
@@ -364,7 +364,7 @@ EXAMPLES
     Returns false:
         matches x xs"#;
 
-pub(crate) const MAN_EXISTS: &str = r#"NAME
+pub const MAN_EXISTS: &str = r#"NAME
     exists - check whether items exist
 
 SYNOPSIS
@@ -420,7 +420,7 @@ AUTHOR
     Written by Fabian Würfl.
     Heavily based on implementation of the test builtin, which was written by Michael Murph."#;
 
-pub(crate) const MAN_WHICH: &str = r#"NAME
+pub const MAN_WHICH: &str = r#"NAME
     which - locate a program file in the current user's path
 
 SYNOPSIS
diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index 7131fa8471006872a9f482e247af3c2d27c6e8e5..1397b4b74beef03786af34cc9adb949958e033bd 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -1,15 +1,15 @@
-pub mod functions;
 pub mod man_pages;
-pub mod source;
-pub mod variables;
 
 mod command_info;
 mod exec;
 mod exists;
+mod functions;
 mod is;
 mod job_control;
 mod set;
+mod source;
 mod status;
+mod variables;
 
 use ion_builtins::{calc, conditionals, echo, random, test};
 
@@ -18,7 +18,7 @@ use self::{
     echo::echo,
     exec::exec,
     exists::exists,
-    functions::fn_,
+    functions::print_functions,
     is::is,
     man_pages::*,
     source::source,
@@ -36,13 +36,7 @@ use hashbrown::HashMap;
 use liner::Context;
 
 use crate::{
-    shell::{
-        self,
-        fork_function::fork_function,
-        job_control::{JobControl, ProcessState},
-        status::*,
-        Shell,
-    },
+    shell::{self, status::*, ProcessState, Shell},
     sys, types,
 };
 use small;
@@ -72,7 +66,7 @@ macro_rules! map {
 /// Note: To reduce allocations, function are provided as pointer rather than boxed closures
 /// ```
 /// use ion_shell::builtins::BuiltinMap;
-/// use ion_shell::shell::Shell;
+/// use ion_shell::Shell;
 ///
 /// // create a builtin
 /// let mut custom = |_args: &[small::String], _shell: &mut Shell| {
@@ -262,7 +256,7 @@ pub fn builtin_cd(args: &[small::String], shell: &mut Shell) -> i32 {
 
     match shell.directory_stack.cd(args, &shell.variables) {
         Ok(()) => {
-            let _ = fork_function(shell, "CD_CHANGE", &["ion"]);
+            let _ = shell.fork_function("CD_CHANGE", &["ion"]);
             SUCCESS
         }
         Err(why) => {
@@ -362,7 +356,7 @@ fn builtin_unalias(args: &[small::String], shell: &mut Shell) -> i32 {
 
 // TODO There is a man page for fn however the -h and --help flags are not
 // checked for.
-fn builtin_fn(_: &[small::String], shell: &mut Shell) -> i32 { fn_(&mut shell.variables) }
+fn builtin_fn(_: &[small::String], shell: &mut Shell) -> i32 { print_functions(&shell.variables) }
 
 fn builtin_read(args: &[small::String], shell: &mut Shell) -> i32 {
     if check_help(args, MAN_READ) {
diff --git a/src/lib/builtins/source.rs b/src/lib/builtins/source.rs
index 6de7be31f516d9d35e6267862d347cf3bddfcf06..7319fe4f6a4f063c5775da6be36735eb0fad7bb3 100644
--- a/src/lib/builtins/source.rs
+++ b/src/lib/builtins/source.rs
@@ -3,7 +3,7 @@ use small;
 use std::fs::File;
 
 /// Evaluates the given file and returns 'SUCCESS' if it succeeds.
-pub(crate) fn source(shell: &mut Shell, arguments: &[small::String]) -> Result<(), String> {
+pub fn source(shell: &mut Shell, arguments: &[small::String]) -> Result<(), String> {
     match arguments.get(1) {
         Some(argument) => {
             if let Ok(file) = File::open(argument.as_str()) {
diff --git a/src/lib/builtins/variables.rs b/src/lib/builtins/variables.rs
index d2010896569741c26b4cada83f0a808e2192b05f..1df4a2853ad5f128cc70131349ae41425676fa16 100644
--- a/src/lib/builtins/variables.rs
+++ b/src/lib/builtins/variables.rs
@@ -69,7 +69,7 @@ fn parse_alias(args: &str) -> Binding {
 
 /// The `alias` command will define an alias for another command, and thus may be used as a
 /// command itself.
-pub(crate) fn alias(vars: &mut Variables, args: &str) -> i32 {
+pub fn alias(vars: &mut Variables, args: &str) -> i32 {
     match parse_alias(args) {
         Binding::InvalidKey(key) => {
             eprintln!("ion: alias name, '{}', is invalid", key);
@@ -88,7 +88,7 @@ pub(crate) fn alias(vars: &mut Variables, args: &str) -> i32 {
 }
 
 /// Dropping an alias will erase it from the shell.
-pub(crate) fn drop_alias<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 {
+pub fn drop_alias<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 {
     if args.len() <= 1 {
         eprintln!("ion: you must specify an alias name");
         return FAILURE;
@@ -103,7 +103,7 @@ pub(crate) fn drop_alias<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32
 }
 
 /// Dropping an array will erase it from the shell.
-pub(crate) fn drop_array<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 {
+pub fn drop_array<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 {
     if args.len() <= 2 {
         eprintln!("ion: you must specify an array name");
         return FAILURE;
@@ -124,7 +124,7 @@ pub(crate) fn drop_array<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32
 }
 
 /// Dropping a variable will erase it from the shell.
-pub(crate) fn drop_variable<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 {
+pub fn drop_variable<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 {
     if args.len() <= 1 {
         eprintln!("ion: you must specify a variable name");
         return FAILURE;
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index 5c18f2acd3e876ea881297b1f00efe4afebb1ec6..2cda3cffd611083d70c54d7233340821c8286e5b 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -13,10 +13,7 @@ pub mod types;
 pub mod parser;
 pub mod builtins;
 mod memory;
-pub mod shell;
+mod shell;
 
 pub(crate) use self::memory::IonPool;
-pub use crate::shell::{
-    binary::MAN_ION, pipe_exec::job_control::JobControl, status, Capture, Fork, InteractiveBinary,
-    IonError, IonResult, Shell,
-};
+pub use crate::shell::*;
diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs
index 4681047bbd307c28054983afd9a81cf3b91d380c..9f352bdc244734de610a163d76e1bf5af41dd0ce 100644
--- a/src/lib/parser/mod.rs
+++ b/src/lib/parser/mod.rs
@@ -3,15 +3,16 @@ mod loops;
 pub(crate) mod pipelines;
 mod quotes;
 pub(crate) mod shell_expand;
-pub mod statement;
+mod statement;
 
-pub use self::statement::StatementSplitter;
-
-pub use self::quotes::Terminator;
 pub(crate) use self::{
     loops::ForValueExpression,
-    shell_expand::{expand_string, Expander, Select},
-    statement::parse_and_validate,
+    shell_expand::{expand_string, Select},
+};
+pub use self::{
+    quotes::Terminator,
+    shell_expand::Expander,
+    statement::{is_valid_name, parse_and_validate, StatementSplitter},
 };
 
 #[cfg(fuzzing)]
diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs
index 97d495cc86768b0a32a75af13426ee3cd386b75d..f9ec1b7234fd66c70c822e0e4705d6dc46d254ec 100644
--- a/src/lib/parser/shell_expand/mod.rs
+++ b/src/lib/parser/shell_expand/mod.rs
@@ -38,7 +38,7 @@ fn join_with_spaces<S: AsRef<str>>(input: &mut small::String, mut iter: impl Ite
 // TODO: Make array expansions iterators instead of arrays.
 // TODO: Use Cow<'a, types::Str> for hashmap values.
 /// Trait representing different elements of string expansion
-pub(crate) trait Expander: Sized {
+pub trait Expander: Sized {
     /// Expand a tilde form to the correct directory.
     fn tilde(&self, _input: &str) -> Option<String> { None }
     /// Expand an array variable with some selection.
diff --git a/src/lib/parser/statement/mod.rs b/src/lib/parser/statement/mod.rs
index 70cad1cf662125fa66c061c4e59909607c768498..da3203f30e8516fa26d0cd82502c4cf6a7758cea 100644
--- a/src/lib/parser/statement/mod.rs
+++ b/src/lib/parser/statement/mod.rs
@@ -1,18 +1,17 @@
 mod case;
 mod functions;
-#[cfg(not(fuzzing))]
-pub mod parse;
-#[cfg(fuzzing)]
-pub mod parse;
+mod parse;
 mod splitter;
 
-pub(crate) use self::parse::parse;
-pub use self::splitter::{StatementError, StatementSplitter, StatementVariant};
+pub use self::{
+    parse::{is_valid_name, parse},
+    splitter::{StatementError, StatementSplitter, StatementVariant},
+};
 use crate::{builtins::BuiltinMap, shell::flow_control::Statement};
 
 /// Parses a given statement string and return's the corresponding mapped
 /// `Statement`
-pub(crate) fn parse_and_validate<'b>(
+pub fn parse_and_validate<'b>(
     statement: Result<StatementVariant, StatementError>,
     builtins: &BuiltinMap<'b>,
 ) -> Statement<'b> {
diff --git a/src/lib/parser/statement/parse.rs b/src/lib/parser/statement/parse.rs
index aaf4f48e330eae71efe076b4f2f09cd3a4e2a7d6..5fc25d0b9e242b574714997d6fd60f6807c5bd92 100644
--- a/src/lib/parser/statement/parse.rs
+++ b/src/lib/parser/statement/parse.rs
@@ -20,7 +20,7 @@ pub fn is_valid_name(name: &str) -> bool {
         && chars.all(|b| b.is_alphanumeric() || b == '_')
 }
 
-pub(crate) fn parse<'a>(code: &str, builtins: &BuiltinMap<'a>) -> Statement<'a> {
+pub fn parse<'a>(code: &str, builtins: &BuiltinMap<'a>) -> Statement<'a> {
     let cmd = code.trim();
     match cmd {
         "end" => Statement::End,
diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs
index 47520dc442100e688a4d736dc75193e39c0f5b63..a6cc5003cca91e469aa25e0cd7a7fd8ae843a6c2 100644
--- a/src/lib/shell/assignments.rs
+++ b/src/lib/shell/assignments.rs
@@ -5,7 +5,7 @@ use super::{
 };
 use crate::{
     lexers::assignments::{Key, Operator, Primitive},
-    parser::{assignments::*, statement::parse::is_valid_name},
+    parser::{assignments::*, is_valid_name},
     shell::variables::{EuclDiv, Modifications, OpError, Pow, Value},
     types,
 };
diff --git a/src/lib/shell/binary/mod.rs b/src/lib/shell/binary/mod.rs
index f4c2b9418dfbb425a1b90f73f9699284b59af144..5248b7e1d2a4414fe8ad92d6c19e76cbb3cf2b3f 100644
--- a/src/lib/shell/binary/mod.rs
+++ b/src/lib/shell/binary/mod.rs
@@ -6,9 +6,8 @@ mod readln;
 
 use self::{history::ShellHistory, prompt::prompt, readln::readln};
 use super::{
-    pipe_exec::job_control::JobControl,
     status::{FAILURE, SUCCESS},
-    FlowLogic, Shell,
+    Shell,
 };
 use crate::{
     builtins::man_pages,
diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs
index ea18cf71bed4723bc2c1caf20eb1ec7b4c6cd496..c6cadbdb1c5e5cf2eef7dca35dba60fae0a9a74a 100644
--- a/src/lib/shell/flow.rs
+++ b/src/lib/shell/flow.rs
@@ -1,6 +1,5 @@
 use super::{
     flow_control::{insert_statement, Case, ElseIf, Function, Statement},
-    job_control::JobControl,
     signals,
     status::*,
     Shell,
@@ -19,56 +18,16 @@ use itertools::Itertools;
 use small;
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
-pub(crate) enum Condition {
+pub enum Condition {
     Continue,
     Break,
     NoOp,
     SigInt,
 }
 
-pub(crate) trait FlowLogic<'a> {
-    /// Receives a command and attempts to execute the contents.
-    fn on_command(&mut self, command_string: &str);
-
-    /// Executes all of the statements within a while block until a certain
-    /// condition is met.
-    fn execute_while(
-        &mut self,
-        expression: &[Statement<'a>],
-        statements: &[Statement<'a>],
-    ) -> Condition;
-
-    /// Executes all of the statements within a for block for each value
-    /// specified in the range.
-    fn execute_for(
-        &mut self,
-        variables: &[types::Str],
-        values: &[small::String],
-        statements: &[Statement<'a>],
-    ) -> Condition;
-
+impl<'a> Shell<'a> {
     /// Conditionally executes branches of statements according to evaluated
     /// expressions
-    fn execute_if(
-        &mut self,
-        expression: &[Statement<'a>],
-        success: &[Statement<'a>],
-        else_if: &[ElseIf<'a>],
-        failure: &[Statement<'a>],
-    ) -> Condition;
-
-    /// Simply executes all supplied statements.
-    fn execute_statements(&mut self, statements: &[Statement<'a>]) -> Condition;
-
-    /// Executes a single statement
-    fn execute_statement(&mut self, statement: &Statement<'a>) -> Condition;
-
-    /// Expand an expression and run a branch based on the value of the
-    /// expanded expression
-    fn execute_match<T: AsRef<str>>(&mut self, expression: T, cases: &[Case<'a>]) -> Condition;
-}
-
-impl<'a> FlowLogic<'a> for Shell<'a> {
     fn execute_if(
         &mut self,
         expression: &[Statement<'a>],
@@ -98,6 +57,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> {
         self.execute_statements(&failure)
     }
 
+    /// Executes all of the statements within a for block for each value
+    /// specified in the range.
     fn execute_for(
         &mut self,
         variables: &[types::Str],
@@ -148,6 +109,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> {
         Condition::NoOp
     }
 
+    /// Executes all of the statements within a while block until a certain
+    /// condition is met.
     fn execute_while(
         &mut self,
         expression: &[Statement<'a>],
@@ -168,7 +131,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> {
         }
     }
 
-    fn execute_statement(&mut self, statement: &Statement<'a>) -> Condition {
+    /// Executes a single statement
+    pub fn execute_statement(&mut self, statement: &Statement<'a>) -> Condition {
         match statement {
             Statement::Error(number) => {
                 self.previous_status = *number;
@@ -305,7 +269,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> {
         }
     }
 
-    fn execute_statements(&mut self, statements: &[Statement<'a>]) -> Condition {
+    /// Simply executes all supplied statements.
+    pub fn execute_statements(&mut self, statements: &[Statement<'a>]) -> Condition {
         self.variables.new_scope(false);
 
         let condition = statements
@@ -319,6 +284,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> {
         condition
     }
 
+    /// Expand an expression and run a branch based on the value of the
+    /// expanded expression
     fn execute_match<T: AsRef<str>>(&mut self, expression: T, cases: &[Case<'a>]) -> Condition {
         // Logic for determining if the LHS of a match-case construct (the value we are
         // matching against) matches the RHS of a match-case construct (a value
@@ -380,7 +347,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> {
         Condition::NoOp
     }
 
-    fn on_command(&mut self, command_string: &str) {
+    /// Receives a command and attempts to execute the contents.
+    pub fn on_command(&mut self, command_string: &str) {
         self.break_flow = false;
         for stmt in command_string.bytes().batching(|cmd| Terminator::new(cmd).terminate()) {
             // Go through all of the statements and build up the block stack
diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs
index 8cadd85044b29f1839dbf34259a8f8cc2819ded3..62b57250a8fcd9edcc7326d8d9878eed2262ba6d 100644
--- a/src/lib/shell/flow_control.rs
+++ b/src/lib/shell/flow_control.rs
@@ -1,7 +1,7 @@
 use crate::{
     lexers::assignments::{KeyBuf, Operator, Primitive},
     parser::{assignments::*, pipelines::Pipeline},
-    shell::{flow::FlowLogic, Shell},
+    shell::Shell,
     types,
 };
 use small;
@@ -9,7 +9,7 @@ use smallvec::SmallVec;
 use std::fmt::{self, Display, Formatter};
 
 #[derive(Debug, PartialEq, Clone)]
-pub(crate) struct ElseIf<'a> {
+pub struct ElseIf<'a> {
     pub expression: Vec<Statement<'a>>,
     pub success:    Vec<Statement<'a>>,
 }
@@ -42,7 +42,7 @@ pub(crate) struct ElseIf<'a> {
 /// Case { value: None, ... }
 /// ```
 #[derive(Debug, PartialEq, Clone)]
-pub(crate) struct Case<'a> {
+pub struct Case<'a> {
     pub value:       Option<String>,
     pub binding:     Option<String>,
     pub conditional: Option<String>,
@@ -50,13 +50,13 @@ pub(crate) struct Case<'a> {
 }
 
 #[derive(Debug, PartialEq, Clone)]
-pub(crate) enum LocalAction {
+pub enum LocalAction {
     List,
     Assign(String, Operator, String),
 }
 
 #[derive(Debug, PartialEq, Clone)]
-pub(crate) enum ExportAction {
+pub enum ExportAction {
     List,
     LocalExport(String),
     Assign(String, Operator, String),
@@ -64,7 +64,7 @@ pub(crate) enum ExportAction {
 
 // TODO: Enable statements and expressions to contain &str values.
 #[derive(Debug, PartialEq, Clone)]
-pub(crate) enum Statement<'a> {
+pub enum Statement<'a> {
     Let(LocalAction),
     Case(Case<'a>),
     Export(ExportAction),
diff --git a/src/lib/shell/fork_function.rs b/src/lib/shell/fork_function.rs
index 68713014e8a32f0ad397e93fb4052d1b7eccd458..d20bbdec9bd486f24f7e68fb3f33f5556319b217 100644
--- a/src/lib/shell/fork_function.rs
+++ b/src/lib/shell/fork_function.rs
@@ -4,32 +4,25 @@ use crate::{
 };
 use std::process;
 
-#[inline]
-pub(crate) fn command_not_found(shell: &mut Shell, command: &str) -> Result<(), ()> {
-    fork_function(shell, "COMMAND_NOT_FOUND", &["ion", command])
-}
-
-/// High-level function for executing a function programmatically.
-/// NOTE: Always add "ion" as a first argument in `args`.
-pub fn fork_function<S: AsRef<str>>(
-    shell: &mut Shell,
-    fn_name: &str,
-    args: &[S],
-) -> Result<(), ()> {
-    if let Some(Value::Function(function)) = shell.variables.get_ref(fn_name) {
-        if let Err(err) = shell.fork(Capture::None, move |child| {
-            if let Err(err) = function.execute(child, args) {
-                eprintln!("ion: {} function call: {}", fn_name, err);
+impl<'a> Shell<'a> {
+    /// High-level function for executing a function programmatically.
+    /// NOTE: Always add "ion" as a first argument in `args`.
+    pub fn fork_function<S: AsRef<str>>(&mut self, fn_name: &str, args: &[S]) -> Result<(), ()> {
+        if let Some(Value::Function(function)) = self.variables.get_ref(fn_name) {
+            if let Err(err) = self.fork(Capture::None, move |child| {
+                if let Err(err) = function.execute(child, args) {
+                    eprintln!("ion: {} function call: {}", fn_name, err);
+                }
+            }) {
+                eprintln!("ion: fork error: {}", err);
+                Err(())
+            } else {
+                // Ensure that the parent retains ownership of the terminal before exiting.
+                let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
+                Ok(())
             }
-        }) {
-            eprintln!("ion: fork error: {}", err);
-            Err(())
         } else {
-            // Ensure that the parent retains ownership of the terminal before exiting.
-            let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
-            Ok(())
+            Err(())
         }
-    } else {
-        Err(())
     }
 }
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index 22c18c48ffd1c60d9fe9699f17f042263d802c9d..409c11574520bf02f42d39d9f44a06e8435d5f2b 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -1,13 +1,13 @@
 mod assignments;
 pub(crate) mod binary;
-pub(crate) mod colors;
+mod colors;
 mod completer;
 pub(crate) mod directory_stack;
 pub(crate) mod escape;
 mod flow;
 pub(crate) mod flow_control;
 mod fork;
-pub mod fork_function;
+mod fork_function;
 mod job;
 pub(crate) mod pipe_exec;
 pub(crate) mod signals;
@@ -15,13 +15,11 @@ pub mod status;
 pub mod variables;
 
 pub use self::{
-    binary::InteractiveBinary,
-    fork::{Capture, Fork, IonResult},
-};
-pub(crate) use self::{
-    flow::FlowLogic,
+    binary::{InteractiveBinary, MAN_ION},
+    fork::{Capture, IonResult},
     job::Job,
-    pipe_exec::{foreground, job_control},
+    pipe_exec::job_control::ProcessState,
+    variables::Value,
 };
 
 use self::{
@@ -29,9 +27,10 @@ use self::{
     escape::tilde,
     flow_control::{FlowControl, Function, FunctionError},
     foreground::ForegroundSignals,
-    job_control::BackgroundProcess,
+    fork::Fork,
+    pipe_exec::{foreground, job_control::BackgroundProcess},
     status::*,
-    variables::{GetVariable, Value, Variables},
+    variables::{GetVariable, Variables},
 };
 use crate::{
     builtins::BuiltinMap,
diff --git a/src/lib/shell/pipe_exec/fork.rs b/src/lib/shell/pipe_exec/fork.rs
index 9aeeffc63a0ba85be45712794f9c92710de04c5a..d45586245e2f82acbc58c6ea8cd2fd71553e6d03 100644
--- a/src/lib/shell/pipe_exec/fork.rs
+++ b/src/lib/shell/pipe_exec/fork.rs
@@ -5,7 +5,7 @@ pub(crate) fn create_process_group(pgid: u32) { let _ = sys::setpgid(0, pgid); }
 
 use super::{
     super::{status::*, Shell},
-    job_control::{JobControl, ProcessState},
+    job_control::ProcessState,
 };
 use crate::parser::pipelines::Pipeline;
 use std::process::exit;
diff --git a/src/lib/shell/pipe_exec/job_control.rs b/src/lib/shell/pipe_exec/job_control.rs
index e1fa6ed9aee3281726e5683f9c6a520f43db766f..b86b4972764561d1f8c5e4d8ffb3a6b4479de8c8 100644
--- a/src/lib/shell/pipe_exec/job_control.rs
+++ b/src/lib/shell/pipe_exec/job_control.rs
@@ -18,22 +18,6 @@ pub(crate) fn set_foreground_as(pid: u32) {
     signals::unblock();
 }
 
-pub trait JobControl {
-    /// Waits for background jobs to finish before returning.
-    fn wait_for_background(&mut self);
-    /// Takes a background tasks's PID and whether or not it needs to be continued; resumes the
-    /// task
-    /// and sets it as the foreground process. Once the task exits or stops, the exit status
-    /// will
-    /// be returned, and ownership of the TTY given back to the shell.
-    fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32;
-    fn resume_stopped(&mut self);
-    fn handle_signal(&self, signal: i32) -> bool;
-    fn background_send(&self, signal: i32);
-    fn watch_foreground<T: Into<String>>(&mut self, pid: i32, command: T) -> i32;
-    fn send_to_background(&mut self, child: u32, state: ProcessState, command: String);
-}
-
 #[derive(Clone, Copy, Debug, PartialEq)]
 /// Defines whether the background process is running or stopped.
 pub enum ProcessState {
@@ -90,10 +74,10 @@ pub struct BackgroundProcess {
     pub name:          String,
 }
 
-impl<'a> JobControl for Shell<'a> {
+impl<'a> Shell<'a> {
     /// If a SIGTERM is received, a SIGTERM will be sent to all background processes
     /// before the shell terminates itself.
-    fn handle_signal(&self, signal: i32) -> bool {
+    pub fn handle_signal(&self, signal: i32) -> bool {
         if signal == sys::SIGTERM || signal == sys::SIGHUP {
             self.background_send(signal);
             true
@@ -102,7 +86,7 @@ impl<'a> JobControl for Shell<'a> {
         }
     }
 
-    fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) {
+    pub fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) {
         // Increment the `Arc` counters so that these fields can be moved into
         // the upcoming background thread.
         let processes = self.background.clone();
@@ -117,13 +101,11 @@ impl<'a> JobControl for Shell<'a> {
         // Spawn a background thread that will monitor the progress of the
         // background process, updating it's state changes until it finally
         // exits.
-        let _ = spawn(move || {
-            watch_background(&fg_signals, &processes, pid, njob as usize);
-        });
+        let _ = spawn(move || watch_background(&fg_signals, &processes, pid, njob as usize));
     }
 
     /// Send a kill signal to all running background tasks.
-    fn background_send(&self, signal: i32) {
+    pub fn background_send(&self, signal: i32) {
         if signal == sys::SIGHUP {
             for process in self.background.lock().unwrap().iter() {
                 if !process.ignore_sighup {
@@ -140,7 +122,7 @@ impl<'a> JobControl for Shell<'a> {
     }
 
     /// Resumes all stopped background jobs
-    fn resume_stopped(&mut self) {
+    pub fn resume_stopped(&mut self) {
         for process in self.background.lock().unwrap().iter() {
             if process.state == ProcessState::Stopped {
                 signals::resume(process.pid);
@@ -148,7 +130,7 @@ impl<'a> JobControl for Shell<'a> {
         }
     }
 
-    fn watch_foreground<T: Into<String>>(&mut self, pid: i32, command: T) -> i32 {
+    pub fn watch_foreground<T: Into<String>>(&mut self, pid: i32, command: T) -> i32 {
         let mut signaled = 0;
         let mut exit_status = 0;
 
@@ -200,7 +182,7 @@ impl<'a> JobControl for Shell<'a> {
 
     /// Waits until all running background tasks have completed, and listens for signals in the
     /// event that a signal is sent to kill the running tasks.
-    fn wait_for_background(&mut self) {
+    pub fn wait_for_background(&mut self) {
         while self.background.lock().unwrap().iter().any(|p| p.state == ProcessState::Running) {
             if let Some(signal) = signals::SignalHandler.find(|&s| s != sys::SIGTSTP) {
                 self.background_send(signal);
@@ -210,7 +192,10 @@ impl<'a> JobControl for Shell<'a> {
         }
     }
 
-    fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 {
+    /// Takes a background tasks's PID and whether or not it needs to be continued; resumes the
+    /// task and sets it as the foreground process. Once the task exits or stops, the exit status
+    /// will be returned, and ownership of the TTY given back to the shell.
+    pub fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 {
         // Pass the TTY to the background job
         set_foreground_as(pid);
         // Signal the background thread that is waiting on this process to stop waiting.
diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs
index 3f96a8ae4d381e22dbc85dff858a55714353c19a..a021d373af786a8cf81f296f9f05d596a1e11c0d 100644
--- a/src/lib/shell/pipe_exec/mod.rs
+++ b/src/lib/shell/pipe_exec/mod.rs
@@ -12,13 +12,12 @@ mod pipes;
 pub mod streams;
 
 use self::{
-    job_control::{JobControl, ProcessState},
+    job_control::ProcessState,
     pipes::TeePipe,
     streams::{duplicate_streams, redirect_streams},
 };
 use super::{
     flow_control::{Function, FunctionError},
-    fork_function::command_not_found,
     job::{Job, JobVariant, RefinedJob, TeeItem},
     signals::{self, SignalHandler},
     status::*,
@@ -224,7 +223,7 @@ impl<'b> Shell<'b> {
                 self.watch_foreground(-(pid as i32), "")
             }
             Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
-                if let Err(_) = command_not_found(self, &name) {
+                if self.fork_function("COMMAND_NOT_FOUND", &["ion", &name]).is_err() {
                     eprintln!("ion: command not found: {}", name);
                 }
                 NO_SUCH_COMMAND
@@ -538,7 +537,7 @@ fn spawn_proc(
                     *current_pid = pid;
                 }
                 Err(ref mut err) if err.kind() == io::ErrorKind::NotFound => {
-                    if let Err(_) = command_not_found(shell, &name) {
+                    if shell.fork_function("COMMAND_NOT_FOUND", &["ion", &name]).is_err() {
                         eprintln!("ion: command not found: {}", name);
                     }
                 }
diff --git a/src/main.rs b/src/main.rs
index 29903013c554c7786d62c5e9589b3d887545c696..2eddf4592ad294b186a196a62d378977d551b572 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
 extern crate ion_sys as sys;
 
-use ion_shell::{shell::variables::Value, InteractiveBinary, JobControl, Shell, MAN_ION};
+use ion_shell::{InteractiveBinary, Shell, Value, MAN_ION};
 use liner::KeyBindings;
 use std::{
     alloc::System,