infinitered / gluegun

A delightful toolkit for building TypeScript-powered command-line apps.
MIT License
2.97k stars 147 forks source link

Displaying command specific help #427

Open mikeerickson opened 5 years ago

mikeerickson commented 5 years ago

Does any interface exist to display command specific help?

jamonholmgren commented 5 years ago

@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.

RichiCoder1 commented 5 years ago

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

mikeerickson commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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

jamonholmgren commented 5 years ago

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.

mikeerickson commented 5 years ago

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.

jamonholmgren commented 5 years ago

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.)

mikeerickson commented 5 years ago

@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.

jamonholmgren commented 5 years ago

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

mikeerickson commented 5 years ago

@jamonholmgren I have already joined :-)

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

mikeerickson commented 5 years ago

@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

RichiCoder1 commented 5 years ago

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

mikeerickson commented 5 years ago

@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 :-)

RichiCoder1 commented 5 years ago

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?

mikeerickson commented 5 years ago

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

mikeerickson commented 5 years ago

@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)
  },
}
jamonholmgren commented 5 years ago

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']).

mikeerickson commented 5 years ago

@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 commented 5 years ago

@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.

jamonholmgren commented 5 years ago

@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?

danawoodman commented 5 years ago

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

jamonholmgren commented 5 years ago

@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!

danawoodman commented 5 years ago

@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 },
]
jamonholmgren commented 5 years ago

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)?

danawoodman commented 5 years ago

@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

HurricaneInteractive commented 4 years ago

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

benhickson commented 3 years ago

+1 interested to see how this develops! :)

jamonholmgren commented 2 years ago

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.