Skip to content

Add [help] recipe attribute so just <recipe> --help runs just --usage <recipe> #3064

Closed

Description

@pygarap

Summary

Add a new recipe attribute, [help], that makes just <recipe> --help print the recipe usage output (same as just --usage <recipe>) and exit, without running the recipe.

Motivation

just --usage <recipe> already generates a useful usage block, including parameter help added via [arg(..., help="...")]. For recipes that are meant to be invoked like small CLIs, it is more natural to support the common pattern just <recipe> --help.

Proposed behavior

Given a recipe marked with [help]:

  • just <recipe> --help behaves exactly like just --usage <recipe>.
  • The recipe body is not executed.
  • Exit code is 0.

If a recipe is not marked with [help], behavior is unchanged.

Example

[help]
[arg("FLAG", long="flag", value="--flag", help="Enable the optional behavior")]
[arg("MODE", long="mode", value="--mode <mode>", help="Select a mode (example: fast, safe)")]
[arg("TARGET", long="target", value="--target <path>", help="Path to operate on")]
do-thing FLAG="" MODE="" TARGET="":
  echo "running with {{FLAG}} {{MODE}} {{TARGET}}"

Expected:

  • just do-thing --help prints the same output as just --usage do-thing, including the help="..." strings from the [arg(...)] annotations.
  • just do-thing --mode fast --target ./src --flag runs the recipe normally.

Compatibility notes

This is opt-in per recipe. Marking a recipe with [help] reserves the literal --help token for usage output for that recipe, rather than treating it as a normal argument value.

