diff --git a/ConvertCommand.hs b/ConvertCommand.hs
new file mode 100644
--- /dev/null
+++ b/ConvertCommand.hs
@@ -0,0 +1,114 @@
+{-|
+
+Convert account data in CSV format (eg downloaded from a bank) to ledger
+format, and print it on stdout.
+
+Usage: hledger convert CSVFILE ACCOUNTNAME RULESFILE
+
+ACCOUNTNAME is the base account to use for transactions.  RULESFILE
+provides some rules to help convert the data. It should contain paragraphs
+separated by one blank line.  The first paragraph is a single line of five
+comma-separated numbers, which are the csv field positions corresponding
+to the ledger transaction's date, status, code, description, and amount.
+All other paragraphs specify one or more regular expressions, followed by
+the ledger account to use when a transaction's description matches any of
+them. A regexp may optionally have a replacement pattern specified after =.
+Here's an example rules file:
+
+> 0,2,3,4,1
+>
+> ATM DEPOSIT
+> assets:bank:checking
+>
+> (TO|FROM) SAVINGS
+> assets:bank:savings
+>
+> ITUNES
+> BLKBSTR=BLOCKBUSTER
+> expenses:entertainment
+
+Roadmap: 
+Support for other formats will be added. To update a ledger file, pipe the
+output into the import command. The rules will move to a hledger config
+file. When no rule matches, accounts will be guessed based on similarity
+to descriptions in the current ledger, with interactive prompting and
+optional rule saving.
+
+-}
+
+module ConvertCommand where
+import Data.Maybe (isJust)
+import Data.List.Split (splitOn)
+import Options -- (Opt,Debug)
+import Ledger.Types (Ledger,AccountName)
+import Ledger.Utils (strip)
+import System (getArgs)
+import System.IO (stderr, hPutStrLn)
+import Text.CSV (parseCSVFromFile, Record)
+import Text.Printf (printf)
+import Text.Regex.PCRE ((=~))
+import Data.Maybe
+import Ledger.Dates (firstJust, showDate)
+import System.Locale (defaultTimeLocale)
+import Data.Time.Format (parseTime)
+import Control.Monad (when)
+
+
+convert :: [Opt] -> [String] -> Ledger -> IO ()
+convert opts args l = do
+  when (length args /= 3) (error "please specify a csv file, base account, and import rules file.")
+  let [csvfile,baseacct,rulesfile] = args
+  rulesstr <- readFile rulesfile
+  (fieldpositions,rules) <- parseRules rulesstr
+  parse <- parseCSVFromFile csvfile
+  let records = case parse of
+                  Left e -> error $ show e
+                  Right rs -> reverse rs
+  mapM_ (print_ledger_txn (Debug `elem` opts) (baseacct,fieldpositions,rules)) records
+
+
+type Rule = ([[String]]   -- list of [pattern,replacement]. replacement may or may not be present.
+            ,AccountName) -- account name to use for a transaction matching this rule
+
+parseRules :: String -> IO ([Int],[Rule])
+parseRules s = do
+  let ls = map strip $ lines s
+  let paras = splitOn [""] ls
+  let fieldpositions = map read $ splitOn "," $ head $ head paras
+  let rules = [(map (splitOn "=") $ init p, last p) | p <- tail paras]
+  return (fieldpositions,rules)
+
+print_ledger_txn debug (baseacct,fieldpositions,rules) record@(a:b:c:d:e) = do
+  let [date,cleared,number,description,amount] = map (record !!) fieldpositions
+      amount' = strnegate amount where strnegate ('-':s) = s
+                                       strnegate s = '-':s
+      unknownacct | (read amount' :: Double) < 0 = "income:unknown"
+                  | otherwise = "expenses:unknown"
+      (acct,desc) = choose_acct_desc rules (unknownacct,description)
+  when (debug) $ hPutStrLn stderr $ printf "using %s for %s" desc description
+  putStrLn $ printf "%s%s %s" (fixdate date) (if not (null number) then printf " (%s)" number else "") desc
+  putStrLn $ printf "    %-30s  %15s" acct (printf "$%s" amount' :: String)
+  putStrLn $ printf "    %s\n" baseacct
+print_ledger_txn True _ record = do
+  hPutStrLn stderr $ printf "ignoring %s" $ show record
+print_ledger_txn _ _ _ = return ()
+
+choose_acct_desc rules (acct,desc) | null matches = (acct,desc)
+                                   | otherwise = (a,d)
+    where
+      matches = filter (any (desc =~) . map head . fst) rules
+      (pats,a) = head matches :: Rule
+      ((before,match,after,groups),repl) = head $ filter isMatch $ map (\(pat:repl) -> (desc=~pat,repl)) pats
+      d = head $ repl ++ [match]  -- show the replacement text if any, or the matched text
+
+isMatch :: ((String, String, String, [String]),[String]) -> Bool
+isMatch ((_,m,_,_),_) = not $ null m
+
+fixdate :: String -> String
+fixdate s = maybe "0000/00/00" showDate $ 
+              firstJust
+              [parseTime defaultTimeLocale "%Y/%m/%d" s
+              ,parseTime defaultTimeLocale "%Y-%m-%d" s
+              ,parseTime defaultTimeLocale "%m/%d/%Y" s
+              ,parseTime defaultTimeLocale "%m-%d-%Y" s
+              ]
diff --git a/Version.hs b/Version.hs
--- a/Version.hs
+++ b/Version.hs
@@ -5,7 +5,7 @@
 import Options (progname)
 
 -- updated by build process from VERSION
-version       = "0.5.0"
+version       = "0.5.1"
 #ifdef PATCHES
 -- a "make" development build defines PATCHES from the repo state
 patchlevel = "." ++ show PATCHES -- must be numeric !
diff --git a/hledger.cabal b/hledger.cabal
--- a/hledger.cabal
+++ b/hledger.cabal
@@ -1,6 +1,6 @@
 Name:           hledger
 -- updated by build process from VERSION
-Version:        0.5
+Version:        0.5.1
 Category:       Finance
 Synopsis:       A ledger-compatible text-based accounting tool.
 Description:    hledger is a partial haskell clone of John Wiegley's "ledger" text-based
@@ -20,7 +20,7 @@
 
 Flag vty
   description: enable the curses ui
-  default:     True
+  default:     False
 
 Flag happs
   description: enable the web ui
@@ -58,6 +58,7 @@
   Other-Modules:  
                   AddCommand
                   BalanceCommand
+                  ConvertCommand
                   HistogramCommand
                   Options
                   PrintCommand
