diff --git a/Kit/CmdArgs.hs b/Kit/CmdArgs.hs
new file mode 100644
--- /dev/null
+++ b/Kit/CmdArgs.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+module Kit.CmdArgs (parseArgs, KitCmdArgs(..)) where
+
+  import System.Console.CmdArgs as CA
+
+  appVersion :: String
+  appVersion = "0.6" -- TODO how to keep this up to date with kit.cabal?
+
+  data KitCmdArgs = Update
+                  | Package
+                  | PublishLocal
+                  | Verify { sdk :: String }
+                  | CreateSpec { name :: String, version :: String } deriving (Show, Data, Typeable)
+
+  parseMode :: KitCmdArgs
+  parseMode = modes [
+        Update &= help "Create an Xcode project to wrap the dependencies defined in `KitSpec`"
+               &= auto -- The default mode to run. This is most common usage.
+      , CreateSpec { Kit.CmdArgs.name = (def &= typ "NAME") &= argPos 0, version = (def &= typ "VERSION") &= argPos 1 } &=
+                            explicit &=
+                            CA.name "create-spec" &=
+                            help "Write out a KitSpec file using the given name and version."
+      , Package &=
+                    help "Create a tar.gz wrapping this kit"
+      , PublishLocal &= explicit &= CA.name "publish-local" &=
+                    help "Package this kit, then deploy it to the local repository (~/.kit/repository)"
+      , Verify { sdk = "iphonesimulator4.0" &= typ "SDK" &= help "iphoneos, iphonesimulator4.0, etc."} &=
+                    help "Package this kit, then attempt to use it as a dependency in an empty project. This will assert that the kit and all its dependencies can be compiled together."
+    ]
+
+  parseArgs :: IO KitCmdArgs
+  parseArgs = cmdArgs $ parseMode &= program "kit" &= summary ("Kit v" ++ appVersion ++ ". It's a dependency manager for Objective-C projects built with Xcode.")
+
diff --git a/Kit/Commands.hs b/Kit/Commands.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Commands.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+module Kit.Commands (
+  Command,
+  liftKit,
+  mySpec,
+  myKitSpec,
+  myRepository,
+  runCommand,
+  defaultLocalRepoPath,
+  defaultLocalRepository
+) where
+
+import Kit.Util
+import Kit.Spec
+import Kit.Repository
+import Kit.Project
+
+import Control.Monad.Error
+import Control.Monad.Reader
+
+newtype Command a = Command (ReaderT (KitSpec, KitRepository) (ErrorT String IO) a) deriving Monad
+
+instance MonadIO Command where
+  liftIO a = Command $ liftIO a
+
+liftKit :: KitIO a -> Command a
+liftKit = Command . ReaderT . const
+
+mySpec :: Command KitSpec
+mySpec = Command $ ask >>= (return . fst) 
+
+myRepository :: Command KitRepository
+myRepository = Command $ ask >>= (return . snd)
+
+runCommand :: Command a -> IO ()
+runCommand (Command cmd) = run $ do
+  spec <- readSpec "KitSpec"
+  repository <- liftIO defaultLocalRepository
+  runReaderT cmd (spec, repository)
+  where run = (handleFails =<<) . runErrorT
+        handleFails = either (putStrLn . ("kit error: " ++)) (const $ return ())
+
+myKitSpec :: KitIO KitSpec
+myKitSpec = readSpec "KitSpec"
+
+defaultLocalRepoPath :: IO FilePath
+defaultLocalRepoPath = getHomeDirectory >>= \h -> return $ h </> ".kit" </> "repository"
+
+defaultLocalRepository :: IO KitRepository
+defaultLocalRepository = fileRepo <$> defaultLocalRepoPath
+
diff --git a/Kit/Contents.hs b/Kit/Contents.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Contents.hs
@@ -0,0 +1,53 @@
+module Kit.Contents (
+  KitContents(..),
+  readKitContents,
+  namedPrefix
+  ) where
+
+import Kit.Spec
+import Kit.Util
+import Kit.Xcode.XCConfig
+
+import qualified Data.Traversable as T
+
+-- | The determined contents of a particular Kit
+data KitContents = KitContents { 
+  contentKit :: Kit,
+  contentHeaders :: [FilePath],     -- ^ Paths to headers
+  contentSources :: [FilePath],     -- ^ Paths to source files
+  contentLibs :: [FilePath],        -- ^ Paths to static libs
+  contentConfig :: Maybe XCConfig,  -- ^ Contents of the xcconfig base file
+  contentPrefix :: Maybe String     -- ^ Contents of the prefix header
+}
+
+namedPrefix :: KitContents -> Maybe String
+namedPrefix kc = fmap (\s -> "//" ++ (packageFileName . contentKit $ kc) ++ "\n" ++ s) $ contentPrefix kc
+
+-- | Determine the contents for a Kit, assumes that we're in a projects 'Kits' folder.
+readKitContents :: KitSpec -> IO KitContents
+readKitContents spec  =
+  let kitDir = packageFileName spec
+      find dir tpe = glob ((kitDir </> dir </> "**/*") ++ tpe)
+      findSrc = find $ specSourceDirectory spec
+      headers = findSrc ".h"
+      sources = join <$> mapM findSrc [".m", ".mm", ".c"]
+      libs = find (specLibDirectory spec) ".a" 
+      config = readConfig spec
+      prefix = readHeader spec
+  in  KitContents (specKit spec) <$> headers <*> sources <*> libs <*> config <*> prefix
+
+-- Report missing file
+readHeader :: KitSpec -> IO (Maybe String)
+readHeader spec = do
+  let fp = packageFileName spec </> specPrefixFile spec
+  exists <- doesFileExist fp
+  T.sequence (fmap readFile $ justTrue exists fp)
+
+-- TODO make this report missing file
+readConfig :: KitSpec -> IO (Maybe XCConfig)
+readConfig spec = do
+  let fp = packageFileName spec </> specConfigFile spec
+  exists <- doesFileExist fp
+  contents <- T.sequence (fmap readFile $ justTrue exists fp)
+  return $ fmap (fileContentsToXCC $ packageName spec) contents
+
diff --git a/Kit/Main.hs b/Kit/Main.hs
--- a/Kit/Main.hs
+++ b/Kit/Main.hs
@@ -1,143 +1,79 @@
-{-# LANGUAGE DeriveDataTypeable #-}
 module Kit.Main where
-  import qualified Data.ByteString as BS
 
   import Control.Monad.Trans
-  import Control.Monad.Error
-  import Data.List
-  import Data.Maybe
-  import Data.Monoid
-  import Kit.Model
+  import qualified Kit.CmdArgs as KA 
+  import Kit.Commands
+  import Kit.Spec
   import Kit.Package
   import Kit.Project
-  import Kit.Repository
   import Kit.Util
-  import Kit.XCode.Builder
-  import qualified Data.Traversable as T
   import System.Cmd
-  import System.Environment
-  import Text.JSON
-  import System.Console.CmdArgs
-  import System.Console.CmdArgs as CA
 
-  import Data.Data
-  import Data.Typeable
-
-  appVersion = "0.5"
-
-  defaultLocalRepoPath = do
-      home <- getHomeDirectory
-      return $ home </> ".kit" </> "repository"
-  defaultLocalRepository = fmap fileRepo defaultLocalRepoPath
-
-  handleFails :: Either KitError a -> IO ()
-  handleFails = either (putStrLn . ("kit error: " ++)) (const $ return ())
-
-  run f = runErrorT f >>= handleFails
-
-  doUpdate :: IO ()
-  doUpdate = run $ do
-              repo <- liftIO defaultLocalRepository
-              spec <- myKitSpec
-              deps <- getMyDeps repo
-              puts $ "Dependencies: " ++ (stringJoin ", " $ map kitFileName deps)
-              liftIO $ mapM (installKit repo) deps
-              puts " -> Generating XCode project..."
-              generateXCodeProject deps $ specKitDepsXcodeFlags spec
-              puts "Kit complete. You may need to restart XCode for it to pick up any changes."
-                where p x = liftIO $ print x
-                      puts x = liftIO $ putStrLn x
+  doUpdate :: Command ()
+  doUpdate = do
+              repo <- myRepository
+              spec <- mySpec
+              deps <- liftKit $ totalSpecDependencies repo spec
+              puts $ "Dependencies: " ++ stringJoin ", " (map packageFileName deps)
+              liftIO $ mapM_ (installKit repo) deps
+              puts " -> Generating Xcode project..."
+              liftKit $ generateXcodeProject deps (specKitDepsXcodeFlags spec)
+              puts "Kit complete. You may need to restart Xcode for it to pick up any changes."
 
-  doPackageKit :: IO ()
-  doPackageKit = do
-      mySpec <- runErrorT myKitSpec
-      T.for mySpec package
-      return ()
+  doPackageKit :: Command ()
+  doPackageKit = mySpec >>= liftIO . package 
 
-  doDeployLocal :: IO ()
-  doDeployLocal = do
-    mySpec <- runErrorT myKitSpec
-    T.for mySpec package
-    T.for mySpec x
-    handleFails mySpec
+  doDeployLocal :: Command ()
+  doDeployLocal = mySpec >>= \spec -> liftIO $ do 
+    package spec
+    publishLocal spec 
+    return ()
       where
-        x :: KitSpec -> IO ()
-        x spec = let
-              k = specKit spec
-              pkg = (kitFileName k ++ ".tar.gz")
-            in do
-              repo <- defaultLocalRepoPath
-              let thisKitDir = repo </> "kits" </> kitName k </> kitVersion k
-              mkdir_p thisKitDir
-              copyFile ("dist" </> pkg) $ thisKitDir </> pkg
-              copyFile "KitSpec" $ thisKitDir </> "KitSpec"
+        publishLocal :: KitSpec -> IO ()
+        publishLocal spec = let pkg = (packageFileName spec ++ ".tar.gz")
+                            in do
+                              repo <- defaultLocalRepoPath
+                              let thisKitDir = repo </> "kits" </> packageName spec </> packageVersion spec
+                              mkdir_p thisKitDir
+                              copyFile ("dist" </> pkg) (thisKitDir </> pkg)
+                              copyFile "KitSpec" (thisKitDir </> "KitSpec")
 
-  doVerify :: String -> IO ()
-  doVerify sdk = run $ do
-        mySpec <- myKitSpec
+  doVerify :: String -> Command ()
+  doVerify sdk = do
+        spec <- mySpec
         puts "Checking that the kit can be depended upon..."
         puts " #> Deploying locally"
-        liftIO doDeployLocal
+        doDeployLocal
         puts " #> Building temporary parent project"
         tmp <- liftIO getTemporaryDirectory
-        liftIO $ inDirectory tmp $ do
+        inDirectory tmp $ do
           let kitVerifyDir = "kit-verify"
           cleanOrCreate kitVerifyDir
           inDirectory kitVerifyDir $ do
-            let verifySpec = (defaultSpecForKit $ Kit "verify-kit" "1.0"){ specDependencies = [specKit mySpec] }
-            writeFile "KitSpec" $ encode verifySpec
+            writeSpec "KitSpec" (defaultSpec "verify-kit" "1.0") { specDependencies = [specKit spec] }
             doUpdate
             inDirectory "Kits" $ do
-              system $ "open KitDeps.xcodeproj"
-              system $ "xcodebuild -sdk " ++ sdk
-          putStrLn "OK."
+              liftIO $ system "open KitDeps.xcodeproj"
+              liftIO $ system $ "xcodebuild -sdk " ++ sdk
+          puts "OK."
         puts "End checks."
-       where puts = liftIO . putStrLn
 
-  doCreateSpec :: String -> String -> IO ()
+  doCreateSpec :: String -> String -> Command ()
   doCreateSpec name version = do
-    let kit =(Kit name version)
-    let spec = defaultSpecForKit kit
-    writeFile "KitSpec" $ encode spec
-    putStrLn $ "Created KitSpec for " ++ kitFileName kit
-    return ()
-
-  data KitCmdArgs = Update
-                  | Package
-                  | PublishLocal
-                  | Verify { sdk :: String }
-                  | CreateSpec { name :: String, version :: String } deriving (Show, Data, Typeable)
-
-  parseArgs = cmdArgs $ modes [
-        Update &= help "Create an Xcode project to wrap the dependencies defined in `KitSpec`"
-               &= auto -- The default mode to run. This is most common usage.
-      , (CreateSpec { Kit.Main.name = (def &= typ "NAME") &= argPos 0, version = (def &= typ "VERSION") &= argPos 1 }) &=
-                            explicit &=
-                            CA.name "create-spec" &=
-                            help "Write out a KitSpec file using the given name and version."
-      , Package &=
-                    help "Create a tar.gz wrapping this kit"
-      , PublishLocal &= explicit &= CA.name "publish-local" &=
-                    help "Package this kit, then deploy it to the local repository (~/.kit/repository)"
-      , Verify { sdk = "iphonesimulator4.0" &= typ "SDK" &= help "iphoneos, iphonesimulator4.0, etc."} &=
-                    help "Package this kit, then attempt to use it as a dependency in an empty project. This will assert that the kit and all its dependencies can be compiled together."
-    ] &= program "kit"
-      &= summary ("Kit v" ++ appVersion ++ ". It's a dependency manager for Objective-C projects built with XCode.")
+    let spec = defaultSpec name version 
+    writeSpec "KitSpec" spec
+    puts $ "Created KitSpec for " ++ packageFileName spec
 
-  handleArgs :: KitCmdArgs -> IO ()
-  handleArgs Update = doUpdate
-  handleArgs Package = doPackageKit
-  handleArgs PublishLocal = doDeployLocal
-  handleArgs (Verify sdk) = doVerify sdk
-  handleArgs (CreateSpec name version) = doCreateSpec name version
+  handleArgs :: KA.KitCmdArgs -> Command ()
+  handleArgs KA.Update = doUpdate
+  handleArgs KA.Package = doPackageKit
+  handleArgs KA.PublishLocal = doDeployLocal
+  handleArgs (KA.Verify sdkName) = doVerify sdkName
+  handleArgs (KA.CreateSpec name version) = doCreateSpec name version
 
   kitMain :: IO ()
   kitMain = do
-      localRepo <- defaultLocalRepository
-      path <- defaultLocalRepoPath
-      mkdir_p path
-      handleArgs =<< parseArgs -- =<< getArgs
-
-
-
+      mkdir_p =<< defaultLocalRepoPath 
+      args <- KA.parseArgs
+      runCommand $ handleArgs args 
 
diff --git a/Kit/Model.hs b/Kit/Model.hs
deleted file mode 100644
--- a/Kit/Model.hs
+++ /dev/null
@@ -1,64 +0,0 @@
-module Kit.Model where
-
-  import Text.JSON
-  import Control.Applicative
-  import System.FilePath.Posix
-
-  data KitSpec = KitSpec {
-    specKit :: Kit,
-    specDependencies :: [Kit],
-    specSourceDirectory :: FilePath,
-    specTestDirectory :: FilePath,
-    specLibDirectory :: FilePath,
-    specPrefixFile :: FilePath,
-    specConfigFile :: FilePath,
-    specKitDepsXcodeFlags :: Maybe String
-  } deriving (Show, Read)
-
-  type Version = String
-
-  data Kit = Kit {
-    kitName :: String,
-    kitVersion :: Version
-  } deriving (Eq, Show, Ord, Read)
-
-  kitFileName :: Kit -> String
-  kitFileName k = kitName k ++ "-" ++ kitVersion k
-
-  defaultSpecForKit :: Kit -> KitSpec
-  defaultSpecForKit kit = KitSpec kit [] "src" "test" "lib" "Prefix.pch" "Config.xcconfig" Nothing
-  -- TODO make this and the json reading use the same defaults
-  -- I suspect that to do this I'll need update functions for each of
-  -- fields in the KitSpec record.
-  -- Look at the 'lenses' package on haskell.
-
-  instance JSON Kit where
-      showJSON kit = makeObj [ ("name", w kitName) , ("version", w kitVersion) ] where w f = showJSON . f $ kit
-
-      readJSON (JSObject obj) = Kit <$> f "name" <*> f "version"
-                                  where f x = f' obj x 
-
-  instance JSON KitSpec where
-      showJSON spec = makeObj [
-            ("name", showJSON $ kitName kit)
-          , ("version", showJSON $ kitVersion kit)
-          , ("dependencies", showJSON $ specDependencies spec)
-          ]
-          where kit = specKit spec
-
-      readJSON js@(JSObject obj) =
-              KitSpec <$> readJSON js 
-                      <*> (f "dependencies" <|> pure []) 
-                      <*> (f "source-directory" <|> pure "src")
-                      <*> (f "test-directory" <|> pure "test")
-                      <*> (f "lib-directory" <|> pure "lib")
-                      <*> (f "prefix-header" <|> pure "Prefix.pch")
-                      <*> (f "xcconfig" <|> pure "Config.xcconfig")
-                      <*> (Just <$> f "kitdeps-xcode-flags" <|> pure Nothing)
-        where f x = f' obj x
-
-  f' obj x = mLookup x (fromJSObject obj) >>= readJSON
-
-  mLookup :: Monad m => String -> [(String, b)] -> m b
-  mLookup a as = maybe (fail $ "No such element: " ++ a) return (lookup a as)
-
diff --git a/Kit/Package.hs b/Kit/Package.hs
--- a/Kit/Package.hs
+++ b/Kit/Package.hs
@@ -1,51 +1,33 @@
 module Kit.Package (package) where
 
-  import Kit.Model
+  import Kit.Spec
   import Kit.Util
-  import Kit.Project
   import System.Cmd
   import Control.Monad.Trans
-  import Control.Monad
   import Data.List
-  import Data.Maybe
   import Debug.Trace
 
-  {-
-    Package format
-     src/ | configurable source dir
-     test/ | test directory
-     lib/ | lib directory
-     *.xcconfig
-     *.pch
-     KitSpec
-     *.codeproj
-  -}
-
   fileBelongsInPackage :: KitSpec -> FilePath -> Bool
   fileBelongsInPackage config fp = let
-    a = elem fp [specSourceDirectory config, specTestDirectory config, specLibDirectory config, "KitSpec"]
-    b = "xcodeproj" `isSuffixOf` fp
-    c = "xcconfig" `isSuffixOf` fp
-    d = ".pch" `isSuffixOf` fp
-      in a || b || c || d
+    isCore = elem fp [specSourceDirectory config, specTestDirectory config, specLibDirectory config, "KitSpec"]
+    isProject = or $ map (`isSuffixOf` fp) ["xcodeproj", "xcconfig", ".pch"]
+      in isCore || isProject
 
   package :: KitSpec -> IO ()
   package spec = do
       tempDir <- getTemporaryDirectory
-      current <- getCurrentDirectory
-      distDir <- fmap (</> "dist") getCurrentDirectory
+      distDir <- (</> "dist") <$> getCurrentDirectory
       let kd = tempDir </> kitPath
       cleanOrCreate kd
       contents <- getDirectoryContents "."
-      mapM_ (cp_r_to kd) (filter (fileBelongsInPackage spec) contents)
+      mapM_ (copyAllTo kd) (filter (fileBelongsInPackage spec) contents)
       mkdir_p distDir
-      inDirectory tempDir $ sh $ "tar czf " ++ (distDir </> (kitPath ++ ".tar.gz")) ++ " " ++ kitPath
+      inDirectory tempDir $ do
+        sh $ "tar czf " ++ (distDir </> (kitPath ++ ".tar.gz")) ++ " " ++ kitPath
       return ()
     where
-      kitPath = kitFileName . specKit $ spec
+      kitPath = packageFileName spec
       sh c = liftIO $ system (trace c c)
-      puts c = liftIO $ putStrLn c
-      p c = liftIO $ print c
-      cp_r_to kd c = sh $ "cp -r " ++ c ++ " " ++ kd ++ "/"
+      copyAllTo kd c = sh $ "cp -r " ++ c ++ " " ++ kd ++ "/"
 
 
diff --git a/Kit/Project.hs b/Kit/Project.hs
--- a/Kit/Project.hs
+++ b/Kit/Project.hs
@@ -1,153 +1,107 @@
+{-# LANGUAGE TupleSections #-}
 module Kit.Project (
-  getMyDeps,
+  totalSpecDependencies,
   installKit,
-  generateXCodeProject,
-  myKitSpec)
+  generateXcodeProject,
+  readSpec
+  )
     where
 
-  import Control.Monad.Trans
-  import Control.Monad
-  import Control.Monad.Error
-  import Control.Applicative
-  import qualified Data.Foldable as F
-  import Data.Maybe
-  import Data.List
-  import Data.Tree
-  import Data.Monoid
-  import System.Cmd
-  import Kit.Model
-  import Kit.Repository
-  import Kit.Util
-  import Kit.XCode.Builder
-  import Kit.XCode.XCConfig
-  import Text.JSON
-  import qualified Data.Traversable as T
-
-  -- Paths
-  kitDir = "." </> "Kits"
-  projectDir = "KitDeps.xcodeproj"
-  prefixFile = "KitDeps_Prefix.pch"
-  projectFile = projectDir </> "project.pbxproj"
-  xcodeConfigFile = "Kit.xcconfig"
-  depsConfigFile = "DepsOnly.xcconfig"
-  kitUpdateMakeFilePath = "Makefile"
-  kitUpdateMakeFile = "kit: Kit.xcconfig\nKit.xcconfig: ../KitSpec\n\tcd .. && kit update && exit 1\n"
-
-  prefixDefault = "#ifdef __OBJC__\n" ++ "    #import <Foundation/Foundation.h>\n    #import <UIKit/UIKit.h>\n" ++ "#endif\n"
-
-  -- Represents an extracted project
-  -- Headers, Sources, Config, Prefix content
-  data KitContents = KitContents { contentHeaders :: [FilePath], contentSources :: [FilePath], contentLibs :: [FilePath], contentConfig :: Maybe XCConfig, contentPrefix :: Maybe String }
-
-  readKitContents :: KitSpec -> IO KitContents
-  readKitContents spec  =
-    let kit = specKit spec
-        kitDir = kitFileName kit
-        find dir tpe = glob ((kitDir </> dir </> "**/*") ++ tpe)
-        findSrc = find $ specSourceDirectory spec
-        headers = findSrc ".h"
-        sources = fmap join $ T.sequence [findSrc ".m", findSrc ".mm", findSrc ".c"]
-        libs = find (specLibDirectory spec) ".a" 
-        config = readConfig spec
-        prefix = readHeader spec
-    in  KitContents <$> headers <*> sources <*> libs <*> config <*> prefix
+import Kit.Spec
+import Kit.Contents
+import Kit.Repository
+import Kit.Util
+import Kit.Xcode.Builder
+import Kit.Xcode.XCConfig
 
-  generateXCodeProject :: [Kit] -> Maybe String -> KitIO ()
-  generateXCodeProject deps depsOnlyConfig = do
-    liftIO $ mkdir_p kitDir
-    specs <- forM deps $ \kit -> readSpec (kitDir </> kitFileName kit </> "KitSpec")  
-    liftIO $ inDirectory kitDir $ do
-      kitsContents <- forM specs readKitContents
-      createProjectFile kitsContents
-      createHeader kitsContents
-      createConfig kitsContents specs
-      writeFile depsConfigFile $ "#include \"" ++ xcodeConfigFile ++ "\"\n" ++ fromMaybe "" depsOnlyConfig 
-    where kitFileNames = map kitFileName deps
-          sourceDirs specs = specs >>= (\spec -> [
-              kitDir </> (kitFileName . specKit $ spec) </> specSourceDirectory spec, 
-              (kitFileName . specKit $ spec) </> specSourceDirectory spec
-            ])
-          createProjectFile cs = do
-            let headers = cs >>= contentHeaders
-            let sources = cs >>= contentSources
-            let libs = cs >>= contentLibs
-            mkdir_p projectDir
-            writeFile projectFile $ buildXCodeProject headers sources libs
-          createHeader cs = do
-            let headers = mapMaybe contentPrefix cs
-            let combinedHeader = stringJoin "\n" headers
-            writeFile prefixFile $ prefixDefault ++ combinedHeader ++ "\n"
-          createConfig cs specs = do
-            let configs = mapMaybe contentConfig cs
-            let combinedConfig = multiConfig "KitConfig" configs
-            let kitHeaders = "HEADER_SEARCH_PATHS = $(HEADER_SEARCH_PATHS) " ++ (stringJoin " " $ sourceDirs specs)
-            let prefixHeaders = "GCC_PRECOMPILE_PREFIX_HEADER = YES\nGCC_PREFIX_HEADER = $(SRCROOT)/KitDeps_Prefix.pch\n"
-            writeFile xcodeConfigFile $ kitHeaders ++ "\n" ++  prefixHeaders ++ "\n" ++ configToString combinedConfig
-            writeFile kitUpdateMakeFilePath kitUpdateMakeFile
+import Control.Monad.Error
+import Data.Maybe
+import Data.List
+import Data.Tree
+import System.Cmd
 
-  -- Report missing file
-  readHeader :: KitSpec -> IO (Maybe String)
-  readHeader spec = do
-    let fp = (kitFileName . specKit $ spec) </> specPrefixFile spec
-    exists <- doesFileExist fp
-    contents <- T.sequence (fmap readFile $ justTrue exists fp)
-    return contents
+import qualified Data.ByteString as BS
 
-  -- TODO make this report missing file
-  readConfig :: KitSpec -> IO (Maybe XCConfig)
-  readConfig spec = do
-    let kit = specKit spec
-    let fp = kitFileName kit </> specConfigFile spec
-    exists <- doesFileExist fp
-    contents <- T.sequence (fmap readFile $ justTrue exists fp)
-    return $ fmap (fileContentsToXCC $ kitName kit) contents
+-- Paths
 
-  refineDeps :: Tree Kit -> [Kit]
-  refineDeps = nub . concat . reverse . drop 1 . levels
+kitDir, projectDir, prefixFile, projectFile, xcodeConfigFile, depsConfigFile, kitUpdateMakeFilePath :: FilePath
 
-  depsForSpec' :: KitRepository -> KitSpec -> KitIO [Kit]
-  depsForSpec' kr spec = do
-      tree <- unfoldTreeM (unfoldDeps kr) spec
-      -- todo: check for conflicts
-      return $ refineDeps tree
+kitDir = "." </> "Kits"
+projectDir = "KitDeps.xcodeproj"
+prefixFile = "Prefix.pch"
+projectFile = projectDir </> "project.pbxproj"
+xcodeConfigFile = "Kit.xcconfig"
+depsConfigFile = "DepsOnly.xcconfig"
+kitUpdateMakeFilePath = "Makefile"
 
-  unfoldDeps :: KitRepository -> KitSpec -> KitIO (Kit, [KitSpec])
-  unfoldDeps kr ks = do
-      let kit = specKit ks
-      let depKits = specDependencies ks
-      specs <- mapM (getKitSpec kr) depKits
-      return (kit, specs)
+kitUpdateMakeFile :: String
+kitUpdateMakeFile = "kit: Kit.xcconfig\n" ++
+                    "Kit.xcconfig: ../KitSpec\n" ++
+                    "\tcd .. && kit update && exit 1\n"
 
-  getMyDeps :: KitRepository -> KitIO [Kit]
-  getMyDeps kr = myKitSpec >>= depsForSpec' kr
+prefixDefault :: String
+prefixDefault = "#ifdef __OBJC__\n" ++ 
+                "    #import <Foundation/Foundation.h>\n" ++ 
+                "    #import <UIKit/UIKit.h>\n" ++ 
+                "#endif\n"
 
-  installKit :: KitRepository -> Kit -> IO ()
-  installKit kr kit = do
-      tmpDir <- getTemporaryDirectory
-      let fp = tmpDir </> (kitFileName kit ++ ".tar.gz")
-      putStrLn $ " -> Installing " ++ kitFileName kit
-      fmap fromJust $ getKit kr kit fp
-      let dest = kitDir
-      mkdir_p dest
-      inDirectory dest $ sh ("tar zxf " ++ fp)
-      return ()
-    where sh = system
+generateXcodeProject :: [Kit] -> Maybe String -> KitIO ()
+generateXcodeProject deps depsOnlyConfig = do
+  specs <- forM deps $ \kit -> readSpec (kitDir </> packageFileName kit </> "KitSpec")  
+  liftIO $ inDirectory kitDir $ do
+    kitsContents <- forM specs readKitContents
+    createProjectFile kitsContents
+    createHeader kitsContents
+    createConfig kitsContents specs
+    writeFile depsConfigFile $ "#include \"" ++ xcodeConfigFile ++ "\"\n" ++ fromMaybe "" depsOnlyConfig 
+  where sourceDirs specs = specs >>= (\spec -> [
+            kitDir </> packageFileName spec </> specSourceDirectory spec, 
+            packageFileName spec </> specSourceDirectory spec
+          ])
+        createProjectFile cs = do
+          let headers = cs >>= contentHeaders
+          let sources = cs >>= contentSources
+          let libs = cs >>= contentLibs
+          mkdir_p projectDir
+          writeFile projectFile $ renderXcodeProject headers sources libs "libKitDeps.a"
+        createHeader cs = do
+          let headers = mapMaybe namedPrefix cs
+          let combinedHeader = stringJoin "\n" headers
+          writeFile prefixFile $ prefixDefault ++ combinedHeader ++ "\n"
+        createConfig cs specs = do
+          let configs = mapMaybe contentConfig cs
+          let combinedConfig = multiConfig "KitConfig" configs
+          let kitHeaders = "HEADER_SEARCH_PATHS = $(HEADER_SEARCH_PATHS) " ++ stringJoin " " (sourceDirs specs)
+          let prefixHeaders = "GCC_PRECOMPILE_PREFIX_HEADER = YES\nGCC_PREFIX_HEADER = $(SRCROOT)/Prefix.pch\n"
+          writeFile xcodeConfigFile $ kitHeaders ++ "\n" ++  prefixHeaders ++ "\n" ++ configToString combinedConfig ++ "\n"
+          writeFile kitUpdateMakeFilePath kitUpdateMakeFile
 
-  readSpec :: FilePath -> KitIO KitSpec
-  readSpec kitSpecPath = readSpecContents kitSpecPath >>= ErrorT . return . parses
+-- | Return all the (unique) children of this tree (except the top node), in reverse depth order.
+refineDeps :: Eq a => Tree a -> [a]
+refineDeps = nub . concat . reverse . drop 1 . levels
 
-  myKitSpec :: KitIO KitSpec
-  myKitSpec = readSpec "KitSpec"
+totalSpecDependencies :: KitRepository -> KitSpec -> KitIO [Kit]
+totalSpecDependencies kr spec = refineDeps <$> unfoldTreeM (unfoldDeps kr) spec
+    -- todo: check for conflicts
+    -- todo: check for version ranges :)
 
