gluegun icon indicating copy to clipboard operation
gluegun copied to clipboard

Displaying command specific help

Open mikeerickson opened this issue 5 years ago • 29 comments

Does any interface exist to display command specific help?

mikeerickson avatar Dec 23 '18 02:12 mikeerickson

@mikeerickson Hm, good question. Right now, no, but that's something we should add.

Something like:

$ movie imdb --help

... should display help for all commands under ./src/commands/imdb/.

For now, you can check it in your root command:

import { GluegunToolbox } from 'gluegun'

const HELP_MESSAGE = `
movie api
      
  Run \`movie api reset\` to reset your IMDB token.
      
`

module.exports = {
  name: 'api',
  run: async (toolbox: GluegunToolbox) => {
    const {
      meta: { commandInfo },
      print,
      parameters: { options }
    } = toolbox

    if (options.help || options.h) {
      print.info(HELP_MESSAGE)
      return
    }

    // do other things
  }
}

There is already a built-in capability to do this here:

https://github.com/infinitered/gluegun/blob/663972d05a01cb83852a39486a6f5f97abf7ad5b/src/toolbox/meta-tools.ts#L51-L66

However, it's not exposed with the full interface:

https://github.com/infinitered/gluegun/blob/663972d05a01cb83852a39486a6f5f97abf7ad5b/src/core-extensions/meta-extension.ts#L17

If we updated toolbox.meta.commandInfo() to take an argument and pass that on as the second argument, then it would be easy to implement this:

    const { meta, print } = toolbox
    print.info(meta.commandInfo(['api']))

We should also make it so this is essentially what happens when you do mycli foo bar --help by default, especially if you added the .help() call to the CLI builder.

I definitely want to do this.

jamonholmgren avatar Dec 23 '18 02:12 jamonholmgren

Being able to print what arguments a command expects would be nice too. Should I make that a separate ticket?

RichiCoder1 avatar Dec 31 '18 21:12 RichiCoder1

I think these two Items go hand in hand, definitely something that should be supported together. Same ticket, or separate but both should be supported.

mikeerickson avatar Jan 01 '19 01:01 mikeerickson

@RichiCoder1 @jamonholmgren

Back from holiday, figured I would chime in some more on this topic.

I have created CLIs internally using some homegrown libraries and one of the important features is the ability to show command specific help, including support for command attributes

This is how command specific help appears:

image

The content for command help is read from the command file


module.exports = {
  init: function(cli) {
    cli.arguments.commitlint = cli.arguments.c = cli.arguments.commitlint || cli.arguments.c || true;
  },
  name: "husky",
  disabled: false,
  description: "Install and configure husky module",
  flags: {
    "--commitlint, -c": "configures commitlint"
  },
  run: function(cli) {
    cli.arguments = cli.setDefaultFlags(cli, this.flags);

    this.hasOwnProperty("init") ? this.init(cli) : null;

    cli.print.info(`⚙️  Execute ${this.name} command`);
    cli.print.info(cli.strings.stringify(cli.arguments));
  }
};

mikeerickson avatar Jan 02 '19 19:01 mikeerickson

And for completeness sake, here is what the default help looks like (I am thinking of creating a plugin for gluegun to show this as default as opposed to current help) Or, I could just create a simple function for help interface to use

image

mikeerickson avatar Jan 02 '19 19:01 mikeerickson

I really love that @mikeerickson. Any help you can provide is greatly appreciated, as I'm focusing on some other aspects of Gluegun at the moment.

jamonholmgren avatar Jan 02 '19 21:01 jamonholmgren

I will surely add the necessary code to provide this functionality. Would you like me to implement similar UI to what I have shown above.

I was going to create a new plugin for personal use, but if you are keen to having this UI in core, I would prefer to do it there as well.

mikeerickson avatar Jan 02 '19 23:01 mikeerickson

I like the UI you created. It's clean, elegant, and impressive. I'd like to have it in core.

(Hint: It may require fixing a few tests that depend on the exact help message. What I would do is use the shouldContain matcher to be less dependent on exact output.)

jamonholmgren avatar Jan 03 '19 00:01 jamonholmgren

@jamonholmgren sounds good, I have already started the "Getting to know the source" process. I will do the primary help interface, then move onto the command help.

I am reviewing the cli.integration.ts tests right now.

mikeerickson avatar Jan 03 '19 00:01 mikeerickson

Feel free to join the #gluegun channel in http://community.infinite.red Slack to chat about this in realtime, Mike.

jamonholmgren avatar Jan 03 '19 00:01 jamonholmgren

@jamonholmgren I have already joined :-)

I have cross-posted questions about the Plugin API there, so no need to reply :-)

mikeerickson avatar Jan 03 '19 00:01 mikeerickson

@jamonholmgren OK, first pass is done (prettify the main cli help) I have not fixed the tests yet, but wanted to share this with you. There will be a few more tweaks (ie adding the Usage section, not sure how that is going to be implemented just yet)

image

mikeerickson avatar Jan 03 '19 02:01 mikeerickson

Looks awesome! Bit late, but let me know if yah need any help.

RichiCoder1 avatar Jan 03 '19 02:01 RichiCoder1

@RichiCoder1 So far, so good. Getting up to speed with the source code is the longest part, but that is going swimmingly.

One question, do you have place where you store application wide constants? I am sure I could track it down, but since you are here :-)

