diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Pedro Tacla Yamada
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/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/projectroot.cabal b/projectroot.cabal
new file mode 100644
--- /dev/null
+++ b/projectroot.cabal
@@ -0,0 +1,27 @@
+name:                projectroot
+version:             0.1.0.0
+synopsis:            Bindings to the projectroot C logic
+description: Simple way of finding the root of a project given an
+             entry-point. This module provides bindings to the
+             <https://github.com/yamadapc/projectroot projectroot> C library
+homepage:            https://gitlab.com/yamadapc/haskell-projectroot
+license:             MIT
+license-file:        LICENSE
+author:              Pedro Tacla Yamada
+maintainer:          tacla.yamada@gmail.com
+copyright:           Copyright (c) 2015 Pedro Tacla Yamada
+category:            System
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     System.Directory.ProjectRoot
+  build-depends:       base >=4.8 && <4.9
+                     , directory
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  c-sources:           ./projectroot/libprojectroot.c
+                     , ./projectroot/deps/commander/commander.c
+  install-includes:    ./projectroot/libprojectroot.h
+                     , ./projectroot/deps/commander/commander.h
+  include-dirs:        .
diff --git a/projectroot/deps/commander/commander.c b/projectroot/deps/commander/commander.c
new file mode 100644
--- /dev/null
+++ b/projectroot/deps/commander/commander.c
@@ -0,0 +1,269 @@
+
+//
+// commander.c
+//
+// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "commander.h"
+
+/*
+ * Output error and exit.
+ */
+
+static void
+error(char *msg) {
+  fprintf(stderr, "%s\n", msg);
+  exit(1);
+}
+
+/*
+ * Output command version.
+ */
+
+static void
+command_version(command_t *self) {
+  printf("%s\n", self->version);
+  command_free(self);
+  exit(0);
+}
+
+/*
+ * Output command help.
+ */
+
+void
+command_help(command_t *self) {
+  printf("\n");
+  printf("  Usage: %s %s\n", self->name, self->usage);
+  printf("\n");
+  printf("  Options:\n");
+  printf("\n");
+
+  int i;
+  for (i = 0; i < self->option_count; ++i) {
+    command_option_t *option = &self->options[i];
+    printf("    %s, %-25s %s\n"
+      , option->small
+      , option->large_with_arg
+      , option->description);
+  }
+
+  printf("\n");
+  command_free(self);
+  exit(0);
+}
+
+/*
+ * Initialize with program `name` and `version`.
+ */
+
+void
+command_init(command_t *self, const char *name, const char *version) {
+  self->arg = NULL;
+  self->name = name;
+  self->version = version;
+  self->option_count = self->argc = 0;
+  self->usage = "[options]";
+  self->nargv = NULL;
+  command_option(self, "-V", "--version", "output program version", command_version);
+  command_option(self, "-h", "--help", "output help information", command_help);
+}
+
+/*
+ * Free up commander after use.
+ */
+
+void
+command_free(command_t *self) {
+  int i;
+
+  for (i = 0; i < self->option_count; ++i) {
+    command_option_t *option = &self->options[i];
+    free(option->argname);
+    free(option->large);
+  }
+
+  if (self->nargv) {
+    for (i = 0; self->nargv[i]; ++i) {
+      free(self->nargv[i]);
+    }
+    free(self->nargv);
+  }
+}
+
+/*
+ * Parse argname from `str`. For example
+ * Take "--required <arg>" and populate `flag`
+ * with "--required" and `arg` with "<arg>".
+ */
+
+static void
+parse_argname(const char *str, char *flag, char *arg) {
+  int buffer = 0;
+  size_t flagpos = 0;
+  size_t argpos = 0;
+  size_t len = strlen(str);
+  size_t i;
+
+  for (i = 0; i < len; ++i) {
+    if (buffer || '[' == str[i] || '<' == str[i]) {
+      buffer = 1;
+      arg[argpos++] = str[i];
+    } else {
+      if (' ' == str[i]) continue;
+      flag[flagpos++] = str[i];
+    }
+  }
+
+  arg[argpos] = '\0';
+  flag[flagpos] = '\0';
+}
+
+/*
+ * Normalize the argument vector by exploding
+ * multiple options (if any). For example
+ * "foo -abc --scm git" -> "foo -a -b -c --scm git"
+ */
+
+static char **
+normalize_args(int *argc, char **argv) {
+  int size = 0;
+  int alloc = *argc + 1;
+  char **nargv = malloc(alloc * sizeof(char *));
+  int i;
+
+  for (i = 0; argv[i]; ++i) {
+    const char *arg = argv[i];
+    size_t len = strlen(arg);
+
+    // short flag
+    if (len > 2 && '-' == arg[0] && !strchr(arg + 1, '-')) {
+      alloc += len - 2;
+      nargv = realloc(nargv, alloc * sizeof(char *));
+      for (size_t j = 1; j < len; ++j) {
+        nargv[size] = malloc(3);
+        sprintf(nargv[size], "-%c", arg[j]);
+        size++;
+      }
+      continue;
+    }
+
+    // regular arg
+    nargv[size] = malloc(len + 1);
+    strcpy(nargv[size], arg);
+    size++;
+  }
+
+  nargv[size] = NULL;
+  *argc = size;
+  return nargv;
+}
+
+/*
+ * Define an option.
+ */
+
+void
+command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) {
+  if (self->option_count == COMMANDER_MAX_OPTIONS) {
+    command_free(self);
+    error("Maximum option definitions exceeded");
+  }
+  int n = self->option_count++;
+  command_option_t *option = &self->options[n];
+  option->cb = cb;
+  option->small = small;
+  option->description = desc;
+  option->required_arg = option->optional_arg = 0;
+  option->large_with_arg = large;
+  option->argname = malloc(strlen(large) + 1);
+  assert(option->argname);
+  option->large = malloc(strlen(large) + 1);
+  assert(option->large);
+  parse_argname(large, option->large, option->argname);
+  if ('[' == option->argname[0]) option->optional_arg = 1;
+  if ('<' == option->argname[0]) option->required_arg = 1;
+}
+
+/*
+ * Parse `argv` (internal).
+ * Input arguments should be normalized first
+ * see `normalize_args`.
+ */
+
+static void
+command_parse_args(command_t *self, int argc, char **argv) {
+  int literal = 0;
+  int i, j;
+
+  for (i = 1; i < argc; ++i) {
+    const char *arg = argv[i];
+    for (j = 0; j < self->option_count; ++j) {
+      command_option_t *option = &self->options[j];
+
+      // match flag
+      if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) {
+        self->arg = NULL;
+
+        // required
+        if (option->required_arg) {
+          arg = argv[++i];
+          if (!arg || '-' == arg[0]) {
+            fprintf(stderr, "%s %s argument required\n", option->large, option->argname);
+            command_free(self);
+            exit(1);
+          }
+          self->arg = arg;
+        }
+
+        // optional
+        if (option->optional_arg) {
+          if (argv[i + 1] && '-' != argv[i + 1][0]) {
+            self->arg = argv[++i];
+          }
+        }
+
+        // invoke callback
+        option->cb(self);
+        goto match;
+      }
+    }
+
+    // --
+    if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) {
+      literal = 1;
+      goto match;
+    }
+
+    // unrecognized
+    if ('-' == arg[0] && !literal) {
+      fprintf(stderr, "unrecognized flag %s\n", arg);
+      command_free(self);
+      exit(1);
+    }
+
+    int n = self->argc++;
+    if (n == COMMANDER_MAX_ARGS) {
+      command_free(self);
+      error("Maximum number of arguments exceeded");
+    }
+    self->argv[n] = (char *) arg;
+    match:;
+  }
+}
+
+/*
+ * Parse `argv` (public).
+ */
+
+void
+command_parse(command_t *self, int argc, char **argv) {
+  self->nargv = normalize_args(&argc, argv);
+  command_parse_args(self, argc, self->nargv);
+  self->argv[self->argc] = NULL;
+}
diff --git a/projectroot/deps/commander/commander.h b/projectroot/deps/commander/commander.h
new file mode 100644
--- /dev/null
+++ b/projectroot/deps/commander/commander.h
@@ -0,0 +1,88 @@
+
+//
+// commander.h
+//
+// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#ifndef COMMANDER_H
+#define COMMANDER_H
+
+/*
+ * Max options that can be defined.
+ */
+
+#ifndef COMMANDER_MAX_OPTIONS
+#define COMMANDER_MAX_OPTIONS 32
+#endif
+
+/*
+ * Max arguments that can be passed.
+ */
+
+#ifndef COMMANDER_MAX_ARGS
+#define COMMANDER_MAX_ARGS 32
+#endif
+
+/*
+ * Command struct.
+ */
+
+struct command;
+
+/*
+ * Option callback.
+ */
+
+typedef void (* command_callback_t)(struct command *self);
+
+/*
+ * Command option.
+ */
+
+typedef struct {
+  int optional_arg;
+  int required_arg;
+  char *argname;
+  char *large;
+  const char *small;
+  const char *large_with_arg;
+  const char *description;
+  command_callback_t cb;
+} command_option_t;
+
+/*
+ * Command.
+ */
+
+typedef struct command {
+  void *data;
+  const char *usage;
+  const char *arg;
+  const char *name;
+  const char *version;
+  int option_count;
+  command_option_t options[COMMANDER_MAX_OPTIONS];
+  int argc;
+  char *argv[COMMANDER_MAX_ARGS];
+  char **nargv;
+} command_t;
+
+// prototypes
+
+void
+command_init(command_t *self, const char *name, const char *version);
+
+void
+command_free(command_t *self);
+
+void
+command_help(command_t *self);
+
+void
+command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb);
+
+void
+command_parse(command_t *self, int argc, char **argv);
+
+#endif /* COMMANDER_H */
diff --git a/projectroot/libprojectroot.c b/projectroot/libprojectroot.c
new file mode 100644
--- /dev/null
+++ b/projectroot/libprojectroot.c
@@ -0,0 +1,90 @@
+#include <dirent.h>
+#include <glob.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libprojectroot.h"
+
+int weight_mode_on = 0;
+int verbose_on = 0;
+
+static const char* const candidates[] = {
+  ".git",
+  ".hg",
+  ".fslckout",
+  ".bzr",
+  "_darcs",
+  ".svn",
+  "CVS",
+  "rebar.config",
+  "project.clj",
+  "SConstruct",
+  "pom.xml",
+  "build.sbt",
+  "build.gradle",
+  "Gemfile",
+  "requirements.txt",
+  "tox.ini",
+  "package.json",
+  "node_modules",
+  "gulpfile.js",
+  "Gruntfile.js",
+  "bower.json",
+  "composer.json",
+  "Cargo.toml",
+  "stack.yaml",
+  ".cabal-sandbox",
+  "*.cabal",
+  "mix.exs",
+};
+
+int is_project_root(char* cdt) {
+  char pattern[1024];
+  glob_t res;
+  size_t cdt_size = strlen(cdt);
+
+  strcpy(pattern, cdt);
+  strcat(pattern, "/");
+
+  for(int i = 0; i < N_CANDIDATES; i++) {
+    strcat(pattern, candidates[i]);
+    if(verbose_on) fprintf(stderr, "Matching: %s ~= %s\n", cdt, pattern);
+    if(glob(pattern, GLOB_NOSORT, NULL, &res) == 0) {
+      if(verbose_on) fprintf(stderr, "--> Matched (weight %d)\n", N_CANDIDATES - i + 1);
+      return N_CANDIDATES - i + 1;
+    }
+    pattern[cdt_size + 1] = 0;
+  }
+
+  return 0;
+}
+
+char* find_project_root_weighted(char* cwd) {
+  char* cdt = cwd;
+  char* best_cdt = malloc(sizeof(char) * 1024);
+  int best_cdt_weight = 0;
+  int cdt_weight;
+
+  while(strcmp(cdt, "/") != 0) {
+    if((cdt_weight = is_project_root(cdt)) && cdt_weight > best_cdt_weight) {
+      best_cdt_weight = cdt_weight;
+      strcpy(best_cdt, cdt);
+    }
+    cdt = dirname(cdt);
+  }
+
+  return best_cdt;
+}
+
+char* find_project_root(char* cwd) {
+  char* cdt = cwd;
+
+  while(strcmp(cdt, "/") != 0) {
+    if(is_project_root(cdt)) return cdt;
+    cdt = dirname(cdt);
+  }
+
+  return NULL;
+}
diff --git a/projectroot/libprojectroot.h b/projectroot/libprojectroot.h
new file mode 100644
--- /dev/null
+++ b/projectroot/libprojectroot.h
@@ -0,0 +1,16 @@
+#include <dirent.h>
+#include <glob.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define N_CANDIDATES 26
+
+int weight_mode_on;
+int verbose_on;
+
+int is_project_root(char* cdt);
+char* find_project_root_weighted(char* cwd);
+char* find_project_root(char* cwd);
diff --git a/src/System/Directory/ProjectRoot.hs b/src/System/Directory/ProjectRoot.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Directory/ProjectRoot.hs
@@ -0,0 +1,45 @@
+-- |
+-- Module: System.Directory.ProjectRoot
+-- Description: Find the root of a project
+-- Copyright: (c) Pedro Tacla Yamada, 2015
+-- License: MIT
+-- Maintainer: tacla.yamada@gmail.com
+-- Stability: Stable
+-- Portability: POSIX
+--
+-- Simple way of finding the root of a project given an entry-point.
+-- This module provides bindings to the <https://github.com/yamadapc/projectroot projectroot> C library
+module System.Directory.ProjectRoot
+  where
+
+import           Control.Monad    ((<=<), (>=>))
+import           Foreign.C
+import           System.Directory (getCurrentDirectory)
+
+-- * Haskell Wrappers
+
+-- |
+-- Find the project root given an entry point
+getProjectRoot :: FilePath -> IO FilePath
+getProjectRoot = (find_project_root <=< newCString) >=> peekCString
+
+-- |
+-- Find the weighted project root given an entry point
+getProjectRootWeighted :: FilePath -> IO FilePath
+getProjectRootWeighted = (find_project_root_weighted <=< newCString) >=>
+                         peekCString
+
+-- |
+-- Find the project root of the current directory
+getProjectRootCurrent :: IO FilePath
+getProjectRootCurrent = getCurrentDirectory >>= getProjectRoot
+
+-- |
+-- Find the weighted project root of the current directory
+getProjectRootWeightedCurrent :: IO FilePath
+getProjectRootWeightedCurrent = getCurrentDirectory >>= getProjectRootWeighted
+
+-- * C functions
+
+foreign import ccall "find_project_root" find_project_root :: CString -> IO CString
+foreign import ccall "find_project_root_weighted" find_project_root_weighted :: CString -> IO CString
