diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Vanessa McHale (c) 2016
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Vanessa McHale nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Web.Exec
+
+main :: IO ()
+main = exec
diff --git a/clit.cabal b/clit.cabal
new file mode 100644
--- /dev/null
+++ b/clit.cabal
@@ -0,0 +1,40 @@
+name:                clit
+version:             0.1.0.0
+synopsis:            Post tweets that you pipe to stdin
+description:         Please see README.md
+homepage:            https://github.com/vmchale/command-line-tweeter#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Vanessa McHale
+maintainer:          tmchale@wisc.edu
+copyright:           2016 Vanessa McHale
+category:            Web
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Web.Tweet
+                     , Web.Exec
+  build-depends:       base >= 4.7 && < 5
+                     , aeson
+                     , http-client-tls
+                     , http-client
+                     , http-types
+                     , authenticate-oauth
+                     , bytestring
+                     , split
+                     , optparse-applicative
+  default-language:    Haskell2010
+
+executable tweet
+  hs-source-dirs:      app
+  main-is:             Main.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -O2
+  build-depends:       base
+                     , clit
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/vmchale/command-line-tweeter
diff --git a/src/Web/Exec.hs b/src/Web/Exec.hs
new file mode 100644
--- /dev/null
+++ b/src/Web/Exec.hs
@@ -0,0 +1,46 @@
+-- | Provides IO action that parses command line options and tweets from stdin
+module Web.Exec (exec) where
+
+import Web.Tweet
+import Options.Applicative
+import qualified Data.ByteString.Char8 as BS
+import Data.List.Split (chunksOf)
+
+-- | Executes parser
+exec :: IO ()
+exec = execParser opts >>= select
+    where 
+        opts = info (helper <*> program)
+            (fullDesc
+            <> progDesc "Tweet from stdin!"
+            <> header "clit - a Command Line Interface Tweeter")
+
+-- | query twitter to post stdin
+fromStdIn :: Int -> FilePath -> IO ()
+fromStdIn num filepath = do
+    content <- fmap ((take num) . (map BS.pack) .  (chunksOf 140)) getContents
+    sequence_ $ fmap (tweet filepath) content
+
+-- | Data type for our program; just one optional argument for path to credential file.
+data Program = Program { cred :: Maybe FilePath , tweets :: Maybe String }
+
+-- | Executes program
+select :: Program -> IO ()
+select (Program Nothing (Just n)) = fromStdIn (read n) ".cred"
+select (Program Nothing Nothing) = fromStdIn 4 ".cred"
+select (Program (Just file) (Just n)) = fromStdIn (read n) file
+select (Program (Just file) Nothing) = fromStdIn 4 file
+
+-- | Parser to return a program datatype
+program :: Parser Program
+program = Program
+    <$> (optional $ strOption
+        (long "cred"
+        <> short 'c'
+        <> metavar "CREDENTIALS"
+        <> help "path to credentials"))
+    <*> (optional $ strOption
+        (long "tweets"
+        <> short 't'
+        <> metavar "NUM"
+        <> help "Number of tweets in a row, default 4"))
diff --git a/src/Web/Tweet.hs b/src/Web/Tweet.hs
new file mode 100644
--- /dev/null
+++ b/src/Web/Tweet.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveGeneric     #-}
+
+-- | Various utilities to tweet using the twitter api
+-- 
+-- Make sure you have a file credentials file (default `.cred`) with the following info in the correct order:
+--
+-- api-key: API_KEY
+--
+-- api-sec: API_SECRE
+--
+-- tok: OAUTH_TOKEN
+--
+-- tok-sec: TOKEN_SECRET
+
+module Web.Tweet
+    ( tweet
+    , signRequest
+    , urlString
+    ) where
+
+import Data.Aeson
+import GHC.Generics
+import Network.HTTP.Client
+import Network.HTTP.Client.TLS
+import Network.HTTP.Types.Status (statusCode)
+import Web.Authenticate.OAuth
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import Data.Char (toLower)
+
+-- | Data type for our request
+data Tweet = Tweet
+    { status    :: String
+    , trim_user :: Bool
+    } deriving Generic
+
+instance ToJSON Tweet where
+
+-- | tweet a byteString, with credentials from a given file
+tweet :: FilePath -> BS.ByteString -> IO ()
+tweet filepath content = do
+    requestString <- urlString content
+    manager <- newManager tlsManagerSettings
+    initialRequest <- parseRequest ("https://api.twitter.com/1.1/statuses/update.json" ++ requestString)
+    request <- signRequest filepath $ initialRequest { method = "POST" }
+    response request manager
+
+-- | print output of a request
+response :: Request -> Manager -> IO ()
+response request manager = do
+    response <- httpLbs request manager
+    putStrLn $ "The status code was: " ++ show (statusCode $ responseStatus response)
+    BSL.putStrLn $ responseBody response
+
+-- | Sign a request using your OAuth dev token.
+-- Uses the IO monad because signatures require a timestamp
+signRequest :: FilePath -> Request -> IO Request
+signRequest filepath req = do
+    o <- oAuth filepath
+    c <- credential filepath
+    signOAuth o c req
+
+-- | Create an OAuth token
+oAuth :: FilePath -> IO OAuth
+oAuth filepath = do
+    secret <- (flip (!!) 1) <$> getConfigData filepath
+    key <- (flip (!!) 0) <$> getConfigData filepath
+    let url = "api.twitter.com"
+    return newOAuth { oauthConsumerKey = key , oauthConsumerSecret = secret , oauthServerName = url }
+
+-- | Create a new credential from a token and secret component of that token
+credential :: FilePath -> IO Credential
+credential filepath = newCredential <$> token <*> secretToken
+    where token       = (flip (!!) 2) <$> getConfigData filepath
+          secretToken = (flip (!!) 3) <$> getConfigData filepath
+
+getConfigData :: FilePath -> IO [BS.ByteString]
+getConfigData filepath = ((map (BS.pack . filterLine)) . lines) <$> readFile filepath
+
+-- | Filter a line of a file for only the actual data and no descriptors
+filterLine :: String -> String
+filterLine = reverse . (takeWhile (not . (`elem` (" :" :: String)))) . reverse
+
+-- | Convert a byteString to the percent-encoded version
+urlString :: BS.ByteString -> IO String
+urlString content = do
+    return $ "?status=" ++ ((BS.unpack . paramEncode) content) ++ "&trim_user" ++ (map toLower $ show True)