mikeerickson avatar Jan 03 '19 02:01 mikeerickson

I'm as a novice to the code base as you :). But from what I've seen so far, constants have been kept close to the most relevant place they're used. So colors in print-tools, plugin defaults with plugin-loader, etc. Correct @jamonholmgren?

RichiCoder1 avatar Jan 03 '19 02:01 RichiCoder1

Fair enough, that is where I have put it (unless told otherwise)

mikeerickson avatar Jan 03 '19 03:01 mikeerickson

@jamonholmgren Here are my proposed changes to the command object. I have only added flags property to the command object (if it is not supplied, it will operate as normal). Let me know if there are any other properites you might need on the flags property

module.exports = {
  name: 'prompty',
  description: 'Get user information',
  flags: {
    age: {
      alias: ['a', 'age'],
      description: 'Supply age',
    },
    shoe: {
      alias: ['s', 'shoe'],
      description: 'What shoes are you wearing',
      choices: ['Clown', 'Adidas', 'Nike'],
      initial: 'Adidas',
    },
  },
  run: async function(toolbox) {
    const { print } = toolbox

    print.info('Welcome to your CLI')
    let choices = this.flags.shoe.choices || ['Other']

    // let choices = this.flags.shoe.choices
    // text input
    const askAge = { type: 'input', name: 'age', message: 'How old are you?' }

    // multiple choice
    const askShoe = {
      type: 'list',
      name: 'shoe',
      message: 'What shoes are you wearing?',
      choices,
    }
    // ask a series of questions
    const questions = [askAge, askShoe]
    const { age, shoe } = await toolbox.prompt.ask(questions)
    console.log(age, shoe)
  },
}

mikeerickson avatar Jan 04 '19 01:01 mikeerickson

It looks awesome.

Minor nit: would you need to put shoe in the alias list if it's already the name of the flag? Perhaps just any aliases that aren't the name of the flag.

Also, you may want dashed: true|false as an option, since some flags might be --shoe=Adidas or -s Adidas (accessible in toolbox.parameters.options['shoe'] || toolbox.parameters.options['s']).

jamonholmgren avatar Jan 04 '19 01:01 jamonholmgren

@jamonholmgren makes good sense on the alias property. As far as the dashed property is concerned, I didnt realize that is how it works :-)

In my current CLI tool, the params are parsed using yargs-parser so that is not an issue to solve. But, I can see now how it is configured in gluegun. I will make these changes as well.

mikeerickson avatar Jan 04 '19 01:01 mikeerickson

@jamonholmgren Are the CLI arguments available on the toolbox object anywhere? Just thinking ahead as to how the user will access these arguments, or will I need to add code for that as well.

mikeerickson avatar Jan 04 '19 01:01 mikeerickson

@mikeerickson Apologies, I've been out of town. I'm not sure what you're asking, to be honest -- there is toolbox.parameters, is that what you're looking for?

jamonholmgren avatar Jan 11 '19 19:01 jamonholmgren

Is this dead? Is there any way to auto-document toolbox.parameters.options?

danawoodman avatar Sep 20 '19 19:09 danawoodman

@danawoodman Currently I don't have plans to add it (we haven't needed it for our CLIs yet), but if you have any ideas, I'm happy to review!

jamonholmgren avatar Sep 24 '19 19:09 jamonholmgren

@jamonholmgren I don't mind the proposals discussed here. Some way of passing in a definition of the options/flags that the given command supports then those are automatically documented/validated before hand. I find I'm writing a LOT of extra code to do this instead of Gluegun doing which I would hope.

Thinking out-loud:

options: [
  { option: '-y, --yes', type: boolean, default: true },
  { option: '-c, --config', type: string, required: true },
]

danawoodman avatar Sep 28 '19 18:09 danawoodman

I like this. We could possibly use yargs for this. It's currently only a devDependency, though -- we're using yargs-parser in production which has some of this capability but not all.

yargs has a pretty full featured capability:

https://github.com/yargs/yargs/blob/HEAD/docs/api.md

Perhaps see if we want to bring in all of yargs (if it's worth the extra kbs)?

jamonholmgren avatar Sep 30 '19 18:09 jamonholmgren

@jamonholmgren yeah, to me a few kb is fine esp since my CLI has to bundle the whole of Node so it already is bloated haha

Maybe the support is optional? Might be confusing though.

A way to be able to define typed options and have the CLI output help for them would be killer

danawoodman avatar Oct 03 '19 00:10 danawoodman

I managed to get a "auto-generated" help command to work without modifying Gluegun or adding yargs. I will say though, it is quite basic and doesn't support typed options. Another thing is, you have to run a function at the start of each command in order to capture the --help, -h flag.

I have created a gist (using TypeScript) with how I achieved it. Feel free to use it as a starting point. Again, very basic implementation and only displays the command, your description and available flags. If you use the files in the gist, when you run <brand> some command --help, you should get.

Screen Shot 2020-07-09 at 2 09 17 pm

HurricaneInteractive avatar Jul 09 '20 04:07 HurricaneInteractive

+1 interested to see how this develops! :)

benhickson avatar Sep 30 '21 16:09 benhickson

I realize this is an old issue, and not much progress has happened, but I do intend to work on it eventually. Perhaps a good option for hacking on my Twitch stream.

jamonholmgren avatar Jan 19 '22 19:01 jamonholmgren