Repository: yargs/yargs
Pull request #976 (2017-10-14 23:21:42)

chore: new translations for command API overhaul
Sorry for mentioning so many folks. 

I've been undertaking an overhaul of the `.command()` and `.usage()` API (introducing the concept of [`.positional()`](https://github.com/yargs/yargs/blob/f5bc44c66278c28404552c7b292a8f8f53ca4f29/docs/api.md#positionalkey-opt)).

In the process I've introduced a couple more words to the English language file:

```json
{
"Positionals:": "Positionals:",
"command": "command"
}
```

I would love help updating the files for each languages to reflect these new terms.

_... I would also love help testing the new features..._

Given that `yargs@10` represents a pretty large update to the command API, I would also love help testing. The new features are documented here:

https://github.com/yargs/yargs/blob/f5bc44c66278c28404552c7b292a8f8f53ca4f29/docs/api.md#usagemessagecommand-desc-builder-handler

here,

https://github.com/yargs/yargs/blob/f5bc44c66278c28404552c7b292a8f8f53ca4f29/docs/api.md#positionalkey-opt

and finally here:

https://github.com/yargs/yargs/blob/f5bc44c66278c28404552c7b292a8f8f53ca4f29/docs/api.md#positionalkey-opt

you can test out the candidate release by running:

`npm i yargs@next`

Thank you for your contributions to yargs!

@p0v1n0m , @maxrimue, @zkat , @LoicMahieu, @babhishek21, @Hunrik, @rilut, @madrisan, @oti, @disjukr, @sindresorhus, @Tocive, @kamilogorek, @codemonkey3045 , @madcampos, @lvarayut, @feyzo, @andyhu, @RhinoLu

- [x] ko
- [x] jp
- [x] pt-BR
- [x] en
- [x] tr
- [x]  pl-PL
- [x] nl
- [x] id


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b5c810..6bd1752 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,7 @@ All notable changes to this project will be documented in this file. See [standa
 ### BREAKING CHANGES
 
 * version() and help() are now enabled by default, and show up in help output; the implicit help command can no longer be enabled/disabled independently from the help command itself (which can now be disabled).
+* parse() now behaves as an alias for .argv, unless a parseCallback is provided.
 
 
 
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 b528df7..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) {
@@ -293,13 +322,26 @@ module.exports = function command (yargs, usage, validation) {
         return `--${key} ${value}`
       }))
     })
+
+    // short-circuit parse.
+    if (!unparsed.length) return
+
     const parsed = Parser.detailed(unparsed.join(' '), options)
 
     if (parsed.error) {
       yargs.getUsageInstance().fail(parsed.error.message, parsed.error)
     } else {
+      // only copy over positional keys (don't overwrite
+      // flag arguments that were already parsed).
+      const positionalKeys = Object.keys(positionalMap)
+      Object.keys(positionalMap).forEach((key) => {
+        [].push.apply(positionalKeys, parsed.aliases[key])
+      })
+
       Object.keys(parsed.argv).forEach((key) => {
-        if (key !== '_') argv[key] = parsed.argv[key]
+        if (positionalKeys.indexOf(key) !== -1) {
+          argv[key] = parsed.argv[key]
+        }
       })
     }
   }
diff --git a/lib/usage.js b/lib/usage.js
index c742f8d..902fb4d 100644
--- a/lib/usage.js
+++ b/lib/usage.js
@@ -3,6 +3,7 @@
 // failures, etc. keeps logging in one place.
 const stringWidth = require('string-width')
 const objFilter = require('./obj-filter')
+const path = require('path')
 const setBlocking = require('set-blocking')
 const YError = require('./yerror')
 
@@ -66,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) => {
@@ -142,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()
@@ -164,9 +170,26 @@ module.exports = function usage (yargs, y18n) {
     })
 
     // the usage string.
