CLI::Osprey(3pm)
π CLI::Osprey π
okay bestie, so this is the manual page for CLI::Osprey β full name CLI::Osprey(3pm), filed under User Contributed Perl Documentation, and honestly? it's iconic. grab a boba, we're doing this. π§β¨
π NAME π
CLI::Osprey β it's literally MooX::Options + MooX::Cmd + Sanity. that's the whole vibe. π
π VERSION π
version 0.09
π¦ SYNOPSIS π¦
so in Hello.pm you'd write:
package Hello;
use Moo;
use CLI::Osprey;
option 'message' => (
is => 'ro',
format => 's',
doc => 'The message to display',
default => 'Hello world!',
);
sub run {
my ($self) = @_;
print $self->message, "\n";
}
and then in hello.pl:
use Hello;
Hello->new_with_options->run;
that's it. that's the whole app. slay. π₯
π DESCRIPTION π
CLI::Osprey is a module that helps you write commandline apps using the M* OO modules β that's Moose, Moo, and Mo, bestie, all of them. β¨
the way it works is: you structure your app as one or more modules, and those modules get instantiated with the commandline arguments as attributes. arguments get parsed using Getopt::Long::Descriptive, and then β this is the good part β both long and short help messages and complete manual pages are generated automatically for you. no cap. π
your app can be a single command with options, or it can have sub-commands (think git-style, iykyk). and those sub-commands can be defined as modules (with their own options, obviously) or as simple coderefs. flexible queen energy. π
π Differences from MooX::Options π
so Osprey is deliberately similar to MooX::Options, and porting an app over from MooX::Options should be pretty simple in most cases. but there are a few important differences, and you need to know them:
- Osprey is pure-perl, with no mandatory XS dependencies. what does that mean for you? it means it can be used in fatpacked scripts, and in all those other situations where you might need to run on a bunch of diverse machines where a C compiler and control over the environment just aren't guaranteed. π
- Osprey's support for sub-commands is built in from the very beginning. we genuinely think this makes for a better experience than
MooX::Options+MooX::Cmd. periodt. π - here's a big one: while
MooX::Optionsrequires an option's primary name to be the same as the attribute that holds it, andMooX::Cmdderives a sub-command's name from the name of the module that implements it β Osprey separates these things. so your Perl identifier naming conventions do not get to dictate your command line interface. the CLI is the main character, not the variable name. π - Osprey doesn't use an automatic module finder (like
Module::Pluggable) to locate modules for sub-commands. their names are given explicitly. yes, that's a small amount of extra typing, but it gives you more control and less fragility. worth it. π«Ά
now, there are also a few things that MooX::Options has that Osprey lacks. they might get added in the future, but honestly I haven't seen the need yet. currently known missing features are JSON options, config_from_file support, autosplit, and autorange.
for JSON support though, you can just use a coercion on the attribute, turning it from a string into a ref via decode_json. problem solved, bestie. β¨
and if you want to default an app's options from a config file, you may want to do something like this in your script file:
use JSON 'decode_json';
use Path::Tiny;
MyApp->new_with_options(
map decode_json(path($_)->slurp),
grep -f,
"$ENV{HOME}/.myapprc"
)->run;
provided that prefer_commandline is true (and yes, that's the default π), any options in .myapprc will be used as defaults if that file exists β but they'll still be overrideable from the commandline. best of both worlds, no notes.
π« IMPORTED METHODS π«
these are the methods that get imported into a class that uses CLI::Osprey. free gifts, bestie. π
π new_with_options
parses commandline arguments, validates them, and calls the new method with the resulting parameters. any parameters you pass to new_with_options will also be passed to new β and the prefer_commandline import option controls which one overrides which. drama resolved. π
π option
the option keyword acts like has (and yes, it accepts all of the arguments that has does), but it also registers the attribute as a commandline option. see "OPTION PARAMETERS" for usage. β¨
π osprey_usage($code, @messages)
displays a short usage message β the same as if the app was invoked with the -h option. it also displays the lines of text in @messages, if any are passed in. and if $code is passed a defined value, it exits with that as a status. π
π osprey_help($code)
displays a more substantial usage message β the same as if the app was invoked with the --help option. and again, if $code is passed a defined value, it exits with that as a status.
π osprey_man
displays a manual page for the app, containing long descriptive text (if provided) about each command and option, then exits. it's giving documentation, it's giving self-care. πβ¨
π§ IMPORT PARAMETERS π§
the parameters you pass to use CLI::Osprey serve two roles: they customize Osprey's behavior, and they provide information about the app and its options for use in the usage messages. two jobs, one icon. π
πΈ abbreviate
default: true.
if abbreviate is set to a true value, then long options can be abbreviated to the point of uniqueness. meaning: --long-option-name can be called as --lon, as long as there are no other options starting with those same letters. also, an option can always be called by its full name, even if that full name is a prefix of some longer option's name. but if abbreviate is false, then options must always be called by their full names (or by a defined short name). no shortcuts, bestie. π«
πΈ added_order
default: true.
if added_order is set to a true value, then two options with the same order (or no order at all) will appear in the help text in the same order as their option keywords were executed. if it's false, they'll appear in alphabetical order instead. β¨
πΈ desc
default: none.
a short description of the command, to be shown at the top of the manual page β and in the listing of subcommands, if this command happens to be a subcommand. π
πΈ description_pod
default: none.
a description of any length, in POD format, to be included as the "DESCRIPTION" section of the command's manual page. go off, write a whole essay. π
πΈ extra_pod
default: none.
arbitrary extra POD to be included between the "DESCRIPTION" and "OPTIONS" sections of the manual page. it's the little bonus content between the chapters. π
πΈ getopt_options
default: ['require_order'].
contains a list of options to control option parsing behavior (see "Configuring Getopt::Long" in Getopt::Long). but β heads up β note that many of these are not actually helpful with Osprey, and using permute will likely break subcommands entirely. π also, MooX::Options calls this parameter flavour, just so you know the lore.
πΈ prefer_commandline
default: true.
if true, command-line options override key/value pairs passed to new_with_options. if false, the reverse is true. pick your fighter. βοΈ
πΈ preserve_argv
default: false.
if true, the @ARGV array will be localized for the duration of new_with_options, and will be left in the same state after option parsing as it was before. if false, the @ARGV array will be modified by option parsing β removing any recognized options, values, and subcommands, and leaving behind any positional parameters, or anything after and including a -- separator. so, yeah, know which one you want. π
πΈ usage_string
default: "USAGE: $program_name %o"
provides the header of the usage message printed in response to the -h option, or in response to an error during option processing. the format of the string is described in "$usage_desc" in Getopt::Long::Descriptive. β¨
πΈ on_demand
default: false
if set to a true value, the commands' modules won't be loaded at compile time β they'll only be loaded if the command is actually invoked. super useful for minimizing compile time if your application has a lot of commands, or if the commands are on the heavy side. ποΈ
but bestie, be careful: note that enabling this feature may interfere with the ability to fatpack the application. π
π OPTION PARAMETERS π
π doc
default: None.
documentation for the option, used in --help output. for best results, it should be no more than a short paragraph. keep it cute, keep it brief. β¨
π format
default: None (i.e. boolean).
the format of the option argument β same as Getopt::Long. an option with no format is a boolean, meaning it doesn't take an additional argument. the other formats are:
sβ stringiβ decimal integeroβ integer (supports0xfor hex,0bfor binary, and0for octal).fβ floating-point number
π format_doc
default: depends on format.
describes the type of an option's argument. for example, if the string option copy-to specifies a hostname, you can give it format_doc => "hostname" and it'll display as --copy-to hostname in the help text, instead of --copy-to string. it's giving customization. π
π hidden
default: false.
a "hidden" option will be recognized, but not listed in automatically generated documentation. it's there, it works, it's just shy. π
π negatable
default: false.
adds the --no- version of the option, which sets it to a false value. equivalent to ! in Getopt::Long. flip the switch, bestie. π
π option
default: Same as the attribute name, with underscores replaced by hyphens.
allows the command-line option for an attribute to differ from the attribute name β it's like init_arg, except for the commandline. freedom of expression. π¦
π long_doc
default: none.
long documentation of the option, for the manual page. this is POD, so POD formatting is available, and paragraphs need to be separated by "\n\n". if it's not provided, the short documentation will be used instead. no pressure. π
π order
default: None.
allows you to control the order that options are listed in the help text. options without an order attribute are sorted by the order their option statements are executed, if added_order is true β and by alphabetical order otherwise. options without an explicit order are placed as though they had order 9999, so use small values to sort before the automatically-sorted options, and values of 10000 or greater to sort at the end. do the math, queen. π’
π repeatable
default: false.
allows an option to be specified more than once. when used on a "boolean" option with no format, each appearance of the option will increment the value by 1 (equivalent to + in Getopt::Long). when used on an option with arguments, it produces an arrayref β one value per appearance of the option. β¨
π required
default: false.
this is a Moo/Moose feature honored by Osprey. a "required" attribute must be passed on the commandline, unless it's passed to the constructor. generated documentation will show the option as non-optional. no skipping it, bestie. π¨
π short
default: None.
gives an option a single-character "short" form β e.g. -v for --verbose. one letter, so much power. β‘
π spacer_before
default: false.
causes a blank line to appear before this option in help output. it's giving breathing room. π¬οΈ
π spacer_after
default: false.
causes a blank line to appear after this option in help output. symmetric queen. π¬οΈ
πΈ SUBCOMMANDS πΈ
Okay bestie, this is the part where things get iconic. An Osprey command can have subcommands, and those subcommands can have their own options, their own docs, their whole own thing β so you can build a whole complicated app under one single command like it's nothing. π Osprey parses the options for every command in the chain, then constructs them top-to-bottom, and each subcommand gets handed a reference to its parent. Family reunion but make it architecture. π«Ά
π Subcommand Classes
A subcommand can just be another class, and that class also says use CLI::Osprey. Like so, queen:
package MyApp;
use Moo;
use CLI::Osprey;
option verbose => (
is => 'ro',
short => 'v',
);
subcommand frobnicate => 'MyApp::Frobnicate';
package MyApp::Frobnicate;
use Moo;
use CLI::Osprey;
option target => (
is => 'ro',
format => 's',
);
sub run {
my ($self) = @_;
if ($self->parent_command->verbose) {
say "Be dangerous, and unpredictable... and make a lot of noise.";
}
$self->do_something_with($self->target);
}
π¦ Inline Subcommands
Now if making a whole separate class feels like too much (it's giving extra, honestly), you can also pass a coderef as the subcommand. Perfect for when a class would be excessive. Watch this:
package Greet;
use Moo;
use CLI::Osprey;
option target => (
is => 'ro',
default => "world",
);
subcommand hello => sub {
my ($self, $parent) = @_;
say "Hello ", $parent->target;
};
subcommand goodbye => sub {
my ($self, $parent) = @_;
say "Goodbye ", $parent->target;
};
And you'd invoke that whole vibe as greet --target world hello. β¨ Also important, no cap: inline subcommands are implemented using CLI::Osprey::InlineSubcommand. π
π THANKS π
Real talk, this module is based heavily on code from MooX::Options and takes strong inspiration from MooX::Cmd and MooX::Options::Actions. Massive shoutout to celogeek, Jens Reshack, Getty, Tom Bloor, and literally all the contributors to those modules. π«Ά Thanks to mst for prodding me to do this (the peer pressure worked, and it slayed). And thanks Grinnz for helping me update my dzillage. ππ
π AUTHOR π
Andrew Rodland <arodland@cpan.org>
π COPYRIGHT AND LICENSE π
This software is copyright (c) 2025 by Andrew Rodland.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
and that's the whole page bestie, you ate it UP π β¨
This was CLI::Osprey(3pm) from perl v5.42.0, dated 2026-01-16 β go forth and build something iconic, mwah ππ¦π
π¦ where this page comes from
- fedora 44: perl-CLI-Osprey 0.09-2.fc44 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms