watch (root, onChange) {

    let cwd = process.cwd();

    function watchDir (dirname, watchers) {

      if (!watchers) {

        watchers = Object.create(null);
        watchers.directories = Object.create(null);
        watchers.interval = null;

      }

      let pathname = path.join(cwd, dirname);
      let files = fs.readdirSync(pathname);

      watchers.directories[dirname] = Object.create(null);

      files.forEach(function (name) {

        if (name === 'node_modules' || name.indexOf('.') === 0) {
          return;
        }

        let filename = path.join(dirname, name);
        let fullPath = path.join(cwd, filename);

        let stat = fs.statSync(fullPath);

        if (stat.isDirectory()) {
          watchDir(filename, watchers);
          return;
        }

        watchers.directories[dirname][name] = stat;

      });

      return watchers;

    }

    let watchers = watchDir(root || '');
    let self = this;

    watchers.iterate = function (changes) {
      if (!fs.existsSync(path.join(process.cwd(), '.daemon.pause'))) {
        // Skip a cycle if just unpaused...
        if (!this._paused) {
          if (changes.length) {
            onChange.call(self, changes);
          }
        } else {
          this._paused = false;
        }
      } else {
        this._paused = true;
      }
    };

    watchers.interval = setInterval(function() {

      let changes = [];

      Object.keys(watchers.directories).forEach(function (dirname) {

        let dir = watchers.directories[dirname];
        let dirPath = path.join(cwd, dirname);

        if (!fs.existsSync(dirPath)) {

          delete watchers.directories[dirname];
          changes.push({event: 'removed', path: dirPath});

        } else {

          let files = fs.readdirSync(dirPath);
          let added = [];

          let contents = Object.create(null);

          files.forEach(function (filename) {

            if (filename === 'node_modules' || filename.indexOf('.') === 0) {
              return;
            }

            let fullPath = path.join(dirPath, filename);
            let stat = fs.statSync(fullPath);

            if (stat.isDirectory()) {
              let checkPath = path.join(dirname, filename);
              if (!watchers.directories[checkPath]) {
                watchDir(checkPath, watchers);
              }
            } else {
              if (!dir[filename]) {
                added.push([filename, stat]);
                changes.push({event: 'added', path: fullPath});
                return;
              }

              if (stat.mtime.toString() !== dir[filename].mtime.toString()) {
                dir[filename] = stat;
                changes.push({event: 'modified', path: fullPath});
              }

              contents[filename] = true;
            }

          });

          Object.keys(dir).forEach(function (filename) {

            let fullPath = path.join(cwd, dirname, filename);

            if (!contents[filename]) {
              delete dir[filename];
              changes.push({event: 'removed', path: fullPath});
            }

          });

          added.forEach(function (change) {
            let [filename, stat] = change;
            dir[filename] = stat;
          });

        }

      });

      watchers.iterate(changes);

    }, 1000);

    return this._watchers = watchers;

  }
Watches a directory tree for changes
@param {string} root Directory tree to watch
@param {function} onChange Method to be executed when a change is detected