-    if (usages.length && !usageDisabled) {
-      const u = self.getUsage().replace(/\$0/g, 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
@@ -175,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 = []
@@ -228,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(', ')
@@ -275,7 +303,7 @@ module.exports = function usage (yargs, y18n) {
       ui.div(__('Examples:'))
 
       examples.forEach((example) => {
-        example[0] = example[0].replace(/\$0/g, yargs.$0)
+        example[0] = example[0].replace(/\$0/g, base$0)
       })
 
       examples.forEach((example) => {
@@ -304,7 +332,7 @@ module.exports = function usage (yargs, y18n) {
 
     // the usage string.
     if (epilog) {
-      const e = epilog.replace(/\$0/g, yargs.$0)
+      const e = epilog.replace(/\$0/g, base$0)
       ui.div(`${e}\n`)
     }
 
@@ -313,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],
@@ -323,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/en.json b/locales/en.json
index 4a618e7..fc65c2a 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -36,5 +36,7 @@
   "Show help": "Show help",
   "Show version number": "Show version number",
   "Did you mean %s?": "Did you mean %s?",
-  "Arguments %s and %s are mutually exclusive" : "Arguments %s and %s are mutually exclusive"
+  "Arguments %s and %s are mutually exclusive" : "Arguments %s and %s are mutually exclusive",
+  "Positionals:": "Positionals:",
+  "command": "command"
 }
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/ja.json b/locales/ja.json
index 45a64a8..64ee6d3 100644
--- a/locales/ja.json
+++ b/locales/ja.json
@@ -35,5 +35,8 @@
   "Path to JSON config file": "JSONの設定ファイルまでのpath",
   "Show help": "ヘルプを表示",
   "Show version number": "バージョンを表示",
-  "Did you mean %s?": "もしかして %s?"
+  "Did you mean %s?": "もしかして %s?",
+  "Arguments %s and %s are mutually exclusive" : "引数 %s と %s は同時に指定できません",
+  "Positionals:": "位置:",
+  "command": "コマンド"
 }
diff --git a/locales/ko.json b/locales/ko.json
index e714984..0eaeab2 100644
--- a/locales/ko.json
+++ b/locales/ko.json
@@ -35,5 +35,8 @@
   "Path to JSON config file": "JSON 설정파일 경로",
   "Show help": "도움말을 보여줍니다",
   "Show version number": "버전 넘버를 보여줍니다",
-  "Did you mean %s?": "찾고계신게 %s입니까?"
+  "Did you mean %s?": "찾고계신게 %s입니까?",
+  "Arguments %s and %s are mutually exclusive" : "%s와 %s 인자는 같이 사용될 수 없습니다",
+  "Positionals:": "위치:",
+  "command": "명령"
 }
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/locales/pl.json b/locales/pl.json
index 1c834f6..6926a45 100644
--- a/locales/pl.json
+++ b/locales/pl.json
@@ -35,5 +35,8 @@
   "Path to JSON config file": "Ścieżka do pliku konfiguracyjnego JSON",
   "Show help": "Pokaż pomoc",
   "Show version number": "Pokaż numer wersji",
-  "Did you mean %s?": "Czy chodziło Ci o %s?"
+  "Did you mean %s?": "Czy chodziło Ci o %s?",
+  "Arguments %s and %s are mutually exclusive": "Argumenty %s i %s wzajemnie się wykluczają",
+  "Positionals:": "Pozycyjne:",
+  "command": "polecenie"
 }
diff --git a/locales/pt_BR.json b/locales/pt_BR.json
index 60e5ffa..904cb66 100644
--- a/locales/pt_BR.json
+++ b/locales/pt_BR.json
@@ -2,7 +2,7 @@
   "Commands:": "Comandos:",
   "Options:": "Opções:",
   "Examples:": "Exemplos:",
-  "boolean": "boolean",
+  "boolean": "booleano",
   "count": "contagem",
   "string": "string",
   "number": "número",
@@ -36,5 +36,7 @@
   "Show help": "Exibe ajuda",
   "Show version number": "Exibe a versão",
   "Did you mean %s?": "Você quis dizer %s?",
-  "Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos"
+  "Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos",
+  "Positionals:": "Posicionais:",
+  "command": "comando"
 }