-  -- private!
-  checkExists :: FilePath -> KitIO FilePath
-  checkExists kitSpecPath = do
-    doesExist <- liftIO $ doesFileExist kitSpecPath
-    maybeToKitIO ("Couldn't find the spec at " ++ kitSpecPath) (justTrue doesExist kitSpecPath)
+unfoldDeps :: KitRepository -> KitSpec -> KitIO (Kit, [KitSpec])
+unfoldDeps kr ks = (specKit ks,) <$> mapM (getKitSpec kr) (specDependencies ks) -- s/mapM/traverse ?
 
-  readSpecContents :: FilePath -> KitIO String
-  readSpecContents kitSpecPath = checkExists kitSpecPath >>= liftIO . readFile
+installKit :: KitRepository -> Kit -> IO ()
+installKit kr kit = do
+    tmpDir <- getTemporaryDirectory
+    let fp = tmpDir </> (packageFileName kit ++ ".tar.gz")
+    putStrLn $ " -> Installing " ++ packageFileName kit
+    fmap fromJust $ getKit kr kit fp
+    mkdir_p kitDir 
+    inDirectory kitDir $ system ("tar zxf " ++ fp)
+    return ()
 
-  parses :: String -> Either KitError KitSpec
-  parses contents = resultToEither (decode contents)
+readSpec :: FilePath -> KitIO KitSpec
+readSpec path = checkExists path >>= liftIO . BS.readFile >>= ErrorT . return . parses
+  where checkExists pathToSpec = do
+          doesExist <- liftIO $ doesFileExist pathToSpec 
+          if doesExist then return pathToSpec else throwError $ "Couldn't find the spec at " ++ pathToSpec 
+        parses = maybeToRight "Parse error in KitSpec file" . decodeSpec
 
