Repository: yargs/yargs
Pull request #975 (2017-10-14 22:53:04)

feat: .usage() can now be used to configure a default command
This pull allows you to configure the default command (your application's entry point) in the `.usage()` method -- combining the usage and command APIs in a way that I feel is more intuitive.

```js
const argv = require('./')
  .usage('$0 <port> [batman]', 'start the server', (yargs) => {
    yargs.positional('port', {
      describe: 'provide a port for your application',
      type: 'number'
    })
  }).argv
```

Along with introducing this standardization, there have been a few cosmetic changes in an effort to make default commands (and commands in general) more intuitive:

* we now print the positional options accepted by the default command in the top-level help output.
* we now display information about the default command in help output, regardless of whether it has aliases.
* we now default to displaying usage instructions for the entry-point to your application, which demonstrate how to invoke commands.

BREAKING CHANGE: .usage() no longer accepts an options object as the second argument

would love feedback, CC: @matatk, @jeffijoe

bcoe: CC: @laggingreflex 
bcoe: CC: @iarna, @zkat, I think the command/positional argument overhaul is getting pretty close to where I was thinking it should be; would love your feedback.
matatk: I haven't tried this code (yet), though in API it seems to match [the example you gave yesterday](https://github.com/yargs/yargs/pull/967#issuecomment-336647682), without the requirement to include the word 'command'. I think that, for new users, who are just wanting to make a simple single command that accepts positional arguments, this looks pretty-much ideal :-).
lostintangent: Yep, this looks like exactly what I’d love for one of my single command CLIs :)
bcoe: @lostintangent @matatk if you want to take it for a spin, and give me some feedback before it goes live:

`npm i yargs@next`
lostintangent: @bcoe Just pulled down the `next` bits, and it works great! Thanks so much. That said, I had been previously using the `usage` command to override the "generic" CLI-level description text (e.g. `kyte [filePath] [options]`), and now that I'm using `usage` to define my default command (which requires the `$0` syntax, I notice the following help text output:

<img width="586" alt="screen shot 2017-10-15 at 1 42 25 pm" src="https://user-images.githubusercontent.com/116461/31589008-04238874-b1af-11e7-9d2b-2443e734bcf4.png">

