diff --git a/.gitignore b/.gitignore
index a28d669..e8762be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
-mason_packages
\ No newline at end of file
+mason_packages
+cli
+cli.dSYM
+*.o
\ No newline at end of file
diff --git a/.mason b/.mason
index 95ab9ca..6bfa09e 160000
--- a/.mason
+++ b/.mason
@@ -1 +1 @@
-Subproject commit 95ab9caff80d6327a13ed48f97c72ae4da4454bd
+Subproject commit 6bfa09e828b496826d405b74a329c508a3307b8c
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6cb1d4b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,45 @@
+language: generic
+
+env:
+  global:
+   - ASAN_OPTIONS=suppressions=asan_suppressions.txt
+
+matrix:
+  include:
+    - os: linux
+      sudo: false
+      env: CXX=g++-5 BUILDTYPE=Release
+      addons:
+        apt:
+          sources: [ 'ubuntu-toolchain-r-test' ]
+          packages: [ 'g++-5' ]
+    - os: linux
+      sudo: false
+      env: CXX=clang++-3.8 BUILDTYPE=Release
+      addons:
+        apt:
+          sources: [ 'ubuntu-toolchain-r-test' ]
+          packages: [ 'libstdc++6', 'libstdc++-5-dev' ]
+    - os: linux
+      sudo: false
+      env: CXX=clang++-3.8 BUILDTYPE=Debug CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address"
+      addons:
+        apt:
+          sources: [ 'ubuntu-toolchain-r-test' ]
+          packages: [ 'libstdc++6', 'libstdc++-5-dev' ]
+    - os: osx
+      osx_image: xcode7.3
+      env: BUILDTYPE=Release
+
+cache: apt
+
+install:
+  - | 
+    if [[ ${CXX} =~ "clang" ]] && [[ $(uname -s) == "Linux" ]]; then
+      git clone --depth=1 https://github.com/mapbox/mason.git ./.mason
+      ./.mason/mason install clang 3.8.0
+      export PATH=$(./.mason/mason prefix clang 3.8.0)/bin:${PATH}
+      which clang++
+    fi
+script:
+  - make test
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 5dedb0c..67a2bf5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,39 @@
 CC := $(CC)
 CXX := $(CXX)
 CXXFLAGS := $(CXXFLAGS) -Iinclude -Imason_packages/.link/include -std=c++11
+LDFLAGS := $(LDFLAGS) -lboost_program_options -lboost_system
+
+MASON ?= .mason/mason
+MASON_HOME := ./mason_packages/.link
+
 RELEASE_FLAGS := -O3 -DNDEBUG
 WARNING_FLAGS := -Wall -Wextra -Werror -Wsign-compare -Wfloat-equal -Wfloat-conversion -Wshadow -Wno-unsequenced
 DEBUG_FLAGS := -g -O0 -DDEBUG -fno-inline-functions -fno-omit-frame-pointer
-MASON ?= .mason/mason
 
-default: test
+ifeq ($(BUILDTYPE),Release)
+	FINAL_FLAGS := -g $(WARNING_FLAGS) $(RELEASE_FLAGS)
+else
+	FINAL_FLAGS := -g $(WARNING_FLAGS) $(DEBUG_FLAGS)
+endif
 
-clean:
-	rm -rf mason_packages
+default: cli
+
+cli: mason_packages clean
+	$(CXX) cli.cpp -o cli -isystem$(MASON_HOME)/include $(CXXFLAGS) $(FINAL_FLAGS) $(LDFLAGS)
+
+test: cli
+	./tests/cli.test.sh
 
 $(MASON):
 	git submodule update --init
 
 mason_packages: $(MASON)
-	$(MASON) install hpp_skel 0.0.1
+	$(MASON) install hpp_skel 1.0.0 && $(MASON) link hpp_skel 1.0.0
+	$(MASON) install boost_libprogram_options 1.61.0 && $(MASON) link boost_libprogram_options 1.61.0
+
+clean:
+	rm -f cli
+	rm -f *.o
 
-cli: mason_packages
-	# $(CXX) cli.cpp -o cli -isystem$(MASON_HOME)/include -L$(MASON_HOME)/lib $(CXXFLAGS) $(FINAL_FLAGS) $(LDFLAGS);
\ No newline at end of file
+clean-mason:
+	rm -rf mason_packages
\ No newline at end of file
diff --git a/cli.cpp b/cli.cpp
index e872413..75241fd 100644
--- a/cli.cpp
+++ b/cli.cpp
@@ -1,12 +1,50 @@
-#include <hello_world.hpp>
+#include <hello_world/hello_world.hpp>
+#include <boost/program_options.hpp>
 
 #include <iostream>
 #include <cassert>
 
-using namespace hello_world;
+#define VERSION "v0.0.1\n"
 
-int main() {
-  std::string value = exclaim("hello");
-  std::clog << value;
-  return 0;
+using namespace boost::program_options;
+
+int main(int argc, char** argv) {
+  try {
+    options_description desc("\nusage: cli <phrase>");
+
+    desc.add_options()
+    ("help,h", "show help")
+    ("version,v", "show version number")
+    ("phrase,p", value<std::string>(), "Input phrase");
+
+    positional_options_description p;
+    p.add("phrase", 1);
+    variables_map vm;
+
+    auto parser = command_line_parser(argc, argv).options(desc).positional(p);
+    store(parser.run(), vm);
+
+    if (vm.count("help")) {
+      std::cout << desc;
+    } else if (vm.count("version")) {
+      std::cout << VERSION;
+    } else {
+      std::string phrase;
+      if (vm.count("phrase")) {
+        phrase = vm["phrase"].as<std::string>();
+      } else {
+        std::cout << "Error: you must pass in a phrase to be exclaimed." << "\n";
+        return 1;
+      }
+
+      // use hello_world::exclaim
+      std::string output(hello_world::exclaim(phrase));
+      std::cout << output << "\n";
+
+      return 0;
+    }
+  } catch (std::exception const& ex) {
+    std::cout << "error: " << ex.what() << "\n";
+    return -1;
+  }
 }
\ No newline at end of file
diff --git a/tests/cli.test.sh b/tests/cli.test.sh
new file mode 100755
index 0000000..308190a
--- /dev/null
+++ b/tests/cli.test.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+# run cli command directly in bash, capture the result and test it
+PWD=$(pwd)
+RESULT=$($PWD/cli waka)
+if [ $RESULT == "!waka!" ]
+then
+  echo "Success."
+  exit 0
+else
+  echo "Test failed: cli output is not as expected."
+  echo "EXPECTED: !waka! - ACTUAL: $RESULT"
+  exit -1
+fi
\ No newline at end of file
