diff --git a/src/compiler/crystal/command.cr b/src/compiler/crystal/command.cr
index 1180485b591edb4cf070a338fad039dd23ba5f18..115367bbb2de044b33f5d78095030ca1e82a6752 100644
--- a/src/compiler/crystal/command.cr
+++ b/src/compiler/crystal/command.cr
@@ -4,6 +4,7 @@ module Crystal::Command
             Command:\n    \
             build                    compile program file\n    \
             browser                  open an http server to browse program file\n    \
+            deps                     install project dependencies\n    \
             eval                     eval code\n    \
             hierarchy                show type hierarchy\n    \
             run (default)            compile and run program file\n    \
@@ -25,6 +26,9 @@ module Crystal::Command
         when "browser" == command
           options.shift
           browser options
+        when "deps".starts_with?(command)
+          options.shift
+          deps options
         when "eval".starts_with?(command)
           options.shift
           eval options
@@ -113,6 +117,17 @@ module Crystal::Command
     execute output_filename, arguments
   end
 
+  private def self.deps(options)
+    compiler = Compiler.new
+    sources = gather_sources(["Projectfile"])
+    sources.insert 0, Compiler::Source.new("require", %(require "project"))
+
+    output_filename = tempfile "deps"
+
+    result = compiler.compile sources, output_filename
+    execute output_filename, options
+  end
+
   private def self.types(options)
     result = compile_no_build "types", options
     Crystal.print_types result.original_node
diff --git a/src/project/dependency.cr b/src/project/dependency.cr
new file mode 100644
index 0000000000000000000000000000000000000000..9459e72a560f872b27366e3ccbcf55bfa9f47759
--- /dev/null
+++ b/src/project/dependency.cr
@@ -0,0 +1,5 @@
+abstract class Dependency
+  abstract def install
+  property locked_version
+  property! name
+end
diff --git a/src/project/dsl.cr b/src/project/dsl.cr
new file mode 100644
index 0000000000000000000000000000000000000000..f607544e0e85af74724a5b604c1bfbc6f0d25725
--- /dev/null
+++ b/src/project/dsl.cr
@@ -0,0 +1,14 @@
+class Project
+  class DSL::Deps
+    def initialize(@project)
+    end
+
+    def github(repository)
+      @project.dependencies << GitHubDependency.new(repository)
+    end
+  end
+end
+
+def deps
+  with Project::DSL::Deps.new(Project::INSTANCE) yield
+end
diff --git a/src/project/github_dependency.cr b/src/project/github_dependency.cr
new file mode 100644
index 0000000000000000000000000000000000000000..964818dbfb6be1a02d574218c8916e5337f8aebf
--- /dev/null
+++ b/src/project/github_dependency.cr
@@ -0,0 +1,30 @@
+class GitHubDependency < Dependency
+  def initialize(repo)
+    unless repo =~ /(.*)\/(.*)/
+      raise ProjectError.new("Invalid GitHub repository definition: #{repo}")
+    end
+
+    @author = $1
+    @name = @repository = $2
+    @target_dir = ".deps/#{@repository}"
+  end
+
+  def install
+    unless Dir.exists?(@target_dir)
+      `git clone git@github.com:#{@author}/#{@repository}.git #{@target_dir}`
+    end
+    `ln -sf ../#{@target_dir}/src libs/#{@repository}`
+
+    if @locked_version &&
+      if current_version != @locked_version
+        `git -C #{@target_dir} checkout -q #{@locked_version}`
+      end
+    else
+      @locked_version = current_version
+    end
+  end
+
+  def current_version
+    `git -C #{@target_dir} rev-parse HEAD`.chomp
+  end
+end
diff --git a/src/project/project.cr b/src/project/project.cr
new file mode 100644
index 0000000000000000000000000000000000000000..35a4ff9e59294890831047414b0195a7bf62be3b
--- /dev/null
+++ b/src/project/project.cr
@@ -0,0 +1,50 @@
+require "json"
+require "./*"
+
+class Project
+  INSTANCE = Project.new
+  property dependencies
+
+  def initialize
+    @dependencies = [] of Dependency
+  end
+
+  def install_deps
+    # Prepare required directories
+    Dir.mkdir_p ".deps"
+    Dir.mkdir_p "libs"
+
+    # Load lockfile
+    if File.exists?(".deps.lock")
+      lock = Json.parse(File.read(".deps.lock")) as Hash
+      @dependencies.each do |dep|
+        if locked_version = lock[dep.name]?
+          dep.locked_version = locked_version as String
+        end
+      end
+    end
+
+    # Install al dependencies
+    @dependencies.each &.install
+
+    # Save lockfile
+    lock = {} of String => String
+    @dependencies.each do |dep|
+      lock[dep.name] = dep.locked_version.not_nil!
+    end
+    File.write(".deps.lock", lock.to_json)
+  end
+end
+
+class ProjectError < Exception
+end
+
+redefine_main do |main|
+  begin
+    {{main}}
+  rescue ex : ProjectError
+    puts ex.message
+    exit 1
+  end
+  Project::INSTANCE.install_deps
+end