NAME
Punk::Command - the punk command line: registry, dispatcher and commands
SYNOPSIS
punk new MyApp # generate a new application
punk routes # the compiled routing table
punk api sync # stubs for operations added to the spec
punk config check # resolve configuration and every secret
punk doctor # versions, C ABIs, application health
punk console # a REPL with the application compiled
punk dev # Hyperman with restart-on-change
DESCRIPTION
Punk resolves and freezes everything at to_app, which makes a running application fast and opaque in equal measure. These commands open it up - what the router holds, what the specification maps to, what the configuration resolved to, and which C ABIs are in play.
Except for new, each finds the application by walking up from the current directory looking for app.psgi, and takes the class from that file's Class->to_app.
bin/punk is two lines over Punk::Command->main(@ARGV). Commands are specs in a registry; option parsing, generated help and the exit-code contract (0 success, 1 application or environment failure, 2 misuse) live in the dispatcher, so punk help X, punk X --help and every misuse path read one declaration and cannot drift.
THE REGISTRY
Punk::Command->register(serve => {
abstract => 'one line for the command list',
usage => '[options]',
desc => 'a paragraph for the help page',
options => [ { spec => 'port=i', arg => 'N',
doc => 'listen port', default => 5000 } ],
commands => { sync => \%subspec }, # nested verbs
code => sub { my ($opt, @args) = @_; ...; return 0 },
}, __PACKAGE__);
Two owners claiming one name croak naming both. Registration order is display order. A command body signals misuse with die { usage_error => $msg } (message, usage, exit 2); any other die becomes punk <cmd>: msg and exit 1.
An unknown command X gets one attempt at require Punk::Command::<ucfirst X>, whose load-time job is to call register - that is how a plugin distribution adds punk queue ... when it is installed, with no scanning and no cost on the common path.
register_doctor($label => \&probe) adds a row to the doctor report the same way.
TESTING
local $Punk::Command::OUT = $out_fh; # replaces STDOUT
local $Punk::Command::ERR = $err_fh; # replaces STDERR
my $code = Punk::Command->main(@argv);
main returns the exit code and honours the two filehandle globals, so a test gets code, stdout and stderr with no process spawn.
COMMANDS
new
Generates a running application; see Punk::Generate.
generate controller / generate model
Adds a controller or a Punk::Model class to an existing application - new scaffolds once, these add to it. The application class is read from app.psgi without executing anything, so a broken controller cannot stop you generating the fix. An existing file is refused without --force.
test
Runs prove -lr in the application root; extra arguments go to prove verbatim. --env sets PUNK_ENV for the run.
secret
One line of random key material (url-safe base64, or --hex) - the bytes punk.yml's session secret wants behind its $env reference. Nothing else is printed, so it pipes.
version
What punk --version prints: $Punk::VERSION, the only version there is.
routes
Prints method, path, target and guard count for every route, including API operations (from their mount, since the specification is the routing table there), websocket routes and mounted applications. Targets are recovered from the compiled coderef, so what you see is what the route resolved to rather than what was declared.
api sync
Adds a stub for every operation the specification declares that the controllers do not implement, and leaves everything else alone. Methods with no matching operation are reported, never removed. --dry-run reports without writing.
config check
Resolves config/punk.yml for an environment, reporting every $env, $file and $exec reference independently - so one missing secret does not hide the others - then loads it properly to apply the guardrail. Exits non-zero when anything failed, which makes it usable as a deployment gate.
doctor
Versions, C ABI resolution and, if run inside an application, its route and configuration counts. Plugin rows join via "THE REGISTRY"'s register_doctor.
console
A REPL with $app (the registrar), $psgi and $c (a throwaway context) in scope.
dev
Runs the application under Hyperman and restarts it when anything under lib/, config/ or root/templates/ changes. A compiled-at-boot application cannot hot-reload - there is no live structure to patch - so restarting is the honest implementation.
SEE ALSO
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)