Repository: oclif/core
Pull request #188 (2021-06-28 18:13:50)

feat: add docopts
All of this is adopted from [sfdx](https://github.com/forcedotcom/cli-packages/blob/main/packages/command/src/docOpts.ts) with tweaks to work within oclif.


Patch:
diff --git a/src/config/config.ts b/src/config/config.ts
index 3167503..9cd2ef7 100644
--- a/src/config/config.ts
+++ b/src/config/config.ts
@@ -496,6 +496,8 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
         helpLabel: flag.helpLabel,
         helpGroup: flag.helpGroup,
         allowNo: flag.allowNo,
+        dependsOn: flag.dependsOn,
+        exclusive: flag.exclusive,
       }
     } else {
       flags[name] = {
@@ -511,6 +513,8 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
         helpGroup: flag.helpGroup,
         multiple: flag.multiple,
         options: flag.options,
+        dependsOn: flag.dependsOn,
+        exclusive: flag.exclusive,
         // eslint-disable-next-line no-await-in-loop
         default: typeof flag.default === 'function' ? await flag.default({options: {}, flags: {}}) : flag.default,
       }
diff --git a/src/help/command.ts b/src/help/command.ts
index c893eff..cf9d71c 100644
--- a/src/help/command.ts
+++ b/src/help/command.ts
@@ -5,6 +5,7 @@ import {castArray, compact, sortBy} from '../util'
 import * as Interfaces from '../interfaces'
 import {Example} from '../interfaces/command'
 import {HelpFormatter} from './formatter'
+import {DocOpts} from './docopts'
 
 // Don't use os.EOL because we need to ensure that a string
 // written on any platform, that may use \r\n or \n, will be
@@ -75,7 +76,7 @@ export class CommandHelp extends HelpFormatter {
     return [
       {
         header: this.opts.usageHeader || 'USAGE',
-        generate: ({flags}) => this.usage(flags),
+        generate: () => this.usage(),
       },
       {
         header: 'ARGUMENTS',
@@ -121,15 +122,28 @@ export class CommandHelp extends HelpFormatter {
     ]
   }
 
-  protected usage(flags: Interfaces.Command.Flag[]): string {
+  protected usage(): string {
     const usage = this.command.usage
-    const body = (usage ? castArray(usage) : [this.defaultUsage(flags)])
-    .map(u => `$ ${this.config.bin} ${u}`.trim())
+    const body = (usage ? castArray(usage) : [this.defaultUsage()])
+    .map(u => {
+      const allowedSpacing = this.opts.maxWidth - this.indentSpacing
+      const line = `$ ${this.config.bin} ${u}`.trim()
+      if (line.length > allowedSpacing) {
+        const splitIndex = line.substring(0, allowedSpacing).lastIndexOf(' ')
+        return line.substring(0, splitIndex) + '\n' +
+          this.indent(this.wrap(line.substring(splitIndex), this.indentSpacing * 2))
+      }
+      return this.wrap(line)
+    })
     .join('\n')
-    return this.wrap(body)
+    return body
   }
 
-  protected defaultUsage(_: Interfaces.Command.Flag[]): string {
+  protected defaultUsage(): string {
+    // Docopts by default
+    if (this.opts.docopts === undefined || this.opts.docopts) {
+      return DocOpts.generate(this.command)
+    }
     return compact([
       this.command.id,
       this.command.args.filter(a => !a.hidden).map(a => this.arg(a)).join(' '),
diff --git a/src/help/docopts.ts b/src/help/docopts.ts
new file mode 100644
index 0000000..1df88c8
--- /dev/null
+++ b/src/help/docopts.ts
@@ -0,0 +1,178 @@
+import {Interfaces} from '..'
+
+type Flag = Interfaces.Command.Flag
+type Flags = Flag[]
+
+/**
+ * DocOpts - See http://docopt.org/.
+ *
+ * flag.exclusive: groups elements when one of the mutually exclusive cases is a required flag: (--apple | --orange)
+ * flag.exclusive: groups elements when none of the mutually exclusive cases is required (optional flags): [--apple | --orange]
+ * flag.dependsOn: specifies that if one element is present, then another one is required: (--apple --orange)
+ *
+ * @example
+ *  {
+ *      name: 'classnames',
+ *      required: true,
+ *      exclusive: ['suitenames']
+ *      ...
+ *  },{
+ *      name: 'suitenames',
+ *      type: 'array'
+ *      required: true
+ *      ...
+ *  }
+ *
+ *  Results in:
+ *      Usage: <%= command.id %> (-n <string> | -s <array>)
+ *
+ * @example
+ *  {
+ *      name: 'classnames',
+ *      ...
+ *      excludes: ['suitenames']
+ *  },{
+ *      name: 'suitenames',
+ *      ...
+ *  }
+ *
+ *  Results in:
+ *      Usage: <%= command.id %> [-n <string> | -s <string>]
+ *
+ * @example
+ *  {
+ *      name: 'classnames',
+ *      ...
+ *      dependsOn: ['suitenames']
+ *  },{
+ *      name: 'suitenames',
+ *      type: 'flag'
+ *      ...
+ *  }
+ *
+ *  Results in:
+ *      Usage: <%= command.id %> (-n <string> -s)
+ *
+ * TODO:
+ *  - Support nesting, eg:
+ *      Usage: my_program (--either-this <and-that> | <or-this>)
+ *      Usage: my_program [(<one-argument> <another-argument>)]
+ *
+ */
+export class DocOpts {
+  private flagMap: {[index: string]: Flag}
+
+  private flagList: Flags
+
+  public constructor(private cmd: Interfaces.Command) {
+    // Create a new map with references to the flags that we can manipulate.
+    this.flagMap = {}
+    this.flagList = Object.entries(cmd.flags || {})
+    .filter(([_, flag]) => !flag.hidden)
+    .map(([name, flag]) => {
+      this.flagMap[name] = flag
+      return flag
+    })
+  }
+
+  public static generate(cmd: Interfaces.Command): string {
+    return new DocOpts(cmd).toString()
+  }
+
+  public toString(): string {
+    const opts = ['<%= command.id %>']
+    if (this.cmd.args) {
+      opts.push(...this.cmd.args?.map(arg => `[${arg.name.toUpperCase()}]`))
+    }
+    try {
+      opts.push(...Object.values(this.groupFlagElements()))
+    } catch (error) {
+      // If there is an error, just return no usage so we don't fail command help.
+      opts.push(...this.flagList.map(flag => {
+        const name = flag.char ? `-${flag.char}` : `--${flag.name}`
+        if (flag.type === 'boolean') return name
+        return `${name}=<value>`
+      }))
+    }
+    return opts.join(' ')
+  }
+
+  private groupFlagElements(): {[index: string]: string} {
+    const elementMap: {[index: string]: string} = {}
+
+    // Generate all doc opt elements for combining
+    // Show required flags first
+    this.generateElements(elementMap, this.flagList.filter(flag => flag.required))
+    // Then show optional flags
+    this.generateElements(elementMap, this.flagList.filter(flag => !flag.required))
+
+    for (const flag of this.flagList) {
+      if (Array.isArray(flag.dependsOn)) {
+        this.combineElementsToFlag(elementMap, flag.name, flag.dependsOn, ' ')
+      }
+
+      if (Array.isArray(flag.exclusive)) {
+        this.combineElementsToFlag(elementMap, flag.name, flag.exclusive, ' | ')
+      }
+    }
+
+    // Since combineElementsToFlag deletes the references in this.flags when it combines
+    // them, this will go through the remaining list of uncombined elements.
+    for (const remainingFlagName of Object.keys(this.flagMap)) {
+      const remainingFlag = this.flagMap[remainingFlagName] || {}
+
+      if (!remainingFlag.required) {
+        elementMap[remainingFlag.name] = `[${elementMap[remainingFlag.name] || ''}]`
+      }
+    }
+    return elementMap
+  }
+
+  private combineElementsToFlag(
+    elementMap: {[index: string]: string},
+    flagName: string,
+    flagNames: string[],
+    unionString: string,
+  ): void {
+    if (!this.flagMap[flagName]) {
+      return
+    }
+    let isRequired = this.flagMap[flagName]?.required
+    if (typeof isRequired !== 'boolean' || !isRequired) {
+      isRequired = flagNames.reduce(
+        (required: boolean, toCombine) => required || this.flagMap[toCombine]?.required || false,
+        false,
+      )
+    }
+
+    for (const toCombine of flagNames) {
+      elementMap[flagName] = `${elementMap[flagName] || ''}${unionString}${elementMap[toCombine] || ''}`
+      // We handled this flag, don't handle it again
+      delete elementMap[toCombine]
+      delete this.flagMap[toCombine]
+    }
+    if (isRequired) {
+      elementMap[flagName] = `(${elementMap[flagName] || ''})`
+    } else {
+      elementMap[flagName] = `[${elementMap[flagName] || ''}]`
+    }
+    // We handled this flag, don't handle it again
+    delete this.flagMap[flagName]
+  }
+
+  private generateElements(elementMap: {[index: string]: string} = {}, flagGroups: Flags): string[] {
+    const elementStrs = []
+    for (const flag of flagGroups) {
+      let type = ''
+      // not all flags have short names
+      const flagName = flag.char ? `-${flag.char}` : `--${flag.name}`
+      if (flag.type === 'option') {
+        type = flag.options ? ` ${flag.options.join('|')}` : ' <value>'
+      }
+      const element = `${flagName}${type}`
+      elementMap[flag.name] = element
+      elementStrs.push(element)
+    }
+    return elementStrs
+  }
+}
diff --git a/src/interfaces/help.ts b/src/interfaces/help.ts
index 5c07168..970aa9b 100644
--- a/src/interfaces/help.ts
+++ b/src/interfaces/help.ts
@@ -28,4 +28,8 @@ export interface HelpOptions {
    * http://www.gnu.org/software/help2man/#--help-recommendations
    */
   usageHeader?: string;
+  /**
+   * Use docopts as the usage. Defaults to true.
+   */
+  docopts?: boolean;
 }
diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts
index 806ed0c..6694a06 100644
--- a/src/interfaces/parser.ts
+++ b/src/interfaces/parser.ts
@@ -109,6 +109,8 @@ export type FlagProps = {
   helpGroup?: string;
   hidden?: boolean;
   required?: boolean;
+  dependsOn?: string[];
+  exclusive?: string[];
 }
 
 export type BooleanFlagProps = FlagProps & {
@@ -124,8 +126,6 @@ export type OptionFlagProps = FlagProps & {
 }
 
 export type FlagBase<T, I> = FlagProps & {
-  dependsOn?: string[];
-  exclusive?: string[];
   exactlyOne?: string[];
   /**
    * also accept an environment variable as input


Test patch:
diff --git a/test/help/docopts.test.ts b/test/help/docopts.test.ts
new file mode 100644
index 0000000..1b99788
--- /dev/null
+++ b/test/help/docopts.test.ts
@@ -0,0 +1,237 @@
+import {expect} from 'chai'
+import {DocOpts} from '../../src/help/docopts'
+import {Flags} from '../../src'
+
+describe('doc opts', () => {
+  it('shows required string field', async () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.string({
+        name: 'testFlag',
+        description: 'test',
+        required: true,
+        char: 'f',
+      }),
+    }} as any)
+    expect(usage).to.contain(' -f <value>')
+  })
+  it('shows optional boolean field', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.boolean({
+        name: 'testFlag',
+        description: 'test',
+        char: 'f',
+      }),
+    }} as any)
+    // boolean fields don't have a value
+    expect(usage).to.contain(' [-f]')
+  })
+  it('shows no short char', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.enum({
+        name: 'testFlag',
+        description: 'test',
+        options: ['a', 'b'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' [--testFlag a|b]')
+  })
+  it('shows url type', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+      }),
+    }} as any)
+    expect(usage).to.contain(' [-s <value>]')
+  })
+  it('does not show hidden type', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+        hidden: true,
+      }),
+    }} as any)
+    expect(usage).to.not.contain(' [-s <value>]')
+  })
+
+  it('shows optional one-way depended fields', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        dependsOn: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' [-f <value> -s <value>]')
+  })
+  it('shows one-way depended field on required field', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+        required: true,
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        dependsOn: ['testFlag'],
+      }),
+    }} as any)
+    // If a flag depends on a required flag, then it is optional.
+    // So this should technically be "(-f <value> [-s <value>])" but
+    // does that even make sense anymore since -f will always be there?
+    // Maybe it should be just "-f <value> [-s <value>]""
+    expect(usage).to.contain(' (-f <value> -s <value>)')
+  })
+  it('shows required one-way depended field on optional field', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        required: true,
+        dependsOn: ['testFlag'],
+      }),
+    }} as any)
+    // If the required flag depends on an optional, it isn't really optional.
+    expect(usage).to.contain(' (-f <value> -s <value>)')
+  })
+  it('shows optional one-way exclusive fields', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        exclusive: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' [-f <value> | -s <value>]')
+  })
+  it('shows one-way exclusive field on required field', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+        required: true,
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        exclusive: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' (-f <value> | -s <value>)')
+  })
+  it('shows required one-way exclusive field on optional field', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        required: true,
+        exclusive: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' (-f <value> | -s <value>)')
+  })
+
+  it('shows option one-way exclusive field on optional field', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        required: true,
+        exclusive: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' (-f <value> | -s <value>)')
+  })
+  it('shows optional exclusive fields defined twice', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+        exclusive: ['testFlag2'],
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        exclusive: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' [-s <value> | -f <value>]')
+  })
+  it('shows optional two-way depended fields', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+        dependsOn: ['testFlag2'],
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        dependsOn: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' [-s <value> -f <value>]')
+  })
+
+  it('shows required two-way depended fields', () => {
+    const usage = DocOpts.generate({flags: {
+      testFlag: Flags.url({
+        name: 'testFlag',
+        description: 'test',
+        char: 's',
+        required: true,
+        dependsOn: ['testFlag2'],
+      }),
+      testFlag2: Flags.string({
+        name: 'testFlag2',
+        description: 'test',
+        char: 'f',
+        required: true,
+        dependsOn: ['testFlag'],
+      }),
+    }} as any)
+    expect(usage).to.contain(' (-s <value> -f <value>)')
+  })
+})
diff --git a/test/help/format-command-with-options.test.ts b/test/help/format-command-with-options.test.ts
index 73f38b4..d59961e 100644
--- a/test/help/format-command-with-options.test.ts
+++ b/test/help/format-command-with-options.test.ts
@@ -78,7 +78,8 @@ multiline help`
       }
   })
   .it('handles multi-line help output', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [-f <value>] [--force] [--ss] [-r
+    <value>] [-l <value>]
 
 ARGUMENTS
   APP_NAME  app to use
@@ -127,7 +128,8 @@ ALIASES
         }
     })
     .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [-f <value>] [--force] [--ss] [-r
+    <value>]
 
 ARGUMENTS
   APP_NAME
@@ -175,7 +177,8 @@ ALIASES
         }
     })
     .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [-f <value>] [--force] [--ss] [-r
+    <value>]
 
 ARGUMENTS
   APP_NAME
@@ -226,7 +229,7 @@ ALIASES
         }
     })
     .it('outputs command description with values after a \\n newline character', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [--force]
 
 ARGUMENTS
   APP_NAME  app to use
@@ -266,7 +269,7 @@ DESCRIPTION
         }
     })
     .it('outputs flag enum', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--myenum a|b|c]
 
 OPTIONS
   --myenum=a|b|c`))
@@ -287,7 +290,8 @@ OPTIONS
         flag3: flags.string({description: 'flag3 desc'}),
       }
     }).it('outputs with default flag options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [ARG1] [ARG2] [ARG3]
+  $ oclif apps:create [ARG1] [ARG2] [ARG3] [--flag1 <value>] [--flag2
+    <value>] [--flag3 <value>]
 
 ARGUMENTS
   ARG1  [default: .]
@@ -308,7 +312,7 @@ OPTIONS
         }
     })
     .it('outputs with with no options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--opt]
 
 OPTIONS
   --[no-]opt`))
diff --git a/test/help/format-command.test.ts b/test/help/format-command.test.ts
index fe2f703..b6dc28f 100644
--- a/test/help/format-command.test.ts
+++ b/test/help/format-command.test.ts
@@ -57,7 +57,8 @@ multiline help`
       }
   })
   .it('handles multi-line help output', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [--json] [-f <value>] [--force] [--ss]
+    [-r <value>] [-l <value>]
 
 ARGUMENTS
   APP_NAME  app to use
@@ -111,7 +112,8 @@ ALIASES
         }
     })
     .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [--json] [-f <value>] [--force] [--ss]
+    [-r <value>]
 
 ARGUMENTS
   APP_NAME
@@ -165,7 +167,8 @@ ALIASES
         }
     })
     .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [--json] [-f <value>] [--force] [--ss]
+    [-r <value>]
 
 ARGUMENTS
   APP_NAME
@@ -249,7 +252,7 @@ DESCRIPTION
         }
     })
     .it('outputs command description with values after a \\n newline character', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [APP_NAME]
+  $ oclif apps:create [APP_NAME] [--json] [--force]
 
 ARGUMENTS
   APP_NAME  app to use
@@ -320,7 +323,7 @@ DESCRIPTION
         }
     })
     .it('outputs flag enum', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--myenum a|b|c]
 
 FLAGS
   --myenum=<option>  the description
@@ -340,7 +343,7 @@ FLAGS
         }
     })
     .it('outputs flag enum with helpValue', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--myenum a|b|c]
 
 FLAGS
   --myenum=a|b|c`))
@@ -363,7 +366,8 @@ FLAGS
         flag3: flags.string({description: 'flag3 desc'}),
       }
     }).it('outputs with default flag options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create [ARG1] [ARG2] [ARG3]
+  $ oclif apps:create [ARG1] [ARG2] [ARG3] [--flag1 <value>] [--flag2
+    <value>] [--flag3 <value>]
 
 ARGUMENTS
   ARG1  [default: .]
@@ -386,7 +390,7 @@ FLAGS
         }
     })
     .it('outputs with with no options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--opt]
 
 FLAGS
   --[no-]opt`))
@@ -405,7 +409,7 @@ FLAGS
         }
     })
     .it('outputs flag summary and description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--opt <value>]
 
 FLAGS
   --opt=<value>  one line summary
@@ -430,7 +434,7 @@ FLAG DESCRIPTIONS
         }
     })
     .it('outputs flag summary and single line description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--opt <value>]
 
 FLAGS
   --opt=<value>  one line summary
@@ -454,7 +458,7 @@ FLAG DESCRIPTIONS
         }
     })
     .it('outputs long flag summary and single line description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE
-  $ oclif apps:create
+  $ oclif apps:create [--opt <value>]
 
 FLAGS
   --opt=<value>  one line summaryone line summaryone line summaryone line
diff --git a/test/helpers/init.js b/test/helpers/init.js
index 35468b3..dce826a 100644
--- a/test/helpers/init.js
+++ b/test/helpers/init.js
@@ -4,7 +4,8 @@ process.env.NODE_ENV = 'development'
 
 const chai = require('chai')
 const chaiAsPromised = require('chai-as-promised')
-chai.use(chaiAsPromised);
+
+chai.use(chaiAsPromised)
 
 global.oclif = global.oclif || {}
 global.oclif.columns = 80