diff --git a/home/common/nvim/init.lua b/home/common/nvim/init.lua
index b94dc09..1330396 100644
--- a/home/common/nvim/init.lua
+++ b/home/common/nvim/init.lua
@@ -127,6 +127,7 @@ vim.keymap.set('n', '<Leader>f', "<cmd>Pick files_root<cr>", opts("Open file pic
 vim.keymap.set('n', '<c-p>', "<Leader>f", { remap = true })
 vim.keymap.set('n', '<Leader>g', "<cmd>Pick oldfiles<cr>", opts("Open file picker history"))
 vim.keymap.set("n", '<Leader>k', vim.lsp.buf.hover, opts("Show docs for item under cursor"))
+vim.keymap.set('n', '<Leader>p', "<cmd>Pick projects<cr>", opts("Open projects picker"))
 vim.keymap.set('n', '<Leader>q', require('mini.bufremove').delete, opts("Delete buffer"))
 vim.keymap.set('n', '<Leader>s', "<cmd>Pick lsp scope='document_symbol'<cr>", opts("Open symbol picker"))
 vim.keymap.set('n', '<Leader>S', "<cmd>Pick lsp scope='workspace_symbol'<cr>", opts("Open workspace symbol picker"))
diff --git a/home/common/nvim/mini.lua b/home/common/nvim/mini.lua
index c6788f1..a572728 100644
--- a/home/common/nvim/mini.lua
+++ b/home/common/nvim/mini.lua
@@ -64,6 +64,45 @@ require('mini.pick').setup({
     config = picker_win_config,
   },
 })
+
+MiniPick.registry.projects = function(local_opts)
+  local root = vim.fn.expand("~/src")
+
+  local command = {
+    "fd",
+    "--max-depth=8",
+    "--one-file-system",
+    "--unrestricted",
+    "--full-path",
+    "/.jj/repo/store/type$|/.git/HEAD$",
+    root,
+  }
+
+  local postprocess = function(paths)
+    local result = {}
+    for _, path in ipairs(paths) do
+      path = path:gsub("%/.jj/repo/store/type$", "")
+      path = path:gsub("%/.git/HEAD$", "")
+      local item = {
+        path = path,
+        text = path:gsub("%" .. root .. "/", " "),
+      }
+      table.insert(result, item)
+    end
+    return result
+  end
+
+  local choose = function(item)
+    local_opts.cwd = item.path
+    vim.fn.chdir(item.path)
+    vim.schedule(function()
+      MiniPick.builtin.files(local_opts, { source = { cwd = item.path, tool = "rg" } })
+    end)
+  end
+
+  return MiniPick.builtin.cli({ command = command, postprocess = postprocess }, { source = { choose = choose } })
+end
+
 MiniPick.registry.files_root = function(local_opts)
   local root_patterns = { ".jj", ".git" }
   local root_dir = vim.fs.dirname(vim.fs.find(root_patterns, { upward = true })[1])