diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2008, Daniel Wagner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted, provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions, and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions, and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The names of the contributors may not be used to endorse or promote
+      products derived from this software without specific prior written
+      permission.
+
+This software is provided by the copyright holders and contributors as is, and
+any express or implied warranties, including, but not limited to, the implied
+warranties of merchantability and fitness for a particular purpose are
+disclaimed. In no event shall the copyright owner or contributors be liable for
+any direct, indirect, incidental, special, exemplary, or consequential damages
+(including, but not limited to procurement of substitute goods or services,
+loss of use, loss of data, loss of profits, or business interruption) however
+caused and on any theory of liability, whether in contract, strict liability,
+or tort (including negligence or otherwise) arising in any way out of the use
+of this software, even if advised of the possibility of such damage.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+#!/usr/bin/env runhaskell
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/yeganesh.cabal b/yeganesh.cabal
new file mode 100644
--- /dev/null
+++ b/yeganesh.cabal
@@ -0,0 +1,24 @@
+name:               yeganesh
+version:            1.1
+cabal-version:      >=1.2
+build-type:         Simple
+license:            BSD3
+license-file:       LICENSE
+author:             Daniel Wagner
+maintainer:         daniel@wagner-home.com
+homepage:           http://www.dmwit.com/yeganesh
+synopsis:           small dmenu wrapper
+description:        I get so annoyed when I go to use dmenu, and the three
+                    programs I use every day aren't at the beginning of the
+                    list.  Let's make it so, and automatically!
+category:           Text
+
+Executable yeganesh
+    main-is:        yeganesh.hs
+    build-depends:  base >= 3.0,
+                    containers >= 0.1,
+                    directory >= 1.0,
+                    filepath >= 1.1,
+                    process >= 1.0,
+                    strict >= 0.3
+    ghc-options:    -Wall
diff --git a/yeganesh.hs b/yeganesh.hs
new file mode 100644
--- /dev/null
+++ b/yeganesh.hs
@@ -0,0 +1,66 @@
+-- boilerplate {{{
+module Main where
+
+import Control.Arrow ((&&&))
+import Control.Monad (liftM)
+import Data.Char (toLower)
+import Data.List (sortBy)
+import Data.Map (Map, insertWith, fromList, toList, union)
+import Data.Ord (comparing)
+import System.Directory (createDirectoryIfMissing)
+import System.Environment (getEnv)
+import System.FilePath ((</>))
+import System.IO (hClose, hGetContents, hPutStr, hPutStrLn, stderr, stdout)
+import System.Process (runInteractiveProcess, waitForProcess)
+import qualified System.IO.Strict as Strict (getContents, readFile)
+-- }}}
+-- filesystem stuff {{{
+type Commands = Map String Double
+
+fileName :: IO FilePath
+fileName = do
+    home <- flip liftM (getEnv "HOME") (</> ".yeganesh")
+    createDirectoryIfMissing True home
+    return (home </> "default")
+
+readPossiblyNonExistent :: FilePath -> IO Commands
+readPossiblyNonExistent file = fmap read $
+    catch (Strict.readFile file) (const . return $ "fromList []")
+-- }}}
+-- pure {{{
+sortOn :: Ord b => (a -> b) -> [a] -> [a]
+sortOn f = map snd . sortBy (comparing fst) . map (f &&& id)
+
+descSnd :: Num b => (String, b) -> (b, String)
+descSnd = (negate . snd) &&& (map toLower . fst)
+
+showPriority :: Commands -> String
+showPriority = unlines . map fst . sortOn descSnd . toList
+
+updatePriority :: String -> Commands -> Commands
+updatePriority cmd = if null cmd then id else insertWith (+) cmd 1
+
+parseInput :: String -> Commands
+parseInput = fromList . flip zip (repeat 0) . filter (not . null) . lines
+-- }}}
+-- shell stuff {{{
+dmenu :: Commands -> IO Commands
+dmenu cmds = do
+    (hIn, hOut, hErr, p) <- runInteractiveProcess "dmenu" [] Nothing Nothing
+    hPutStrLn hIn (showPriority cmds)
+    hClose hIn
+    o <- hGetContents hOut
+    e <- hGetContents hErr
+    waitForProcess p
+    hPutStr stdout o
+    hPutStr stderr e
+
+    return (updatePriority o cmds)
+-- }}}
+main :: IO ()
+main = do
+    file    <- fileName
+    cached  <- readPossiblyNonExistent file
+    new     <- fmap parseInput Strict.getContents
+    updated <- dmenu (cached `union` new)
+    writeFile file (show updated)