diff --git a/Kit/Repository.hs b/Kit/Repository.hs
--- a/Kit/Repository.hs
+++ b/Kit/Repository.hs
@@ -6,24 +6,21 @@
     KitRepository
   ) where
 
-  import Kit.Model
+  import Kit.Spec
   import Kit.Util
 
+  import Network.BufferType
   import Network.HTTP
   import Network.URI
-  import Network.BufferType
-  import qualified Data.List as L
+  import Control.Monad.Error
   import Data.Maybe
+  import qualified Data.List as L
   import qualified Data.Traversable as T
-  import Control.Monad
-  import Control.Monad.Trans
-  import Control.Monad.Error
-  import Text.JSON
   import qualified Data.ByteString as BS
 
   data KitRepository = KitRepository {
     repoSave :: String -> FilePath -> IO (Maybe ()),
-    repoRead :: String -> IO (Maybe String)
+    repoRead :: String -> IO (Maybe BS.ByteString)
   }
 
   getKit :: KitRepository -> Kit -> FilePath -> IO (Maybe ())
@@ -32,38 +29,38 @@
   getKitSpec :: KitRepository -> Kit -> KitIO KitSpec
   getKitSpec kr k = do
     mbKitStuff <- liftIO $ repoRead kr (kitSpecPath k)
-    maybe (throwError $ "Missing " ++ kitFileName k) f mbKitStuff
-    where f contents = maybeToKitIO ("Invalid KitSpec file for " ++ kitFileName k) $ case (decode contents) of
-                          Ok a -> Just a
-                          Error _ -> Nothing
+    maybe (throwError $ "Missing " ++ packageFileName k) f mbKitStuff
+    where f contents = maybeToKitIO ("Invalid KitSpec file for " ++ packageFileName k) $ decodeSpec contents
 