diff --git a/locales/tr.json b/locales/tr.json
index 000060b..9b06c52 100644
--- a/locales/tr.json
+++ b/locales/tr.json
@@ -35,5 +35,7 @@
   "Path to JSON config file": "JSON yapılandırma dosya konumu",
   "Show help": "Yardım detaylarını göster",
   "Show version number": "Versiyon detaylarını göster",
-  "Did you mean %s?": "Bunu mu demek istediniz: %s?"
+  "Did you mean %s?": "Bunu mu demek istediniz: %s?",
+  "Positionals:": "Sıralılar:",
+  "command": "komut"
 }
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 d468f90..4dfbe9a 100644
--- a/test/command.js
+++ b/test/command.js
@@ -102,6 +102,15 @@ describe('Command', () => {
       expect(handlers.wat).to.not.exist
       command.getCommands().should.deep.equal(['foo', 'wat'])
     })
+
+    it('does not overwrite existing values in argv for keys that are not positional', () => {
+      const argv = yargs('foo foo.js --reporter=html')
+        .command('foo <file>')
+        .default('reporter', 'text')
+        .argv
+      argv.file.should.equal('foo.js')
+      argv.reporter.should.equal('html')
+    })
   })
 
   describe('variadic', () => {
@@ -506,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]',
@@ -523,10 +533,11 @@ describe('Command', () => {
       r.should.have.property('errors').with.length(0)
       r.should.have.property('logs')
       r.logs[0].split(/\n+/).should.deep.equal([
-        './command dream [command] [opts]',
+        '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]',
@@ -544,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]',
@@ -595,7 +607,8 @@ describe('Command', () => {
       r.should.have.property('errors').with.length(0)
       r.should.have.property('logs')
       r.logs.join('\n').split(/\n+/).should.deep.equal([
-        './command cyclic',
+        'command cyclic',
+        'Attempts to (re)apply its own dir',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -611,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]',
@@ -665,9 +679,10 @@ describe('Command', () => {
           .argv, [ './command' ])
 
       const expectedCmd = [
-        './command cmd <sub>',
+        '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]',
@@ -675,7 +690,8 @@ describe('Command', () => {
       ]
 
       const expectedSub = [
-        './command cmd sub',
+        'command cmd sub',
+        'Run the subcommand',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -1360,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 c97592a..22d37fc 100644
--- a/test/usage.js
+++ b/test/usage.js
@@ -30,7 +30,7 @@ describe('usage tests', () => {
         r.result.should.have.property('z', 20)
         r.result.should.have.property('_').with.length(0)
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage -x NUM -y NUM',
+          'Usage: usage -x NUM -y NUM',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -54,7 +54,7 @@ describe('usage tests', () => {
         r.result.should.have.property('y', 10)
         r.result.should.have.property('_').with.length(1)
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage -w NUM -m NUM',
+          'Usage: usage -w NUM -m NUM',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -78,7 +78,7 @@ describe('usage tests', () => {
         r.result.should.have.property('y', 10)
         r.result.should.have.property('_').with.length(0)
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage -w NUM -m NUM',
+          'Usage: usage -w NUM -m NUM',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -119,7 +119,7 @@ describe('usage tests', () => {
           r.result.should.have.property('z', 20)
           r.result.should.have.property('_').with.length(0)
           r.errors.join('\n').split(/\n+/).should.deep.equal([
-            'Usage: ./usage -x NUM -y NUM',
+            'Usage: usage -x NUM -y NUM',
             'Options:',
             '  --help     Show help  [boolean]',
             '  --version  Show version number  [boolean]',
@@ -142,7 +142,7 @@ describe('usage tests', () => {
           r.result.should.have.property('y', 10)
           r.result.should.have.property('_').with.length(1)
           r.errors.join('\n').split(/\n+/).should.deep.equal([
-            'Usage: ./usage -w NUM -m NUM',
+            'Usage: usage -w NUM -m NUM',
             'Options:',
             '  --help     Show help  [boolean]',
             '  --version  Show version number  [boolean]',
@@ -167,7 +167,7 @@ describe('usage tests', () => {
         r.result.should.have.property('y', 10)
         r.result.should.have.property('_').with.length(0)
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage -w NUM -m NUM',
+          'Usage: usage -w NUM -m NUM',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -190,7 +190,7 @@ describe('usage tests', () => {
       r.result.should.have.property('z', 20)
       r.result.should.have.property('_').with.length(0)
       r.errors.join('\n').split(/\n+/).should.deep.equal([
-        'Usage: ./usage -x NUM -y NUM',
+        'Usage: usage -x NUM -y NUM',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -347,7 +347,7 @@ describe('usage tests', () => {
     r.result.should.have.property('z', 20)
     r.result.should.have.property('_').with.length(0)
     r.errors.join('\n').split(/\n+/).should.deep.equal([
-      'Usage: ./usage -x NUM -y NUM',
+      'Usage: usage -x NUM -y NUM',
       'Options:',
       '  --help     Show help  [boolean]',
       '  --version  Show version number  [boolean]',
@@ -375,7 +375,7 @@ describe('usage tests', () => {
     r.should.have.property('exit').and.be.ok
     r.should.have.property('errors')
     r.errors.join('\n').split(/\n+/).should.deep.equal([
-      'Usage: ./usage -x NUM -y NUM',
+      'Usage: usage -x NUM -y NUM',
       'Options:',
       '  --help     Show help  [boolean]',
       '  --version  Show version number  [boolean]',
@@ -419,7 +419,7 @@ describe('usage tests', () => {
     r.should.have.property('exit').and.be.ok
     r.should.have.property('errors')
     r.errors.join('\n').split(/\n+/).join('\n').should.equal(
-      'Usage: ./usage -x NUM -y NUM\n' +
+      'Usage: usage -x NUM -y NUM\n' +
       'Options:\n' +
       '  --help     Show help  [boolean]\n' +
       '  --version  Show version number  [boolean]\n' +
@@ -446,7 +446,7 @@ describe('usage tests', () => {
           }
         })
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage -x NUM -y NUM',
+          'Usage: usage -x NUM -y NUM',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -579,7 +579,7 @@ describe('usage tests', () => {
     r.result.should.have.property('moo', true)
     r.should.have.property('errors')
     r.errors.join('\n').split(/\n+/).should.deep.equal([
-      'Usage: ./usage [x] [y] [z] {OPTIONS}',
+      'Usage: usage [x] [y] [z] {OPTIONS}',
       'Options:',
       '  --help     Show help  [boolean]',
       '  --version  Show version number  [boolean]',
@@ -601,7 +601,7 @@ describe('usage tests', () => {
     r.result.should.have.property('moo', true)
     r.should.have.property('errors')
     r.errors.join('\n').split(/\n+/).should.deep.equal([
-      'Usage: ./usage [x] [y] [z] {OPTIONS} <src> <dest> [extra_files...]',
+      'Usage: usage [x] [y] [z] {OPTIONS} <src> <dest> [extra_files...]',
       'Options:',
       '  --help     Show help  [boolean]',
       '  --version  Show version number  [boolean]',
@@ -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
         })
@@ -686,7 +687,7 @@ describe('usage tests', () => {
         r.should.have.property('logs').with.length(0)
         r.should.have.property('exit').and.be.ok
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage [options]',
+          'Usage: usage [options]',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -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
         })
@@ -714,7 +716,7 @@ describe('usage tests', () => {
         r.should.have.property('logs').with.length(0)
         r.should.have.property('exit').and.be.ok
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage [options]',
+          'Usage: usage [options]',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -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
@@ -745,7 +748,7 @@ describe('usage tests', () => {
         r.should.have.property('logs').with.length(0)
         r.should.have.property('exit').and.be.ok
         r.errors.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage [options]',
+          'Usage: usage [options]',
           'Options:',
           '  --help     Show help  [boolean]',
           '  --version  Show version number  [boolean]',
@@ -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
@@ -794,7 +798,7 @@ describe('usage tests', () => {
       r.result.should.have.property('baz', 30)
       r.should.have.property('errors')
       r.errors.join('\n').split(/\n+/).should.deep.equal([
-        'Usage: ./usage [options]',
+        'Usage: usage [options]',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -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
@@ -829,7 +834,7 @@ describe('usage tests', () => {
       r.result.should.have.property('baz', 30)
       r.should.have.property('errors')
       r.errors.join('\n').split(/\n+/).should.deep.equal([
-        'Usage: ./usage [options]',
+        'Usage: usage [options]',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -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
@@ -865,7 +871,7 @@ describe('usage tests', () => {
       r.result.should.have.property('q', 40)
       r.should.have.property('errors')
       r.errors.join('\n').split(/\n+/).should.deep.equal([
-        'Usage: ./usage [options]',
+        'Usage: usage [options]',
         'Options:',
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
@@ -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
       })
@@ -919,8 +926,8 @@ describe('usage tests', () => {
       '  --version  Show version number  [boolean]',
       '  -y  [required]',
       'Examples:',
-      '  ./usage something       description',
-      '  ./usage something else  other description',
+      '  usage something       description',
+      '  usage something else  other description',
       'Missing required argument: y'
     ])
   })
@@ -941,7 +948,7 @@ describe('usage tests', () => {
         r.result.should.have.property('z', 20)
         r.result.should.have.property('_').with.length(0)
         r.errors.join('\n').split(/\n/).should.deep.equal([
-          'Usage: ./usage -x NUM [-y NUM]',
+          'Usage: usage -x NUM [-y NUM]',
           '',
           'Options:',
           '  --help     Show help  [boolean]',
@@ -971,7 +978,7 @@ describe('usage tests', () => {
         r.result.should.have.property('z', 20)
         r.result.should.have.property('_').with.length(0)
         r.errors.join('\n').split(/\n/).should.deep.equal([
-          'Usage: ./usage -x NUM [-y NUM]',
+          'Usage: usage -x NUM [-y NUM]',
           '',
           'Options:',
           '  --help     Show help  [boolean]',
@@ -1038,7 +1045,7 @@ describe('usage tests', () => {
       r.should.have.property('errors').with.length(0)
       r.should.have.property('logs')
       r.logs.join('\n').split(/\n+/).should.deep.equal([
-        'Usage: ./usage options',
+        'Usage: usage options',
         'Options:',
         '  --help      Show help  [boolean]',
         '  --version   Show version number  [boolean]',
@@ -1066,7 +1073,7 @@ describe('usage tests', () => {
         r.should.have.property('errors').with.length(0)
         r.should.have.property('logs')
         r.logs.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage options',
+          'Usage: usage options',
           'Options:',
           '  --help, -h  Show help  [boolean]',
           '  --version   Show version number  [boolean]',
@@ -1096,7 +1103,7 @@ describe('usage tests', () => {
         r.should.have.property('errors').with.length(0)
         r.should.have.property('logs')
         r.logs.join('\n').split(/\n+/).should.deep.equal([
-          'Usage: ./usage options',
+          'Usage: usage options',
           'Options:',
           '  --help, -h  Show help  [boolean]',
           '  --version   Show version number  [boolean]',
@@ -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]',
@@ -1495,7 +1509,9 @@ describe('usage tests', () => {
         ''
       ])
       commandHelp.logs[0].split('\n').should.deep.equal([
-        './usage upload <dest>',
+        'usage upload <dest>',
+        '',
+        'Upload cwd to remote destination',
         '',
         'Options:',
         '  --help     Show help  [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]',
@@ -1537,7 +1555,9 @@ describe('usage tests', () => {
         ''
       ])
       commandHelp.logs[0].split('\n').should.deep.equal([
-        './usage upload <dest>',
+        'usage upload <dest>',
+        '',
+        'Upload cwd',
         '',
         'Options:',
         '  --help     Show help               [boolean]',
@@ -1563,7 +1583,9 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage upload',
+        'usage upload',
+        '',
+        'upload something',
         '',
         'Flags:',
         '  -q  [boolean]',
@@ -1601,7 +1623,9 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage upload',
+        'usage upload',
+        '',
+        'upload something',
         '',
         'Flags:',
         '  -q  [boolean]',
@@ -1635,7 +1659,9 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage upload',
+        'usage upload',
+        '',
+        'upload something',
         '',
         'Awesome Flags:',
         '  -i  [boolean]',
@@ -1673,7 +1699,9 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage upload',
+        '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]',
@@ -1756,7 +1786,9 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage one two [next]',
+        'usage one two [next]',
+        '',
+        'level two',
         '',
         'Options:',
         '  --help     Show help  [boolean]',
@@ -1773,7 +1805,9 @@ describe('usage tests', () => {
         )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage one two [next]',
+        'usage one two [next]',
+        '',
+        'level two',
         '',
         'Options:',
         '  --help     Show help  [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]',
@@ -1826,8 +1864,8 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage upload [something]',
-        './usage upload [something else]',
+        'usage upload [something]',
+        'usage upload [something else]',
         '',
         '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', () => {
@@ -1884,7 +1944,7 @@ describe('usage tests', () => {
         '  --help     Show help  [boolean]',
         '  --version  Show version number  [boolean]',
         '  -y  [required]',
-        'Try \'./usage --long-help\' for more information',
+        'Try \'usage --long-help\' for more information',
         'Missing required argument: y'
       ])
     })
@@ -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]',
         ''
       ])
     })
@@ -2571,7 +2720,9 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage list [pattern]',
+        'usage list [pattern]',
+        '',
+        'List key-value pairs for pattern',
         '',
         'Positionals:',
         '  pattern  the pattern to list keys for',
@@ -2594,7 +2745,9 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage list [pattern...]',
+        'usage list [pattern...]',
+        '',
+        'List key-value pairs for pattern',
         '',
         'Positionals:',
         '  pattern  the pattern to list keys for                    [array] [default: []]',
@@ -2617,7 +2770,9 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage list <pattern>',
+        'usage list <pattern>',
+        '',
+        'List key-value pairs for pattern',
         '',
         'Positionals:',
         '  pattern  the pattern to list keys for                               [required]',
@@ -2640,7 +2795,9 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage list [pattern|thingy]',
+        'usage list [pattern|thingy]',
+        '',
+        'List key-value pairs for pattern',
         '',
         'Positionals:',
         '  pattern, thingy  the pattern to list keys for',
@@ -2664,7 +2821,9 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage list [pattern]',
+        'usage list [pattern]',
+        '',
+        'List key-value pairs for pattern',
         '',
         'Positionals:',
         '  pattern  the pattern to list keys for                                 [string]',
@@ -2688,7 +2847,9 @@ describe('usage tests', () => {
       )
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage list [pattern]',
+        'usage list [pattern]',
+        '',
+        'List key-value pairs for pattern',
         '',
         'Positionals:',
         '  pattern  the pattern to list keys for                  [choices: "foo", "bar"]',
diff --git a/test/validation.js b/test/validation.js
index ff9a607..213cf7f 100644
--- a/test/validation.js
+++ b/test/validation.js
@@ -793,7 +793,7 @@ describe('validation tests', () => {
       r.should.have.property('exit').and.be.ok
       r.result.should.have.property('_').and.deep.equal(['src', 'dest'])
       r.errors.join('\n').split(/\n+/).should.deep.equal([
-        'Usage: ./usage [x] [y] [z] {OPTIONS} <src> <dest> [extra_files...]',
+        'Usage: usage [x] [y] [z] {OPTIONS} <src> <dest> [extra_files...]',
         'too many arguments are provided'
       ])
     })
diff --git a/test/yargs.js b/test/yargs.js
index a9f41a8..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({
@@ -369,10 +357,12 @@ describe('yargs dsl tests', () => {
       })
 
       r.logs[0].split('\n').should.deep.equal([
-        './usage blerg',
+        '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]',