docs: function RootContext(argv, profileFilenames, view) {
    var self = {
        commands: COMMANDS, // Overwritten when interactive
        profileFilenames: profileFilenames,
        interactive: false,
        argv: argv,
        view: view,
        config: null,
        nick: null,
        privkey: null,
        hubClient: null,
        voxClient: null,
    };
  
    self.initWithConfig = function(config) {
      if (self.voxClient) {
        throw new Error('Already initialized!');
      }
  
      self.config = config;
      self.nick = config.nick;
  
      self.voxClient = new VoxClient({
          config: config,
          agentString: AGENT_STRING,
      });
      return self.voxClient.connect();
    }
  
    self.reinitWithConfig = function(config) {
      self.close();
      return self.initWithConfig(config);
    }
  
    self.close = function() {
      if (self.voxClient) {
        self.voxClient.close();
        self.voxClient = null;
      }
    }
  
    /**
     * Logs connection status events.
     */
    self.listenForConnectionStatusEvents = function() {
      self.voxClient.on('connect', function(info) {
        self.view.log(colors.cyan.dim('Connected: ' + info.interchangeUrl));
      });
      self.voxClient.on('disconnect', function(info) {
        self.view.log(colors.red.dim('Disconnected: ' + info.interchangeUrl));
      });
      self.voxClient.on('error', function(info) {
        self.view.log(colors.red.dim('Connection error: ' + info.interchangeUrl));
      });
      self.voxClient.on('reconnect_failed', function(info) {
        self.view.log(colors.red.dim('Reconnection failed: ' + info.interchangeUrl));
      });
    }
  
    self.printJson = function(obj_or_name, obj) {
      if (obj !== undefined) {
        self.view.log('%s %s', obj_or_name, JSON.stringify(obj));
      } else {
        self.view.log(JSON.stringify(obj_or_name));
      }
    }
  
    return self;
  }