+  -- TODO: Use the base url!
   webRepo :: String -> KitRepository
-  webRepo baseUrl = KitRepository save read where
+  webRepo _ = KitRepository save doRead where
     save = download
-    read = getBody
+    doRead = getBody
 
   fileRepo :: String -> KitRepository
-  fileRepo baseDir = KitRepository save read where
+  fileRepo baseDir = KitRepository save doRead where
     save src destPath = Just <$> copyFile (baseDir </> src) destPath
-    read path = let file = (baseDir </> path) in do
+    doRead fp = let file = (baseDir </> fp) in do
       exists <- doesFileExist file
-      T.sequenceA $ justTrue exists $ readFile file
+      T.sequenceA $ justTrue exists $ BS.readFile file
 
   -- private!
   baseKitPath :: Kit -> String
   baseKitPath k = joinS ["kits", kitName k, kitVersion k] "/"
     where joinS xs x = foldl1 (++) $ L.intersperse x xs
 
-  kitPackagePath k = baseKitPath k ++ "/" ++ kitFileName k ++ ".tar.gz"
+  kitPackagePath, kitSpecPath :: Kit -> String
+
+  kitPackagePath k = baseKitPath k ++ "/" ++ packageFileName k ++ ".tar.gz"
   kitSpecPath k = baseKitPath k ++ "/" ++ "KitSpec"
 
   getBody :: BufferType a => HStream a => String -> IO (Maybe a)
-  getBody path = let
+  getBody p = let
       request = defaultGETRequest_ . fromJust . parseURI
-      checkResponse r = justTrue (rspCode r == (2,0,0)) r
       leftMaybe = either (const Nothing) Just
     in do
-      rr <- Network.HTTP.simpleHTTP $ request path
+      rr <- Network.HTTP.simpleHTTP $ request p 
       return $ fmap rspBody $ leftMaybe rr
 
   download :: String -> FilePath -> IO (Maybe ())