This is probably a dumb question, but it there a way to replace the highlighted text with `kyte` (the name of my CLI in this case? 


lostintangent: Also, I noticed that if I define some additional global settings (e.g. aliases for the `help` arg), within the command builder (as opposed to chaining them on the result of calling `usage`, then the help output is different (e.g. the `Commands` section is no longer displayed.

For optimizing the syntax for single-command CLIs, it would be awesome to be able to write the following:

```javascript
const { filePath } = require("yargs")
  .usage(
    "$0 [filePath] [options]",
    "Start a new co-editing editing session",
    yargs => {
      yargs
        .positional("filePath", {
          describe: "The file path to begin collaborating on"
        })
        .alias({ h: "help", v: "version" })
        .strict()
        .fail(exit);
    }
  )
  .parse();
```

Functionally, this seems to actually work beautifully. It's just that the help output isn't the same as when writing it this way:

```javascript
const { filePath } = require("yargs")
  .usage(
    "$0 [filePath] [options]",
    "Start a new co-editing editing session",
    yargs => {
      yargs.positional("filePath", {
        describe: "The file path to begin collaborating on"
      });
    }
  )
  .fail(exit)
  .strict()
  .alias({ h: "help", v: "version" });
```
lostintangent: @bcoe I noticed that the file path behavior is completely unrelated to this PR, though I wasn't experiencing it previously since my usage of `usage` included explicitly setting the displayed string (e.g. `kyte [filePath] [options]`), whereas this PR requires the use of the `$0` prefix.

I opened up #983 as one potential "solution" to allowing the use of `$0` in `usage`, without sacrificing the "cleanliness" of your help text displaying the CLI's name instead of the file path. That may or may not be the right way to address this scenario though, so I just wanted to convey my scenario and open the discussion. Thanks so much!
bcoe: _regarding $0 behavior:_

@lostintangent my home was that the string literal `$0` might be a good indicator to people that _this is the behavior that will run if you run the application with no commands_. Have any thoughts about what might be more intuitive? I don't want to replace it with the bin name, because this would read as though there's a command of this name.

_regarding difference in output top-level vs. nested configuration:_

Testing, I think the problem might just be that you're missing `.argv` or `.parse()` on the second example?

```js
const { filePath } = require("./")
  .usage(
    "$0 [filePath] [options]",
    "Start a new co-editing editing session",
    yargs => {
      yargs.positional("filePath", {
        describe: "The file path to begin collaborating on"
      });
    }
  )
  .fail(exit)
  .strict()
  .alias({ h: "help", v: "version" })
  .parse()
```

I seem to have the same output (mind confirming?).


lostintangent: @bcoe Yeah that's a good point regarding displaying `$0` in the `Commands:` section. My initial thinking was that it would be more intuitive to display the bin name instead, but I also agree that might be strange. I'm just not sure how immediately obvious it would be to end-users when they saw `$0` displayed in the help text. I'll give it some more thought, but in the meantime, the current behavior seems cool to me.

Regarding the other issue, I actually was calling `parse`. What do you see if you run this?

```javascript
const { filePath } = require("./")
  .usage(
    "$0 [filePath] [options]",
    "Start a new co-editing editing session",
    yargs => {
      yargs.positional("filePath", {
        describe: "The file path to begin collaborating on"
      })
     .fail(exit)
     .strict()
     .alias({ h: "help", v: "version" })
    }
  )
  .parse()
```

In my case, the `Commands:` section disappears, and the value of `$0` is displayed twice in the "usage" text at the top of the help output. Functionally, everything else works as expected though.
iarna: So, if I were a consumer of a binary written with yargs and I saw `$0` I would assume it was a bug. Giving the full path to the script isn't great either ideally one would give the command they typed (but I'm not sure that's actually possible in Node.js)
bcoe: @iarna @lostintangent does it feel less weird to replace it with `.` -- I'd like to allow people to provide a description for the default command that runs, even when there's no alias provided ... I hear that `$0` feels a bit weird:

```bash
Commands:
  . [filePath] [options] start a new co-editing session/
```
bcoe: @lostintangent regarding the behavior around `.alias()`, I see what's happening now. Because there's no top-level `-h` defined, help isn't fired until it reaches the context of the default command. _so_, you're seeing the help for the default command itself, rather than the top-level help which shows a listing of all the available commands, you're seeing this?

```bash
test.js test.js [filePath] [options]

Positionals:
  filePath  The file path to begin collaborating on

Options:
  -h, --help     Show help                                             [boolean]
  -v, --version  Show version number                                   [boolean]
```

rather than this?

```js
test.js [filePath] [options]

Commands:
  $0 [filePath] [options]  Start a new co-editing editing session      [default]

Positionals:
  filePath  The file path to begin collaborating on

Options:
  -h, --help     Show help                                             [boolean]
  -v, --version  Show version number                                   [boolean]
```

I don't think this behavior is a bug per-se (or it's at least expected). Basically, we needed to make some usability decisions around how to display help for all commands, vs. help for a default command ($0). It's hard to get this perfect but it's my hope we've reached an okay compromise with the behavior:

* if help is configured on the top-level yargs builder:
 * the positional and option arguments for the default command ($0) will be shown.
 * all other available commands will also be shown.
* if help is run for a specific command (a command other than $0):
  * the positional and option arguments will be shown.
  * any nested commands that are available will also be shown.

I wonder, is there anywhere in the docs we could call this out?
lostintangent: @bcoe Ah OK, the behavior makes sense now that I think about it. Thanks for explaining that! I was a little confused initially since I have a CLI that is literally just a single command. Therefore, I wasn’t thinking of there being a difference between the top-level help and the command-level help. In fact, in the single-command CLI, maybe the “buggy” behavior is in fact the expected behavior, since you don’t neccesssarily need the top-level help (which then coincidentally gets around the “$0” conversation as well).

Maybe we could just add a quick mention to the new “usage” method docs, that clarifies that the builder function is configuring the default command’s help output, not the top-level CLI’s help output? That would have been helpful in my case. Additionally, there may be some bugs in the default command’s help out, such as the script/bin name being displayed twice in the usage text (see screenshot above)?

Regarding the “.” vs. “$0” display: I definitely think “.” is less strange looking, but I’m still not 100% sure. Is there any prior art we could take inspiration from here? When I look at Now CLI, their help output doesn’t display a special token for the default command since they also alias it as “deploy” and can just annotate it as also being the default (that’s nice and clean). Additionally, Zeit’s serve CLI doesn’t display the default command in its help output at all, which is closer to the command-level help output of Yargs right now (e.g. just showing positional and options).
bcoe: @lostintangent if `$0` was aliased to `sever` you would get the same output you described:

`server          run the server [default]`

I'm wondering if perhaps:

1. we should switch to the `.` from `$0`, if more than one command is configured.
2. if only one command is configured, and it's default, always show help for that command specifically rather than help for the top-level (i.e., don't show help for the top-level unless more than one command is configured).

How are you linking the `kyte` bin when you're seeing `$0` printed twice? are you running `npm i -g` in the kyte folder? would definitely like to get this bug fixed.
lostintangent: I like that proposal! For #1, would the “.” even be displayed if you added an alias for the default command? If not, then this behavior would seem to accommodate most scenarios really well. 

I’m using “yarn link”, so this totally might be a dev-only issue. I’ll check an end-user install when I get home and will report back if it still repros.

bcoe: @lostintangent figured out why `$0` was printing out twice for you:

https://github.com/yargs/yargs/pull/975/commits/91620ab19108287a204439df3bb35f60762767ce

now I'm working on getting fixing how we display command information; my coworkers gave me a prety good suggestion today, which was that we display the command like so:

````
Commands:
my-app.js [foo] [bar]
my-app.js start [port]
my-app.js restart [arg]
```

I think this works great, but we should land your changes that use `basename` rather than the full-path of the bin, so that we don't end up with stupidly long help messages.
lostintangent: @bcoe Awesome! That sounds good.
lostintangent: @bcoe I tried out the latest changes, and can confirm that the double script name display issue is resolved! However, I noticed that the "usage description" in the command-level help now only seems to display the bin name name (e..g `kyte` as opposed to `kyte [filePath] [options]`). Is that the expected behavior now?

<img width="367" alt="screen shot 2017-10-16 at 10 24 29 pm" src="https://user-images.githubusercontent.com/116461/31648274-aa2eb64a-b2c1-11e7-8396-8894496b0902.png">

If I view the top-level help, the usage text is displayed correctly (note that my `basename` change doesn't currently apply to the `Commands:` section 😄):

<img width="579" alt="screen shot 2017-10-16 at 10 32 09 pm" src="https://user-images.githubusercontent.com/116461/31648340-0560632e-b2c2-11e7-9535-728737601cc4.png">
bcoe: @lostintangent whoops, definitely had a somewhat screwed up release/merge with your work there.

I think I've fixed the bugs in question, I've also modified things so that if there's only a default command configured, you'll see the help for this command rather than the top-level help.

Thanks for all your help testing this new feature.
wKovacs64: @lostintangent Have you tried `10.0.0-alpha.3`? My CLI appears to be configured like kyte and it outputs just like the example above:

```Commands:
my-app.js [foo] [bar]
my-app.js start [port]
my-app.js restart [arg]
```

Do we want the `.js` part? Users are typing `my-app`, not `my-app.js` right? So we don't really want the filename at all, we want the `bin` key in `package.json` or something?
bcoe: @wKovacs64 as long as your application is linked, rather than being run locally, I think you'll find that the `.js` goes away. So, in your module, add the following to your package.json:

```json
{
  "bin": "./my-app.js"
}```

Then run `npm install -g` to link the bin globally.

Did you like where I put the command description in the help output? I thought that having it to the left of the command seemed appropriate, and matched git's help.
wKovacs64: @bcoe That's what I was testing, my package.json looks just like that and I've linked it globally. The output includes the `.js`.
bcoe: @wKovacs64 @lostintangent @iarna it took another couple days of coding, but I think I've pulled together all your suggestions into a new usage format that I'm happy with:

_when multiple commands are configured:_

<img width="606" alt="screen shot 2017-10-17 at 9 15 44 pm" src="https://user-images.githubusercontent.com/194609/31700781-7c3e1e8c-b380-11e7-81a3-0671d7507114.png">

_when a single root-level command is configured:_

<img width="392" alt="screen shot 2017-10-17 at 9 16 02 pm" src="https://user-images.githubusercontent.com/194609/31700785-87ee09e0-b380-11e7-9f8f-0341bbeaa71a.png">

You can try things out by running `npm i yargs@next`, unless there are any major issues I think I'm going to land this once tests pass.
iarna: I don't see the `.js` with a linked version, FWIW. That being said, there's probably no winning on Windows (where the thing the user runs is a `.cmd` file which then calls `node` on the `.js` file)

Patch:
diff --git a/docs/api.md b/docs/api.md
index 0dd237a..0828d56 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1128,7 +1128,7 @@ usage information and exit.
 The default behavior is to set the value of any key not followed by an
 option value to `true`.
 
-<a name="reset"></a>.reset()
+<a name="reset"></a>.reset() [DEPRECATED]
 --------
 
 Reset the argument object built up so far. This is useful for
@@ -1285,14 +1285,27 @@ Options:
 If you explicitly specify a `locale()`, you should do so *before* calling
 `updateStrings()`.
 
-.usage(message, [opts])
+.usage(<message|command>, [desc], [builder], [handler])
 ---------------------
 
 Set a usage message to show which commands to use. Inside `message`, the string
 `$0` will get interpolated to the current script name or node command for the
 present script similar to how `$0` works in bash or perl.
 
-`opts` is optional and acts like calling `.options(opts)`.
+If the optional `desc`/`builder`/`handler` are provided, `.usage()`
+acts an an alias for [`.command()`](#commandmodule). This allows you to use
+`.usage()` to configure the [default command](/docs/advanced.md#default-commands) that will be run as an entry-point to your application and allows you
+to provide configuration for the positional arguments accepted by your program:
+
+```js
+const argv = require('yargs')
+  .usage('$0 <port>', 'start the application server', (yargs) => {
+    yargs.positional('port', {
+      describe: 'the port that your application should bind to',
+      type: 'number'
+    })
+  }).argv
+```
 
 <a name="version"></a>
 .version()
diff --git a/lib/command.js b/lib/command.js
index b094ad5..e501ad6 100644
--- a/lib/command.js
+++ b/lib/command.js
@@ -4,7 +4,7 @@ const inspect = require('util').inspect
 const path = require('path')
 const Parser = require('yargs-parser')
 
-const DEFAULT_MARKER = /(\*)|(\$0)/
+const DEFAULT_MARKER = /(^\*)|(^\$0)/
 
 // handles parsing positional arguments,
 // and populating argv with said positional
@@ -50,19 +50,8 @@ module.exports = function command (yargs, usage, validation) {
       }
       return true
     })
-
-    // short-circuit if default with no aliases
-    if (isDefault && parsedAliases.length === 0) {
-      defaultCommand = {
-        original: cmd.replace(DEFAULT_MARKER, '').trim(),
-        handler,
-        builder: builder || {},
-        demanded: parsedCommand.demanded,
-        optional: parsedCommand.optional
-      }
-      handlers['*'] = defaultCommand
-      return
-    }
+    // standardize on $0 for default command.
+    if (parsedAliases.length === 0 && isDefault) parsedAliases.push('$0')
 
     // shift cmd and aliases after filtering out '*'
     if (isDefault) {
@@ -82,6 +71,7 @@ module.exports = function command (yargs, usage, validation) {
 
     handlers[parsedCommand.cmd] = {
       original: cmd,
+      description: description,
       handler,
       builder: builder || {},
       demanded: parsedCommand.demanded,
@@ -182,7 +172,6 @@ module.exports = function command (yargs, usage, validation) {
     let innerArgv = parsed.argv
     let innerYargs = null
     let positionalMap = {}
-
     if (command) {
       currentContext.commands.push(command)
       currentContext.fullCommands.push(commandHandler.original)
@@ -196,8 +185,11 @@ module.exports = function command (yargs, usage, validation) {
       // original command string as usage() for consistent behavior with
       // options object below.
       if (yargs.parsed === false) {
-        if (typeof yargs.getUsageInstance().getUsage() === 'undefined') {
-          yargs.usage(`$0 ${parentCommands.length ? `${parentCommands.join(' ')} ` : ''}${commandHandler.original}`)
+        if (shouldUpdateUsage(yargs)) {
+          yargs.getUsageInstance().usage(
+            usageFromParentCommandsCommandHandler(parentCommands, commandHandler),
+            commandHandler.description
+          )
         }
         innerArgv = innerYargs ? innerYargs._parseArgs(null, null, true, commandIndex) : yargs._parseArgs(null, null, true, commandIndex)
       } else {
@@ -210,7 +202,12 @@ module.exports = function command (yargs, usage, validation) {
       // as a short hand, an object can instead be provided, specifying
       // the options that a command takes.
       innerYargs = yargs.reset(parsed.aliases)
-      innerYargs.usage(`$0 ${parentCommands.length ? `${parentCommands.join(' ')} ` : ''}${commandHandler.original}`)
+      if (shouldUpdateUsage(innerYargs)) {
+        innerYargs.getUsageInstance().usage(
+          usageFromParentCommandsCommandHandler(parentCommands, commandHandler),
+          commandHandler.description
+        )
+      }
       Object.keys(commandHandler.builder).forEach((key) => {
         innerYargs.option(key, commandHandler.builder[key])
       })
@@ -241,6 +238,38 @@ module.exports = function command (yargs, usage, validation) {
     return innerArgv
   }
 
+  function shouldUpdateUsage (yargs) {
+    return !yargs.getUsageInstance().getUsageDisabled() &&
+      yargs.getUsageInstance().getUsage().length === 0
+  }
+
+  function usageFromParentCommandsCommandHandler (parentCommands, commandHandler) {
+    const c = DEFAULT_MARKER.test(commandHandler.original) ? commandHandler.original.replace(DEFAULT_MARKER, '') : commandHandler.original
+    const pc = parentCommands.filter((c) => { return !DEFAULT_MARKER.test(c) })
+    pc.push(c)
+    return `$0 ${pc.join(' ')}`
+  }
+
+  self.runDefaultBuilderOn = function (yargs) {
+    if (shouldUpdateUsage(yargs)) {
+      // build the root-level command string from the default string.
+      const commandString = DEFAULT_MARKER.test(defaultCommand.original)
+        ? defaultCommand.original : defaultCommand.original.replace(/^[^[\]<>]*/, '$0 ')
+      yargs.getUsageInstance().usage(
+        commandString,
+        defaultCommand.description
+      )
+    }
+    const builder = defaultCommand.builder
+    if (typeof builder === 'function') {
+      builder(yargs)
+    } else {
+      Object.keys(builder).forEach((key) => {
+        yargs.option(key, builder[key])
+      })
+    }
+  }
+
   // transcribe all positional arguments "command <foo> <bar> [apple]"
   // onto argv.
   function populatePositionals (commandHandler, argv, context, yargs) {
diff --git a/lib/usage.js b/lib/usage.js
index 24a9a9a..902fb4d 100644
--- a/lib/usage.js
+++ b/lib/usage.js
@@ -67,22 +67,26 @@ module.exports = function usage (yargs, y18n) {
   // methods for ouputting/building help (usage) message.
   let usages = []
   let usageDisabled = false
-  self.usage = (msg) => {
+  self.usage = (msg, description) => {
     if (msg === null) {
       usageDisabled = true
       usages = []
       return
     }
     usageDisabled = false
-    usages.push(msg)
+    usages.push([msg, description || ''])
+    return self
   }
   self.getUsage = () => {
-    if (usageDisabled) return null
-    return usages.reduce((usageMsg, msg) => `${usageMsg}${msg}\n`, '') || undefined
+    return usages
+  }
+  self.getUsageDisabled = () => {
+    return usageDisabled
   }
 
-  const positionalGroupName = 'Positionals:'
-  self.getPositionalGroupName = () => positionalGroupName
+  self.getPositionalGroupName = () => {
+    return __('Positionals:')
+  }
 
   let examples = []
   self.example = (cmd, description) => {
@@ -143,6 +147,7 @@ module.exports = function usage (yargs, y18n) {
     normalizeAliases()
 
     // handle old demanded API
+    const base$0 = path.basename(yargs.$0)
     const demandedOptions = yargs.getDemandedOptions()
     const demandedCommands = yargs.getDemandedCommands()
     const groups = yargs.getGroups()
@@ -165,9 +170,26 @@ module.exports = function usage (yargs, y18n) {
     })
 
     // the usage string.
-    if (usages.length && !usageDisabled) {
-      const u = self.getUsage().replace(/\$0/g, path.basename(yargs.$0))
-      ui.div(`${u}`)
+    if (!usageDisabled) {
+      if (usages.length) {
+        // user-defined usage.
+        usages.forEach((usage) => {
+          ui.div(`${usage[0].replace(/\$0/g, base$0)}`)
+          if (usage[1]) {
+            ui.div({text: `${usage[1]}`, padding: [1, 0, 0, 0]})
+          }
+        })
+        ui.div()
+      } else if (commands.length) {
+        let u = null
+        // demonstrate how commands are used.
+        if (demandedCommands._) {
+          u = `${base$0} <${__('command')}>\n`
+        } else {
+          u = `${base$0} [${__('command')}]\n`
+        }
+        ui.div(`${u}`)
+      }
     }
 
     // your application's commands, i.e., non-option
@@ -176,8 +198,13 @@ module.exports = function usage (yargs, y18n) {
       ui.div(__('Commands:'))
 
       commands.forEach((command) => {
+        const commandString = `${base$0} ${command[0].replace(/^\$0 ?/, '')}` // drop $0 from default commands.
         ui.span(
-          {text: command[0], padding: [0, 2, 0, 2], width: maxWidth(commands, theWrap) + 4},
+          {
+            text: commandString,
+            padding: [0, 2, 0, 2],
+            width: maxWidth(commands, theWrap, base$0) + 4
+          },
           {text: command[1]}
         )
         const hints = []
@@ -229,7 +256,7 @@ module.exports = function usage (yargs, y18n) {
           .map(sw => {
             // for the special positional group don't
             // add '--' or '-' prefix.
-            if (groupName === positionalGroupName) return sw
+            if (groupName === self.getPositionalGroupName()) return sw
             else return (sw.length > 1 ? '--' : '-') + sw
           })
           .join(', ')
@@ -276,7 +303,7 @@ module.exports = function usage (yargs, y18n) {
       ui.div(__('Examples:'))
 
       examples.forEach((example) => {
-        example[0] = example[0].replace(/\$0/g, path.basename(yargs.$0))
+        example[0] = example[0].replace(/\$0/g, base$0)
       })
 
       examples.forEach((example) => {
@@ -305,7 +332,7 @@ module.exports = function usage (yargs, y18n) {
 
     // the usage string.
     if (epilog) {
-      const e = epilog.replace(/\$0/g, path.basename(yargs.$0))
+      const e = epilog.replace(/\$0/g, base$0)
       ui.div(`${e}\n`)
     }
 
@@ -314,7 +341,7 @@ module.exports = function usage (yargs, y18n) {
 
   // return the maximum width of a string
   // in the left-hand column of a table.
-  function maxWidth (table, theWrap) {
+  function maxWidth (table, theWrap, modifier) {
     let width = 0
 
     // table might be of the form [leftColumn],
@@ -324,7 +351,10 @@ module.exports = function usage (yargs, y18n) {
     }
 
     table.forEach((v) => {
-      width = Math.max(stringWidth(v[0]), width)
+      width = Math.max(
+        stringWidth(modifier ? `${modifier} ${v[0]}` : v[0]),
+        width
+      )
     })
 
     // if we've enabled 'wrap' we should limit
diff --git a/locales/id.json b/locales/id.json
index a06e860..87e441c 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -36,5 +36,8 @@
   "Path to JSON config file": "Alamat berkas konfigurasi JSON",
   "Show help": "Lihat bantuan",
   "Show version number": "Lihat nomor versi",
-  "Did you mean %s?": "Maksud Anda: %s?"
+  "Did you mean %s?": "Maksud Anda: %s?",
+  "Arguments %s and %s are mutually exclusive" : "Argumen %s dan %s saling eksklusif",
+  "Positionals:": "Posisional-posisional:",
+  "command": "perintah"
 }
diff --git a/locales/nl.json b/locales/nl.json
index fc029db..1d14472 100644
--- a/locales/nl.json
+++ b/locales/nl.json
@@ -35,5 +35,8 @@
   "Path to JSON config file": "Pad naar JSON configuratiebestand",
   "Show help": "Toon help",
   "Show version number": "Toon versie nummer",
-  "Did you mean %s?": "Bedoelde u misschien %s?"
+  "Did you mean %s?": "Bedoelde u misschien %s?",
+  "Arguments %s and %s are mutually exclusive": "Argumenten %s en %s zijn onderling uitsluitend",
+  "Positionals:": "Positie-afhankelijke argumenten",
+  "command": "commando"
 }
diff --git a/yargs.js b/yargs.js
index 21c4c9b..3d5d7cd 100644
--- a/yargs.js
+++ b/yargs.js
@@ -428,19 +428,21 @@ function Yargs (processArgs, cwd, parentRequire) {
     return self
   }
 
-  self.usage = function (msg, opts) {
-    argsert('<string|null|undefined|object> [object]', [msg, opts], arguments.length)
-
-    if (!opts && typeof msg === 'object') {
-      opts = msg
-      msg = null
+  self.usage = function (msg, description, builder, handler) {
+    argsert('<string|null|undefined> [string|boolean] [function|object] [function]', [msg, description, builder, handler], arguments.length)
+
+    if (description !== undefined) {
+      // .usage() can be used as an alias for defining
+      // a default command.
+      if ((msg || '').match(/^\$0( |$)/)) {
+        return self.command(msg, description, builder, handler)
+      } else {
+        throw new YError('.usage() description must start with $0 if being used as alias for .command()')
+      }
+    } else {
+      usage.usage(msg)
+      return self
     }
-
-    usage.usage(msg)
-
-    if (opts) self.options(opts)
-
-    return self
   }
 
   self.epilogue = self.epilog = function (msg) {
@@ -741,6 +743,10 @@ function Yargs (processArgs, cwd, parentRequire) {
   self.showHelp = function (level) {
     argsert('[string|function]', [level], arguments.length)
     if (!self.parsed) self._parseArgs(processArgs) // run parser, if it has not already been executed.
+    if (command.hasDefaultCommand()) {
+      context.resets++ // override the restriction on top-level positoinals.
+      command.runDefaultBuilderOn(self, true)
+    }
     usage.showHelp(level)
     return self
   }
@@ -976,23 +982,25 @@ function Yargs (processArgs, cwd, parentRequire) {
         return argv
       }
 
-      if (argv._.length) {
-        if (helpOpt) {
-          // consider any multi-char helpOpt alias as a valid help command
-          // unless all helpOpt aliases are single-char
-          // note that parsed.aliases is a normalized bidirectional map :)
-          const helpCmds = [helpOpt]
-            .concat(aliases[helpOpt] || [])
-            .filter(k => k.length > 1)
-          // check if help should trigger and strip it from _.
-          if (~helpCmds.indexOf(argv._[argv._.length - 1])) {
-            argv._.pop()
-            argv[helpOpt] = true
-          }
+      // if there's a handler associated with a
+      // command defer processing to it.
+      if (helpOpt) {
+        // consider any multi-char helpOpt alias as a valid help command
+        // unless all helpOpt aliases are single-char
+        // note that parsed.aliases is a normalized bidirectional map :)
+        const helpCmds = [helpOpt]
+          .concat(aliases[helpOpt] || [])
+          .filter(k => k.length > 1)
+        // check if help should trigger and strip it from _.
+        if (~helpCmds.indexOf(argv._[argv._.length - 1])) {
+          argv._.pop()
+          argv[helpOpt] = true
         }
-        // if there's a handler associated with a
-        // command defer processing to it.
-        const handlerKeys = command.getCommands()
+      }
+      const handlerKeys = command.getCommands()
+      const skipDefaultCommand = argv[helpOpt] && (handlerKeys.length > 1 || handlerKeys[0] !== '$0')
+
+      if (argv._.length) {
         if (handlerKeys.length) {
           let firstUnknownCommand
           for (let i = (commandIndex || 0), cmd; argv._[i] !== undefined; i++) {
@@ -1010,7 +1018,7 @@ function Yargs (processArgs, cwd, parentRequire) {
           }
 
           // run the default command, if defined
-          if (command.hasDefaultCommand() && !argv[helpOpt]) {
+          if (command.hasDefaultCommand() && !skipDefaultCommand) {
             setPlaceholderKeys(argv)
             return command.runCommand(null, self, parsed)
           }
@@ -1028,7 +1036,7 @@ function Yargs (processArgs, cwd, parentRequire) {
           self.showCompletionScript()
           self.exit(0)
         }
-      } else if (command.hasDefaultCommand() && !argv[helpOpt]) {
+      } else if (command.hasDefaultCommand() && !skipDefaultCommand) {
         setPlaceholderKeys(argv)
         return command.runCommand(null, self, parsed)
       }


Test patch:
diff --git a/test/command.js b/test/command.js
index 6656716..4dfbe9a 100644
--- a/test/command.js
+++ b/test/command.js
@@ -515,8 +515,9 @@ describe('Command', () => {
       r.should.have.property('errors').with.length(0)
       r.should.have.property('logs')
       r.logs.join('\n').split(/\n+/).should.deep.equal([
+        'usage [command]',
         'Commands:',
-        '  dream [command] [opts]  Go to sleep and dream',
+        '  usage dream [command] [opts]  Go to sleep and dream',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -533,9 +534,10 @@ describe('Command', () => {
       r.should.have.property('logs')
       r.logs[0].split(/\n+/).should.deep.equal([
         'command dream [command] [opts]',
+        'Go to sleep and dream',
         'Commands:',
-        '  of-memory <memory>               Dream about a specific memory',
-        '  within-a-dream [command] [opts]  Dream within a dream',
+        '  command of-memory <memory>               Dream about a specific memory',
+        '  command within-a-dream [command] [opts]  Dream within a dream',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -553,11 +555,12 @@ describe('Command', () => {
       r.should.have.property('errors').with.length(0)
       r.should.have.property('logs')
       r.logs.join('\n').split(/\n+/).should.deep.equal([
+        'usage [command]',
         'Commands:',
-        '  limbo [opts]                     Get lost in pure subconscious',
-        '  inception [command] [opts]       Enter another dream, where inception is possible',
-        '  within-a-dream [command] [opts]  Dream within a dream',
-        '  dream [command] [opts]           Go to sleep and dream',
+        '  usage limbo [opts]                     Get lost in pure subconscious',
+        '  usage inception [command] [opts]       Enter another dream, where inception is possible',
+        '  usage within-a-dream [command] [opts]  Dream within a dream',
+        '  usage dream [command] [opts]           Go to sleep and dream',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -605,6 +608,7 @@ describe('Command', () => {
       r.should.have.property('logs')
       r.logs.join('\n').split(/\n+/).should.deep.equal([
         'command cyclic',
+        'Attempts to (re)apply its own dir',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -620,8 +624,9 @@ describe('Command', () => {
       r.should.have.property('errors').with.length(0)
       r.should.have.property('logs')
       r.logs.join('\n').split(/\n+/).should.deep.equal([
+        'usage [command]',
         'Commands:',
-        '  nameless  Command name derived from module filename',
+        '  usage nameless  Command name derived from module filename',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -675,8 +680,9 @@ describe('Command', () => {
 
       const expectedCmd = [
         'command cmd <sub>',
+        'Try a command',
         'Commands:',
-        '  sub  Run the subcommand',
+        '  command sub  Run the subcommand',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -685,6 +691,7 @@ describe('Command', () => {
 
       const expectedSub = [
         'command cmd sub',
+        'Run the subcommand',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -1369,4 +1376,30 @@ describe('Command', () => {
         argv._.should.eql(['foo', 'bar'])
       })
   })
+
+  describe('usage', () => {
+    it('allows you to configure a default command', () => {
+      yargs()
+        .usage('$0 <port>', 'default command', (yargs) => {
+          yargs.positional('port', {
+            type: 'string'
+          })
+        })
+        .parse('33', (err, argv) => {
+          expect(err).to.equal(null)
+          argv.port.should.equal('33')
+        })
+    })
+
+    it('throws exception if default command does not have leading $0', () => {
+      expect(() => {
+        yargs()
+          .usage('<port>', 'default command', (yargs) => {
+            yargs.positional('port', {
+              type: 'string'
+            })
+          })
+      }).to.throw(/.*\.usage\(\) description must start with \$0.*/)
+    })
+  })
 })
diff --git a/test/usage.js b/test/usage.js
index 201ea7f..22d37fc 100644
--- a/test/usage.js
+++ b/test/usage.js
@@ -676,7 +676,8 @@ describe('usage tests', () => {
           }
 
           return yargs('-f --bar 20')
-          .usage('Usage: $0 [options]', opts)
+          .usage('Usage: $0 [options]')
+          .options(opts)
           .wrap(null)
           .argv
         })
@@ -704,7 +705,8 @@ describe('usage tests', () => {
           }
 
           return yargs('-f --bar')
-          .usage('Usage: $0 [options]', opts)
+          .usage('Usage: $0 [options]')
+          .options(opts)
           .wrap(null)
           .argv
         })
@@ -734,7 +736,8 @@ describe('usage tests', () => {
           }
 
           return yargs('-f --bar 20')
-          .usage('Usage: $0 [options]', opts)
+          .usage('Usage: $0 [options]')
+          .options(opts)
           .requiresArg(['foo', 'bar'])
           .wrap(null)
           .argv
@@ -779,7 +782,8 @@ describe('usage tests', () => {
         }
 
         return yargs('-f 10 --bar 20 --baz 30')
-        .usage('Usage: $0 [options]', opts)
+        .usage('Usage: $0 [options]')
+        .options(opts)
         .strict()
         .wrap(null)
         .argv
@@ -814,7 +818,8 @@ describe('usage tests', () => {
         }
 
         return yargs('-f 10 --bar 20 --baz 30')
-        .usage('Usage: $0 [options]', opts)
+        .usage('Usage: $0 [options]')
+        .options(opts)
         .strict()
         .wrap(null)
         .argv
@@ -849,7 +854,8 @@ describe('usage tests', () => {
         }
 
         return yargs('-f 10 --bar 20 --baz 30 -q 40')
-          .usage('Usage: $0 [options]', opts)
+          .usage('Usage: $0 [options]')
+          .options(opts)
           .strict()
           .wrap(null)
           .argv
@@ -885,7 +891,8 @@ describe('usage tests', () => {
         }
 
         return yargs('--foo 10 --bar 20')
-          .usage('Usage: $0 [options]', opts)
+          .usage('Usage: $0 [options]')
+          .options(opts)
           .strict()
           .argv
       })
@@ -1412,9 +1419,10 @@ describe('usage tests', () => {
         )
 
       r.errors.join('\n').split(/\n+/).should.deep.equal([
+        'usage [command]',
         'Commands:',
-        '  upload    upload something',
-        '  download  download something from somewhere',
+        '  usage upload    upload something',
+        '  usage download  download something from somewhere',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -1433,8 +1441,10 @@ describe('usage tests', () => {
         )
 
       r.errors.join('\n').split(/\s+/).should.deep.equal([
+        'usage',
+        '[command]',
         'Commands:',
-        'upload', 'upload', 'something',
+        'usage', 'upload', 'upload', 'something',
         'Options:',
         '--help', 'Show', 'help', '[boolean]',
         '--version', 'Show', 'version', 'number', '[boolean]',
@@ -1453,8 +1463,10 @@ describe('usage tests', () => {
         )
 
       r.errors.join('\n').split(/\s+/).should.deep.equal([
+        'usage',
+        '[command]',
         'Commands:',
-        'upload', 'upload', 'something',
+        'usage', 'upload', 'upload', 'something',
         'Options:',
         '--help', 'Show', 'help', '[boolean]',
         '--version', 'Show', 'version', 'number', '[boolean]',
@@ -1486,8 +1498,10 @@ describe('usage tests', () => {
         )
 
       generalHelp.logs[0].split('\n').should.deep.equal([
+        'usage [command]',
+        '',
         'Commands:',
-        '  upload <dest>  Upload cwd to remote destination',
+        '  usage upload <dest>  Upload cwd to remote destination',
         '',
         'Options:',
         '  --help     Show help  [boolean]',
@@ -1497,6 +1511,8 @@ describe('usage tests', () => {
       commandHelp.logs[0].split('\n').should.deep.equal([
         'usage upload <dest>',
         '',
+        'Upload cwd to remote destination',
+        '',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -1507,7 +1523,7 @@ describe('usage tests', () => {
 
     it('allows a command to override global wrap()', () => {
       const uploadCommand = 'upload <dest>'
-      const uploadDesc = 'Upload cwd to remote destination'
+      const uploadDesc = 'Upload cwd'
       const uploadBuilder = yargs => yargs
           .option('force', {
             describe: 'Force overwrite of remote directory contents',
@@ -1528,8 +1544,10 @@ describe('usage tests', () => {
         )
 
       generalHelp.logs[0].split('\n').should.deep.equal([
+        'usage [command]',
+        '',
         'Commands:',
-        '  upload <dest>  Upload cwd to remote destination',
+        '  usage upload <dest>  Upload cwd',
         '',
         'Options:',
         '  --help     Show help  [boolean]',
@@ -1539,6 +1557,8 @@ describe('usage tests', () => {
       commandHelp.logs[0].split('\n').should.deep.equal([
         'usage upload <dest>',
         '',
+        'Upload cwd',
+        '',
         'Options:',
         '  --help     Show help               [boolean]',
         '  --version  Show version number     [boolean]',
@@ -1565,6 +1585,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage upload',
         '',
+        'upload something',
+        '',
         'Flags:',
         '  -q  [boolean]',
         '',
@@ -1603,6 +1625,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage upload',
         '',
+        'upload something',
+        '',
         'Flags:',
         '  -q  [boolean]',
         '',
@@ -1637,6 +1661,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage upload',
         '',
+        'upload something',
+        '',
         'Awesome Flags:',
         '  -i  [boolean]',
         '  -q  [boolean]',
@@ -1675,6 +1701,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage upload',
         '',
+        'upload something',
+        '',
         'Awesome Flags:',
         '  -i  [boolean]',
         '',
@@ -1701,8 +1729,10 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
+        'usage [command]',
+        '',
         'Commands:',
-        '  upload  upload something',
+        '  usage upload  upload something',
         '',
         'Options:',
         '  --help     Show help  [boolean]',
@@ -1758,6 +1788,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage one two [next]',
         '',
+        'level two',
+        '',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -1775,6 +1807,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage one two [next]',
         '',
+        'level two',
+        '',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -1790,8 +1824,10 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
+        'usage [command]',
+        '',
         'Commands:',
-        '  copy <src> [dest]  Copy something  [aliases: cp, dupe]',
+        '  usage copy <src> [dest]  Copy something  [aliases: cp, dupe]',
         '',
         'Options:',
         '  --help     Show help  [boolean]',
@@ -1808,8 +1844,10 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
+        'usage [command]',
+        '',
         'Commands:',
-        '  copy <src> [dest]  Copy something                          [aliases: cp, dupe]',
+        '  usage copy <src> [dest]  Copy something                    [aliases: cp, dupe]',
         '',
         'Options:',
         '  --help     Show help                                                 [boolean]',
@@ -1850,6 +1888,28 @@ describe('usage tests', () => {
         ''
       ])
     })
+
+    it('does not display $0 twice when default commands are enabled', () => {
+      const r = checkUsage(() => yargs('-h')
+        .usage('$0', 'do something', yargs => {
+          yargs
+            .alias('h', 'help')
+        })
+        .wrap(null)
+        .argv
+      )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'usage',
+        '',
+        'do something',
+        '',
+        'Options:',
+        '  --version   Show version number  [boolean]',
+        '  -h, --help  Show help  [boolean]',
+        ''
+      ])
+    })
   })
 
   describe('epilogue', () => {
@@ -2537,7 +2597,7 @@ describe('usage tests', () => {
   })
 
   describe('default command', () => {
-    it('--help should display top-level help with no command given', () => {
+    it('should display top-level help with no command given', () => {
       const r = checkUsage(() => yargs('--help')
           .command(['list [pattern]', 'ls', '*'], 'List key-value pairs for pattern', {}, noop)
           .command('get <key>', 'Get value for key', {}, noop)
@@ -2546,14 +2606,103 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
+        'usage [pattern]',
+        '',
+        'List key-value pairs for pattern',
+        '',
+        'Commands:',
+        '  usage list [pattern]     List key-value pairs for pattern',
+        '                                                         [default] [aliases: ls]',
+        '  usage get <key>          Get value for key',
+        '  usage set <key> [value]  Set value for key',
+        '',
+        'Options:',
+        '  --help     Show help                                                 [boolean]',
+        '  --version  Show version number                                       [boolean]',
+        ''
+      ])
+    })
+
+    it('should display default command as ./$0 if it has no aliases', () => {
+      const r = checkUsage(() => yargs('--help')
+          .command('* [pattern]', 'List key-value pairs for pattern', {}, noop)
+          .command('get <key>', 'Get value for key', {}, noop)
+          .command('set <key> [value]', 'Set value for key', {}, noop)
+          .argv
+        )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'usage [pattern]',
+        '',
+        'List key-value pairs for pattern',
+        '',
+        'Commands:',
+        '  usage [pattern]          List key-value pairs for pattern            [default]',
+        '  usage get <key>          Get value for key',
+        '  usage set <key> [value]  Set value for key',
+        '',
+        'Options:',
+        '  --help     Show help                                                 [boolean]',
+        '  --version  Show version number                                       [boolean]',
+        ''
+      ])
+    })
+
+    it('should display positionals that have been configured', () => {
+      const r = checkUsage(() => yargs('--help')
+          .command('* [pattern]', 'List key-value pairs for pattern', (yargs) => {
+            yargs.positional('pattern', {
+              type: 'string',
+              default: '.*'
+            })
+          }, noop)
+          .command('get <key>', 'Get value for key', {}, noop)
+          .command('set <key> [value]', 'Set value for key', {}, noop)
+          .argv
+        )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'usage [pattern]',
+        '',
+        'List key-value pairs for pattern',
+        '',
+        'Commands:',
+        '  usage [pattern]          List key-value pairs for pattern            [default]',
+        '  usage get <key>          Get value for key',
+        '  usage set <key> [value]  Set value for key',
+        '',
+        'Positionals:',
+        '  pattern                                               [string] [default: ".*"]',
+        '',
+        'Options:',
+        '  --help     Show help                                                 [boolean]',
+        '  --version  Show version number                                       [boolean]',
+        ''
+      ])
+    })
+
+    it('should display options that have been configured', () => {
+      const r = checkUsage(() => yargs('--help')
+          .command('* [pattern]', 'List key-value pairs for pattern', {uuid: {required: true}}, noop)
+          .command('get <key>', 'Get value for key', {}, noop)
+          .command('set <key> [value]', 'Set value for key', {}, noop)
+          .argv
+        )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'usage [pattern]',
+        '',
+        'List key-value pairs for pattern',
+        '',
         'Commands:',
-        '  list [pattern]     List key-value pairs for pattern    [default] [aliases: ls]',
-        '  get <key>          Get value for key',
-        '  set <key> [value]  Set value for key',
+        '  usage [pattern]          List key-value pairs for pattern            [default]',
+        '  usage get <key>          Get value for key',
+        '  usage set <key> [value]  Set value for key',
         '',
         'Options:',
         '  --help     Show help                                                 [boolean]',
         '  --version  Show version number                                       [boolean]',
+        '  --uuid                                                              [required]',
         ''
       ])
     })
@@ -2573,6 +2722,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage list [pattern]',
         '',
+        'List key-value pairs for pattern',
+        '',
         'Positionals:',
         '  pattern  the pattern to list keys for',
         '',
@@ -2596,6 +2747,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage list [pattern...]',
         '',
+        'List key-value pairs for pattern',
+        '',
         'Positionals:',
         '  pattern  the pattern to list keys for                    [array] [default: []]',
         '',
@@ -2619,6 +2772,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage list <pattern>',
         '',
+        'List key-value pairs for pattern',
+        '',
         'Positionals:',
         '  pattern  the pattern to list keys for                               [required]',
         '',
@@ -2642,6 +2797,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage list [pattern|thingy]',
         '',
+        'List key-value pairs for pattern',
+        '',
         'Positionals:',
         '  pattern, thingy  the pattern to list keys for',
         '',
@@ -2666,6 +2823,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage list [pattern]',
         '',
+        'List key-value pairs for pattern',
+        '',
         'Positionals:',
         '  pattern  the pattern to list keys for                                 [string]',
         '',
@@ -2690,6 +2849,8 @@ describe('usage tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage list [pattern]',
         '',
+        'List key-value pairs for pattern',
+        '',
         'Positionals:',
         '  pattern  the pattern to list keys for                  [choices: "foo", "bar"]',
         '',
diff --git a/test/yargs.js b/test/yargs.js
index b2fd60e..b17785b 100644
--- a/test/yargs.js
+++ b/test/yargs.js
@@ -74,18 +74,6 @@ describe('yargs dsl tests', () => {
     r.result.should.not.have.property('[object Object]')
   })
 
-  it('treats usage as alias for options, if object provided as first argument', () => {
-    const argv = yargs([])
-      .usage({
-        a: {
-          default: 99
-        }
-      })
-      .argv
-
-    argv.a.should.eql(99)
-  })
-
   it('a function can be provided, to execute when a parsing failure occurs', (done) => {
     yargs(['--x=33'])
       .implies({
@@ -371,8 +359,10 @@ describe('yargs dsl tests', () => {
       r.logs[0].split('\n').should.deep.equal([
         'usage blerg',
         '',
+        'handle blerg things',
+        '',
         'Commands:',
-        '  snuh  snuh command',
+        '  usage snuh  snuh command',
         '',
         'Options:',
         '  --version  Show version number  [boolean]',
@@ -395,8 +385,10 @@ describe('yargs dsl tests', () => {
       })
 
       r.logs[0].split('\n').should.deep.equal([
+        'usage [command]',
+        '',
         'Commands:',
-        '  blerg  handle blerg things',
+        '  usage blerg  handle blerg things',
         '',
         'Options:',
         '  --version  Show version number  [boolean]',
@@ -1073,6 +1065,8 @@ describe('yargs dsl tests', () => {
         output1.split('\n').should.deep.equal([
           'ndm batman <api-token>',
           '',
+          'batman command',
+          '',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -1082,6 +1076,8 @@ describe('yargs dsl tests', () => {
         output2.split('\n').should.deep.equal([
           'ndm robin <egg>',
           '',
+          'robin command',
+          '',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -1112,6 +1108,8 @@ describe('yargs dsl tests', () => {
         output1.split('\n').should.deep.equal([
           'ndm batman <api-token>',
           '',
+          'batman command',
+          '',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -1123,6 +1121,8 @@ describe('yargs dsl tests', () => {
         output2.split('\n').should.deep.equal([
           'ndm robin <egg>',
           '',
+          'robin command',
+          '',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -1152,8 +1152,10 @@ describe('yargs dsl tests', () => {
         x.should.equal('two')
         err.should.have.property('message').and.equal('Must call a command')
         output.split('\n').should.deep.equal([
+          'ndm <command>',
+          '',
           'Commands:',
-          '  one <x>  The one and only command',
+          '  ndm one <x>  The one and only command',
           '',
           'Options:',
           '  --help     Show help  [boolean]',