diff --git a/src/builtins/functions.rs b/src/builtins/functions.rs
index 37400a5832c9fde13335b316e9f11fe4f7f404ba..3efda681808e687db3c4b97d9a837826cb38127c 100644
--- a/src/builtins/functions.rs
+++ b/src/builtins/functions.rs
@@ -9,7 +9,7 @@ fn print_functions(functions: &FnvHashMap<Identifier, Function>) {
     let stdout = &mut stdout.lock();
     let _ = writeln!(stdout, "# Functions");
     for fn_name in functions.keys() {
-        let description = &functions.get(fn_name).unwrap().description;
+        let description = &functions.get(fn_name).unwrap().get_description();
         if let Some(ref description) = *description {
             let _ = writeln!(stdout, "    {} -- {}", fn_name, description);
         } else {
diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs
index c69f4f11ca4e268b930c5c5c2679984c66ecac12..b03f72ca33feb3a617935ecceb33f2c2b21d44bf 100644
--- a/src/builtins/mod.rs
+++ b/src/builtins/mod.rs
@@ -33,7 +33,7 @@ use sys;
 /// Structure which represents a Terminal's command.
 /// This command structure contains a name, and the code which run the
 /// functionnality associated to this one, with zero, one or several argument(s).
-pub(crate) struct Builtin {
+pub struct Builtin {
     pub name: &'static str,
     pub help: &'static str,
     pub main: fn(&[&str], &mut Shell) -> i32,
@@ -41,7 +41,7 @@ pub(crate) struct Builtin {
 
 impl Builtin {
     /// Return the map from command names to commands
-    pub(crate) fn map() -> FnvHashMap<&'static str, Self> {
+    pub fn map() -> FnvHashMap<&'static str, Self> {
         let mut commands: FnvHashMap<&str, Self> =
             FnvHashMap::with_capacity_and_hasher(32, Default::default());
 
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..836cebf15172c902e75a4da869a76f27401b9861
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,44 @@
+#![allow(unknown_lints)]
+#![allow(while_let_on_iterator)]
+extern crate app_dirs;
+#[macro_use]
+extern crate bitflags;
+extern crate calc;
+extern crate fnv;
+extern crate glob;
+#[macro_use]
+extern crate lazy_static;
+#[cfg(all(unix, not(target_os = "redox")))]
+extern crate libc;
+#[cfg(all(unix, not(target_os = "redox")))]
+extern crate libloading;
+extern crate liner;
+#[cfg(all(unix, not(target_os = "redox")))]
+extern crate nix;
+extern crate regex;
+extern crate smallstring;
+extern crate smallvec;
+#[cfg(target_os = "redox")]
+extern crate syscall;
+extern crate unicode_segmentation;
+#[cfg(all(unix, not(target_os = "redox")))]
+extern crate users as users_unix;
+
+#[cfg(target_os = "redox")]
+#[path = "sys/redox.rs"]
+mod sys;
+
+#[cfg(unix)]
+#[path = "sys/unix.rs"]
+mod sys;
+
+#[macro_use]
+mod types;
+#[macro_use]
+mod parser;
+mod builtins;
+pub mod shell;
+mod ascii_helpers;
+
+pub use builtins::Builtin;
+pub use shell::Shell;
diff --git a/src/shell/binary.rs b/src/shell/binary.rs
index addcc4d8295758ab4dc64d0841256fe11fb23e3b..c844cf91dbc1e1d0f1e5281444fe940ea7095b6f 100644
--- a/src/shell/binary.rs
+++ b/src/shell/binary.rs
@@ -4,6 +4,7 @@ use super::{DirectoryStack, FlowLogic, JobControl, Shell, ShellHistory, Variable
 use super::completer::*;
 use super::flow_control::Statement;
 use super::status::*;
+use super::library::IonLibrary;
 use liner::{BasicCompleter, Buffer, Context, CursorPosition, Event, EventKind};
 use parser::*;
 use parser::QuoteTerminator;
@@ -11,7 +12,7 @@ use smallstring::SmallString;
 use smallvec::SmallVec;
 use std::env;
 use std::fs::File;
-use std::io::{self, ErrorKind, Read, Write};
+use std::io::{self, ErrorKind, Write};
 use std::iter::{self, FromIterator};
 use std::mem;
 use std::path::{Path, PathBuf};
@@ -27,8 +28,6 @@ pub(crate) trait Binary {
     fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
     /// Creates an interactive session that reads from a prompt provided by Liner.
     fn execute_interactive(self);
-    /// Executes all of the statements contained within a given script.
-    fn execute_script<P: AsRef<Path>>(&mut self, path: P);
     /// Ensures that read statements from a script are terminated.
     fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I);
     /// Ensures that read statements from the interactive prompt is terminated.
@@ -300,7 +299,9 @@ impl<'a> Binary for Shell<'a> {
                         array.push(arg.into());
                     }
                     self.variables.set_array("args", array);
-                    self.execute_script(&path);
+                    if let Err(err) = self.execute_script(&path) {
+                        eprintln!("ion: {}", err);
+                    }
                 }
             }
 
@@ -316,31 +317,6 @@ impl<'a> Binary for Shell<'a> {
         println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
         process::exit(0);
     }
-
-    fn execute_script<P: AsRef<Path>>(&mut self, path: P) {
-        let path = path.as_ref();
-        match File::open(path) {
-            Ok(mut file) => {
-                let capacity = file.metadata().ok().map_or(0, |x| x.len());
-                let mut command_list = String::with_capacity(capacity as usize);
-                match file.read_to_string(&mut command_list) {
-                    Ok(_) => {
-                        self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned()))
-                    }
-                    Err(err) => {
-                        let stderr = io::stderr();
-                        let mut stderr = stderr.lock();
-                        let _ = writeln!(stderr, "ion: failed to read {:?}: {}", path, err);
-                    }
-                }
-            }
-            Err(err) => {
-                let stderr = io::stderr();
-                let mut stderr = stderr.lock();
-                let _ = writeln!(stderr, "ion: failed to open {:?}: {}", path, err);
-            }
-        }
-    }
 }
 
 fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
diff --git a/src/shell/directory_stack.rs b/src/shell/directory_stack.rs
index fa1cf29ac352a6dabb4fb08cf0468a8881efc643..25684f2afcc328093dc0e2acf1138aa727954bdb 100644
--- a/src/shell/directory_stack.rs
+++ b/src/shell/directory_stack.rs
@@ -5,7 +5,7 @@ use std::collections::VecDeque;
 use std::env::{current_dir, home_dir, set_current_dir};
 use std::path::PathBuf;
 
-pub(crate) struct DirectoryStack {
+pub struct DirectoryStack {
     dirs: VecDeque<PathBuf>, // The top is always the current directory
 }
 
diff --git a/src/shell/flags.rs b/src/shell/flags.rs
index 7322416ec2bdec8974b879e29da48f7378c68efd..e2a72ceddd7467196e31585f7ce78a6c31e93d34 100644
--- a/src/shell/flags.rs
+++ b/src/shell/flags.rs
@@ -1,2 +1,2 @@
-pub(crate) const ERR_EXIT: u8 = 1;
-pub(crate) const PRINT_COMMS: u8 = 2;
+pub const ERR_EXIT: u8 = 1;
+pub const PRINT_COMMS: u8 = 2;
diff --git a/src/shell/flow.rs b/src/shell/flow.rs
index 0c825b4049eeb7387c512ad172d8c127683180b9..7ceb281449e8faa8043464293da9d228b4b4d357 100644
--- a/src/shell/flow.rs
+++ b/src/shell/flow.rs
@@ -195,12 +195,7 @@ impl<'a> FlowLogic for Shell<'a> {
                         } => {
                             shell.functions.insert(
                                 name.clone(),
-                                Function {
-                                    name:        name,
-                                    args:        args,
-                                    statements:  statements,
-                                    description: description,
-                                },
+                                Function::new(description, name, args, statements),
                             );
                         }
                         Statement::If {
@@ -449,12 +444,7 @@ impl<'a> FlowLogic for Shell<'a> {
                 collect_loops(&mut iterator, &mut statements, &mut self.flow_control.level);
                 self.functions.insert(
                     name.clone(),
-                    Function {
-                        description: description,
-                        name:        name,
-                        args:        args,
-                        statements:  statements,
-                    },
+                    Function::new(description, name, args, statements),
                 );
             }
             Statement::Pipeline(mut pipeline) => {
@@ -737,12 +727,7 @@ impl<'a> FlowLogic for Shell<'a> {
                     // All blocks were read, thus we can add it to the list
                     self.functions.insert(
                         name.clone(),
-                        Function {
-                            description: description,
-                            name:        name,
-                            args:        args,
-                            statements:  statements,
-                        },
+                        Function::new(description, name, args, statements),
                     );
                 } else {
                     // Store the partial function declaration in memory.
diff --git a/src/shell/flow_control.rs b/src/shell/flow_control.rs
index 8144064b988682a65ad06fefa13a6db13a6a522e..e65135af570d90230b9f9cb46b7256d7e4e7c6ac 100644
--- a/src/shell/flow_control.rs
+++ b/src/shell/flow_control.rs
@@ -123,11 +123,11 @@ impl Default for FlowControl {
 }
 
 #[derive(Clone)]
-pub(crate) struct Function {
-    pub description: Option<String>,
-    pub name:        Identifier,
-    pub args:        Vec<KeyBuf>,
-    pub statements:  Vec<Statement>,
+pub struct Function {
+    description: Option<String>,
+    name:        Identifier,
+    args:        Vec<KeyBuf>,
+    statements:  Vec<Statement>,
 }
 
 pub(crate) enum FunctionError {
@@ -136,6 +136,19 @@ pub(crate) enum FunctionError {
 }
 
 impl Function {
+    pub(crate) fn new(
+        description: Option<String>,
+        name: Identifier,
+        args: Vec<KeyBuf>,
+        statements: Vec<Statement>
+    ) -> Function {
+        Function { description, name, args, statements }
+    }
+
+    pub(crate) fn get_description<'a>(&'a self) -> Option<&'a String> {
+        self.description.as_ref()
+    }
+
     pub(crate) fn execute(self, shell: &mut Shell, args: &[&str]) -> Result<(), FunctionError> {
         if args.len() - 1 != self.args.len() {
             return Err(FunctionError::InvalidArgumentCount);
diff --git a/src/shell/library.rs b/src/shell/library.rs
new file mode 100644
index 0000000000000000000000000000000000000000..da1fb44ca1f818d373016fe534eaa12c4d33613c
--- /dev/null
+++ b/src/shell/library.rs
@@ -0,0 +1,29 @@
+use super::{FlowLogic, Binary, Shell};
+use std::io::{self, Read};
+use std::fs::File;
+use std::path::Path;
+
+pub trait IonLibrary {
+    /// Executes the given command and returns the exit status.
+    fn execute_command(&mut self, command: &str) -> i32;
+    /// Executes all of the statements contained within a given script,
+    /// returning the final exit status.
+    fn execute_script<P: AsRef<Path>>(&mut self, path: P) -> io::Result<i32>;
+}
+
+impl<'a> IonLibrary for Shell<'a> {
+    fn execute_command(&mut self, command: &str) -> i32 {
+        self.on_command(command);
+        self.previous_status
+    }
+
+    fn execute_script<P: AsRef<Path>>(&mut self, path: P) -> io::Result<i32> {
+        let path = path.as_ref();
+        let mut file = File::open(path)?;
+        let capacity = file.metadata().ok().map_or(0, |x| x.len());
+        let mut command_list = String::with_capacity(capacity as usize);
+        let _ = file.read_to_string(&mut command_list)?;
+        self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned()));
+        Ok(self.previous_status)
+    }
+}
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index ab97e46ed99efe56072a170f2efab9a2eeb8095e..d8ba3292d57b87e261c6479c415078b66ee653d3 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -5,14 +5,15 @@ mod flow;
 mod history;
 mod job;
 mod pipe_exec;
-pub mod colors;
-pub mod directory_stack;
+pub(crate) mod colors;
+pub(crate) mod directory_stack;
 pub mod flags;
-pub mod plugins;
-pub mod flow_control;
-pub mod signals;
+pub(crate) mod plugins;
+pub(crate) mod flow_control;
+pub(crate) mod signals;
 pub mod status;
 pub mod variables;
+pub mod library;
 
 pub(crate) use self::binary::Binary;
 pub(crate) use self::flow::FlowLogic;
@@ -25,6 +26,7 @@ use self::flags::*;
 use self::flow_control::{FlowControl, Function, FunctionError};
 use self::foreground::ForegroundSignals;
 use self::job_control::{BackgroundProcess, JobControl};
+use self::library::IonLibrary;
 use self::pipe_exec::PipelineExecution;
 use self::status::*;
 use self::variables::Variables;
@@ -49,7 +51,7 @@ use types::*;
 /// the entirety of the
 /// program. It is initialized at the beginning of the program, and lives until the end of the
 /// program.
-pub(crate) struct Shell<'a> {
+pub struct Shell<'a> {
     /// Contains a list of built-in commands that were created when the program started.
     pub builtins: &'a FnvHashMap<&'static str, Builtin>,
     /// Contains the history, completions, and manages writes to the history file.
@@ -80,7 +82,7 @@ pub(crate) struct Shell<'a> {
     pub break_flow: bool,
     /// When the `fg` command is run, this will be used to communicate with the specified
     /// background process.
-    pub foreground_signals: Arc<ForegroundSignals>,
+    foreground_signals: Arc<ForegroundSignals>,
     /// Stores the patterns used to determine whether a command should be saved in the history
     /// or not
     ignore_setting: IgnoreSetting,
@@ -88,7 +90,7 @@ pub(crate) struct Shell<'a> {
 
 impl<'a> Shell<'a> {
     /// Panics if DirectoryStack construction fails
-    pub(crate) fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> {
+    pub fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> {
         Shell {
             builtins:            builtins,
             context:             None,
@@ -147,7 +149,7 @@ impl<'a> Shell<'a> {
     }
 
     /// Evaluates the source init file in the user's home directory.
-    pub(crate) fn evaluate_init_file(&mut self) {
+    pub fn evaluate_init_file(&mut self) {
         match app_root(
             AppDataType::UserConfig,
             &AppInfo {
@@ -158,7 +160,9 @@ impl<'a> Shell<'a> {
             Ok(mut initrc) => {
                 initrc.push("initrc");
                 if initrc.exists() {
-                    self.execute_script(&initrc);
+                    if let Err(err) = self.execute_script(&initrc) {
+                        eprintln!("ion: {}", err);
+                    }
                 } else {
                     eprintln!("ion: creating initrc file at {:?}", initrc);
                     if let Err(why) = File::create(initrc) {
diff --git a/src/shell/pipe_exec/job_control.rs b/src/shell/pipe_exec/job_control.rs
index b0679124b265df32d8073a2c677c5e9b91f62efc..55b8f80eaf90d2731e2bf1b316506c0f427eba4d 100644
--- a/src/shell/pipe_exec/job_control.rs
+++ b/src/shell/pipe_exec/job_control.rs
@@ -44,7 +44,7 @@ pub(crate) trait JobControl {
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 /// Defines whether the background process is running or stopped.
-pub(crate) enum ProcessState {
+pub enum ProcessState {
     Running,
     Stopped,
     Empty,
@@ -95,7 +95,7 @@ pub(crate) fn add_to_background(
 /// by the shell. The shell will only retain information about the process, such
 /// as the process ID, state that the process is in, and the command that the
 /// process is executing.
-pub(crate) struct BackgroundProcess {
+pub struct BackgroundProcess {
     pub pid:           u32,
     pub ignore_sighup: bool,
     pub state:         ProcessState,
diff --git a/src/shell/status.rs b/src/shell/status.rs
index 64715262732b7e73c1cf556e0b1c0169d51e3f2e..65d0b1ef980c9dc25fbbd08d9fb91aa77e1b6c90 100644
--- a/src/shell/status.rs
+++ b/src/shell/status.rs
@@ -1,8 +1,8 @@
-pub(crate) const SUCCESS: i32 = 0;
-pub(crate) const FAILURE: i32 = 1;
-pub(crate) const BAD_ARG: i32 = 2;
-pub(crate) const COULD_NOT_EXEC: i32 = 126;
-pub(crate) const NO_SUCH_COMMAND: i32 = 127;
-pub(crate) const TERMINATED: i32 = 143;
+pub const SUCCESS: i32 = 0;
+pub const FAILURE: i32 = 1;
+pub const BAD_ARG: i32 = 2;
+pub const COULD_NOT_EXEC: i32 = 126;
+pub const NO_SUCH_COMMAND: i32 = 127;
+pub const TERMINATED: i32 = 143;
 
-pub(crate) fn get_signal_code(signal: i32) -> i32 { 128 + signal }
+pub fn get_signal_code(signal: i32) -> i32 { 128 + signal }
diff --git a/src/shell/variables/mod.rs b/src/shell/variables/mod.rs
index e641c2e31865b1623a058c305b28d613a720f847..fe82abfda305ffd1bf6ac5a956700b5571fe3b07 100644
--- a/src/shell/variables/mod.rs
+++ b/src/shell/variables/mod.rs
@@ -22,7 +22,7 @@ lazy_static! {
 }
 
 #[derive(Debug)]
-pub(crate) struct Variables {
+pub struct Variables {
     pub hashmaps:  HashMapVariableContext,
     pub arrays:    ArrayVariableContext,
     pub variables: VariableContext,
@@ -111,7 +111,7 @@ impl Variables {
         SUCCESS
     }
 
-    pub(crate) fn set_var(&mut self, name: &str, value: &str) {
+    pub fn set_var(&mut self, name: &str, value: &str) {
         if name == "NS_PLUGINS" {
             match value {
                 "0" => self.disable_plugins(),
@@ -132,7 +132,7 @@ impl Variables {
         }
     }
 
-    pub(crate) fn set_array(&mut self, name: &str, value: Array) {
+    pub fn set_array(&mut self, name: &str, value: Array) {
         if !name.is_empty() {
             if value.is_empty() {
                 self.arrays.remove(name);
@@ -156,11 +156,11 @@ impl Variables {
         }
     }
 
-    pub(crate) fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) }
+    pub fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) }
 
-    pub(crate) fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) }
+    pub fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) }
 
-    pub(crate) fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }
+    pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }
 
     /// Obtains the value for the **SWD** variable.
     ///
@@ -202,7 +202,7 @@ impl Variables {
         swd
     }
 
-    pub(crate) fn get_var(&self, name: &str) -> Option<Value> {
+    pub fn get_var(&self, name: &str) -> Option<Value> {
         match name {
             "SWD" => return Some(self.get_simplified_directory()),
             "MWD" => return Some(self.get_minimal_directory()),
@@ -253,11 +253,11 @@ impl Variables {
         }
     }
 
-    pub(crate) fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() }
+    pub fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() }
 
-    pub(crate) fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) }
+    pub fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) }
 
-    pub(crate) fn get_vars(&self) -> Vec<Identifier> {
+    pub fn get_vars(&self) -> Vec<Identifier> {
         self.variables.keys().cloned().chain(env::vars().map(|(k, _)| k.into())).collect()
     }