diff --git a/Kit/JSON.hs b/Kit/JSON.hs
new file mode 100644
--- /dev/null
+++ b/Kit/JSON.hs
@@ -0,0 +1,31 @@
+module Kit.JSON where
+  import Kit.Kit
+  import Kit.Spec
+  import Kit.Util
+  import Control.Applicative
+  import Text.JSON
+  
+  instance JSON Kit where
+      showJSON kit = makeObj
+          [ ("name", showJSON $ kitName kit)
+          , ("version", showJSON $ kitVersion kit)
+          ]
+
+      readJSON (JSObject obj) = Kit <$> f "name" <*> f "version"
+        where f x = mLookup x jsonObjAssoc >>= readJSON
+              jsonObjAssoc = fromJSObject obj
+  
+  instance JSON KitSpec where
+      showJSON spec = makeObj
+          [ ("name", showJSON $ kitName kit)
+          , ("version", showJSON $ kitVersion kit)
+          , ("dependencies", showJSON $ specDependencies spec)
+          ]
+          where kit = specKit spec
+
+      readJSON (JSObject obj) = 
+          let myKit = Kit <$> f "name" <*> f "version"
+              myConfig = (KitConfiguration <$> f "dependencies" <*> (f "sourceDir" <|> pure "src"))
+           in KitSpec <$> myKit <*> myConfig
+        where f x = mLookup x jsonObjAssoc >>= readJSON
+              jsonObjAssoc = fromJSObject obj
diff --git a/Kit/Kit.hs b/Kit/Kit.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Kit.hs
@@ -0,0 +1,13 @@
+module Kit.Kit where
+  import Kit.Util
+    
+  data Kit = Kit {
+    kitName :: String,
+    kitVersion :: String
+  } deriving (Eq, Show, Ord, Read)
+
+  kitFileName :: Kit -> String
+  kitFileName k = kitName k ++ "-" ++ kitVersion k
+
+  kitConfigFile :: Kit -> String
+  kitConfigFile kit = kitFileName kit </> (kitName kit ++ ".xcconfig")
diff --git a/Kit/Main.hs b/Kit/Main.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Main.hs
@@ -0,0 +1,126 @@
+module Kit.Main where
+  import qualified Data.ByteString as BS
+
+  import Control.Monad.Trans
+  import Data.List
+  import Data.Maybe
+  import Data.Monoid
+  import Kit.Kit
+  import Kit.Package
+  import Kit.Project
+  import Kit.Repository
+  import Kit.Spec
+  import Kit.Util  
+  import Kit.XCode.Builder
+  import qualified Data.Traversable as T
+  import System.Cmd
+  import System.Environment
+  import Text.JSON
+
+  defaultLocalRepoPath = do
+      home <- getHomeDirectory
+      return $ home </> ".kit" </> "repository"
+  defaultLocalRepository = fmap fileRepo defaultLocalRepoPath
+  
+  handleFails (Left e) = do
+    print e
+    return ()
+  handleFails (Right _) = return ()
+  
+  update :: IO ()
+  update = unKitIO g >>= handleFails
+      where g = do
+              repo <- liftIO defaultLocalRepository
+              deps <- getMyDeps repo
+              puts $ "Dependencies: " ++ (stringJoin ", " $ map kitFileName deps)
+              liftIO $ mapM (installKit repo) deps
+              puts " -> Generating XCode project..."
+              liftIO $ generateXCodeProject $ deps |> kitFileName
+              liftIO $ generateXCodeConfig $ deps |> kitFileName
+              puts "Kit complete."
+                where p x = liftIO $ print x
+                      puts x = liftIO $ putStrLn x
+  
+  packageKit :: IO ()
+  packageKit = do
+      mySpec <- unKitIO myKitSpec
+      T.for mySpec package
+      return ()
+  
+  deployLocal :: IO ()
+  deployLocal = do
+    mySpec <- unKitIO myKitSpec
+    T.for mySpec package
+    T.for mySpec x
+    handleFails mySpec
+      where
+        x :: KitSpec -> IO ()
+        x spec = let 
+              k = specKit spec
+              kf = kitFileName . specKit $ spec
+              pkg = (kf ++ ".tar.gz")
+            in do
+              repo <- defaultLocalRepoPath
+              let thisKitDir = repo </> "kits" </> kitName k </> kitVersion k
+              mkdir_p thisKitDir
+              copyFile pkg $ thisKitDir </> pkg
+              copyFile "KitSpec" $ thisKitDir </> "KitSpec"
+  
+  verify :: Maybe String -> IO ()
+  verify sdk = (unKitIO f >>= handleFails)
+    where
+      f = do
+        mySpec <- myKitSpec
+        puts "Checking that the kit can be depended upon..."
+        puts " #> Deploying locally"
+        liftIO deployLocal
+        puts " #> Building temporary parent project"
+        tmp <- liftIO getTemporaryDirectory
+        liftIO $ inDirectory tmp $ do
+          let kitVerifyDir = "kit-verify"
+          cleanOrCreate kitVerifyDir
+          inDirectory kitVerifyDir $ do
+            writeFile "KitSpec" $ encode (KitSpec (Kit "verify-kit" "1.0") $ defaultConfiguration{kitConfigDependencies=[specKit mySpec]})
+            update
+            inDirectory "Kits" $ do
+              system $ "xcodebuild -sdk " ++ (fromMaybe "iphonesimulator4.0" sdk)
+          putStrLn "OK."
+        puts "End checks."
+      puts = liftIO . putStrLn
+      
+  createSpec :: String -> String -> IO ()
+  createSpec name version = do
+    let kit =(Kit name version)
+    let spec = KitSpec kit defaultConfiguration
+    writeFile "KitSpec" $ encode spec
+    putStrLn $ "Created KitSpec for " ++ kitFileName kit
+    return ()
+  
+  handleArgs :: [String] -> IO ()
+  handleArgs ["update"] = update
+  handleArgs ["package"] = packageKit
+  handleArgs ["publish-local"] = deployLocal
+  handleArgs ["verify"] = verify Nothing
+  handleArgs ["verify", sdk] = verify $ Just sdk
+  handleArgs ["create-spec", name, version] = createSpec name version
+    
+  handleArgs _ = putStrLn f where
+    f = "Usage: kit command [arguments]" `nl`
+        "Commands:" `nl`
+        "\tupdate\t\t\t\tCreate an Xcode project to wrap the dependencies defined in ./KitSpec" `nl`
+        "\tcreate-spec NAME VERSION\tWrite out a KitSpec file using the given name and version." `nl`
+        "\tpackage\t\t\t\tCreate a tar.gz wrapping this kit" `nl`
+        "\tverify [SDK]\t\t\tPackage this kit, then attempt to use it as a dependency in an empty project." `nl`
+        "\tpublish-local\t\t\tPackage this kit, then deploy it to the local repository (~/.kit/repository)" 
+    nl a b = a ++ "\n" ++ b
+    
+  kitMain :: IO ()
+  kitMain = do
+      localRepo <- defaultLocalRepository
+      path <- defaultLocalRepoPath
+      mkdir_p path
+      args <- getArgs
+      handleArgs args
+
+      
+          
diff --git a/Kit/Package.hs b/Kit/Package.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Package.hs
@@ -0,0 +1,47 @@
+module Kit.Package (package) where
+
+  import Kit.Kit
+  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/
+     KitSpec
+     *.codeproj
+  -}
+  
+  fileBelongsInPackage :: KitConfiguration -> FilePath -> Bool
+  fileBelongsInPackage config fp = let
+    a = elem fp [sourceDir config, "test", "KitSpec"]
+    b = "xcodeproj" `isSuffixOf` fp
+    c = "xcconfig" `isSuffixOf` fp
+    d = ".pch" `isSuffixOf` fp
+      in a || b || c || d
+  
+  package :: KitSpec -> IO ()
+  package spec = do
+      tempDir <- getTemporaryDirectory
+      current <- getCurrentDirectory
+      let kd = tempDir </> kitPath
+      cleanOrCreate kd
+      contents <- getDirectoryContents "."
+      mapM_ (cp_r_to kd) (filter (fileBelongsInPackage . specConfiguration $ spec) contents)
+      inDirectory tempDir $ sh $ "tar czf " ++ (current </> (kitPath ++ ".tar.gz")) ++ " " ++ kitPath
+      return ()
+    where
+      kitPath = kitFileName . specKit $ 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 ++ "/"
+  
+  
diff --git a/Kit/Project.hs b/Kit/Project.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Project.hs
@@ -0,0 +1,108 @@
+module Kit.Project (
+  getDeps,
+  getMyDeps,
+  installKit,
+  generateXCodeConfig,
+  generateXCodeProject,
+  myKitSpec)
+    where
+      
+  import Control.Monad.Trans
+  import Control.Monad
+  import Control.Applicative
+  import Data.Foldable
+  import Data.Maybe
+  import Data.List
+  import Data.Monoid
+  import System.Cmd
+  import Kit.Kit
+  import Kit.Repository
+  import Kit.Util
+  import Kit.Spec
+  import Kit.XCode.Builder
+  import Kit.XCode.XCConfig
+  import Kit.XCode.Prefix
+  import Text.JSON
+  import Kit.JSON
+  import qualified Data.Traversable as T
+  
+  kitDir = "." </> "Kits"
+  projectDir = "KitDeps.xcodeproj"
+  prefixFile = "KitDeps_Prefix.pch"
+  projectFile = projectDir </> "project.pbxproj"
+  prefixDefault = "#ifdef __OBJC__\n" ++ "    #import <Foundation/Foundation.h>\n    #import <UIKit/UIKit.h>\n" ++ "#endif\n"
+      
+  generateXCodeProject :: [FilePath] -> IO ()
+  generateXCodeProject kitFileNames = do
+    mkdir_p kitDir
+    inDirectory kitDir $ do
+      let find tpe inDir = glob (inDir ++ "/src/**/*" ++ tpe)
+      let find' tpe = fmap join . T.for kitFileNames $ find tpe
+      headers <- find' ".h"
+      sources <- (\a b c -> a ++ b ++ c) <$> glob "**/src/**/*.m" <*> glob "**/src/**/*.mm" <*> glob "**/src/**/*.c"
+      mkdir_p projectDir
+      writeFile projectFile $ buildXCodeProject headers sources
+      combinedHeader <- generatePrefixHeader kitFileNames
+      writeFile prefixFile $ prefixDefault ++ combinedHeader ++ "\n"
+
+  readConfig' :: Kit -> IO (Maybe XCConfig)
+  readConfig' kit = do
+    exists <- doesFileExist fp
+    contents <- T.sequence (fmap readFile $ justTrue exists fp)
+    return $ fmap (fileContentsToXCC $ kitName kit) contents
+        where
+          fp = kitConfigFile kit
+
+  generateXCodeConfig :: [FilePath] -> IO ()
+  generateXCodeConfig kitFileNames = do
+    inDirectory kitDir $ unKitIO $ do
+      ca <- readMany kitFileNames "KitSpec" (\x -> readSpec x |> specKit >>= (liftIO . readConfig'))
+      let combinedConfig = multiConfig "KitConfig" ca
+      let contents = configToString combinedConfig
+      let xcconfig = "HEADER_SEARCH_PATHS = $(HEADER_SEARCH_PATHS) " ++ (stringJoin " "  $ kitFileNames >>= (\x -> [kitDir </> x </> "src", x </> "src"])) ++ "\n" ++ "GCC_PRECOMPILE_PREFIX_HEADER = YES\nGCC_PREFIX_HEADER = $(SRCROOT)/KitDeps_Prefix.pch\n"
+      liftIO $ writeFile "Kit.xcconfig" $ xcconfig ++ "\n" ++ contents
+    return ()
+      
+  depsForSpec :: KitRepository -> KitSpec -> KitIO [Kit]
+  depsForSpec kr spec = do
+      deps <- mapM (getDeps kr) (specDependencies spec)
+      return $ specDependencies spec ++ join deps      
+  
+  getDeps :: KitRepository -> Kit -> KitIO [Kit]
+  getDeps kr kit = getKitSpec kr kit >>= depsForSpec kr
+
+  getMyDeps :: KitRepository -> KitIO [Kit]
+  getMyDeps kr = myKitSpec >>= depsForSpec kr
+
+  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
+
+  readSpec :: FilePath -> KitIO KitSpec
+  readSpec kitSpecPath = readSpecContents kitSpecPath >>= KitIO . return . parses
+  
+  myKitSpec :: KitIO KitSpec
+  myKitSpec = readSpec "KitSpec"
+
+  -- private!
+  checkExists :: FilePath -> KitIO FilePath
+  checkExists kitSpecPath = do
+    doesExist <- liftIO $ doesFileExist kitSpecPath
+    maybeToKitIO ("Couldn't find the spec at " ++ kitSpecPath) (justTrue doesExist kitSpecPath)
+  
+  readSpecContents :: FilePath -> KitIO String
+  readSpecContents kitSpecPath = checkExists kitSpecPath >>= liftIO . readFile
+  
+  parses :: String -> Either [KitError] KitSpec
+  parses contents = case (decode contents) of 
+                      Ok a -> Right a
+                      Error a -> Left [a]
+  
diff --git a/Kit/Repository.hs b/Kit/Repository.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Repository.hs
@@ -0,0 +1,84 @@
+{-
+
+  Exposes the repositories.
+  Allows you to:
+    * copy down kit packages (getKit)
+    * read kit specs (getKitSpec)
+-}
+
+module Kit.Repository (
+    getKit,
+    getKitSpec,
+    webRepo,
+    fileRepo,
+    KitRepository
+  ) where
+    
+  import Kit.Spec
+  import Kit.Kit
+  import Kit.Util
+  import Data.List
+  import Network.HTTP
+  import Network.URI
+  import Network.BufferType
+  import Data.Maybe
+  import Data.Traversable
+  import Control.Monad
+  import Control.Monad.Trans
+  import Debug.Trace
+  import Text.JSON
+  import Kit.JSON
+  import qualified Data.ByteString as BS
+
+  data KitRepository = KitRepository {
+    repoSave :: String -> FilePath -> IO (Maybe ()),
+    repoRead :: String -> IO (Maybe String)
+  }
+  
+  getKit :: KitRepository -> Kit -> FilePath -> IO (Maybe ())
+  getKit kr k = repoSave kr (kitPackagePath k)
+  
+  getKitSpec :: KitRepository -> Kit -> KitIO KitSpec
+  getKitSpec kr k = do
+    mbKitStuff <- liftIO $ repoRead kr (kitSpecPath k)
+    maybe (kitError $ "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
+  
+  webRepo :: String -> KitRepository
+  webRepo baseUrl = KitRepository save read where
+    save = download 
+    read = getBody
+
+  fileRepo :: String -> KitRepository
+  fileRepo baseDir = KitRepository save read where
+    save src destPath = Just <$> copyFile (baseDir </> src) destPath
+    read path = let file = (baseDir </> path) in do
+      exists <- doesFileExist file
+      sequenceA $ justTrue exists $ readFile file
+      
+  -- private!
+  baseKitPath :: Kit -> String
+  baseKitPath k = joinS ["kits", kitName k, kitVersion k] "/"
+    where joinS xs x = foldl1 (++) $ intersperse x xs 
+  
+  kitPackagePath k = baseKitPath k ++ "/" ++ kitFileName k ++ ".tar.gz"
+  kitSpecPath k = baseKitPath k ++ "/" ++ "KitSpec"
+  
+  getBody :: BufferType a => HStream a => String -> IO (Maybe a)
+  getBody path = 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
+      return $ fmap rspBody $ leftMaybe rr
+  
+  download :: String -> FilePath -> IO (Maybe ())
+  download url destination = do
+      body <- getBody url
+      sequenceA $ fmap (BS.writeFile destination) body
+   
+   
+   
diff --git a/Kit/Spec.hs b/Kit/Spec.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Spec.hs
@@ -0,0 +1,18 @@
+module Kit.Spec where
+  import Kit.Kit
+  
+  data KitSpec = KitSpec {
+    specKit :: Kit,
+    specConfiguration :: KitConfiguration
+  } deriving (Show, Read)
+  
+  data KitConfiguration = KitConfiguration {
+    kitConfigDependencies :: [Kit],
+    sourceDir :: FilePath
+  } deriving (Show, Read)
+  
+  specDependencies :: KitSpec -> [Kit]
+  specDependencies = kitConfigDependencies . specConfiguration
+  
+  defaultConfiguration :: KitConfiguration
+  defaultConfiguration = KitConfiguration [] "src"
diff --git a/Kit/Util.hs b/Kit/Util.hs
new file mode 100644
--- /dev/null
+++ b/Kit/Util.hs
@@ -0,0 +1,98 @@
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module Kit.Util(
+  module Kit.Util, 
+  module Control.Applicative, 
+  module System.FilePath.Posix,
+  module System.Directory
+  ) 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 Control.Monad
+  import Data.Monoid
+  import Control.Monad.Trans
+      
+  (|>) ma f = fmap f ma
+        
+  maybeRead :: Read a => String -> Maybe a
+  maybeRead = fmap fst . listToMaybe . reads
+  
+  justTrue :: Bool -> a -> Maybe a
+  justTrue True a = Just a
+  justTrue False _ = Nothing
+  
+  maybeToRight :: b -> Maybe a -> Either b a
+  maybeToRight _ (Just a) = Right a
+  maybeToRight b Nothing = Left b
+  
+  maybeToLeft :: b -> Maybe a -> Either a b
+  maybeToLeft _ (Just a) = Left a
+  maybeToLeft b Nothing = Right b
+  
+  instance Foldable (Either c) where
+    foldr f z (Left c) = z
+    foldr f z (Right a) = f a z
+    
+  instance T.Traversable (Either c) where
+    sequenceA = either (pure . Left) (Right <$>)
+    
+  type KitError = String
+  
+  data KitIO a = KitIO { unKitIO :: (IO (Either [KitError] a)) }
+  
+  kitError :: KitError -> KitIO a
+  kitError e = KitIO . return $ Left [e]
+  
+  maybeToKitIO msg = maybe (kitError msg) return
+  
+  instance Monad KitIO where
+    (KitIO ioE) >>= f = KitIO $ ioE >>= g
+      where g l@(Left v) = return (Left v)
+            g (Right v) = unKitIO $ f v
+            
+    return = KitIO . return . Right 
+    
+  instance Functor KitIO where
+    fmap f kio = kio >>= (return . f)
+    
+  instance MonadIO KitIO where
+    liftIO v = KitIO (fmap Right v)
+
+  mLookup a as = maybe (fail $ "No such element: " ++ a) return (lookup a as)  
+  
+  mkdir_p :: FilePath -> IO ()
+  mkdir_p = createDirectoryIfMissing True
+  
+  cleanOrCreate :: FilePath -> IO ()
+  cleanOrCreate directory = do
+    exists <- doesDirectoryExist directory
+    when exists $ removeDirectoryRecursive directory
+    mkdir_p directory
+    
+  inDirectory :: FilePath -> IO a -> IO a
+  inDirectory dir actions = do
+    cwd <- getCurrentDirectory
+    setCurrentDirectory dir
+    v <- actions
+    setCurrentDirectory cwd
+    return v
+  
+  glob :: String -> IO [String]
+  glob pattern = fmap lines (readProcess "ruby" ["-e", "puts Dir.glob(\"" ++ pattern ++ "\")"] [])
+    
+  stringJoin :: Monoid a => a -> [a] -> a
+  stringJoin x = mconcat . intersperse x
+  
+  -- For each directory that exists, execute the function with the given filepath
+  readMany :: MonadIO m => [FilePath] -> FilePath -> (String -> m (Maybe a)) -> m [a]
+  readMany dirs fileInDir f = do
+    directories <- liftIO $ filterM doesDirectoryExist dirs
+    let kitSpecFiles = map (</> fileInDir) directories
+    kitContents <- mapM f kitSpecFiles
+    return $ catMaybes kitContents
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,148 @@
+
+module Kit.XCode.Builder (buildXCodeProject) where
+
+  import Data.Monoid
+  import Data.List
+  import Kit.XCode.Common
+  import Kit.XCode.ProjectFile
+  import Kit.Util
+  
+  createBuildFile :: Integer -> FilePath -> PBXBuildFile
+  createBuildFile i path = PBXBuildFile uuid1 $ PBXFileReference uuid2 path
+    where uuid1 = uuid i
+          uuid2 = uuid $ i + 10000000
+  
+  buildXCodeProject :: [FilePath] -> [FilePath] -> String
+  buildXCodeProject headers sources = 
+      projectPbxProj bfs frs classes hs srcs 
+    where
+      sourceStart = toInteger (length headers + 1)
+      headerBuildFiles = zipWith createBuildFile [1..] headers
+      sourceBuildFiles = zipWith createBuildFile [sourceStart..] sources
+      allBuildFiles = (sourceBuildFiles ++ headerBuildFiles)
+      bfs = buildFileSection allBuildFiles
+      frs = fileReferenceSection $ map buildFileReference allBuildFiles
+      classes = classesSection $ map buildFileReference (sourceBuildFiles ++ headerBuildFiles)
+      hs = headersSection headerBuildFiles
+      srcs = sourcesSection sourceBuildFiles
+  
+  xxx :: FilePath -> UUID -> UUID -> PBXBuildFile
+  xxx fp buildId fileId = PBXBuildFile buildId (PBXFileReference fileId fp)
+
+  -- 47021EC1117A7776003DB5B7 /* motive.m in Sources */ = {isa = PBXBuildFile; fileRef = 47021EBF117A7776003DB5B7 /* motive.m */; };
+  buildFileItem :: PBXBuildFile -> String
+  buildFileItem bf = lineItem i comment dict
+    where fr = buildFileReference bf
+          i = buildFileId bf
+          comment = let ft Unknown = "Unknown"
+                        ft Header = "Headers"
+                        ft Source = "Sources"
+                        bit = ft . fileType . fileReferencePath $ fr
+                    in fileReferenceName fr ++ " in " ++ bit
+          dict = [
+              "isa" ~> "PBXBuildFile",
+              "fileRef" ~> (fileReferenceId fr ++ " /* " ++ fileReferenceName fr ++ " */")
+            ]
+
+  fileTypeBit :: FileType -> String
+  fileTypeBit Header = "sourcecode.c.h"
+  fileTypeBit Source = "sourcecode.c.objc"
+  fileTypeBit Unknown = "sourcecode.unknown"
+
+  -- 47021EBE117A7776003DB5B7 /* motive.h */ = {isa = PBXFileReference; fileEncoding = 4;
+  -- lastKnownFileType = sourcecode.c.h; name = motive.h; path = "kits/motive-0.1/src/motive.h"; sourceTree = "<group>"; };
+  fileReferenceItem :: PBXFileReference -> String
+  fileReferenceItem fr = lineItem fid fileName dict
+    where
+      fid = fileReferenceId fr
+      path = fileReferencePath fr
+      fileName = fileReferenceName fr
+      dict = [
+          "isa" ~> "PBXFileReference",
+          "fileEncoding" ~> "4",
+          "lastKnownFileType" ~> (fileTypeBit . fileType $ fileName),
+          "name" ~> show fileName,
+          "path" ~> show path,
+          "sourceTree" ~> show "<group>"
+        ]
+      
+  layoutSection :: String -> [String] -> String
+  layoutSection name body = let 
+      front  = "/* Begin " ++ name ++ " section */"
+      middle = map ("    " ++) body
+      back   = "/* End " ++ name ++ " section */"
+      in mconcat $ intersperse "\n" ((front : middle) ++ [back])
+
+  buildFileSection :: [PBXBuildFile] -> String
+  buildFileSection bfs = layoutSection "PBXBuildFile" (map buildFileItem bfs ++ [
+      "4728C530117C02B10027D7D1 /* Kit.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 4728C52F117C02B10027D7D1 /* Kit.xcconfig */; };",
+      "AA747D9F0F9514B9006C5449 /* KitDeps_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* KitDeps_Prefix.pch */; };",
+      "AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };"
+    ])  
+    
+  fileReferenceSection :: [PBXFileReference] -> String
+  fileReferenceSection refs = layoutSection "PBXFileReference" (map fileReferenceItem refs ++ [
+  		"4728C52F117C02B10027D7D1 /* Kit.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Kit.xcconfig; sourceTree = \"<group>\"; };",
+      "AA747D9E0F9514B9006C5449 /* KitDeps_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KitDeps_Prefix.pch; sourceTree = SOURCE_ROOT; };",
+	    "AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };",
+	    "D2AAC07E0554694100DB518D /* libKitDeps.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKitDeps.a; sourceTree = BUILT_PRODUCTS_DIR; };"
+    ])
+
+  classesSection :: [PBXFileReference] -> String
+  classesSection files = lineItem "08FB77AEFE84172EC02AAC07" "Classes" dict
+	    where dict = [
+            	    "isa" ~> "PBXGroup",
+            	    "children" ~> ("(" ++ (mconcat (intersperse "," (map fileReferenceId files))) ++ ",)"),
+            	    "name" ~> "Classes",
+            	    "sourceTree" ~> show "<group>"
+	                ]
+	
+	{-/* Begin PBXHeadersBuildPhase section */
+  		D2AAC07A0554694100DB518D /* Headers */ = {
+  			isa = PBXHeadersBuildPhase;
+  			buildActionMask = 2147483647;
+  			files = (
+  				AA747D9F0F9514B9006C5449 /* KitDeps_Prefix.pch in Headers */,
+  				4791C244117A7893001EB278 /* motive.h in Headers */,
+  			);
+  			runOnlyForDeploymentPostprocessing = 0;
+  		};
+  /* End PBXHeadersBuildPhase section */-}                
+  headersSection :: [PBXBuildFile] -> String
+  headersSection bfs = layoutSection "PBXHeadersBuildPhase" [lineItem "D2AAC07A0554694100DB518D" "Headers" [
+    "isa" ~> "PBXHeadersBuildPhase",
+    "buildActionMask" ~> "2147483647",
+    "files" ~> ("(" ++ (stringJoin "," $ prefixPchUUID : map buildFileId bfs) ++ ",)"),
+    "runOnlyForDeploymentPostprocessing" ~> "0"
+	  ]]
+	  where prefixPchUUID = "AA747D9F0F9514B9006C5449"
+	  
+	{-
+    /* Begin PBXSourcesBuildPhase section */
+    		D2AAC07B0554694100DB518D /* Sources */ = {
+    			isa = PBXSourcesBuildPhase;
+    			buildActionMask = 2147483647;
+    			files = (
+    				47021EC1117A7776003DB5B7 /* motive.m in Sources */,
+    			);
+    			runOnlyForDeploymentPostprocessing = 0;
+    		};
+    /* End PBXSourcesBuildPhase section */
+	-}
+  sourcesSection :: [PBXBuildFile] -> String
+  sourcesSection bfs = layoutSection "PBXSourcesBuildPhase" [lineItem "D2AAC07B0554694100DB518D" "Sources" [
+    "isa" ~> "PBXSourcesBuildPhase",
+    "buildActionMask" ~> "2147483647",
+    "files" ~> ("(" ++ (stringJoin "," $ map buildFileId bfs) ++ ",)"),
+    "runOnlyForDeploymentPostprocessing" ~> "0"
+    ]]
+  
+  testBuilder = let
+    header = PBXFileReference "1" "fk/fk.h"
+    source = PBXFileReference "2" "fk/fk.m"
+    headerBF = PBXBuildFile "10" header
+    sourceBF = PBXBuildFile "20" source
+      in do
+          putStrLn . buildFileSection $  [headerBF, sourceBF]
+          putStrLn . fileReferenceSection $ [header, source]
+
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,45 @@
+module Kit.XCode.Common where
+  import Data.Monoid
+  import Data.List
+  import System.FilePath.Posix
+          
+  type UUID = String
+  type Dict = [(String, String)]
+  
+  data FileType = Header | Source | 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 _ = Unknown
+  
+  (~>) = (,)
+  
+  data PBXBuildFile = PBXBuildFile {
+    buildFileId :: String,
+    buildFileReference :: PBXFileReference
+  }
+  
+  data PBXFileReference = PBXFileReference {
+    fileReferenceId :: String,
+    fileReferencePath :: String
+  }
+  
+  fileReferenceName = takeFileName . fileReferencePath
+  
+  lineItem :: UUID -> String -> Dict -> String
+  lineItem uuid comment dict = uuid ++ " /* " ++ comment ++ " */ = " ++ buildDict dict ++ ";"
+  
+  buildDict :: Dict -> String
+  buildDict ps = g . mconcat . map (\x -> fst x ++ " = " ++ snd x ++ "; ") $ ps
+    where g s = "{"  ++ s ++ "}"
+  
+  uuid :: Integer -> UUID
+  uuid i = let
+      s = show i
+      lengthS = length s
+      pad = 24 - lengthS
+    in
+      replicate pad '0' ++ s 
diff --git a/Kit/XCode/Prefix.hs b/Kit/XCode/Prefix.hs
new file mode 100644
--- /dev/null
+++ b/Kit/XCode/Prefix.hs
@@ -0,0 +1,13 @@
+module Kit.XCode.Prefix where
+  
+  import Kit.Util
+  import qualified Data.Traversable as T
+  
+  kitPrefixFile = "Prefix.pch"
+  
+  generatePrefixHeader :: [FilePath] -> IO String
+  generatePrefixHeader kitFileNames = do
+      ca <- readMany kitFileNames kitPrefixFile $ \s -> do
+        exists <- doesFileExist s
+        T.for (justTrue exists s) readFile
+      return $ stringJoin "\n" ca
diff --git a/Kit/XCode/ProjectFile.hs b/Kit/XCode/ProjectFile.hs
new file mode 100644
--- /dev/null
+++ b/Kit/XCode/ProjectFile.hs
@@ -0,0 +1,198 @@
+{-
+
+  Constant sections of the project file
+-}
+module Kit.XCode.ProjectFile (projectPbxProj) where
+  
+  nl a b = a ++ "\n" ++ b 
+  
+  projectPbxProj bfs fileRefsSection classes headers sources = 
+      top `nl` bfs `nl` fileRefsSection `nl` next1 `nl` classes `nl` next2 `nl` headers `nl` next3 `nl` sources `nl` bottom
+  
+  top = "// !$*UTF8*$!" `nl` 
+    "{" `nl`
+    " archiveVersion = 1;" `nl`
+    " classes = {" `nl`
+    " };" `nl`
+    " objectVersion = 45;" `nl`
+    " objects = {"
+  
+  next1 = "/* Begin PBXFrameworksBuildPhase section */" `nl`
+    		"D2AAC07C0554694100DB518D /* Frameworks */ = {" `nl`
+    			"isa = PBXFrameworksBuildPhase;" `nl`
+    			"buildActionMask = 2147483647;" `nl`
+    			"files = (" `nl`
+    				"AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */," `nl`
+    			");" `nl`
+    			"runOnlyForDeploymentPostprocessing = 0;" `nl`
+    		"};" `nl`
+    "/* End PBXFrameworksBuildPhase section */" `nl`
+    "" `nl`
+    "/* Begin PBXGroup section */" `nl`
+    		"034768DFFF38A50411DB9C8B /* Products */ = {" `nl`
+    			"isa = PBXGroup;" `nl`
+    			"children = (" `nl`
+    				"D2AAC07E0554694100DB518D /* libKitDeps.a */," `nl`
+    			");" `nl`
+    			"name = Products;" `nl`
+    			"sourceTree = \"<group>\";" `nl`
+    		"};" `nl`
+    		"0867D691FE84028FC02AAC07 /* KitDeps */ = {" `nl`
+    			"isa = PBXGroup;" `nl`
+    			"children = (" `nl`
+    				"08FB77AEFE84172EC02AAC07 /* Classes */," `nl`
+    				"32C88DFF0371C24200C91783 /* Other Sources */," `nl`
+    				"0867D69AFE84028FC02AAC07 /* Frameworks */," `nl`
+    				"034768DFFF38A50411DB9C8B /* Products */," `nl`
+    				"4728C52F117C02B10027D7D1 /* Kit.xcconfig */," `nl`
+    			");" `nl`
+    			"name = KitDeps;" `nl`
+    			"sourceTree = \"<group>\";" `nl`
+    		"};" `nl`
+    		"0867D69AFE84028FC02AAC07 /* Frameworks */ = {" `nl`
+    			"isa = PBXGroup;" `nl`
+    			"children = (" `nl`
+    				"AACBBE490F95108600F1A2B1 /* Foundation.framework */," `nl`
+    			");" `nl`
+    			"name = Frameworks;" `nl`
+    			"sourceTree = \"<group>\";" `nl`
+    		"};"
+    		
+  next2 = "32C88DFF0371C24200C91783 /* Other Sources */ = {" `nl`
+        			"isa = PBXGroup;" `nl`
+        			"children = (" `nl`
+        				"AA747D9E0F9514B9006C5449 /* KitDeps_Prefix.pch */," `nl`
+        			");" `nl`
+        			"name = \"Other Sources\";" `nl`
+        			"sourceTree = \"<group>\";" `nl`
+        		"};" `nl`
+        "/* End PBXGroup section */"
+        
+  next3 = "/* Begin PBXNativeTarget section */" `nl`
+        		"D2AAC07D0554694100DB518D /* KitDeps */ = {" `nl`
+        			"isa = PBXNativeTarget;" `nl`
+        			"buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget \"KitDeps\" */;" `nl`
+        			"buildPhases = (" `nl`
+        				"D2AAC07A0554694100DB518D /* Headers */," `nl`
+        				"D2AAC07B0554694100DB518D /* Sources */," `nl`
+        				"D2AAC07C0554694100DB518D /* Frameworks */," `nl`
+        			");" `nl`
+              "buildRules = (" `nl`
+        			");" `nl`
+        			"dependencies = (" `nl`
+        			");" `nl`
+        			"name = KitDeps;" `nl`
+        			"productName = KitDeps;" `nl`
+        			"productReference = D2AAC07E0554694100DB518D /* libKitDeps.a */;" `nl`
+        			"productType = \"com.apple.product-type.library.static\";" `nl`
+        		"};" `nl`
+        "/* End PBXNativeTarget section */" `nl`
+        "" `nl`
+        "/* Begin PBXProject section */" `nl`
+        		"0867D690FE84028FC02AAC07 /* Project object */ = {" `nl`
+        			"isa = PBXProject;" `nl`
+        			"buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject \"KitDepsCustom\" */;" `nl`
+        			"compatibilityVersion = \"Xcode 3.1\";" `nl`
+        			"hasScannedForEncodings = 1;" `nl`
+        			"mainGroup = 0867D691FE84028FC02AAC07 /* KitDeps */;" `nl`
+        			"productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;" `nl`
+        			"projectDirPath = \"\";" `nl`
+        			"projectRoot = \"\";" `nl`
+        			"targets = (" `nl`
+        				"D2AAC07D0554694100DB518D /* KitDeps */," `nl`
+        			");" `nl`
+        		"};" `nl`
+        "/* End PBXProject section */"
+  
+  bottom = "/* Begin XCBuildConfiguration section */" `nl`
+        		"1DEB921F08733DC00010E9CD /* Debug */ = {" `nl`
+        			"isa = XCBuildConfiguration;" `nl`
+        			"buildSettings = {" `nl`
+        				"ALWAYS_SEARCH_USER_PATHS = NO;" `nl`
+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`
+        				"COPY_PHASE_STRIP = NO;" `nl`
+        				"DSTROOT = /tmp/KitDeps.dst;" `nl`
+        				"GCC_DYNAMIC_NO_PIC = NO;" `nl`
+        				"GCC_ENABLE_FIX_AND_CONTINUE = YES;" `nl`
+        				"GCC_MODEL_TUNING = G5;" `nl`
+        				"GCC_OPTIMIZATION_LEVEL = 0;" `nl`
+        				"GCC_PRECOMPILE_PREFIX_HEADER = YES;" `nl`
+        				"GCC_PREFIX_HEADER = KitDeps_Prefix.pch;" `nl`
+        				"INSTALL_PATH = /usr/local/lib;" `nl`
+        				"PRODUCT_NAME = KitDeps;" `nl`
+        			"};" `nl`
+        			"name = Debug;" `nl`
+        		"};" `nl`
+        		"1DEB922008733DC00010E9CD /* Release */ = {" `nl`
+        			"isa = XCBuildConfiguration;" `nl`
+        			"buildSettings = {" `nl`
+        				"ALWAYS_SEARCH_USER_PATHS = NO;" `nl`
+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`
+        				"DSTROOT = /tmp/KitDeps.dst;" `nl`
+        				"GCC_MODEL_TUNING = G5;" `nl`
+        				"GCC_PRECOMPILE_PREFIX_HEADER = YES;" `nl`
+        				"GCC_PREFIX_HEADER = KitDeps_Prefix.pch;" `nl`
+        				"INSTALL_PATH = /usr/local/lib;" `nl`
+        				"PRODUCT_NAME = KitDeps;" `nl`
+        			"};" `nl`
+        			"name = Release;" `nl`
+        		"};" `nl`
+        		"1DEB922308733DC00010E9CD /* Debug */ = {" `nl`
+        			"isa = XCBuildConfiguration;" `nl`
+        			"baseConfigurationReference = 4728C52F117C02B10027D7D1 /* Kit.xcconfig */;" `nl`
+        			"buildSettings = {" `nl`
+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`
+        				"GCC_C_LANGUAGE_STANDARD = c99;" `nl`
+        				"GCC_OPTIMIZATION_LEVEL = 0;" `nl`
+        				"GCC_WARN_ABOUT_RETURN_TYPE = YES;" `nl`
+        				"GCC_WARN_UNUSED_VARIABLE = YES;" `nl`
+        				"OTHER_LDFLAGS = \"-ObjC\";" `nl`
+        				"PREBINDING = NO;" `nl`
+        				"SDKROOT = iphoneos4.0;" `nl`
+        			"};" `nl`
+        			"name = Debug;" `nl`
+        		"};" `nl`
+        		"1DEB922408733DC00010E9CD /* Release */ = {" `nl`
+        			"isa = XCBuildConfiguration;" `nl`
+        			"baseConfigurationReference = 4728C52F117C02B10027D7D1 /* Kit.xcconfig */;" `nl`
+        			"buildSettings = {" `nl`
+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`
+        				"GCC_C_LANGUAGE_STANDARD = c99;" `nl`
+        				"GCC_WARN_ABOUT_RETURN_TYPE = YES;" `nl`
+        				"GCC_WARN_UNUSED_VARIABLE = YES;" `nl`
+        				"OTHER_LDFLAGS = \"-ObjC\";" `nl`
+        				"PREBINDING = NO;" `nl`
+        				"SDKROOT = iphoneos4.0;" `nl`
+        			"};" `nl`
+        			"name = Release;" `nl`
+        		"};" `nl`
+        "/* End XCBuildConfiguration section */" `nl`
+        "" `nl`
+        "/* Begin XCConfigurationList section */" `nl`
+        		"1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget \"KitDeps\" */ = {" `nl`
+        			"isa = XCConfigurationList;" `nl`
+        			"buildConfigurations = (" `nl`
+        				"1DEB921F08733DC00010E9CD /* Debug */," `nl`
+        				"1DEB922008733DC00010E9CD /* Release */," `nl`
+        			");" `nl`
+        			"defaultConfigurationIsVisible = 0;" `nl`
+        			"defaultConfigurationName = Release;" `nl`
+        		"};" `nl`
+        		"1DEB922208733DC00010E9CD /* Build configuration list for PBXProject \"KitDepsCustom\" */ = {" `nl`
+        			"isa = XCConfigurationList;" `nl`
+        			"buildConfigurations = (" `nl`
+        				"1DEB922308733DC00010E9CD /* Debug */," `nl`
+        				"1DEB922408733DC00010E9CD /* Release */," `nl`
+        			");" `nl`
+        			"defaultConfigurationIsVisible = 0;" `nl`
+        			"defaultConfigurationName = Release;" `nl`
+        		"};" `nl`
+        "/* End XCConfigurationList section */" `nl`
+        	"};" `nl`
+        	"rootObject = 0867D690FE84028FC02AAC07 /* Project object */;" `nl`
+        "}"
+  
+  
+  main = do
+    putStrLn $ projectPbxProj "" "" "" "" ""
+    
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,56 @@
+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.String.Utils
+
+  type XCCSetting = (String, String)
+  type XCCInclude = String
+                    
+  data XCConfig = XCC { configName :: String, configSettings :: M.Map String String, configIncludes :: [XCCInclude] } deriving (Eq, Show)
+  
+  includeStart = "#include "
+  
+  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
+  
+  -- TODO incorporate the config name
+  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/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,1 @@
+TODO
diff --git a/Main.hs b/Main.hs
new file mode 100644
--- /dev/null
+++ b/Main.hs
@@ -0,0 +1,10 @@
+module Main where
+  import Kit.Main
+  import qualified Data.Map as M
+  import Kit.XCode.XCConfig
+  
+  conf1 = XCC "conf1" (M.fromList [("A", "1")]) []
+  conf2 = XCC "conf2" (M.fromList [("A", "2")]) []
+  
+  --main = putStrLn $ show $ multiConfig "combo" [conf1, conf2]
+  main = kitMain
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/kit.cabal b/kit.cabal
new file mode 100644
--- /dev/null
+++ b/kit.cabal
@@ -0,0 +1,16 @@
+Name:                kit
+Version:             0.2
+Synopsis:            A dependency manager for XCode (Objective-C) projects
+Description:         A dependency manager for XCode (Objective-C) projects
+Category:            Development
+License:             BSD3
+License-file:        LICENSE
+Author:              Nick Partridge
+Maintainer:          nkpart@gmail.com
+Build-Type:          Simple
+Cabal-Version:       >=1.2
+Executable kit
+  Main-is:           Main.hs
+  Other-Modules:     Kit.JSON,  Kit.Kit,  Kit.Main,  Kit.Package,  Kit.Project,  Kit.Repository,  Kit.Spec,  Kit.Util,  Kit.XCode.Builder,  Kit.XCode.Common,  Kit.XCode.Prefix,  Kit.XCode.ProjectFile, Kit.XCode.XCConfig
+  Build-Depends:     base >= 3 && < 5, HTTP >= 4000.0.9, network, bytestring, filepath, directory, process, json, mtl, cmdargs, MissingH, containers, QuickCheck
+  