Activity

  1. casey commented on Jan 24, 2026

    @casey
    Owner

    I see the utility, but I'm not totally convinced, just because it's so close to just --usage recipe. One thing that I would prefer to do as a first step would be make --help take an argument, and then print out the usage string of that recipe if it's passed an argument. That would make just -h recipe work, which seems pretty convenient, and maybe gets you 80% of the benefit without having to add a new attribute, which then has to get added to every recipe where you want to use it.

  2. drewhoener commented on Mar 10, 2026

    @drewhoener

    I see the utility, but I'm not totally convinced, just because it's so close to just --usage recipe. One thing that I would prefer to do as a first step would be make --help take an argument, and then print out the usage string of that recipe if it's passed an argument. That would make just -h recipe work, which seems pretty convenient, and maybe gets you 80% of the benefit without having to add a new attribute, which then has to get added to every recipe where you want to use it.

    For me it's more about what devs are used to. I expect generally within a command system that command <sub_command> --help/-h will show the usage menu for that command, I'd support this feature

  3. djpeach commented on Mar 29, 2026

    @djpeach

    Also expected this to just be how it worked. Most cli tools have something like mycli --help (all commands docs) or mycli subcommand --help (just subcommand docs).

    Happy to see just --usage cmd I did not see that in the README (I searched for --help)

  4. andelink commented on Jul 18, 2026

    @andelink

    This is my preferred behavior as well, but I understand why it's not implemented.

    I add this functionality to my recipes via:

    • ? sigil
    • {{ usage(help) }} user-defined function at the start of every recipe
    • [arg("help", long, short, value="--help", help="show usage help")] recipe parameter

    It's a tad verbose but sufficient for me.

    Example

    # justfile
    
    set shell := ["zsh", "-fc"]
    set unstable
    set guards
    set positional-arguments
    
    
    # Was a recipe invoked with -h/--help? Dependencies use this to know when to short-circuit
    help := `echo ${(M)$(ps -o command= $PPID):#(-h|--help)}`
    
    # Place at start of recipe to run `just --usage recipe`
    # and short-circuit when invoked with `-h|--help`.
    usage(help) := (
      if (is_dependency() / help) =~ "true/(-h|--help)" {
        "exit ${${ZSH_SCRIPT:+0}:-1};"
      } else if help != "" {
        "just --usage " + quote(recipe_name()) + "; exit ${${ZSH_SCRIPT:+0}:-1};"
      } else {
        "true"
      }
    )
    
    
    # Normal recipes `usage()` exits with return code 1 for `?` sigil
    [arg("help", long, short, value="--help", help="show usage help")]
    checks help=help:
      @?{{ usage(help) }}
      prek run
    
    
    # Script recipes `usage()` exits with return code 0, no sigil
    [arg("www", long, short, value="^$", help="include www routers")]
    [arg("help", long, short, value="--help", help="show usage help")]
    routers www="^https?://www." help=help:
      #!/usr/bin/env zsh
      {{ usage(help) }}
      curl -ksSL http://traefik.localhost/api/http/routers | jq -r '
          map(select(.rule|trim|test("^Host"))
          | .rule | split("||")[]
          | match("`([^`]+)").captures[0].string)
          | unique[] | "http://\(.)"
        ' | egrep -v "{{ www }}"
    
    
    # Dependencies are short-circuited silently
    [arg("services", help="filter to these services")]
    [arg("context", long, short, help="docker context to use")]
    [arg("help", long, short, value="--help", help="show usage help")]
    [arg("pull", long, short, value="--pull always", help="pull down new images if available")]
    [arg("force-recreate", long, short, value="--force-recreate", help="recreate containers even if their config and image hasn't changed")]
    up context="default" force-recreate="" pull="" help=help *services="": checks && routers
      @?{{ usage(help) }}
      docker -c {{ context }} compose up --detach {{ force-recreate }} {{ pull }} {{ services }}
    

    Then with either -h or --help:

    ❯ just checks -h
    Usage: just checks [OPTIONS]
    Options:
      -h, --help show usage help
    
    
    ❯ just routers -h
    Usage: just routers [OPTIONS]
    Options:
      -w, --www include www routers
      -h, --help show usage help
    
    
    ❯ just up --help
    Usage: just up [OPTIONS] [services...]
    
    Arguments:
      [services...] filter to these services [default: ""]
    
    Options:
      -c, --context context docker context to use [default: "default"]
      -f, --force-recreate recreate containers even if their config and image hasn't changed
      -p, --pull pull down new images if available
      -h, --help show usage help
    

  5. DanteMarshal commented on Oct 15, 2026

    @DanteMarshal

    2. what devs are used to

    Exactly, --help | -h as an option displaying help about the current command (and preferably with details about the current subcommand chain) is a really helpful convention, and it's already used widely everywhere.
    My muscle memory automatically types -h or --help at the end of my commands very frequently 😅

    Based on @andelink 's workaround, here's what I'm using without dependence on zsh (tested on `bash` and `dash` (`sh`)):
    • Define this first (can be imported from a shared module)
      display_help(is_requested) := if is_requested == "true" { "just --usage " + quote(recipe_name()) + "; exit 1;" } else { "" }
      
    • Then use this pattern for every recipe
      [arg('is_help_requested', long='help', short='h', value='true', help='Show recipe usage')]
      <recipe> [params] is_help_requested='false' [*args]:
          #!/usr/bin/env bash
          {{ display_help(is_help_requested) }}
          # ... recipe commands ...
      
    • Example
      [arg('args', help='Test runner arguments; place options after --')]
      [arg('is_help_requested', long='help', short='h', value='true', help='Show recipe usage')]
      test is_help_requested='false' *args:
          #!/usr/bin/env bash
          {{ display_help(is_help_requested) }}
          cargo x test "{{ args }}"
      
  6. casey commented on Oct 17, 2026

    @casey
    Owner

    I warmed up to this after realizing that we didn't even need a [help] attribute. I added this in #3755. Now, when a recipe that defines options but does not define --help or -h gets either, both options will print the recipe's usage string and stop execution. Defining either --help or -h is still fine, and will cause the option to be treated normally. --help cannot be used with recipes that do not accept options, since for those recipes, all arguments are positional and are not inspected by just, and treating -h and --help specially would break legal invocations.

    I'm unsure if we should add a way to allow recipes without options to support --help. I'm leaning towards not doing so, since the implementation is gross, and those recipes are simple anyways. However, it is an unfortunate difference in behavior.

  7. radiorambo commented on Oct 18, 2026

    @radiorambo

    @casey my workflow is that, I have several modules with many recipes which contain many flags. Most probably I wont remember them all.

    The way I want to discover them is first run, just foo --help, then based on the help text, i find recipe needed and then do just foo bar --help, to find all the supported flags or options and finally run my required complete command.

    Present implementation takes care of just foo bar --help but not just foo --help.

  8. casey commented on Oct 19, 2026

    @casey
    Owner

    @radiorambo Implemented in #3757.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions