diff --git a/package.json b/package.json
index 0a797ec0..3594007f 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
     "build:legacy": "BABEL_ENV=legacy babel src -d dist/legacy",
     "build:modern": "BABEL_ENV=modern babel src -d dist/modern",
     "build": "yarn run clean && yarn build:legacy && yarn build:modern",
+    "dev": "yarn run clean && yarn build:modern --watch",
     "prepublish": "yarn build",
     "precommit": "lint-staged"
   },
@@ -32,14 +33,15 @@
     "babel-preset-env": "^1.6.0",
     "babel-preset-flow": "^6.23.0",
     "fixturez": "^1.1.0",
-    "flow-bin": "^0.63.1",
+    "flow-bin": "^0.66.0",
     "husky": "^0.14.3",
     "jest": "^20.0.4",
     "jest-expect-contain-deep": "^1.0.1",
     "lint-staged": "^4.1.3",
     "mode-to-permissions": "^0.0.2",
     "path-exists": "^3.0.0",
-    "prettier": "^1.6.1"
+    "prettier": "^1.6.1",
+    "sinon": "^4.1.4"
   },
   "dependencies": {
     "array-includes": "^3.0.3",
@@ -51,7 +53,7 @@
     "detect-indent": "^5.0.0",
     "find-up": "^2.1.0",
     "globby": "^6.1.0",
-    "inquirer": "3.3.0",
+    "inquirer": "^4.0.2",
     "is-glob": "^4.0.0",
     "make-dir": "^1.0.0",
     "meow": "^4.0.0",
diff --git a/src/DependencyGraph.js b/src/DependencyGraph.js
new file mode 100644
index 00000000..8594d603
--- /dev/null
+++ b/src/DependencyGraph.js
@@ -0,0 +1,95 @@
+// @flow
+import Project from './Project';
+import Workspace from './Workspace';
+import semver from 'semver';
+import * as logger from './utils/logger';
+import * as messages from './utils/messages';
+
+export type SourceDepGraph = Map<
+  Workspace,
+  {
+    dependencies: Set<Workspace>,
+    dependents: Set<Workspace>
+  }
+>;
+
+export default class DependencyGraph {
+  _workspaces: Map<
+    Workspace,
+    {
+      dependencies: Set<Workspace>,
+      dependents: Set<Workspace>
+    }
+  >;
+  _workspacesByName: { [key: string]: Workspace };
+  _isValid: boolean;
+
+  constructor(project: Project, workspaces: Array<Workspace>) {
+    this._workspaces = new Map();
+    this._isValid = true;
+    this._workspacesByName = {};
+
+    for (let workspace of workspaces) {
+      this._workspacesByName[workspace.pkg.config.getName()] = workspace;
+      this._workspaces.set(workspace, {
+        dependencies: new Set(),
+        dependents: new Set()
+      });
+    }
+
+    for (let workspace of workspaces) {
+      let allDependencies = workspace.pkg.getAllDependencies();
+
+      for (let [depName, depVersion] of allDependencies) {
+        let match = this._workspacesByName[depName];
+        if (!match) continue;
+
+        let actual = depVersion;
+        let expected = match.pkg.config.getVersion();
+
+        if (!semver.satisfies(expected, depVersion)) {
+          this._isValid = false;
+          logger.error(
+            messages.packageMustDependOnCurrentVersion(
+              workspace.pkg.config.getName(),
+              depName,
+              expected,
+              depVersion
+            )
+          );
+          continue;
+        }
+
+        let wNode = this._workspaces.get(workspace);
+        let mNode = this._workspaces.get(match);
+
+        if (wNode) wNode.dependencies.add(match);
+        if (mNode) mNode.dependents.add(workspace);
+      }
+    }
+  }
+
+  getDepsByWorkspace(workspace: Workspace) {
+    return this._workspaces.get(workspace);
+  }
+
+  getWorkspaceByName(pkgName: string) {
+    return this._workspacesByName[pkgName];
+  }
+
+  getDepsByName(pkgName: string) {
+    return this.getDepsByWorkspace(this.getWorkspaceByName(pkgName));
+  }
+
+  has(pkgName: string) {
+    return !!this.getWorkspaceByName(pkgName);
+  }
+
+  isValid() {
+    return this._isValid;
+  }
+
+  entries() {
+    return this._workspaces.entries();
+  }
+}
diff --git a/src/Project.js b/src/Project.js
index 4ad53d84..21d5398f 100644
--- a/src/Project.js
+++ b/src/Project.js
@@ -13,9 +13,19 @@ import { BoltError } from './utils/errors';
 import * as globs from './utils/globs';
 import taskGraphRunner from 'task-graph-runner';
 import minimatch from 'minimatch';
+import Repository from './Repository';
+import DependencyGraph from './DependencyGraph';
 
 export type Task = (workspace: Workspace) => Promise<mixed>;
 
+export type DepGraph = Map<
+  Workspace,
+  {
+    dependencies: Set<Workspace>,
+    dependents: Set<Workspace>
+  }
+>;
+
 export default class Project {
   pkg: Package;
 
@@ -24,9 +34,11 @@ export default class Project {
   }
 
   static async init(cwd: string) {
-    let filePath = await Config.getProjectConfig(cwd);
-    if (!filePath)
+    let realPath = await fs.realpath(cwd);
+    let filePath = await Config.getProjectConfig(realPath);
+    if (!filePath) {
       throw new BoltError(`Unable to find root of project in ${cwd}`);
+    }
     let pkg = await Package.init(filePath);
     return new Project(pkg);
   }
@@ -57,110 +69,18 @@ export default class Project {
     return workspaces;
   }
 
-  async getDependencyGraph(workspaces: Array<Workspace>) {
-    let graph: Map<
-      string,
-      { pkg: Package, dependencies: Array<string> }
-    > = new Map();
-    let packages = [this.pkg];
-    let packagesByName = { [this.pkg.config.getName()]: this.pkg };
-    let valid = true;
-
-    for (let workspace of workspaces) {
-      packages.push(workspace.pkg);
-      packagesByName[workspace.pkg.config.getName()] = workspace.pkg;
-    }
-
-    for (let pkg of packages) {
-      let name = pkg.config.getName();
-      let dependencies = [];
-      let allDependencies = pkg.getAllDependencies();
-
-      for (let [depName, depVersion] of allDependencies) {
-        let match = packagesByName[depName];
-        if (!match) continue;
-
-        let actual = depVersion;
-        let expected = match.config.getVersion();
-
-        // Workspace dependencies only need to semver satisfy, not '==='
-        if (!semver.satisfies(expected, depVersion)) {
-          valid = false;
-          logger.error(
-            messages.packageMustDependOnCurrentVersion(
-              name,
-              depName,
-              expected,
-              depVersion
-            )
-          );
-          continue;
-        }
-
-        dependencies.push(depName);
-      }
-
-      graph.set(name, { pkg, dependencies });
-    }
-
-    return { graph, valid };
-  }
-
-  async getDependentsGraph(workspaces: Array<Workspace>) {
-    let graph = new Map();
-    let { valid, graph: dependencyGraph } = await this.getDependencyGraph(
-      workspaces
-    );
-
-    let dependentsLookup: {
-      [string]: { pkg: Package, dependents: Array<string> }
-    } = {};
-
-    workspaces.forEach(workspace => {
-      dependentsLookup[workspace.pkg.config.getName()] = {
-        pkg: workspace.pkg,
-        dependents: []
-      };
-    });
-
-    workspaces.forEach(workspace => {
-      let dependent = workspace.pkg.config.getName();
-      let valFromDependencyGraph = dependencyGraph.get(dependent) || {};
-      let dependencies = valFromDependencyGraph.dependencies || [];
-
-      dependencies.forEach(dependency => {
-        dependentsLookup[dependency].dependents.push(dependent);
-      });
-    });
-
-    // can't use Object.entries here as the flow type for it is Array<[string, mixed]>;
-    Object.keys(dependentsLookup).forEach(key => {
-      graph.set(key, dependentsLookup[key]);
-    });
-
-    return { valid, graph };
-  }
-
   async runWorkspaceTasks(workspaces: Array<Workspace>, task: Task) {
-    let { graph: dependentsGraph, valid } = await this.getDependencyGraph(
-      workspaces
-    );
-
+    let depGraph = new DependencyGraph(this, workspaces);
     let graph = new Map();
 
-    for (let [pkgName, pkgInfo] of dependentsGraph) {
-      graph.set(pkgName, pkgInfo.dependencies);
+    for (let [workspace, { dependencies }] of depGraph.entries()) {
+      graph.set(workspace, Array.from(dependencies));
     }
 
     let { safe } = await taskGraphRunner({
       graph,
       force: true,
-      task: async workspaceName => {
-        let workspace = this.getWorkspaceByName(workspaces, workspaceName);
-        if (workspace) {
-          return task(workspace);
-        }
-      }
+      task
     });
 
     if (!safe) {
diff --git a/src/Workspace.js b/src/Workspace.js
index 1665a7bb..30fe5013 100644
--- a/src/Workspace.js
+++ b/src/Workspace.js
@@ -11,4 +11,8 @@ export default class Workspace {
   static async init(pkg: Package) {
     return new Workspace(pkg);
   }
+
+  getName() {
+    return this.pkg.config.getName();
+  }
 }
diff --git a/src/__tests__/Project.test.js b/src/__tests__/Project.test.js
index 13cc4034..60066ffd 100644
--- a/src/__tests__/Project.test.js
+++ b/src/__tests__/Project.test.js
@@ -1,8 +1,9 @@
 // @flow
 import path from 'path';
-import Project from '../Project';
+import Project, { type DepGraph } from '../Project';
 import Package from '../Package';
 import Workspace from '../Workspace';
+import DependencyGraph from '../DependencyGraph';
 import * as logger from '../utils/logger';
 import fixtures from 'fixturez';
 
@@ -15,9 +16,24 @@ function assertDependencies(graph, pkg, dependencies) {
   expect(val && val.dependencies).toEqual(dependencies);
 }
 
-function assertDependents(graph, pkg, dependents) {
-  let val = graph.get(pkg);
-  expect(val && val.dependents).toEqual(dependents);
+type ExpectedDepGraph = {
+  [pkgName: string]: {
+    dependencies: Array<string>,
+    dependents: Array<string>
+  }
+};
+
+function assertDepGraph(graph: DependencyGraph, expected: ExpectedDepGraph) {
+  let actual: ExpectedDepGraph = {};
+
+  for (let [workspace, { dependencies, dependents }] of graph.entries()) {
+    actual[workspace.getName()] = {
+      dependencies: Array.from(dependencies).map(ws => ws.getName()),
+      dependents: Array.from(dependents).map(ws => ws.getName())
+    };
+  }
+
+  expect(actual).toEqual(expected);
 }
 
 // Asserts that a set of workspaces contains all (and only) the expected ones
@@ -31,7 +47,7 @@ function assertWorkspaces(workspaces, expectedNames) {
 }
 
 describe('Project', () => {
-  let project;
+  let project: Project;
 
   describe('A simple project', () => {
     beforeEach(async () => {
@@ -74,40 +90,16 @@ describe('Project', () => {
       expect(workspaces[0]).toBeInstanceOf(Workspace);
     });
 
-    it('should be able to getDependencyGraph', async () => {
+    it('should be able to getDepGraph', async () => {
       let workspaces = await project.getWorkspaces();
-      let { valid, graph } = await project.getDependencyGraph(workspaces);
-      let expectedDependencies = {
-        'fixture-project-nested-workspaces': [],
-        foo: ['bar'],
-        bar: [],
-        baz: ['bar']
-      };
-
-      expect(valid).toEqual(true);
-      expect(graph).toBeInstanceOf(Map);
-      expect(graph.size).toBe(4);
-
-      Object.entries(expectedDependencies).forEach(([pkg, dependencies]) => {
-        assertDependencies(graph, pkg, dependencies);
-      });
-    });
+      let graph = new DependencyGraph(project, workspaces);
 
-    it('should be able to getDependentsGraph', async () => {
-      let workspaces = await project.getWorkspaces();
-      let { valid, graph } = await project.getDependentsGraph(workspaces);
-      let expectedDependents = {
-        bar: ['foo', 'baz'],
-        foo: [],
-        baz: []
-      };
-
-      expect(valid).toEqual(true);
-      expect(graph).toBeInstanceOf(Map);
-      expect(graph.size).toBe(Object.keys(expectedDependents).length);
-
-      Object.entries(expectedDependents).forEach(([pkg, dependents]) => {
-        assertDependents(graph, pkg, dependents);
+      expect(graph.isValid()).toEqual(true);
+
+      assertDepGraph(graph, {
+        foo: { dependents: [], dependencies: ['bar'] },
+        bar: { dependents: ['foo', 'baz'], dependencies: [] },
+        baz: { dependents: [], dependencies: ['bar'] }
       });
     });
   });
@@ -124,47 +116,29 @@ describe('Project', () => {
       expect(workspaces[0]).toBeInstanceOf(Workspace);
     });
 
-    it('should be able to getDependencyGraph', async () => {
+    it('should be able to getDepGraph', async () => {
       let workspaces = await project.getWorkspaces();
-      let { valid, graph } = await project.getDependencyGraph(workspaces);
-      let expectedDependencies = {
-        'nested-workspaces-transitive-dependents': [],
-        'pkg-a': [],
-        'workspace-a': ['pkg-a'],
-        'pkg-b': ['pkg-a'],
-        'pkg-c': ['pkg-b']
-      };
-
-      expect(valid).toEqual(true);
-      expect(graph).toBeInstanceOf(Map);
-      expect(graph.size).toBe(Object.keys(expectedDependencies).length);
-
-      let assertDependencies = (pkg, deps) => {
-        let val = graph.get(pkg);
-        expect(val && val.dependencies).toEqual(deps);
-      };
-
-      Object.entries(expectedDependencies).forEach(([pkg, dependencies]) => {
-        assertDependencies(pkg, dependencies);
+      let graph = new DependencyGraph(project, workspaces);
+
+      expect(graph.isValid()).toEqual(true);
+      assertDepGraph(graph, {
+        'pkg-a': { dependents: ['workspace-a', 'pkg-b'], dependencies: [] },
+        'workspace-a': { dependents: [], dependencies: ['pkg-a'] },
+        'pkg-b': { dependents: ['pkg-c'], dependencies: ['pkg-a'] },
+        'pkg-c': { dependents: [], dependencies: ['pkg-b'] }
       });
     });
 
-    it('should be able to getDependentsGraph', async () => {
+    it('should be able to getDepGraph', async () => {
       let workspaces = await project.getWorkspaces();
-      let { valid, graph } = await project.getDependentsGraph(workspaces);
-      let expectedDependents = {
-        'pkg-a': ['workspace-a', 'pkg-b'],
-        'workspace-a': [],
-        'pkg-b': ['pkg-c'],
-        'pkg-c': []
-      };
-
-      expect(valid).toEqual(true);
-      expect(graph).toBeInstanceOf(Map);
-      expect(graph.size).toBe(Object.keys(expectedDependents).length);
-
-      Object.entries(expectedDependents).forEach(([pkg, dependents]) => {
-        assertDependents(graph, pkg, dependents);
+      let graph = new DependencyGraph(project, workspaces);
+
+      expect(graph.isValid()).toEqual(true);
+      assertDepGraph(graph, {
+        'pkg-a': { dependents: ['workspace-a', 'pkg-b'], dependencies: [] },
+        'pkg-b': { dependents: ['pkg-c'], dependencies: ['pkg-a'] },
+        'pkg-c': { dependents: [], dependencies: ['pkg-b'] },
+        'workspace-a': { dependents: [], dependencies: ['pkg-a'] }
       });
     });
   });
diff --git a/src/commands/__tests__/remove.test.js b/src/commands/__tests__/remove.test.js
index 8033fa1e..e50256bb 100644
--- a/src/commands/__tests__/remove.test.js
+++ b/src/commands/__tests__/remove.test.js
@@ -12,14 +12,17 @@ jest.mock('../../utils/logger');
 jest.mock('../../utils/yarn');
 
 describe('bolt remove', () => {
-  test('removing a project dependency only used by the project', async () => {
-    let tempDir = f.copy('package-with-external-deps-installed');
+  test.only(
+    'removing a project dependency only used by the project',
+    async () => {
+      let tempDir = f.copy('package-with-external-deps-installed');
 
-    await remove(toRemoveOptions(['project-only-dep'], { cwd: tempDir }));
+      await remove(toRemoveOptions(['project-only-dep'], { cwd: tempDir }));
 
-    expect(yarn.remove).toHaveBeenCalledTimes(1);
-    expect(yarn.remove).toHaveBeenCalledWith(['project-only-dep'], tempDir);
-  });
+      expect(yarn.remove).toHaveBeenCalledTimes(1);
+      expect(yarn.remove).toHaveBeenCalledWith(['project-only-dep'], tempDir);
+    }
+  );
 
   test('removing a workspace dependency', async () => {
     let tempDir = f.copy('package-with-external-deps-installed');
diff --git a/src/commands/__tests__/version.test.js b/src/commands/__tests__/version.test.js
index 37a96798..4e0e76fd 100644
--- a/src/commands/__tests__/version.test.js
+++ b/src/commands/__tests__/version.test.js
@@ -1,4 +1,56 @@
-// @flow
-import { version, toVersionOptions } from '../version';
-
-test('bolt version');
+// // @flow
+// import { version, toVersionOptions } from '../version';
+// import { copyFixtureIntoTempDir } from 'jest-fixtures';
+// import * as git from '../../utils/git';
+// import * as fs from '../../utils/fs';
+// import { BoltError } from '../../utils/errors';
+// import * as path from 'path';
+// import Project from '../../Project';
+// import * as semver from 'semver';
+//
+// describe('bolt version', () => {
+//   test('dirty tree', async () => {
+//     let cwd = await copyFixtureIntoTempDir(__dirname, 'simple-repo');
+//     let opts = toVersionOptions([], { cwd });
+//
+//     // unstaged
+//     await git.initRepository({ cwd });
+//     await expect(version(opts)).rejects.toBeInstanceOf(BoltError);
+//
+//     // staged
+//     await git.addAll({ cwd });
+//     await expect(version(opts)).rejects.toBeInstanceOf(BoltError);
+//
+//     // unstaged on top of commit
+//     await git.commit('init', { cwd });
+//     await fs.writeFile(path.join(cwd, 'foo'), '');
+//     await expect(version(opts)).rejects.toBeInstanceOf(BoltError);
+//   });
+//
+//   test('clean tree, no prev version commits', async () => {
+//     let cwd = await copyFixtureIntoTempDir(__dirname, 'simple-repo');
+//
+//     let opts = toVersionOptions([], { cwd });
+//     let project = await Project.init(cwd);
+//     let workspaces = await project.getWorkspaces();
+//
+//     await git.initRepository({ cwd });
+//     await git.addAll({ cwd });
+//     await git.commit('init', { cwd });
+//
+//     await Promise.all(
+//       workspaces.slice(0, 1).map(async workspace => {
+//         let json = workspace.pkg.config.getConfig();
+//         await workspace.pkg.config.write({
+//           ...json,
+//           version: semver.inc(json.version, 'major')
+//         });
+//       })
+//     );
+//
+//     await git.addAll({ cwd });
+//     await git.commit('bump', { cwd });
+//
+//     await version(opts);
+//   });
+// });
diff --git a/src/commands/add.js b/src/commands/add.js
index 41703e4b..e4d0b6ff 100644
--- a/src/commands/add.js
+++ b/src/commands/add.js
@@ -4,14 +4,14 @@ import Package from '../Package';
 import * as options from '../utils/options';
 import * as logger from '../utils/logger';
 import addDependenciesToPackage from '../utils/addDependenciesToPackages';
-import type { Dependency, configDependencyType } from '../types';
+import type { Dependency, ConfigDependencyType } from '../types';
 import { DEPENDENCY_TYPE_FLAGS_MAP } from '../constants';
 import type { ProjectAddOptions } from './project/add';
 
 export type AddOptions = {
   cwd?: string,
   deps: Array<Dependency>,
-  type: configDependencyType
+  type: ConfigDependencyType
 };
 
 export function toAddOptions(
diff --git a/src/commands/project/add.js b/src/commands/project/add.js
index ee802b98..3159738c 100644
--- a/src/commands/project/add.js
+++ b/src/commands/project/add.js
@@ -3,14 +3,14 @@ import addDependenciesToPackage from '../../utils/addDependenciesToPackages';
 import Project from '../../Project';
 import * as options from '../../utils/options';
 import * as logger from '../../utils/logger';
-import type { Dependency, configDependencyType } from '../../types';
+import type { Dependency, ConfigDependencyType } from '../../types';
 import { DEPENDENCY_TYPE_FLAGS_MAP } from '../../constants';
 import { add } from '../add';
 
 export type ProjectAddOptions = {
   cwd?: string,
   deps: Array<Dependency>,
-  type: configDependencyType
+  type: ConfigDependencyType
 };
 
 export function toProjectAddOptions(
diff --git a/src/commands/version.js b/src/commands/version.js
index ecc704a9..b251954b 100644
--- a/src/commands/version.js
+++ b/src/commands/version.js
@@ -1,16 +1,178 @@
 // @flow
+import Project, { type DepGraph } from '../Project';
+import Repository from '../Repository';
+import Workspace from '../Workspace';
+import DependencyGraph from '../DependencyGraph';
 import * as options from '../utils/options';
+import * as changes from '../utils/changes';
+import * as git from '../utils/git';
+import * as prompt from '../utils/prompt';
+import * as versions from '../utils/versions';
+import * as constants from '../constants';
+import * as messages from '../utils/messages';
 import { BoltError } from '../utils/errors';
 
-export type VersionOptions = {};
+export type VersionOptions = {
+  cwd?: string
+};
 
 export function toVersionOptions(
   args: options.Args,
   flags: options.Flags
 ): VersionOptions {
-  return {};
+  return {
+    cwd: options.string(flags.cwd, 'cwd')
+  };
+}
+
+type IncrementTypeChoice = versions.IncrementType | 'skip';
+type VersionChoice = 'diff' | IncrementTypeChoice;
+
+type VersionChoices = Array<
+  prompt.Separator | { name: messages.Message, value: VersionChoice }
+>;
+
+function getNextVersionOptions(
+  currentVersion: versions.Version
+): VersionChoices {
+  let choices: VersionChoices = [];
+
+  choices.push(
+    {
+      name: messages.skip(),
+      value: 'skip'
+    },
+    {
+      name: messages.diff(),
+      value: 'diff'
+    },
+    prompt.separator()
+  );
+
+  if (versions.getPrereleaseType(currentVersion)) {
+    choices.push({
+      name: messages.prerelease(
+        versions.increment(currentVersion, 'prerelease')
+      ),
+      value: 'prerelease'
+    });
+  }
+
+  versions.VERSION_TYPES.forEach(versionType => {
+    if (
+      versionType === 'prerelease' &&
+      !versions.getPrereleaseType(currentVersion)
+    ) {
+      return;
+    }
+
+    choices.push({
+      name: versions.getIncrementMessage(currentVersion, versionType),
+      value: versionType
+    });
+  });
+
+  choices.push(prompt.separator());
+
+  return choices;
+}
+
+async function workspaceDiff(workspace, versionCommits, repo, tty: boolean) {
+  let versionCommit = versionCommits.get(workspace);
+
+  if (!versionCommit) {
+    versionCommit = git.MAGIC_EMPTY_STATE_COMMIT;
+  }
+
+  return await git.getDiffForPathSinceCommit(
+    workspace.pkg.dir,
+    versionCommit.hash,
+    { cwd: repo.dir, tty }
+  );
+}
+
+let prevIncrementTypeChoice: IncrementTypeChoice = 'patch';
+
+async function getNextVersion(workspace, versionCommits, repo) {
+  let name = workspace.pkg.config.getName();
+  let currentVersionStr = workspace.pkg.config.getVersion();
+  let currentVersion = versions.toVersion(currentVersionStr);
+  let nextVersion = null;
+
+  while (!nextVersion) {
+    let choice: VersionChoice = await prompt.list(
+      messages.selectVersion(name, currentVersionStr),
+      getNextVersionOptions(currentVersion),
+      { default: prevIncrementTypeChoice }
+    );
+
+    if (choice === 'diff') {
+      await workspaceDiff(workspace, versionCommits, repo, true);
+    } else if (choice === 'skip') {
+      prevIncrementTypeChoice = 'skip';
+      break;
+    } else {
+      prevIncrementTypeChoice = choice;
+      nextVersion = versions.increment(currentVersion, choice);
+    }
+  }
+
+  return nextVersion;
 }
 
 export async function version(opts: VersionOptions) {
-  throw new BoltError('Unimplemented command "version"');
+  let cwd = opts.cwd || process.cwd();
+  let project = await Project.init(cwd);
+  let repo = await Repository.init(project.pkg.dir);
+  let workspaces = await project.getWorkspaces();
+  let graph = new DependencyGraph(project, workspaces);
+
+  let status = await git.status({ cwd: repo.dir });
+
+  if (status.length) {
+    throw new BoltError(
+      'Cannot run `bolt version` while you have a dirty tree:\n\n' +
+        status
+          .split('\n')
+          .map(line => `  ${line}`)
+          .join('\n')
+    );
+  }
+
+  let versionCommits = await changes.getWorkspaceVersionCommits(
+    repo,
+    workspaces
+  );
+
+  let changedWorkspaces: Array<Workspace> = [];
+
+  for (let workspace of workspaces) {
+    let diff = await workspaceDiff(workspace, versionCommits, repo, false);
+    if (diff.length) changedWorkspaces.push(workspace);
+  }
+
+  let newVersions: Map<Workspace, versions.IncrementType> = new Map();
+  let dependentPackageQueue = [];
+
+  for (let changedWorkspace of changedWorkspaces) {
+    let nextVersion = await getNextVersion(
+      changedWorkspace,
+      versionCommits,
+      repo
+    );
+
+    if (nextVersion) {
+      newVersions.set(changedWorkspace, nextVersion);
+
+      let node = graph.getDepsByWorkspace(changedWorkspace);
+      if (!node) continue;
+
+      for (let dependent of node.dependents) {
+      }
+    }
+  }
+
+  for (let [workspace, nextVersion] of newVersions) {
+    console.log(workspace.pkg.config.getName(), nextVersion);
+  }
 }
diff --git a/src/commands/workspace/add.js b/src/commands/workspace/add.js
index 9c0b66eb..958150dd 100644
--- a/src/commands/workspace/add.js
+++ b/src/commands/workspace/add.js
@@ -5,14 +5,14 @@ import * as options from '../../utils/options';
 import * as logger from '../../utils/logger';
 import addDependenciesToPackage from '../../utils/addDependenciesToPackages';
 import { BoltError } from '../../utils/errors';
-import type { Dependency, configDependencyType } from '../../types';
+import type { Dependency, ConfigDependencyType } from '../../types';
 import { DEPENDENCY_TYPE_FLAGS_MAP } from '../../constants';
 
 export type WorkspaceAddOptions = {
   cwd?: string,
   workspaceName: string,
   deps: Array<Dependency>,
-  type: configDependencyType
+  type: ConfigDependencyType
 };
 
 export function toWorkspaceAddOptions(
diff --git a/src/constants.js b/src/constants.js
index 6b6b0aa8..e8adb6b0 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -23,3 +23,13 @@ export const DEPENDENCY_TYPE_FLAGS_MAP = {
 };
 
 export const BOLT_VERSION = boltPkg.version;
+
+export const SEMVER_TYPES = [
+  'patch',
+  'minor',
+  'major',
+  'prerelease',
+  'prepatch',
+  'preminor',
+  'premajor'
+];
diff --git a/src/functions/getDependencyGraph.js b/src/functions/getDependencyGraph.js
index 72acce1c..49109924 100644
--- a/src/functions/getDependencyGraph.js
+++ b/src/functions/getDependencyGraph.js
@@ -1,34 +1,34 @@
 // @flow
 import Project from '../Project';
+import DependencyGraph from '../DependencyGraph';
 import * as yarn from '../utils/yarn';
 
 type Options = {
   cwd?: string
 };
 
-type DependencyGraph = Map<string, Array<string>>;
+type DepGraph = Map<string, Array<string>>;
 
 export default async function getDependencyGraph(
   opts: Options = {}
-): Promise<DependencyGraph> {
+): Promise<DepGraph> {
   let cwd = opts.cwd || process.cwd();
-  let project = await Project.init(cwd);
+  let project: Project = await Project.init(cwd);
   let workspaces = await project.getWorkspaces();
+  let graph = new DependencyGraph(project, workspaces);
 
-  let {
-    graph: dependencyGraph,
-    valid: graphIsValid
-  } = await project.getDependencyGraph(workspaces);
-
-  if (!graphIsValid) {
+  if (!graph.isValid()) {
     throw new Error('Dependency graph is not valid');
   }
 
   let simplifiedDependencyGraph = new Map();
 
-  dependencyGraph.forEach((pkgInfo, pkgName) => {
-    simplifiedDependencyGraph.set(pkgName, pkgInfo.dependencies);
-  });
+  for (let [workspace, { dependencies }] of graph.entries()) {
+    simplifiedDependencyGraph.set(
+      workspace.getName(),
+      Array.from(dependencies).map(dep => dep.getName())
+    );
+  }
 
   return simplifiedDependencyGraph;
 }
diff --git a/src/functions/getDependentsGraph.js b/src/functions/getDependentsGraph.js
index 643a1155..4e388bc4 100644
--- a/src/functions/getDependentsGraph.js
+++ b/src/functions/getDependentsGraph.js
@@ -1,34 +1,34 @@
 // @flow
 import Project from '../Project';
+import DependencyGraph from '../DependencyGraph';
 import * as yarn from '../utils/yarn';
 
 type Options = {
   cwd?: string
 };
 
-type DependentsGraph = Map<string, Array<string>>;
+type DepGraph = Map<string, Array<string>>;
 
 export default async function getDependentsGraph(
   opts: Options = {}
-): Promise<DependentsGraph> {
+): Promise<DepGraph> {
   let cwd = opts.cwd || process.cwd();
-  let project = await Project.init(cwd);
+  let project: Project = await Project.init(cwd);
   let workspaces = await project.getWorkspaces();
+  let graph = new DependencyGraph(project, workspaces);
 
-  let {
-    graph: dependentsGraph,
-    valid: graphIsValid
-  } = await project.getDependentsGraph(workspaces);
-
-  if (!graphIsValid) {
+  if (!graph.isValid()) {
     throw new Error('Dependents graph is not valid');
   }
 
   let simplifiedDependentsGraph = new Map();
 
-  dependentsGraph.forEach((pkgInfo, pkgName) => {
-    simplifiedDependentsGraph.set(pkgName, pkgInfo.dependents);
-  });
+  for (let [workspace, { dependents }] of graph.entries()) {
+    simplifiedDependentsGraph.set(
+      workspace.getName(),
+      Array.from(dependents).map(dep => dep.getName())
+    );
+  }
 
   return simplifiedDependentsGraph;
 }
diff --git a/src/functions/updatePackageVersions.js b/src/functions/updatePackageVersions.js
index 30021f43..b34fb3b5 100644
--- a/src/functions/updatePackageVersions.js
+++ b/src/functions/updatePackageVersions.js
@@ -3,6 +3,7 @@ import semver from 'semver';
 
 import Project from '../Project';
 import Workspace from '../Workspace';
+import DependencyGraph from '../DependencyGraph';
 import * as logger from '../utils/logger';
 import * as messages from '../utils/messages';
 import includes from 'array-includes';
@@ -41,14 +42,17 @@ export default async function updatePackageVersions(
   opts: Options = {}
 ): Promise<Array<string>> {
   let cwd = opts.cwd || process.cwd();
-  let project = await Project.init(cwd);
+  let project: Project = await Project.init(cwd);
   let workspaces = await project.getWorkspaces();
-  let { graph } = await project.getDependencyGraph(workspaces);
+  let graph = new DependencyGraph(project, workspaces);
   let editedPackages = new Set();
 
-  let internalDeps = Object.keys(updatedPackages).filter(dep => graph.has(dep));
+  let internalDeps = Object.keys(updatedPackages).filter(dep =>
+    graph.getDepsByName(dep)
+  );
+
   let externalDeps = Object.keys(updatedPackages).filter(
-    dep => !graph.has(dep)
+    dep => !graph.getDepsByName(dep)
   );
 
   if (externalDeps.length !== 0) {
diff --git a/src/functions/updateWorkspaceDependencies.js b/src/functions/updateWorkspaceDependencies.js
index b2882075..af1cd18b 100644
--- a/src/functions/updateWorkspaceDependencies.js
+++ b/src/functions/updateWorkspaceDependencies.js
@@ -1,5 +1,6 @@
 // @flow
 import Project from '../Project';
+import DependencyGraph from '../DependencyGraph';
 
 type VersionMap = {
   [x: string]: any
@@ -20,9 +21,8 @@ export default async function updateWorkspaceDependencies(
   opts: Options = {}
 ) {
   let cwd = opts.cwd || process.cwd();
-  let project = await Project.init(cwd);
+  let project: Project = await Project.init(cwd);
   let workspaces = await project.getWorkspaces();
-  let { graph } = await project.getDependencyGraph(workspaces);
   let editedPackages = new Set();
 
   // Note: all dependencyToUpgrade are external dependencies
diff --git a/src/types.js b/src/types.js
index 578208f4..c9f5a128 100644
--- a/src/types.js
+++ b/src/types.js
@@ -28,7 +28,7 @@ export type Dependency = {
   version?: string
 };
 
-export type configDependencyType =
+export type ConfigDependencyType =
   | 'dependencies'
   | 'devDependencies'
   | 'peerDependencies'
diff --git a/src/utils/__mocks__/processes.js b/src/utils/__mocks__/processes.js
new file mode 100644
index 00000000..f3a83082
--- /dev/null
+++ b/src/utils/__mocks__/processes.js
@@ -0,0 +1,49 @@
+const actualProcesses = require.requireActual('../processes');
+const processes = jest.genMockFromModule('../processes');
+const { ChildProcessError } = actualProcesses;
+
+function isArrayShape(actual, expected) {
+  return !expected.find((value, index) => {
+    return value !== actual[index];
+  });
+}
+
+function isObjectShape(actual, expected) {
+  return !Object.keys(expected).find(key => {
+    return expected[key] === actual[key];
+  });
+}
+
+const mocks = [];
+
+processes.spawn = jest.fn((cmd, args, opts) => {
+  let found = mocks.find(mock => {
+    if (cmd !== mock.cmd) return false;
+    if (!isArrayShape(args, mock.args)) return false;
+    if (!isObjectShape(opts, mock.opts)) return false;
+    return true;
+  });
+
+  let res = {
+    code: (found && found.code) || 0,
+    stdout: (found && found.stdout) || '',
+    stderr: (found && found.stderr) || ''
+  };
+
+  if (res.code === 0) {
+    return Promise.resolve(res);
+  } else {
+    return Project.reject(
+      new ChildProcessError(res.code, res.stdout, res.stderr)
+    );
+  }
+});
+
+processes.ChildProcessError = ChildProcessError;
+
+// __mock('echo', ['test'], {}, { code: 0, stdout: '', stderr: '' });
+processes.__mock = ({ cmd, args, opts }, { stdout, stderr, code }) => {
+  mocks.push({ cmd, args, opts, stdout, stderr, code });
+};
+
+module.exports = processes;
diff --git a/src/utils/__mocks__/spawn.js b/src/utils/__mocks__/spawn.js
deleted file mode 100644
index 06a49c50..00000000
--- a/src/utils/__mocks__/spawn.js
+++ /dev/null
@@ -1,38 +0,0 @@
-const actualSpawn = require.requireActual('../spawn');
-const spawn = jest.genMockFromModule('../spawn');
-
-function isArrayShape(actual, expected) {
-  return !expected.find((value, index) => {
-    return value !== actual[index];
-  });
-}
-
-function isObjectShape(actual, expected) {
-  return !Object.keys(expected).find(key => {
-    return expected[key] === actual[key];
-  });
-}
-
-const mocks = [];
-
-spawn.default = async function mockSpawn(cmd, args, opts) {
-  let found = mocks.find(mock => {
-    if (cmd !== mock.cmd) return false;
-    if (!isArrayShape(args, mock.args)) return false;
-    if (!isObjectShape(opts, mock.opts)) return false;
-    return true;
-  });
-
-  let result = { code: 0, stdout: '', stderr: '' };
-
-  if (!found) {
-    return Promise.resolve();
-  }
-};
-
-// __mock('echo', ['test'], {}, { code: 0, stdout: '', stderr: '' });
-spawn.__mock = ({cmd, args, opts}, {stdout, stderr, code}) => {
-  mocks.push({cmd, args, opts, stdout, stderr, code});
-};
-
-module.exports = spawn;
diff --git a/src/utils/__tests__/changes.test.js b/src/utils/__tests__/changes.test.js
index 86743cd8..a32d9594 100644
--- a/src/utils/__tests__/changes.test.js
+++ b/src/utils/__tests__/changes.test.js
@@ -41,8 +41,8 @@ describe('changes', () => {
       workspaces
     );
 
-    expect(versionCommits1.get(fooWorkspace)).toBe(null);
-    expect(versionCommits1.get(barWorkspace)).toBe(null);
+    expect(versionCommits1.get(fooWorkspace)).toBe(undefined);
+    expect(versionCommits1.get(barWorkspace)).toBe(undefined);
 
     await updateConfig(fooConfig, { version: '2.0.0' });
     await git.addAll({ cwd });
diff --git a/src/utils/__tests__/symlinkPackageDependencies.test.js b/src/utils/__tests__/symlinkPackageDependencies.test.js
index 8740a844..af8e4169 100644
--- a/src/utils/__tests__/symlinkPackageDependencies.test.js
+++ b/src/utils/__tests__/symlinkPackageDependencies.test.js
@@ -42,6 +42,12 @@ describe('utils/symlinkPackageDependencies()', () => {
     let tempDir = f.copy('nested-workspaces-with-root-dependencies-installed');
     project = await Project.init(tempDir);
     workspaces = await project.getWorkspaces();
+    // We use the foo package as it has internal and external dependencies
+    let workspaceToSymlink =
+      project.getWorkspaceByName(workspaces, 'foo') || {};
+    pkgToSymlink = workspaceToSymlink.pkg;
+    nodeModules = pkgToSymlink.nodeModules;
+    nodeModulesBin = pkgToSymlink.nodeModulesBin;
   });
 
   /********************
@@ -49,15 +55,6 @@ describe('utils/symlinkPackageDependencies()', () => {
   ********************/
 
   describe('linking packages', () => {
-    beforeEach(() => {
-      // We use the foo package as it has internal and external dependencies
-      let workspaceToSymlink =
-        project.getWorkspaceByName(workspaces, 'foo') || {};
-      pkgToSymlink = workspaceToSymlink.pkg;
-      nodeModules = pkgToSymlink.nodeModules;
-      nodeModulesBin = pkgToSymlink.nodeModulesBin;
-    });
-
     it('should create node modules and node_modules/.bin if not existing', async () => {
       expect(await dirExists(pkgToSymlink.nodeModules)).toEqual(false);
       expect(await dirExists(pkgToSymlink.nodeModulesBin)).toEqual(false);
@@ -141,13 +138,18 @@ describe('utils/symlinkPackageDependencies()', () => {
     });
   });
 
-  it('should symlink internal dependencies bin files (when declared using string)', async () => {
-    expect(await symlinkExists(nodeModulesBin, 'bar')).toEqual(false);
+  it.only(
+    'should symlink internal dependencies bin files (when declared using string)',
+    async () => {
+      expect(await symlinkExists(nodeModulesBin, 'bar')).toEqual(false);
 
-    await symlinkPackageDependencies(project, pkgToSymlink, ['bar']);
+      console.log(pkgToSymlink);
 
-    expect(await symlinkExists(nodeModulesBin, 'bar')).toEqual(true);
-  });
+      await symlinkPackageDependencies(project, pkgToSymlink, ['bar']);
+
+      expect(await symlinkExists(nodeModulesBin, 'bar')).toEqual(true);
+    }
+  );
 
   it('should symlink internal dependencies bin files (when declared using object)', async () => {
     expect(await symlinkExists(nodeModulesBin, 'baz-1')).toEqual(false);
diff --git a/src/utils/__tests__/versions.test.js b/src/utils/__tests__/versions.test.js
new file mode 100644
index 00000000..b859e7d3
--- /dev/null
+++ b/src/utils/__tests__/versions.test.js
@@ -0,0 +1,47 @@
+// @flow
+import * as versions from '../versions';
+
+const ONE_POINT_OH = versions.toVersion('1.0.0');
+const BETA_VERSION = versions.toVersion('2.0.0-beta.2');
+const ALPHA_VERSION = versions.toVersion('3.0.0-alpha.0');
+
+describe('versions', () => {
+  test('.toVersion()', () => {
+    expect(versions.toVersion('1.0.0')).toBe('1.0.0');
+    expect(() => versions.toVersion('nope')).toThrow();
+  });
+
+  test('.getPrereleaseType()', () => {
+    expect(versions.getPrereleaseType(ONE_POINT_OH)).toBe(null);
+    expect(versions.getPrereleaseType(BETA_VERSION)).toBe('beta');
+    expect(versions.getPrereleaseType(ALPHA_VERSION)).toBe('alpha');
+  });
+
+  test('.increment()', () => {
+    expect(versions.increment(ONE_POINT_OH, 'patch')).toBe('1.0.1');
+    expect(versions.increment(ONE_POINT_OH, 'minor')).toBe('1.1.0');
+    expect(versions.increment(ONE_POINT_OH, 'major')).toBe('2.0.0');
+    expect(versions.increment(ONE_POINT_OH, 'prepatch')).toBe('1.0.1-beta.0');
+    expect(versions.increment(ONE_POINT_OH, 'preminor')).toBe('1.1.0-beta.0');
+    expect(versions.increment(ONE_POINT_OH, 'premajor')).toBe('2.0.0-beta.0');
+    expect(versions.increment(ONE_POINT_OH, 'prerelease')).toBe('1.0.1-beta.0');
+
+    expect(versions.increment(BETA_VERSION, 'patch')).toBe('2.0.0');
+    expect(versions.increment(BETA_VERSION, 'minor')).toBe('2.0.0');
+    expect(versions.increment(BETA_VERSION, 'major')).toBe('2.0.0');
+    expect(versions.increment(BETA_VERSION, 'prepatch')).toBe('2.0.1-beta.0');
+    expect(versions.increment(BETA_VERSION, 'preminor')).toBe('2.1.0-beta.0');
+    expect(versions.increment(BETA_VERSION, 'premajor')).toBe('3.0.0-beta.0');
+    expect(versions.increment(BETA_VERSION, 'prerelease')).toBe('2.0.0-beta.3');
+
+    expect(versions.increment(ALPHA_VERSION, 'patch')).toBe('3.0.0');
+    expect(versions.increment(ALPHA_VERSION, 'minor')).toBe('3.0.0');
+    expect(versions.increment(ALPHA_VERSION, 'major')).toBe('3.0.0');
+    expect(versions.increment(ALPHA_VERSION, 'prepatch')).toBe('3.0.1-alpha.0');
+    expect(versions.increment(ALPHA_VERSION, 'preminor')).toBe('3.1.0-alpha.0');
+    expect(versions.increment(ALPHA_VERSION, 'premajor')).toBe('4.0.0-alpha.0');
+    expect(versions.increment(ALPHA_VERSION, 'prerelease')).toBe(
+      '3.0.0-alpha.1'
+    );
+  });
+});
diff --git a/src/utils/addDependenciesToPackages.js b/src/utils/addDependenciesToPackages.js
index 0930d1fd..be9e093f 100644
--- a/src/utils/addDependenciesToPackages.js
+++ b/src/utils/addDependenciesToPackages.js
@@ -3,9 +3,10 @@
 import semver from 'semver';
 
 import Project from '../Project';
-import type Workspace from '../Workspace';
-import type Package from '../Package';
-import type { Dependency, configDependencyType } from '../types';
+import Workspace from '../Workspace';
+import Package from '../Package';
+import DependencyGraph from '../DependencyGraph';
+import type { Dependency, ConfigDependencyType } from '../types';
 import * as messages from './messages';
 import { BoltError } from './errors';
 import * as logger from './logger';
@@ -16,16 +17,17 @@ export default async function addDependenciesToPackage(
   project: Project,
   pkg: Package,
   dependencies: Array<Dependency>,
-  type?: configDependencyType = 'dependencies'
+  type?: ConfigDependencyType = 'dependencies'
 ) {
   let workspaces = await project.getWorkspaces();
+  let graph = new DependencyGraph(project, workspaces);
+
   let projectDependencies = project.pkg.getAllDependencies();
   let pkgDependencies = pkg.getAllDependencies();
-  let { graph: depGraph } = await project.getDependencyGraph(workspaces);
 
   let dependencyNames = dependencies.map(dep => dep.name);
-  let externalDeps = dependencies.filter(dep => !depGraph.has(dep.name));
-  let internalDeps = dependencies.filter(dep => depGraph.has(dep.name));
+  let externalDeps = dependencies.filter(dep => !graph.has(dep.name));
+  let internalDeps = dependencies.filter(dep => graph.has(dep.name));
 
   let externalDepsToInstallForProject = externalDeps.filter(
     dep => !projectDependencies.has(dep.name)
@@ -64,8 +66,8 @@ export default async function addDependenciesToPackage(
   }
 
   for (let dep of internalDeps) {
-    let dependencyPkg = (depGraph.get(dep.name) || {}).pkg;
-    let internalVersion = dependencyPkg.config.getVersion();
+    let dependencyWorkspace = graph.getWorkspaceByName(dep.name);
+    let internalVersion = dependencyWorkspace.pkg.config.getVersion();
     // If no version is requested, default to caret at the current version
     let requestedVersion = dep.version || `^${internalVersion}`;
     if (!semver.satisfies(internalVersion, requestedVersion)) {
diff --git a/src/utils/changes.js b/src/utils/changes.js
index 5adaa171..33a4def3 100644
--- a/src/utils/changes.js
+++ b/src/utils/changes.js
@@ -6,7 +6,7 @@ import * as git from '../utils/git';
 async function getLastVersionCommitForWorkspace(
   repo: Repository,
   workspace: Workspace
-) {
+): Promise<git.Commit | null> {
   let cwd = repo.dir;
   let filePath = workspace.pkg.config.filePath;
   let commits = await git.getCommitsToFile(filePath, { cwd });
@@ -16,9 +16,7 @@ async function getLastVersionCommitForWorkspace(
     let parentCommit = await git.getCommitParent(commit.hash, { cwd });
     if (!parentCommit) continue;
 
-    let before = await git.showFileAtCommit(filePath, parentCommit, {
-      cwd
-    });
+    let before = await git.showFileAtCommit(filePath, parentCommit, { cwd });
     let after = await git.showFileAtCommit(filePath, commit.hash, { cwd });
 
     let jsonBefore = JSON.parse(before);
@@ -36,12 +34,12 @@ async function getLastVersionCommitForWorkspace(
 export async function getWorkspaceVersionCommits(
   repo: Repository,
   workspaces: Array<Workspace>
-) {
+): Promise<Map<Workspace, git.Commit>> {
   let versionCommits = new Map();
 
   for (let workspace of workspaces) {
     let commit = await getLastVersionCommitForWorkspace(repo, workspace);
-    versionCommits.set(workspace, commit);
+    if (commit) versionCommits.set(workspace, commit);
   }
 
   return versionCommits;
diff --git a/src/utils/git.js b/src/utils/git.js
index 1aef5c43..d9296da5 100644
--- a/src/utils/git.js
+++ b/src/utils/git.js
@@ -18,7 +18,7 @@ const gitCommandLimit = pLimit(1);
 const GIT_LOG_LINE_FORMAT_FLAG = '--pretty=format:%H %s';
 const GIT_LOG_LINE_FORMAT_SPLITTER = /^([a-zA-Z0-9]+) (.*)/;
 
-opaque type CommitHash = string;
+export opaque type CommitHash = string;
 
 export type Commit = {
   hash: CommitHash,
@@ -188,16 +188,22 @@ export async function showFileAtCommit(
 export const MAGIC_EMPTY_STATE_HASH: CommitHash =
   '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
 
+export const MAGIC_EMPTY_STATE_COMMIT: Commit = {
+  hash: MAGIC_EMPTY_STATE_HASH,
+  message: '(empty state)'
+};
+
 export async function getDiffForPathSinceCommit(
   filePath: string,
   commitHash: CommitHash,
-  opts: { cwd: string }
+  opts: { cwd: string, tty?: boolean }
 ) {
   let gitPath = toGitPath(opts.cwd, filePath);
   let { stdout } = await git(
     ['diff', commitHash, '--color=always', '--', filePath],
     {
-      cwd: opts.cwd
+      cwd: opts.cwd,
+      tty: opts.tty
     }
   );
   return stdout.trim();
diff --git a/src/utils/messages.js b/src/utils/messages.js
index 6d6331f3..fac68af4 100644
--- a/src/utils/messages.js
+++ b/src/utils/messages.js
@@ -362,3 +362,48 @@ export function errorWorkspacesUpgrade(filterOpts: Array<string>): Message {
 export function noNeedToSymlinkInternalDependency(): Message {
   return `Internal packages are symlinked, there is no need update them`;
 }
+
+export function diff(): Message {
+  return 'Diff';
+}
+
+export function skip(): Message {
+  return 'Skip (no new version)';
+}
+
+export function patch(nextVersion: string): Message {
+  return `Patch (${nextVersion})`;
+}
+
+export function minor(nextVersion: string): Message {
+  return `Minor (${nextVersion})`;
+}
+
+export function major(nextVersion: string): Message {
+  return `Major (${nextVersion})`;
+}
+
+export function prerelease(nextVersion: string): Message {
+  return `Prerelease (${nextVersion})`;
+}
+
+export function prepatch(nextVersion: string): Message {
+  return `Prepatch (${nextVersion})`;
+}
+
+export function preminor(nextVersion: string): Message {
+  return `Preminor (${nextVersion})`;
+}
+
+export function premajor(nextVersion: string): Message {
+  return `Premajor (${nextVersion})`;
+}
+
+export function selectVersion(
+  pkgName: string,
+  currentVersion: string
+): Message {
+  return `Select a new version for ${normalPkg(
+    pkgName
+  )} (currently ${currentVersion}, depends on changed package(s) "foo", "bar", "baz", and 3 others)`;
+}
diff --git a/src/utils/processes.js b/src/utils/processes.js
index 611e9a45..c713c4a8 100644
--- a/src/utils/processes.js
+++ b/src/utils/processes.js
@@ -46,7 +46,7 @@ export function spawn(
   cmd: string,
   args: Array<string>,
   opts: SpawnOptions = {}
-) {
+): Promise<{ code: number, stdout: string, stderr: string }> {
   return limit(
     () =>
       new Promise((resolve, reject) => {
diff --git a/src/utils/prompt.js b/src/utils/prompt.js
new file mode 100644
index 00000000..37a59e02
--- /dev/null
+++ b/src/utils/prompt.js
@@ -0,0 +1,64 @@
+// @flow
+import inquirer from 'inquirer';
+import * as messages from './messages';
+
+const prompt = inquirer.createPromptModule();
+const KEY = 'value';
+
+type Choice<Value> = { name: messages.Message, value: Value };
+type Choices<Value> = Array<Choice<Value> | Separator>;
+export opaque type Separator = Object;
+
+export function separator(): Separator {
+  return new inquirer.Separator();
+}
+
+export async function list<Value: string>(
+  message: messages.Message,
+  choices: Choices<Value>,
+  opts: { default?: Value } = {}
+): Promise<Value> {
+  let answers = await prompt([
+    {
+      type: 'list',
+      name: KEY,
+      message,
+      choices,
+      default: opts.default
+    }
+  ]);
+  return answers[KEY];
+}
+
+export async function input(message: messages.Message): Promise<string> {
+  let answers = await prompt([
+    {
+      type: 'input',
+      name: KEY,
+      message
+    }
+  ]);
+  return answers[KEY];
+}
+
+export async function password(message: messages.Message): Promise<string> {
+  let answers = await prompt([
+    {
+      type: 'password',
+      name: KEY,
+      message
+    }
+  ]);
+  return answers[KEY];
+}
+
+export async function editor(message: messages.Message): Promise<string> {
+  let answers = await prompt([
+    {
+      type: 'editor',
+      name: KEY,
+      message
+    }
+  ]);
+  return answers[KEY];
+}
diff --git a/src/utils/symlinkPackageDependencies.js b/src/utils/symlinkPackageDependencies.js
index 9c7c6e87..90786614 100644
--- a/src/utils/symlinkPackageDependencies.js
+++ b/src/utils/symlinkPackageDependencies.js
@@ -4,8 +4,9 @@ import pathIsInside from 'path-is-inside';
 import includes from 'array-includes';
 
 import Project from '../Project';
-import type Workspace from '../Workspace';
-import type Package from '../Package';
+import DependencyGraph from '../DependencyGraph';
+import Workspace from '../Workspace';
+import Package from '../Package';
 import { BoltError } from './errors';
 import * as fs from './fs';
 import * as logger from './logger';
@@ -20,13 +21,10 @@ export default async function symlinkPackageDependencies(
   let projectDeps = project.pkg.getAllDependencies();
   let pkgDependencies = project.pkg.getAllDependencies();
   let workspaces = await project.getWorkspaces();
-  let {
-    graph: dependencyGraph,
-    valid: dependencyGraphValid
-  } = await project.getDependencyGraph(workspaces);
+  let depGraph = new DependencyGraph(project, workspaces);
   let pkgName = pkg.config.getName();
   // get all the dependencies that are internal workspaces in this project
-  let internalDeps = (dependencyGraph.get(pkgName) || {}).dependencies || [];
+  let internalDeps = (depGraph.getDepsByName(pkgName) || {}).dependencies || [];
 
   let directoriesToCreate = [];
   let symlinksToCreate = [];
@@ -44,7 +42,7 @@ export default async function symlinkPackageDependencies(
     let versionInPkg = pkg.getDependencyVersionRange(depName);
 
     // If dependency is internal we can ignore it (we symlink below)
-    if (dependencyGraph.has(depName)) {
+    if (!!depGraph.getDepsByName(depName)) {
       continue;
     }
 
@@ -91,14 +89,14 @@ export default async function symlinkPackageDependencies(
   **********************************************************************/
 
   for (let dependency of internalDeps) {
-    let depWorkspace = dependencyGraph.get(dependency) || {};
-    let src = depWorkspace.pkg.dir;
-    let dest = path.join(pkg.nodeModules, dependency);
+    let depWorkspace = depGraph.getDepsByWorkspace(dependency) || {};
+    let src = dependency.pkg.dir;
+    let dest = path.join(pkg.nodeModules, dependency.pkg.config.getName());
 
     symlinksToCreate.push({ src, dest, type: 'junction' });
   }
 
-  if (!dependencyGraphValid || !valid) {
+  if (!depGraph.isValid() || !valid) {
     throw new BoltError('Cannot symlink invalid set of dependencies.');
   }
 
@@ -163,15 +161,9 @@ export default async function symlinkPackageDependencies(
   // TODO: Same as above, we should really be making sure we get all the transitive bins as well
 
   for (let dependency of internalDeps) {
-    let depWorkspace = dependencyGraph.get(dependency) || {};
-    let depBinFiles =
-      depWorkspace.pkg &&
-      depWorkspace.pkg.config &&
-      depWorkspace.pkg.config.getBin();
-
-    if (!depBinFiles) {
-      continue;
-    }
+    let depWorkspace = depGraph.getDepsByWorkspace(dependency) || {};
+    let depBinFiles = dependency.pkg.config.getBin();
+    if (!depBinFiles) continue;
 
     if (!includes(dependencies, dependency)) {
       // dependency is not one we are supposed to symlink right now
@@ -180,8 +172,11 @@ export default async function symlinkPackageDependencies(
 
     if (typeof depBinFiles === 'string') {
       // package may be scoped, name will only be the second part
-      let binName = dependency.split('/').pop();
-      let src = path.join(depWorkspace.pkg.dir, depBinFiles);
+      let binName = dependency.pkg.config
+        .getName()
+        .split('/')
+        .pop();
+      let src = path.join(dependency.pkg.dir, depBinFiles);
       let dest = path.join(pkg.nodeModulesBin, binName);
 
       symlinksToCreate.push({ src, dest, type: 'exec' });
@@ -189,7 +184,7 @@ export default async function symlinkPackageDependencies(
     }
 
     for (let [binName, binPath] of Object.entries(depBinFiles)) {
-      let src = path.join(depWorkspace.pkg.dir, String(binPath));
+      let src = path.join(dependency.pkg.dir, String(binPath));
       let dest = path.join(pkg.nodeModulesBin, binName);
 
       symlinksToCreate.push({ src, dest, type: 'exec' });
diff --git a/src/utils/upgradeDependenciesInPackages.js b/src/utils/upgradeDependenciesInPackages.js
index 7fce82cf..659ed076 100644
--- a/src/utils/upgradeDependenciesInPackages.js
+++ b/src/utils/upgradeDependenciesInPackages.js
@@ -1,8 +1,9 @@
 // @flow
 
 import Project from '../Project';
-import type Workspace from '../Workspace';
-import type Package from '../Package';
+import Workspace from '../Workspace';
+import Package from '../Package';
+import DependencyGraph from '../DependencyGraph';
 import type { Dependency } from '../types';
 import * as messages from './messages';
 import { BoltError } from './errors';
@@ -19,10 +20,20 @@ export default async function upgradeDependenciesInPackage(
 ) {
   let workspaces = await project.getWorkspaces();
   let pkgDependencies = pkg.getAllDependencies();
-  let { graph: depGraph } = await project.getDependencyGraph(workspaces);
+  let depGraph = new DependencyGraph(project, workspaces);
+
+  let workspaceDependencies = dependencies.map(dep => {
+    return project.getWorkspaceByName(workspaces, dep.name);
+  });
+
+  let externalDeps = dependencies.filter(
+    dep => !depGraph.getDepsByName(dep.name)
+  );
+
+  let internalDeps = dependencies.filter(dep =>
+    depGraph.getDepsByName(dep.name)
+  );
 
-  let externalDeps = dependencies.filter(dep => !depGraph.has(dep.name));
-  let internalDeps = dependencies.filter(dep => depGraph.has(dep.name));
   let projectDependencies = project.pkg.getAllDependencies();
 
   if (project.pkg.isSamePackage(pkg)) {
diff --git a/src/utils/validateProject.js b/src/utils/validateProject.js
index 30b8786e..b7ea5b22 100644
--- a/src/utils/validateProject.js
+++ b/src/utils/validateProject.js
@@ -13,7 +13,7 @@ export default async function validateProject(project: Project) {
   let workspaces = await project.getWorkspaces();
   let projectDependencies = project.pkg.getAllDependencies();
   let projectConfig = project.pkg.config;
-  let { graph: depGraph } = await project.getDependencyGraph(workspaces);
+  // let { graph: depGraph } = await project.getDepGraph(workspaces);
 
   let projectIsValid = true;
 
diff --git a/src/utils/versions.js b/src/utils/versions.js
new file mode 100644
index 00000000..a7b982c7
--- /dev/null
+++ b/src/utils/versions.js
@@ -0,0 +1,62 @@
+// @flow
+import * as semver from 'semver';
+import * as messages from './messages';
+
+export opaque type Version = string;
+
+export type IncrementType =
+  | 'patch'
+  | 'minor'
+  | 'major'
+  | 'prepatch'
+  | 'preminor'
+  | 'premajor'
+  | 'prerelease';
+
+export const VERSION_TYPES = [
+  'patch',
+  'minor',
+  'major',
+  'prerelease',
+  'prepatch',
+  'preminor',
+  'premajor'
+];
+
+export function toVersion(version: string): Version {
+  if (semver.valid(version)) {
+    return version;
+  } else {
+    throw new Error(
+      `Invalid semver version: ${version} (See https://github.com/npm/node-semver)`
+    );
+  }
+}
+
+export function getPrereleaseType(version: Version): string | null {
+  let parts = semver.prerelease(version);
+  if (parts) return parts[0];
+  return null;
+}
+
+export function increment(version: Version, type: IncrementType) {
+  let prereleaseType = getPrereleaseType(version) || 'beta';
+  return semver.inc(version, type, prereleaseType);
+}
+
+const MESSSAGES = {
+  patch: ver => messages.patch(increment(ver, 'patch')),
+  minor: ver => messages.minor(increment(ver, 'minor')),
+  major: ver => messages.major(increment(ver, 'major')),
+  prepatch: ver => messages.prepatch(increment(ver, 'prepatch')),
+  preminor: ver => messages.preminor(increment(ver, 'preminor')),
+  premajor: ver => messages.premajor(increment(ver, 'premajor')),
+  prerelease: ver => messages.prerelease(increment(ver, 'prerelease'))
+};
+
+export function getIncrementMessage(
+  currentVersion: Version,
+  type: IncrementType
+): messages.Message {
+  return MESSSAGES[type](currentVersion);
+}
diff --git a/src/utils/yarn.js b/src/utils/yarn.js
index fdadee18..b8e2c97c 100644
--- a/src/utils/yarn.js
+++ b/src/utils/yarn.js
@@ -2,7 +2,7 @@
 import includes from 'array-includes';
 import projectBinPath from 'project-bin-path';
 import * as path from 'path';
-import type { Dependency, configDependencyType } from '../types';
+import type { Dependency, ConfigDependencyType } from '../types';
 import type Package from '../Package';
 import Project from '../Project';
 import * as processes from './processes';
@@ -25,7 +25,7 @@ function depTypeToFlag(depType) {
 export async function add(
   pkg: Package,
   dependencies: Array<Dependency>,
-  type?: configDependencyType
+  type?: ConfigDependencyType
 ) {
   let localYarn = path.join(await getLocalBinPath(), 'yarn');
   let spawnArgs = ['add'];
diff --git a/yarn.lock b/yarn.lock
index 68aaf471..415667b4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1211,6 +1211,10 @@ detect-indent@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
 
+diff@^3.1.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c"
+
 diff@^3.2.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.0.tgz#056695150d7aa93237ca7e378ac3b1682b7963b9"
@@ -1352,7 +1356,7 @@ extend@~3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
 
-external-editor@^2.0.4:
+external-editor@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
   dependencies:
@@ -1442,9 +1446,9 @@ fixturez@^1.1.0:
     signal-exit "^3.0.2"
     tempy "^0.2.1"
 
-flow-bin@^0.63.1:
-  version "0.63.1"
-  resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.63.1.tgz#ab00067c197169a5fb5b4996c8f6927b06694828"
+flow-bin@^0.66.0:
+  version "0.66.0"
+  resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.66.0.tgz#a96dde7015dc3343fd552a7b4963c02be705ca26"
 
 for-in@^1.0.1:
   version "1.0.2"
@@ -1472,6 +1476,12 @@ form-data@~2.1.1:
     combined-stream "^1.0.5"
     mime-types "^2.1.12"
 
+formatio@1.2.0, formatio@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb"
+  dependencies:
+    samsam "1.x"
+
 fs-extra@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd"
@@ -1736,15 +1746,15 @@ ini@~1.3.0:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
 
-inquirer@3.3.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
+inquirer@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-4.0.2.tgz#cc678b4cbc0e183a3500cc63395831ec956ab0a3"
   dependencies:
     ansi-escapes "^3.0.0"
     chalk "^2.0.0"
     cli-cursor "^2.1.0"
     cli-width "^2.0.0"
-    external-editor "^2.0.4"
+    external-editor "^2.1.0"
     figures "^2.0.0"
     lodash "^4.3.0"
     mute-stream "0.0.7"
@@ -1913,6 +1923,10 @@ is-utf8@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
 
+isarray@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
 isarray@1.0.0, isarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -2336,6 +2350,10 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.3.6"
 
+just-extend@^1.1.26:
+  version "1.1.27"
+  resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905"
+
 kind-of@^3.0.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -2470,6 +2488,10 @@ locate-path@^2.0.0:
     p-locate "^2.0.0"
     path-exists "^3.0.0"
 
+lodash.get@^4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+
 lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -2493,6 +2515,14 @@ log-update@^1.0.2:
     ansi-escapes "^1.0.0"
     cli-cursor "^1.0.2"
 
+lolex@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6"
+
+lolex@^2.2.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.1.tgz#3d2319894471ea0950ef64692ead2a5318cff362"
+
 longest@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
@@ -2647,6 +2677,16 @@ natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
 
+nise@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/nise/-/nise-1.2.0.tgz#079d6cadbbcb12ba30e38f1c999f36ad4d6baa53"
+  dependencies:
+    formatio "^1.2.0"
+    just-extend "^1.1.26"
+    lolex "^1.6.0"
+    path-to-regexp "^1.7.0"
+    text-encoding "^0.6.4"
+
 node-int64@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -2898,6 +2938,12 @@ path-parse@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
 
+path-to-regexp@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+  dependencies:
+    isarray "0.0.1"
+
 path-type@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -3264,6 +3310,10 @@ safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
 
+samsam@1.x:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50"
+
 sane@~1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775"
@@ -3319,6 +3369,18 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
+sinon@^4.1.4:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.4.tgz#36bb237bae38ddf9cc92dcc1b16c51e7785bbc9c"
+  dependencies:
+    diff "^3.1.0"
+    formatio "1.2.0"
+    lodash.get "^4.4.2"
+    lolex "^2.2.0"
+    nise "^1.2.0"
+    supports-color "^4.4.0"
+    type-detect "^4.0.5"
+
 slash@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -3503,6 +3565,12 @@ supports-color@^4.0.0:
   dependencies:
     has-flag "^2.0.0"
 
+supports-color@^4.4.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
+  dependencies:
+    has-flag "^2.0.0"
+
 symbol-observable@^1.0.1:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
@@ -3533,8 +3601,8 @@ tar@^2.2.1:
     inherits "2"
 
 task-graph-runner@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/task-graph-runner/-/task-graph-runner-1.0.1.tgz#321eb31f06b915dd9504e369187d11c14ddc6b26"
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/task-graph-runner/-/task-graph-runner-1.0.2.tgz#dfc73e4f92d74b974a854ccd3b2a8c7f1fbf4137"
   dependencies:
     array-includes "^3.0.3"
 
@@ -3570,6 +3638,10 @@ test-exclude@^4.1.1:
     read-pkg-up "^1.0.1"
     require-main-filename "^1.0.1"
 
+text-encoding@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
+
 throat@^3.0.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836"
@@ -3632,6 +3704,10 @@ type-check@~0.3.2:
   dependencies:
     prelude-ls "~1.1.2"
 
+type-detect@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.5.tgz#d70e5bc81db6de2a381bcaca0c6e0cbdc7635de2"
+
 typeable-promisify@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/typeable-promisify/-/typeable-promisify-2.0.1.tgz#1baee82abaf13280198eb11e98589c881a6bd80d"