diff --git a/Kit/Spec.hs b/Kit/Spec.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Spec.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE KindSignatures, TypeSynonymInstances, NoMonomorphismRestriction, OverlappingInstances #-}
+module Kit.Spec (
+  -- | The Core Kit types
+  KitSpec(..),
+  Kit(..),
+  -- | Duck typing the name/version of a Kit/Spec
+  Packageable(..),
+  packageFileName,
+  -- | Utils
+  defaultSpec,
+  -- | Serialisation
+  decodeSpec,
+  encodeSpec,
+  writeSpec
+  ) where
+
+  import Control.Applicative
+  import Control.Monad.Trans
+ 
+  import Data.Object
+  import qualified Data.ByteString as BS 
+  import qualified Data.Object.Yaml as Y
+
+  data KitSpec = KitSpec {
+    specKit :: Kit,
+    specDependencies :: [Kit],
+    specSourceDirectory :: FilePath,
+    specTestDirectory :: FilePath,
+    specLibDirectory :: FilePath,
+    specPrefixFile :: FilePath,
+    specConfigFile :: FilePath,
+    specKitDepsXcodeFlags :: Maybe String
+  } deriving (Show, Read)
+
+  data Kit = Kit {
+    kitName :: String,
+    kitVersion :: String
+  } deriving (Eq, Show, Ord, Read)
+
+  class Packageable a where
+    packageName :: a -> String
+    packageVersion :: a -> String
+
+  packageFileName :: Packageable a => a -> String
+  packageFileName a = packageName a ++ "-" ++ packageVersion a
+
+  instance Packageable Kit where
+    packageName = kitName
+    packageVersion = kitVersion
+
+  instance Packageable KitSpec where
+    packageName = kitName . specKit
+    packageVersion = kitVersion . specKit
+
+  defaultSpec :: String -> String -> KitSpec
+  defaultSpec name version = KitSpec (Kit name version) [] "src" "test" "lib" "Prefix.pch" "Config.xcconfig" Nothing
+  -- TODO make this and the json reading use the same defaults
+  -- I suspect that to do this I'll need update functions for each of
+  -- fields in the KitSpec record.
+  -- Look at the 'lenses' package on hackage.
+
+  decodeSpec :: BS.ByteString -> Maybe KitSpec
+  decodeSpec s = Y.decode s >>= readObject 
+
+  encodeSpec :: KitSpec -> BS.ByteString
+  encodeSpec = Y.encode . showObject
+
+  writeSpec :: MonadIO m => FilePath -> KitSpec -> m ()
+  writeSpec fp spec = liftIO $ BS.writeFile fp $ encodeSpec spec
+
+  class IsObject x where
+    showObject :: x -> StringObject
+    readObject :: StringObject -> Maybe x
+ 
+   
+  (#>) :: IsObject b => [(String, Object String String)] -> String -> Maybe b
+  obj #> key = lookupObject key obj >>= readObject
+
+  instance IsObject a => IsObject [a] where
+    showObject xs = Sequence $ map showObject xs
+    readObject x = fromSequence x >>= mapM readObject 
+
+  instance IsObject String where
+    showObject = Scalar
+    readObject = fromScalar
+
+  instance IsObject Kit where
+    showObject kit = Mapping [("name", w kitName), ("version", w kitVersion)] where w f = showObject . f $ kit
+
+    readObject x = fromMapping x >>= \obj -> Kit <$> obj #> "name" <*> obj #> "version" 
+
+  instance IsObject KitSpec where
+    showObject spec = Mapping [
+         "name" ~> (val $ kitName . specKit),
+         "version" ~> (val $ kitVersion . specKit),
+         "dependencies" ~> showObject (specDependencies spec)
+      ] where a ~> b = (a, b)
+              val f = Scalar . f $ spec
+
+    readObject x = fromMapping x >>= parser
+        where or' a b = a <|> pure b
+              parser obj =  KitSpec <$> readObject x
+                                    <*> (obj #> "dependencies" `or'` [])
+                                    <*> (obj #> "source-directory" `or'` "src")
+                                    <*> (obj #> "test-directory" `or'` "test")
+                                    <*> (obj #> "lib-directory" `or'` "lib")
+                                    <*> (obj #> "prefix-header" `or'` "Prefix.pch")
+                                    <*> (obj #> "xcconfig" `or'` "Config.xcconfig")
+                                    <*> (Just <$> obj #> "kitdeps-xcode-flags") `or'` Nothing
+
diff --git a/Kit/Util.hs b/Kit/Util.hs
--- a/Kit/Util.hs
+++ b/Kit/Util.hs
@@ -4,22 +4,32 @@
   module Kit.Util,
   module Control.Applicative,
   module System.FilePath.Posix,
-  module System.Directory
+  module System.Directory,
+  module Control.Monad
   ) where
-  import System.FilePath.Posix
-  import System.Process
   import System.Directory
-  import Data.Maybe
-  import Data.List
-  import qualified Data.Traversable as T
-  import Data.Foldable
-  import Control.Applicative
+  import System.FilePath.Posix
+  import System.FilePath.Glob
 
+  import Data.List
+  import Data.Maybe
   import Data.Monoid
+
+  import Control.Applicative
+  import Control.Monad
   import Control.Monad.Error
 
-  import System.FilePath.Glob
+  import qualified Control.Monad.State as S
 
+  popS :: S.State [a] a
+  popS = do
+    (x:t) <- S.get
+    S.put t
+    return x
+  
+  puts :: MonadIO m => String -> m ()
+  puts a = liftIO $ putStrLn a
+
   maybeRead :: Read a => String -> Maybe a
   maybeRead = fmap fst . listToMaybe . reads
 
@@ -32,30 +42,23 @@
   maybeToLeft :: b -> Maybe a -> Either a b
   maybeToLeft v = maybe (Right v) Left
 
-  instance Foldable (Either c) where
-    foldr f z = either (const z) (\v -> f v z)
-
-  instance T.Traversable (Either c) where
-    sequenceA = either (pure . Left) (Right <$>)
-
-  type KitError = String
-
-  type KitIO a = ErrorT KitError IO a
-
+  type KitIO a = ErrorT String IO a
+  
+  maybeToKitIO :: String -> Maybe a -> KitIO a
   maybeToKitIO msg = maybe (throwError msg) return
 
-  mkdir_p :: FilePath -> IO ()
-  mkdir_p = createDirectoryIfMissing True
+  mkdir_p :: MonadIO m => FilePath -> m ()
+  mkdir_p = liftIO . createDirectoryIfMissing True
 
-  cleanOrCreate :: FilePath -> IO ()
-  cleanOrCreate directory = do
+  cleanOrCreate :: MonadIO m => FilePath -> m ()
+  cleanOrCreate directory = liftIO $ do
     exists <- doesDirectoryExist directory
     when exists $ removeDirectoryRecursive directory
     mkdir_p directory
 
   inDirectory :: MonadIO m => FilePath -> m a -> m a
   inDirectory dir actions = do
-    cwd <- liftIO $ getCurrentDirectory
+    cwd <- liftIO getCurrentDirectory
     liftIO $ setCurrentDirectory dir
     v <- actions
     liftIO $ setCurrentDirectory cwd
@@ -66,4 +69,4 @@
 
   stringJoin :: Monoid a => a -> [a] -> a
   stringJoin x = mconcat . intersperse x
-
+  
diff --git a/Kit/XCode/Builder.hs b/Kit/XCode/Builder.hs
deleted file mode 100644
--- a/Kit/XCode/Builder.hs
+++ /dev/null
@@ -1,106 +0,0 @@
-
-module Kit.XCode.Builder (buildXCodeProject) where
-  import Data.Monoid
-  import Kit.XCode.Common
-  import Kit.XCode.ProjectFileTemplate
-  import Text.PList
-  import Kit.Util
-
-  import Debug.Trace
-
-  import System.FilePath.Posix (dropFileName)
-  import Data.List (nub)
-
-  createBuildFile :: Integer -> FilePath -> PBXBuildFile
-  createBuildFile i path = PBXBuildFile uuid1 $ PBXFileReference uuid2 path
-    where uuid1 = uuid i
-          uuid2 = uuid $ i + 10000000
-
-  buildXCodeProject :: [FilePath] -> [FilePath] -> [FilePath] -> String
-  buildXCodeProject headers sources libs = show $ makeProjectPList bfs frs classes hs srcs xxx fg libDirs
-    where
-      libDirs = nub $ map dropFileName libs 
-      sourceStart = toInteger (length headers + 1)
-      libStart = toInteger (length headers + length sources + 1)
-      headerBuildFiles = zipWith createBuildFile [1..] headers
-      sourceBuildFiles = zipWith createBuildFile [sourceStart..] sources
-      libBuildFiles = zipWith createBuildFile [libStart..] libs
-      allBuildFiles = (sourceBuildFiles ++ headerBuildFiles ++ libBuildFiles)
-      bfs = buildFileSection allBuildFiles
-      frs = fileReferenceSection $ map buildFileReference allBuildFiles
-      classes = classesGroup $ map buildFileReference (sourceBuildFiles ++ headerBuildFiles)
-      hs = headersBuildPhase headerBuildFiles
-      srcs = sourcesBuildPhase sourceBuildFiles
-      xxx = frameworksBuildPhase $ traceShow libBuildFiles libBuildFiles
-      fg = frameworksGroup $ map buildFileReference libBuildFiles
-
-  buildFileSection :: [PBXBuildFile] -> [PListObjectItem]
-  buildFileSection bfs = (map buildFileItem bfs ++ [
-      "4728C530117C02B10027D7D1" ~> buildFile kitConfigRefUUID,
-      "AA747D9F0F9514B9006C5449" ~> buildFile "AA747D9E0F9514B9006C5449",
-      "AACBBE4A0F95108600F1A2B1" ~> buildFile "AACBBE490F95108600F1A2B1"
-    ])
-
-  fileReferenceSection :: [PBXFileReference] -> [PListObjectItem]
-  fileReferenceSection refs = map fileReferenceItem refs ++ [
-  		kitConfigRefUUID ~> obj [ 
-        "isa" ~> val "PBXFileReference",
-        "fileEncoding" ~> val "4",
-        "lastKnownFileType" ~> val "text.xcconfig",
-        "path" ~> val "DepsOnly.xcconfig",
-        "sourceTree" ~> val "<group>"
-        ],
-      "AA747D9E0F9514B9006C5449" ~> obj [
-        "isa" ~> val "PBXFileReference",
-        "fileEncoding" ~> val "4",
-        "lastKnownFileType" ~> val "sourcecode.c.h",
-        "path" ~> val "KitDeps_Prefix.pch",
-        "sourceTree" ~> val "SOURCE_ROOT"
-        ],
-      "AACBBE490F95108600F1A2B1" ~> obj [
-        "isa" ~> val "PBXFileReference",
-        "lastKnownFileType" ~> val "wrapper.framework",
-        "name" ~> val "Foundation.framework", 
-        "path" ~> val "System/Library/Frameworks/Foundation.framework",
-        "sourceTree" ~> val "SDKROOT"
-        ],
-      productRefUUID ~> obj [
-        "isa" ~> val "PBXFileReference",
-        "explicitFileType" ~> val "archive.ar",
-        "includeInIndex" ~> val "0",
-        "path" ~> val "libKitDeps.a",
-        "sourceTree" ~> val "BUILT_PRODUCTS_DIR"
-      ]
-    ]
-
-  classesGroup :: [PBXFileReference] -> PListObjectItem 
-  classesGroup files = classesGroupUUID ~> group "Classes" (map (val . fileReferenceId) files)
-	
-  frameworksGroup :: [PBXFileReference] -> PListObjectItem
-  frameworksGroup files = frameworksGroupUUID ~> group "Frameworks" (val "AACBBE490F95108600F1A2B1" :  (map (val . fileReferenceId) files))
-
-  frameworksBuildPhase :: [PBXBuildFile] -> PListObjectItem  
-  frameworksBuildPhase libs = frameworksBuildPhaseUUID ~> obj [
-            "isa" ~> val "PBXFrameworksBuildPhase",
-            "buildActionMask" ~> val "2147483647",
-            "files" ~> arr (val "AACBBE4A0F95108600F1A2B1" : map (val . buildFileId) libs),
-            "runOnlyForDeploymentPostprocessing" ~> val "0"
-          ]
-
-  headersBuildPhase :: [PBXBuildFile] -> PListObjectItem
-  headersBuildPhase bfs = headersBuildPhaseUUID ~> obj [
-    "isa" ~> val "PBXHeadersBuildPhase",
-    "buildActionMask" ~> val "2147483647",
-    "files" ~> arr (val "AA747D9F0F9514B9006C5449" : map (val . buildFileId) bfs),
-    "runOnlyForDeploymentPostprocessing" ~> val "0"
-	  ]
-	
-  sourcesBuildPhase :: [PBXBuildFile] -> PListObjectItem
-  sourcesBuildPhase bfs = sourcesBuildPhaseUUID ~> obj [
-    "isa" ~> val "PBXSourcesBuildPhase",
-    "buildActionMask" ~> val "22147483647147483647",
-    "files" ~>  arr (map (val . buildFileId) bfs),
-    "runOnlyForDeploymentPostprocessing" ~> val "0"
-    ]
-
-
diff --git a/Kit/XCode/Common.hs b/Kit/XCode/Common.hs
deleted file mode 100644
--- a/Kit/XCode/Common.hs
+++ /dev/null
@@ -1,69 +0,0 @@
-module Kit.XCode.Common where
-  import Data.Monoid
-  import Data.List
-  import System.FilePath.Posix
-  
-  import Text.PList
-
-  
-  type UUID = String
-  
-  data FileType = Header | Source | Archive | Unknown
-  
-  fileType :: String -> FileType
-  fileType x | ".h" `isSuffixOf` x = Header
-  fileType x | ".m" `isSuffixOf` x = Source
-  fileType x | ".mm" `isSuffixOf` x = Source
-  fileType x | ".c" `isSuffixOf` x = Source
-  fileType x | ".a" `isSuffixOf` x = Archive
-  fileType _ = Unknown
-
-  fileTypeBit :: FileType -> String
-  fileTypeBit Header = "sourcecode.c.h"
-  fileTypeBit Source = "sourcecode.c.objc"
-  fileTypeBit Unknown = "sourcecode.unknown"
-  fileTypeBit Archive = "archive.ar"
-
-  data PBXBuildFile = PBXBuildFile {
-    buildFileId :: UUID,
-    buildFileReference :: PBXFileReference
-  } deriving (Eq, Show)
-  
-  data PBXFileReference = PBXFileReference {
-    fileReferenceId :: UUID,
-    fileReferencePath :: String
-  } deriving (Eq, Show)
-  
-  group name children = obj [
-      "isa" ~> val "PBXGroup",
-      "name" ~> val name,
-      "sourceTree" ~> val "<group>",
-      "children" ~> arr children
-    ]
-
-  buildFile refUUID = obj [ "isa" ~> val "PBXBuildFile", "fileRef" ~> val refUUID ]
-
-  buildFileItem :: PBXBuildFile -> PListObjectItem 
-  buildFileItem bf = buildFileId bf ~> buildFile (fileReferenceId . buildFileReference $ bf) 
-
-  fileReferenceItem :: PBXFileReference -> PListObjectItem
-  fileReferenceItem fr = (fileReferenceId fr) ~> dict
-    where
-      fileName = fileReferenceName fr
-      dict = obj [
-          "isa" ~> val "PBXFileReference",
-          "fileEncoding" ~> val "4",
-          "lastKnownFileType" ~> val (fileTypeBit . fileType $ fileName),
-          "name" ~> val fileName,
-          "path" ~> (val $ fileReferencePath fr),
-          "sourceTree" ~> val "<group>"
-        ]
-
-
-  fileReferenceName = takeFileName . fileReferencePath
-  
-  uuid :: Integer -> UUID
-  uuid i = let
-       s = show i
-       pad = 24 - length s
-    in replicate pad '0' ++ s 
diff --git a/Kit/XCode/ProjectFileTemplate.hs b/Kit/XCode/ProjectFileTemplate.hs
deleted file mode 100644
--- a/Kit/XCode/ProjectFileTemplate.hs
+++ /dev/null
@@ -1,202 +0,0 @@
-{-
-
-  Constant sections of the project file
--}
-module Kit.XCode.ProjectFileTemplate where
-
-  import Kit.XCode.Common
-  import Text.PList
-
-  import qualified Data.List as L
-
-  projectFile objects uuid = PListFile "!$*UTF8*$!" $ obj [
-        "archiveVersion" ~> val "1",
-        "classes" ~> obj [],
-        "objectVersion" ~> val "45",
-        "objects" ~> obj objects, 
-        "rootObject" ~> val uuid
-    ]
-
-  groupProductsUUID = "034768DFFF38A50411DB9C8B"
-  mainGroupUUID = "0867D691FE84028FC02AAC07"
-  classesGroupUUID = "08FB77AEFE84172EC02AAC07"
-  otherSourcesGroupUUID = "32C88DFF0371C24200C91783"
-  frameworksGroupUUID = "0867D69AFE84028FC02AAC07"
-
-  productRefUUID = "D2AAC07E0554694100DB518D"
-  kitConfigRefUUID = "4728C52F117C02B10027D7D1"
-
-  headersBuildPhaseUUID = "D2AAC07A0554694100DB518D"
-  sourcesBuildPhaseUUID = "D2AAC07B0554694100DB518D"
-  frameworksBuildPhaseUUID = "D2AAC07C0554694100DB518D"
-
-  makeProjectPList :: 
-    [PListObjectItem] -> -- build file section
-    [PListObjectItem] -> -- file refs section
-    PListObjectItem -> -- classes item
-    PListObjectItem -> -- headers section
-    PListObjectItem -> -- sources section
-    PListObjectItem -> -- frameworks section
-    PListObjectItem -> -- frameworks group
-    [FilePath] -> -- lib directories
-    PListFile 
-  makeProjectPList bfs fileRefsSection classes headers sources frameworks fg libDirs = projectFile objs "0867D690FE84028FC02AAC07" where
-      objs = bfs ++ fileRefsSection ++ [frameworks] ++ [fg] ++ next1 ++ [classes] ++ [next2] ++ [headers] ++ next3 ++ [sources] ++ buildConfigurations libDirs
-
-  next1 = [ groupProductsUUID ~> group "Products" [ val productRefUUID ],
-            mainGroupUUID ~> group "KitDeps" [
-              val classesGroupUUID,
-              val otherSourcesGroupUUID,
-              val frameworksGroupUUID,
-              val groupProductsUUID,
-              val kitConfigRefUUID
-            ]
-          ]
-    		
-  next2 = otherSourcesGroupUUID ~> group "Other Sources" [val "AA747D9E0F9514B9006C5449"]
-
-  next3 = ("D2AAC07D0554694100DB518D" ~> obj [
-        			"isa" ~> val "PBXNativeTarget",
-        			"buildConfigurationList" ~> val "1DEB921E08733DC00010E9CD",
-        			"buildPhases" ~> arr [ val headersBuildPhaseUUID, val sourcesBuildPhaseUUID, val frameworksBuildPhaseUUID ],
-              "buildRules" ~> arr [],
-        			"dependencies" ~> arr [],
-        			"name" ~> val "KitDeps",
-        			"productName" ~> val "KitDeps",
-        			"productReference" ~> val productRefUUID,
-        			"productType" ~> val "com.apple.product-type.library.static"
-        	  ]) : kitUpdateTarget ++ ["0867D690FE84028FC02AAC07" ~> obj [
-        			"isa" ~> val "PBXProject",
-        			"buildConfigurationList" ~> val "1DEB922208733DC00010E9CD",
-        			"compatibilityVersion" ~> val "Xcode 3.1",
-        			"hasScannedForEncodings" ~> val "1",
-        			"mainGroup" ~> val mainGroupUUID ,
-        			"productRefGroup" ~> val groupProductsUUID,
-        			"projectDirPath" ~> val "",
-        			"projectRoot" ~> val "",
-        			"targets" ~> arr [ val "D2AAC07D0554694100DB518D", val "470E2D641287730A0084AE6F" ] 
-        	]]
-
-  librarySearchPaths libDirs = "LIBRARY_SEARCH_PATHS" ~> val ("$(inherited) " ++ (L.intersperse " " libDirs >>= id))
-
-  buildConfigurations libDirs = let libSearch = librarySearchPaths libDirs in ["1DEB921F08733DC00010E9CD" ~> obj [
-        			"isa" ~> val "XCBuildConfiguration",
-        			"buildSettings" ~> obj [
-        				"ALWAYS_SEARCH_USER_PATHS" ~> val "NO",
-        				"ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
-        				"COPY_PHASE_STRIP" ~> val "NO",
-        				"DSTROOT" ~> val "/tmp/KitDeps.dst",
-        				"GCC_DYNAMIC_NO_PIC" ~> val "NO",
-        				"GCC_ENABLE_FIX_AND_CONTINUE" ~> val "YES",
-        				"GCC_MODEL_TUNING" ~> val "G5",
-        				"GCC_OPTIMIZATION_LEVEL" ~> val "0",
-        				"GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",
-        				"GCC_PREFIX_HEADER" ~> val "KitDeps_Prefix.pch",
-        				"INSTALL_PATH" ~> val "/usr/local/lib",
-        				"PRODUCT_NAME" ~> val "KitDeps",
-                libSearch 
-              ],
-        			"name" ~> val "Debug"
-            ],
-        		"1DEB922008733DC00010E9CD" ~> obj [
-        			"isa" ~> val "XCBuildConfiguration",
-        			"buildSettings" ~> obj [
-        				"ALWAYS_SEARCH_USER_PATHS" ~> val "NO",
-        				"ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
-        				"DSTROOT" ~> val "/tmp/KitDeps.dst",
-        				"GCC_MODEL_TUNING" ~> val "G5",
-        				"GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",
-        				"GCC_PREFIX_HEADER" ~> val "KitDeps_Prefix.pch",
-        				"INSTALL_PATH" ~> val "/usr/local/lib",
-        				"PRODUCT_NAME" ~> val "KitDeps",
-                libSearch
-                ],
-        			"name" ~> val "Release"
-              ],
-        		"1DEB922308733DC00010E9CD" ~> obj [
-        			"isa" ~> val "XCBuildConfiguration",
-        			"baseConfigurationReference" ~> val kitConfigRefUUID,
-        			"buildSettings" ~> obj [
-        				"ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
-        				"GCC_C_LANGUAGE_STANDARD" ~> val "c99",
-        				"GCC_OPTIMIZATION_LEVEL" ~> val "0",
-        				"GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",
-        				"GCC_WARN_UNUSED_VARIABLE" ~> val "YES",
-        				"OTHER_LDFLAGS" ~> val "-ObjC",
-        				"PREBINDING" ~> val "NO",
-        				"SDKROOT" ~> val "iphoneos",
-                libSearch
-        			],
-        			"name" ~> val "Debug"
-        		],
-        		"1DEB922408733DC00010E9CD" ~> obj [
-        			"isa" ~> val "XCBuildConfiguration",
-        			"baseConfigurationReference" ~> val kitConfigRefUUID,
-        			"buildSettings" ~> obj [
-        				"ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
-        				"GCC_C_LANGUAGE_STANDARD" ~> val "c99",
-        				"GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",
-        				"GCC_WARN_UNUSED_VARIABLE" ~> val "YES",
-        				"OTHER_LDFLAGS" ~> val "-ObjC",
-        				"PREBINDING" ~> val "NO",
-        				"SDKROOT" ~> val "iphoneos",
-                libSearch
-        			],
-        			"name" ~> val "Release"
-        		],
-        		"1DEB921E08733DC00010E9CD" ~> obj [
-        			"isa" ~> val "XCConfigurationList",
-        			"buildConfigurations" ~> arr [
-        				val "1DEB921F08733DC00010E9CD",
-        				val "1DEB922008733DC00010E9CD"
-        			],
-        			"defaultConfigurationIsVisible" ~> val "0",
-        			"defaultConfigurationName" ~> val "Release"
-        		],
-        		"1DEB922208733DC00010E9CD" ~> obj [
-        			"isa" ~> val "XCConfigurationList",
-        			"buildConfigurations" ~> arr [
-        				val "1DEB922308733DC00010E9CD",
-        				val "1DEB922408733DC00010E9CD"
-        			],
-        			"defaultConfigurationIsVisible" ~> val "0",
-        			"defaultConfigurationName" ~> val "Release"
-        		]
-        	]
-
-  kitUpdateTarget = [
-        "470E2D641287730A0084AE6F" ~> obj [ 
-          "isa" ~> val "PBXLegacyTarget",
-          "buildArgumentsString" ~> val "",
-          "buildConfigurationList" ~> val "470E2D721287731E0084AE6F",
-          "buildPhases" ~> arr [],
-          "buildToolPath" ~> val "/usr/bin/make",
-          "buildWorkingDirectory" ~> val "",
-          "dependencies" ~> arr [],
-          "name" ~> val "KitUpdate",
-          "passBuildSettingsInEnvironment" ~> val "1",
-          "productName" ~> val "KitUpdate"
-        ],
-        "470E2D721287731E0084AE6F" ~> obj [
-          "isa" ~> val "XCConfigurationList",
-          "buildConfigurations" ~> arr [ val "470E2D651287730B0084AE6F", val "470E2D661287730B0084AE6F" ],
-          "defaultConfigurationIsVisible" ~> val "0",
-          "defaultConfigurationName" ~> val "Debug"
-        ],
-        "470E2D651287730B0084AE6F" ~> obj [
-          "isa" ~> val "XCBuildConfiguration",
-          "buildSettings" ~> obj [
-            "COPY_PHASE_STRIP" ~> val "NO",
-            "GCC_DYNAMIC_NO_PIC" ~> val "NO",
-            "GCC_OPTIMIZATION_LEVEL" ~> val "0", 
-            "PRODUCT_NAME" ~> val "KitUpdate" 
-          ],
-          "name" ~> val "Debug"
-        ], 
-        "470E2D661287730B0084AE6F" ~> obj [
-          "isa" ~> val "XCBuildConfiguration",
-          "buildSettings" ~> obj [ "PRODUCT_NAME" ~> val "KitUpdate" ],
-          "name" ~> val "Release"
-        ]]
-  
-
diff --git a/Kit/XCode/XCConfig.hs b/Kit/XCode/XCConfig.hs
deleted file mode 100644
--- a/Kit/XCode/XCConfig.hs
+++ /dev/null
@@ -1,61 +0,0 @@
-module Kit.XCode.XCConfig where
-
-  import Kit.Util
-
-  import qualified Data.Map as M
-  import Data.Maybe
-  import Data.Either
-  import Data.List
-  import Data.Char (isSpace)
-
-  type XCCSetting = (String, String)
-  type XCCInclude = String
-                    
-  data XCConfig = XCC { 
-    configName :: String, 
-    configSettings :: M.Map String String, 
-    configIncludes :: [XCCInclude]
-  } deriving (Eq, Show)
-  
-  includeStart = "#include "
-  
-  strip :: String -> String
-  strip = f . f where f = reverse . dropWhile isSpace
-  
-  parseLine :: String -> Maybe (Either XCCSetting XCCInclude)
-  parseLine l | includeStart `isPrefixOf` l = Just . Right . fromJust $ stripPrefix includeStart l
-  parseLine l | "=" `isInfixOf` l = fmap (Left) $ breakAround '=' l
-    where breakAround a xs = let (q,r) = break (a ==) xs in nonEmptyPair (strip q, strip $ drop 1 r)
-          nonEmptyPair ([], _) = Nothing
-          nonEmptyPair (_, []) = Nothing
-          nonEmptyPair a@_ = Just a
-  parseLine _ = Nothing
-  
-  settingToString :: XCCSetting -> String
-  settingToString (a, b) = a ++ " = " ++ b
-  
-  includeToString :: XCCInclude -> String
-  includeToString a = includeStart ++ a
-  
-  configToString :: XCConfig -> String
-  configToString (XCC _ settings includes) = stringJoin "\n" $ (map includeToString includes) ++ (map settingToString $ M.toList settings)
-                  
-  fileContentsToXCC :: String -> String -> XCConfig
-  fileContentsToXCC name content = let ls = lines content
-                                       (settings, includes) = partitionEithers (ls >>= (maybeToList . parseLine))
-                                    in XCC name (M.fromList settings) includes
-
-  configAsMap :: XCConfig -> M.Map String [(String, String)]
-  configAsMap (XCC name settings _) = M.map (return . (,) name) settings
-  
-  multiConfig :: String -> [XCConfig] -> XCConfig
-  multiConfig name configs = XCC name settings includes
-    where includes = configs >>= configIncludes
-          settingsWithName = foldl (M.unionWith (++)) M.empty (map configAsMap configs)
-          aggregateSettings = M.mapWithKey f settingsWithName
-            where f key namesAndValues = stringJoin " " $ map ((\x -> "$(" ++ x ++ "_" ++ key ++ ")") . fst) namesAndValues
-          individualSettings = M.fromList $ concatMap f $ M.toList settingsWithName
-            where f (key, namesAndValues) = map (g key) namesAndValues
-                  g key (n, v) = (n ++ "_" ++ key, v)
-          settings = M.union aggregateSettings individualSettings
-  
diff --git a/Kit/Xcode/Builder.hs b/Kit/Xcode/Builder.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Xcode/Builder.hs
@@ -0,0 +1,113 @@
+
+module Kit.Xcode.Builder (renderXcodeProject) where
+  import Kit.Xcode.Common
+  import Kit.Xcode.ProjectFileTemplate
+  import Text.PList
+  import Kit.Util
+  import Data.List (nub)
+  import Control.Monad.State
+
+  createBuildFile :: Integer -> FilePath -> PBXBuildFile
+  createBuildFile i path = PBXBuildFile uuid1 $ PBXFileReference uuid2 path
+    where uuid1 = uuid i
+          uuid2 = uuid $ i + 10000000
+
+  buildFileFromState :: FilePath -> State [Integer] PBXBuildFile
+  buildFileFromState filePath = flip createBuildFile filePath <$> popS 
+
+  -- | Render an Xcode project!
+  renderXcodeProject :: 
+    [FilePath]    -- ^ Headers  
+    -> [FilePath] -- ^ Sources
+    -> [FilePath] -- ^ Static Libs
+    -> String     -- ^ Output lib name
+    -> String     
+  renderXcodeProject headers sources libs outputLibName = fst . flip runState [1..] $ do
+      headerBuildFiles <- mapM buildFileFromState headers
+      sourceBuildFiles <- mapM buildFileFromState sources
+      libBuildFiles <- mapM buildFileFromState libs -- Build File Items
+      let allBuildFiles = (sourceBuildFiles ++ headerBuildFiles ++ libBuildFiles)
+          -- Build And FileRef sections
+          bfs = buildFileSection allBuildFiles
+          frs = fileReferenceSection (map buildFileReference allBuildFiles) outputLibName 
+          -- Groups
+          classes = classesGroup $ map buildFileReference (sourceBuildFiles ++ headerBuildFiles)
+          fg = frameworksGroup $ map buildFileReference libBuildFiles
+          -- Phases
+          headersPhase = headersBuildPhase headerBuildFiles
+          srcsPhase = sourcesBuildPhase sourceBuildFiles
+          frameworksPhase = frameworksBuildPhase libBuildFiles
+          -- UUID indices
+          libDirs = nub $ map dropFileName libs 
+      return . show $ makeProjectPList (bfs ++ frs ++ [classes, headersPhase, srcsPhase, frameworksPhase, fg]) libDirs
+
+  buildFileSection :: [PBXBuildFile] -> [PListObjectItem]
+  buildFileSection bfs = map buildFileItem bfs ++ [
+      "4728C530117C02B10027D7D1" ~> buildFile kitConfigRefUUID,
+      "AA747D9F0F9514B9006C5449" ~> buildFile "AA747D9E0F9514B9006C5449",
+      "AACBBE4A0F95108600F1A2B1" ~> buildFile "AACBBE490F95108600F1A2B1"
+    ]
+
+  fileReferenceSection :: [PBXFileReference] -> String -> [PListObjectItem]
+  fileReferenceSection refs archiveName = map fileReferenceItem refs ++ [
+  		kitConfigRefUUID ~> obj [ 
+        "isa" ~> val "PBXFileReference",
+        "fileEncoding" ~> val "4",
+        "lastKnownFileType" ~> val "text.xcconfig",
+        "path" ~> val "DepsOnly.xcconfig",
+        "sourceTree" ~> val "<group>"
+        ],
+      "AA747D9E0F9514B9006C5449" ~> obj [
+        "isa" ~> val "PBXFileReference",
+        "fileEncoding" ~> val "4",
+        "lastKnownFileType" ~> val "sourcecode.c.h",
+        "path" ~> val "Prefix.pch",
+        "sourceTree" ~> val "SOURCE_ROOT"
+        ],
+      "AACBBE490F95108600F1A2B1" ~> obj [
+        "isa" ~> val "PBXFileReference",
+        "lastKnownFileType" ~> val "wrapper.framework",
+        "name" ~> val "Foundation.framework", 
+        "path" ~> val "System/Library/Frameworks/Foundation.framework",
+        "sourceTree" ~> val "SDKROOT"
+        ],
+      productRefUUID ~> obj [
+        "isa" ~> val "PBXFileReference",
+        "explicitFileType" ~> val "archive.ar",
+        "includeInIndex" ~> val "0",
+        "path" ~> val archiveName,
+        "sourceTree" ~> val "BUILT_PRODUCTS_DIR"
+      ]
+    ]
+
+  classesGroup :: [PBXFileReference] -> PListObjectItem 
+  classesGroup files = classesGroupUUID ~> group "Classes" (map (val . fileReferenceId) files)
+	
+  frameworksGroup :: [PBXFileReference] -> PListObjectItem
+  frameworksGroup files = frameworksGroupUUID ~> group "Frameworks" (val "AACBBE490F95108600F1A2B1" :  map (val . fileReferenceId) files)
+
+  frameworksBuildPhase :: [PBXBuildFile] -> PListObjectItem  
+  frameworksBuildPhase libs = frameworksBuildPhaseUUID ~> obj [
+            "isa" ~> val "PBXFrameworksBuildPhase",
+            "buildActionMask" ~> val "2147483647",
+            "files" ~> arr (val "AACBBE4A0F95108600F1A2B1" : map (val . buildFileId) libs),
+            "runOnlyForDeploymentPostprocessing" ~> val "0"
+          ]
+
+  headersBuildPhase :: [PBXBuildFile] -> PListObjectItem
+  headersBuildPhase bfs = headersBuildPhaseUUID ~> obj [
+    "isa" ~> val "PBXHeadersBuildPhase",
+    "buildActionMask" ~> val "2147483647",
+    "files" ~> arr (val "AA747D9F0F9514B9006C5449" : map (val . buildFileId) bfs),
+    "runOnlyForDeploymentPostprocessing" ~> val "0"
+	  ]
+	
+  sourcesBuildPhase :: [PBXBuildFile] -> PListObjectItem
+  sourcesBuildPhase bfs = sourcesBuildPhaseUUID ~> obj [
+    "isa" ~> val "PBXSourcesBuildPhase",
+    "buildActionMask" ~> val "22147483647147483647",
+    "files" ~>  arr (map (val . buildFileId) bfs),
+    "runOnlyForDeploymentPostprocessing" ~> val "0"
+    ]
+
+
diff --git a/Kit/Xcode/Common.hs b/Kit/Xcode/Common.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Xcode/Common.hs
@@ -0,0 +1,71 @@
+module Kit.Xcode.Common where
+  import Data.List
+  import System.FilePath.Posix
+  
+  import Text.PList
+
+  
+  type UUID = String
+  
+  data FileType = Header | Source | Archive | Unknown
+  
+  fileType :: String -> FileType
+  fileType x | ".h" `isSuffixOf` x = Header
+  fileType x | ".m" `isSuffixOf` x = Source
+  fileType x | ".mm" `isSuffixOf` x = Source
+  fileType x | ".c" `isSuffixOf` x = Source
+  fileType x | ".a" `isSuffixOf` x = Archive
+  fileType _ = Unknown
+
+  fileTypeBit :: FileType -> String
+  fileTypeBit Header = "sourcecode.c.h"
+  fileTypeBit Source = "sourcecode.c.objc"
+  fileTypeBit Unknown = "sourcecode.unknown"
+  fileTypeBit Archive = "archive.ar"
+
+  data PBXBuildFile = PBXBuildFile {
+    buildFileId :: UUID,
+    buildFileReference :: PBXFileReference
+  } deriving (Eq, Show)
+  
+  data PBXFileReference = PBXFileReference {
+    fileReferenceId :: UUID,
+    fileReferencePath :: String
+  } deriving (Eq, Show)
+ 
+  group :: String -> [PListType] -> PListType 
+  group name children = obj [
+      "isa" ~> val "PBXGroup",
+      "name" ~> val name,
+      "sourceTree" ~> val "<group>",
+      "children" ~> arr children
+    ]
+
+  buildFile :: String -> PListType
+  buildFile refUUID = obj [ "isa" ~> val "PBXBuildFile", "fileRef" ~> val refUUID ]
+
+  buildFileItem :: PBXBuildFile -> PListObjectItem 
+  buildFileItem bf = buildFileId bf ~> buildFile (fileReferenceId . buildFileReference $ bf) 
+
+  fileReferenceItem :: PBXFileReference -> PListObjectItem
+  fileReferenceItem fr = fileReferenceId fr ~> dict
+    where
+      fileName = fileReferenceName fr
+      dict = obj [
+          "isa" ~> val "PBXFileReference",
+          "fileEncoding" ~> val "4",
+          "lastKnownFileType" ~> val (fileTypeBit . fileType $ fileName),
+          "name" ~> val fileName,
+          "path" ~> val (fileReferencePath fr),
+          "sourceTree" ~> val "<group>"
+        ]
+
+
+  fileReferenceName :: PBXFileReference -> String
+  fileReferenceName = takeFileName . fileReferencePath
+  
+  uuid :: Integer -> UUID
+  uuid i = let
+       s = show i
+       pad = 24 - length s
+    in replicate pad '0' ++ s 
diff --git a/Kit/Xcode/ProjectFileTemplate.hs b/Kit/Xcode/ProjectFileTemplate.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Xcode/ProjectFileTemplate.hs
@@ -0,0 +1,190 @@
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+module Kit.Xcode.ProjectFileTemplate where
+
+import Kit.Xcode.Common
+import Text.PList
+import Control.Monad (join)
+
+import qualified Data.List as L
+
+projectFile objects rootId = PListFile "!$*UTF8*$!" $ obj [
+      "archiveVersion" ~> val "1",
+      "classes" ~> obj [],
+      "objectVersion" ~> val "45",
+      "objects" ~> obj objects, 
+      "rootObject" ~> val rootId 
+  ]
+
+groupProductsUUID = "034768DFFF38A50411DB9C8B"
+mainGroupUUID = "0867D691FE84028FC02AAC07"
+classesGroupUUID = "08FB77AEFE84172EC02AAC07"
+otherSourcesGroupUUID = "32C88DFF0371C24200C91783"
+frameworksGroupUUID = "0867D69AFE84028FC02AAC07"
+
+productRefUUID = "D2AAC07E0554694100DB518D"
+kitConfigRefUUID = "4728C52F117C02B10027D7D1"
+
+headersBuildPhaseUUID = "D2AAC07A0554694100DB518D"
+sourcesBuildPhaseUUID = "D2AAC07B0554694100DB518D"
+frameworksBuildPhaseUUID = "D2AAC07C0554694100DB518D"
+
+makeProjectPList :: [PListObjectItem] -> [FilePath] -> PListFile 
+makeProjectPList objects libDirs = projectFile objs "0867D690FE84028FC02AAC07" where
+    objs = objects ++ groups ++ targets ++ buildConfigurations libDirs
+
+groups = [ groupProductsUUID ~> group "Products" [ val productRefUUID ],
+          mainGroupUUID ~> group "KitDeps" [
+            val classesGroupUUID,
+            val otherSourcesGroupUUID,
+            val frameworksGroupUUID,
+            val groupProductsUUID,
+            val kitConfigRefUUID
+          ],
+          otherSourcesGroupUUID ~> group "Other Sources" [val "AA747D9E0F9514B9006C5449"]
+        ]
+      
+targets = ("D2AAC07D0554694100DB518D" ~> obj [
+            "isa" ~> val "PBXNativeTarget",
+            "buildConfigurationList" ~> val "1DEB921E08733DC00010E9CD",
+            "buildPhases" ~> arr [ val headersBuildPhaseUUID, val sourcesBuildPhaseUUID, val frameworksBuildPhaseUUID ],
+            "buildRules" ~> arr [],
+            "dependencies" ~> arr [],
+            "name" ~> val "KitDeps",
+            "productName" ~> val "KitDeps",
+            "productReference" ~> val productRefUUID,
+            "productType" ~> val "com.apple.product-type.library.static"
+          ]) : kitUpdateTarget ++ ["0867D690FE84028FC02AAC07" ~> obj [
+            "isa" ~> val "PBXProject",
+            "buildConfigurationList" ~> val "1DEB922208733DC00010E9CD",
+            "compatibilityVersion" ~> val "Xcode 3.1",
+            "hasScannedForEncodings" ~> val "1",
+            "mainGroup" ~> val mainGroupUUID ,
+            "productRefGroup" ~> val groupProductsUUID,
+            "projectDirPath" ~> val "",
+            "projectRoot" ~> val "",
+            "targets" ~> arr [ val "D2AAC07D0554694100DB518D", val "470E2D641287730A0084AE6F" ] 
+        ]]
+
+librarySearchPaths libDirs = "LIBRARY_SEARCH_PATHS" ~> val ("$(inherited) " ++ join (L.intersperse " " libDirs))
+
+buildConfigurations libDirs = let libSearch = librarySearchPaths libDirs in ["1DEB921F08733DC00010E9CD" ~> obj [
+            "isa" ~> val "XCBuildConfiguration",
+            "buildSettings" ~> obj [
+              "ALWAYS_SEARCH_USER_PATHS" ~> val "NO",
+              "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
+              "COPY_PHASE_STRIP" ~> val "NO",
+              "DSTROOT" ~> val "/tmp/KitDeps.dst",
+              "GCC_DYNAMIC_NO_PIC" ~> val "NO",
+              "GCC_ENABLE_FIX_AND_CONTINUE" ~> val "YES",
+              "GCC_MODEL_TUNING" ~> val "G5",
+              "GCC_OPTIMIZATION_LEVEL" ~> val "0",
+              "GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",
+              "GCC_PREFIX_HEADER" ~> val "Prefix.pch",
+              "INSTALL_PATH" ~> val "/usr/local/lib",
+              "PRODUCT_NAME" ~> val "KitDeps",
+              libSearch 
+            ],
+            "name" ~> val "Debug"
+          ],
+          "1DEB922008733DC00010E9CD" ~> obj [
+            "isa" ~> val "XCBuildConfiguration",
+            "buildSettings" ~> obj [
+              "ALWAYS_SEARCH_USER_PATHS" ~> val "NO",
+              "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
+              "DSTROOT" ~> val "/tmp/KitDeps.dst",
+              "GCC_MODEL_TUNING" ~> val "G5",
+              "GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",
+              "GCC_PREFIX_HEADER" ~> val "Prefix.pch",
+              "INSTALL_PATH" ~> val "/usr/local/lib",
+              "PRODUCT_NAME" ~> val "KitDeps",
+              libSearch
+              ],
+            "name" ~> val "Release"
+            ],
+          "1DEB922308733DC00010E9CD" ~> obj [
+            "isa" ~> val "XCBuildConfiguration",
+            "baseConfigurationReference" ~> val kitConfigRefUUID,
+            "buildSettings" ~> obj [
+              "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
+              "GCC_C_LANGUAGE_STANDARD" ~> val "c99",
+              "GCC_OPTIMIZATION_LEVEL" ~> val "0",
+              "GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",
+              "GCC_WARN_UNUSED_VARIABLE" ~> val "YES",
+              "OTHER_LDFLAGS" ~> val "-ObjC",
+              "PREBINDING" ~> val "NO",
+              "SDKROOT" ~> val "iphoneos",
+              libSearch
+            ],
+            "name" ~> val "Debug"
+          ],
+          "1DEB922408733DC00010E9CD" ~> obj [
+            "isa" ~> val "XCBuildConfiguration",
+            "baseConfigurationReference" ~> val kitConfigRefUUID,
+            "buildSettings" ~> obj [
+              "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",
+              "GCC_C_LANGUAGE_STANDARD" ~> val "c99",
+              "GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",
+              "GCC_WARN_UNUSED_VARIABLE" ~> val "YES",
+              "OTHER_LDFLAGS" ~> val "-ObjC",
+              "PREBINDING" ~> val "NO",
+              "SDKROOT" ~> val "iphoneos",
+              libSearch
+            ],
+            "name" ~> val "Release"
+          ],
+          "1DEB921E08733DC00010E9CD" ~> obj [
+            "isa" ~> val "XCConfigurationList",
+            "buildConfigurations" ~> arr [
+              val "1DEB921F08733DC00010E9CD",
+              val "1DEB922008733DC00010E9CD"
+            ],
+            "defaultConfigurationIsVisible" ~> val "0",
+            "defaultConfigurationName" ~> val "Release"
+          ],
+          "1DEB922208733DC00010E9CD" ~> obj [
+            "isa" ~> val "XCConfigurationList",
+            "buildConfigurations" ~> arr [
+              val "1DEB922308733DC00010E9CD",
+              val "1DEB922408733DC00010E9CD"
+            ],
+            "defaultConfigurationIsVisible" ~> val "0",
+            "defaultConfigurationName" ~> val "Release"
+          ]
+        ]
+
+kitUpdateTarget = [
+      "470E2D641287730A0084AE6F" ~> obj [ 
+        "isa" ~> val "PBXLegacyTarget",
+        "buildArgumentsString" ~> val "",
+        "buildConfigurationList" ~> val "470E2D721287731E0084AE6F",
+        "buildPhases" ~> arr [],
+        "buildToolPath" ~> val "/usr/bin/make",
+        "buildWorkingDirectory" ~> val "",
+        "dependencies" ~> arr [],
+        "name" ~> val "KitUpdate",
+        "passBuildSettingsInEnvironment" ~> val "1",
+        "productName" ~> val "KitUpdate"
+      ],
+      "470E2D721287731E0084AE6F" ~> obj [
+        "isa" ~> val "XCConfigurationList",
+        "buildConfigurations" ~> arr [ val "470E2D651287730B0084AE6F", val "470E2D661287730B0084AE6F" ],
+        "defaultConfigurationIsVisible" ~> val "0",
+        "defaultConfigurationName" ~> val "Debug"
+      ],
+      "470E2D651287730B0084AE6F" ~> obj [
+        "isa" ~> val "XCBuildConfiguration",
+        "buildSettings" ~> obj [
+          "COPY_PHASE_STRIP" ~> val "NO",
+          "GCC_DYNAMIC_NO_PIC" ~> val "NO",
+          "GCC_OPTIMIZATION_LEVEL" ~> val "0", 
+          "PRODUCT_NAME" ~> val "KitUpdate" 
+        ],
+        "name" ~> val "Debug"
+      ], 
+      "470E2D661287730B0084AE6F" ~> obj [
+        "isa" ~> val "XCBuildConfiguration",
+        "buildSettings" ~> obj [ "PRODUCT_NAME" ~> val "KitUpdate" ],
+        "name" ~> val "Release"
+      ]]
+
+
diff --git a/Kit/Xcode/XCConfig.hs b/Kit/Xcode/XCConfig.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Xcode/XCConfig.hs
@@ -0,0 +1,66 @@
+module Kit.Xcode.XCConfig where
+
+  import Kit.Util
+
+  import qualified Data.Map as M
+  import Data.Maybe
+  import Data.Either
+  import Data.List
+  import Data.Char (isSpace)
+
+  type XCCSetting = (String, String)
+  type XCCInclude = String
+                    
+  data XCConfig = XCC { 
+    configName :: String, 
+    configSettings :: M.Map String String, 
+    configIncludes :: [XCCInclude]
+  } deriving (Eq, Show)
+  
+
+  includeStart :: String
+  includeStart = "#include "
+  
+  strip :: String -> String
+  strip = f . f where f = reverse . dropWhile isSpace
+  
+  parseLine :: String -> Maybe (Either XCCSetting XCCInclude)
+  parseLine l | includeStart `isPrefixOf` l = Just . Right . fromJust $ stripPrefix includeStart l
+  parseLine l | "=" `isInfixOf` l = fmap Left $ breakAround '=' l
+    where breakAround a xs = let (q,r) = break (a ==) xs in nonEmptyPair (strip q, strip $ drop 1 r)
+          nonEmptyPair ([], _) = Nothing
+          nonEmptyPair (_, []) = Nothing
+          nonEmptyPair a@_ = Just a
+  parseLine _ = Nothing
+  
+  settingToString :: XCCSetting -> String
+  settingToString (a, b) = a ++ " = " ++ b
+  
+  includeToString :: XCCInclude -> String
+  includeToString a = includeStart ++ a
+  
+  configToString :: XCConfig -> String
+  configToString (XCC _ settings includes) = stringJoin "\n" $ map includeToString includes ++ map settingToString (M.toList settings)
+                  
+  cleanName :: String -> String
+  cleanName = map (\a -> if a == '-' then '_' else a)
+
+  fileContentsToXCC :: String -> String -> XCConfig
+  fileContentsToXCC name content = let ls = lines content
+                                       (settings, includes) = partitionEithers (ls >>= (maybeToList . parseLine))
+                                    in XCC (cleanName name) (M.fromList settings) includes
+
+  configAsMap :: XCConfig -> M.Map String [(String, String)]
+  configAsMap (XCC name settings _) = M.map (return . (,) name) settings
+  
+  multiConfig :: String -> [XCConfig] -> XCConfig
+  multiConfig name configs = XCC name settings includes
+    where includes = configs >>= configIncludes
+          settingsWithName = foldl (M.unionWith (++)) M.empty (map configAsMap configs)
+          aggregateSettings = M.mapWithKey f settingsWithName
+            where f key namesAndValues = stringJoin " " $ map ((\x -> "$(" ++ x ++ "_" ++ key ++ ")") . fst) namesAndValues
+          individualSettings = M.fromList $ concatMap f $ M.toList settingsWithName
+            where f (key, namesAndValues) = map (g key) namesAndValues
+                  g key (n, v) = (n ++ "_" ++ key, v)
+          settings = aggregateSettings `M.union` individualSettings
+  
diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -1,3 +1,6 @@
 module Main where
-  import Kit.Main
-  main = kitMain
+
+import Kit.Main
+
+main :: IO ()
+main = kitMain
diff --git a/Text/PList.hs b/Text/PList.hs
--- a/Text/PList.hs
+++ b/Text/PList.hs
@@ -2,6 +2,7 @@
 module Text.PList where
 
 import Data.List (isInfixOf, isPrefixOf, intersperse)
+import Control.Monad
 
 -------------------------------------------------------------------------------
 -- | A Key/Value pair in a PList Object
@@ -15,24 +16,32 @@
                | PListObject [PListObjectItem]
                deriving Eq
 
-val x = PListValue x 
+val :: String -> PListType
+arr :: [PListType] -> PListType
+obj :: [PListObjectItem] -> PListType
+
+val = PListValue
 arr = PListArray
 obj = PListObject
 
 infixl 1 ~> 
+(~>) :: String -> PListType -> PListObjectItem
 a ~> b = PListObjectItem a b
 
+quote :: String -> String
 quote s = "\"" ++ s ++ "\""
+
+quotable :: String -> Bool
 quotable "" = True
-quotable s = or $ map (\x -> x `isInfixOf` s && (not $ "sourcecode" `isPrefixOf` s)) quote_triggers
+quotable s = any (\x -> x `isInfixOf` s && not ("sourcecode" `isPrefixOf` s)) quote_triggers
               where quote_triggers = ["-", "<", ">", " ", ".m", ".h", "_", "$"]
 
 instance Show PListObjectItem where
   show (PListObjectItem k v) = k ++ " = " ++ show v ++ ";\n"
 
 instance Show PListType where
-  show (PListValue a) = (if (quotable a) then quote a else a) 
-  show (PListArray xs) = "(" ++ ((intersperse ", " $ map show xs) >>= id) ++ ")"
+  show (PListValue a) = if quotable a then quote a else a 
+  show (PListArray xs) = "(" ++ join (intersperse ", " $ map show xs) ++ ")"
   show (PListObject kvs) = "{\n" ++ (kvs >>= (\x -> "  " ++ show x)) ++ "}\n"
 
 data PListFile = PListFile { pListFileCharset :: String, pListFileValue :: PListType }
diff --git a/kit.cabal b/kit.cabal
--- a/kit.cabal
+++ b/kit.cabal
@@ -1,24 +1,26 @@
 name:           kit
-version:        0.5.2
+version:        0.6
 cabal-version:  >=1.2
 build-type:     Simple
 license:        BSD3
 license-file:   LICENSE
 maintainer:     nkpart@gmail.com
 
-synopsis:       A dependency manager for XCode (Objective-C) projects
-description:    A dependency manager for XCode (Objective-C) projects
+synopsis:       A dependency manager for Xcode (Objective-C) projects
+description:    A dependency manager for Xcode (Objective-C) projects
 category:       Development
 author:         Nick Partridge
 
 Executable kit
   main-is:        Main.hs
   buildable:      True
-  build-depends:  process -any, network -any, mtl -any, json -any,
+  build-depends:  process -any, network -any, mtl -any, 
                   filepath -any, directory -any, containers -any, cmdargs >=0.3,
                   bytestring -any, base >=3 && <5, QuickCheck -any,
-                  HTTP >=4000.0.9, Glob >= 0.5
+                  HTTP >=4000.0.9, Glob >= 0.5, data-object -any, data-object-yaml -any
 
   hs-source-dirs: .
-  other-modules:  Kit.Model Kit.Main Kit.Package Kit.Project Kit.Repository Kit.Util Kit.XCode.Builder
-                  Kit.XCode.Common Kit.XCode.ProjectFileTemplate Kit.XCode.XCConfig Text.PList
+  other-modules:  Kit.Spec Kit.Main Kit.Package Kit.Project Kit.Repository Kit.Util Kit.Xcode.Builder
+                  Kit.Xcode.Common Kit.Xcode.ProjectFileTemplate Kit.Xcode.XCConfig Text.PList
+                  Kit.Contents Kit.Commands Kit.CmdArgs
+  Ghc-options:    -Wall -fno-warn-unused-do-bind
