diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*.tix
+/.cabal-sandbox/
+/.hpc/
+/cabal.sandbox.config
+/dist/
+cabal.config
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: haskell
+ghc:
+  - 7.8
+  - 7.6
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Geraud Boyer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+# CmdItem
+[![Build Status](https://travis-ci.org/geraud/cmd-item.svg?branch=master)](https://travis-ci.org/geraud/cmd-item)
+
+CmdItem allows you to compose command lines by combining fragments of commands.
+
+## Installation
+
+```haskell
+cabal update
+cabal install cmd-item
+```
+
+## Example
+
+```haskell
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedLists #-}
+
+import Data.CmdItem
+import System.Environment (getArgs)
+
+main :: IO ()
+main = do
+    args <- getArgs
+    case args of
+    [] -> return ()
+    (name:ns) -> do
+        constants <- getConstants name
+        makeProject constants
+
+makeProject :: CmdItem -> IO ()
+makeProject contants = do
+    let cmdItem = pants <> "idea" <> ideaOptions <> constants
+    shellCommand <- render cmdItem
+    print shellcommand
+
+getConstants :: Text -> IO CmdItem
+getConstants name =  do
+    home <- getHomeDirectory
+    nCpu <- getNumProcessors
+    let result = [ ("java_version", "7")
+                 , ("project_root", T.pack $ home <> "/workspace/project")
+                 , ("num_cpu", T.pack $ show nCpu)
+                 , ("project_name", name)
+                 ]
+    return result
+
+pants :: CmdItem
+pants = "%{project_root}/pants"
+
+ideaOptions :: CmdItem
+ideaOptions = "--idea-java-language-level=%{java_version}"
+            <> "--idea-java-maximum-heap-size=2096"
+            <> "--idea-scala-maximum-heap-size=2096"
+            <> "--idea-project-name=%{project_name}"
+```
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/cmd-item.cabal b/cmd-item.cabal
new file mode 100644
--- /dev/null
+++ b/cmd-item.cabal
@@ -0,0 +1,57 @@
+name: cmd-item
+version: 0.0.1.0
+homepage: https://github.com/geraud/cmd-item
+bug-reports: https://github.com/geraud/cmd-item/issues
+license: MIT
+license-file: LICENSE
+author: Geraud Boyer
+maintainer: Geraud Boyer <geraud@gmail.com>
+copyright: Copyright (C) 2015 Geraud Boyer
+synopsis: Library to compose and reuse command line fragments
+description: Library to compose and reuse command line fragments
+category: System
+build-type: Simple
+cabal-version: >=1.10
+
+tested-with:
+  GHC == 7.6
+  , GHC == 7.8
+  , GHC == 7.10
+
+extra-source-files:
+  .gitignore
+  .travis.yml
+  README.md
+
+source-repository head
+  type: git
+  location: git://github.com/geraud/cmd-item.git
+
+library
+  exposed-modules: Data.CmdItem
+  build-depends:
+    base >= 4.5 && < 4.9
+    , containers >= 0.5
+    , text >= 1.1
+    , templater >= 0
+  hs-source-dirs: src
+  default-language: Haskell2010
+  ghc-options: -Wall
+
+test-suite tests
+  main-is: Spec.hs
+  build-depends:
+    base
+    , cmd-item
+    , hspec == 2.*
+    , hspec-laws
+    , HUnit
+    , QuickCheck
+    , quickcheck-instances
+    , text
+  default-language: Haskell2010
+  ghc-options:
+    -Wall
+    -Werror
+  hs-source-dirs: test
+  type: exitcode-stdio-1.0
diff --git a/src/Data/CmdItem.hs b/src/Data/CmdItem.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/CmdItem.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE CPP               #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TypeFamilies      #-}
+-- | CmdItem is a tool to build command lines.
+module Data.CmdItem  where
+import           Control.Applicative ((<$>))
+import           Data.Either         (either, partitionEithers)
+import           Data.List           (intercalate)
+import           Data.Map.Strict     (Map)
+import qualified Data.Map.Strict     as M
+import           Data.Monoid
+import           Data.Text           (Text, pack)
+import qualified Data.Text           as T
+import           GHC.Exts
+
+import           Text.Templater      (template)
+
+data CmdItem
+    = CmdItem
+        { fragments :: [Text]         -- pieces of command line
+        , localVars :: Map Text Text  -- context of substitution
+        }
+    deriving (Show, Eq)
+
+-- | Renders a command line item to a @Text@ string
+-- This will throw an error if anything goes wrong
+render :: CmdItem -> IO Text
+render c = either error return (renderEither c)
+
+-- | Renders a command line item to a @Maybe Text@
+renderMaybe :: CmdItem -> Maybe Text
+renderMaybe c = either (const Nothing) return (renderEither c)
+
+-- | Renders a command line item to a @Either String Text@
+renderEither :: CmdItem -> Either String Text
+renderEither (CmdItem {..}) =
+    let (lefts, rights) = partitionEithers $ (\x -> template x (`M.lookup` localVars)) <$> fragments
+        isOk = null lefts
+    in if isOk
+       then Right $ T.intercalate " " rights
+       else Left $ intercalate ", " lefts
+
+instance Monoid CmdItem where
+    mempty = CmdItem mempty mempty
+    mappend (CmdItem cmdA localA) (CmdItem cmdB localB) =
+        CmdItem (cmdA <> cmdB) (localA <> localB)
+
+instance IsString CmdItem where
+    fromString "" = mempty
+    fromString st = CmdItem [pack st] mempty
+
+#if MIN_VERSION_base(4,7,0)
+instance IsList CmdItem where
+    type Item CmdItem = (Text, Text)
+    fromList [] = mempty
+    fromList lst = CmdItem mempty (M.fromList lst)
+    toList (CmdItem _ m) = M.toList m
+#endif
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
