Inline Scripts

PVX lets you declare a script's dependencies and Perl version requirements directly inside the script file itself. Instead of maintaining a separate cpanfile or passing flags on the command line every time you run the script, the metadata lives alongside the code that needs it.

Two formats are supported: a comment-based format modeled after PEP 723, and a POD-based format that fits naturally into Perl's documentation conventions.

Comment format

The comment format uses a fenced block of #-prefixed lines. The fence markers are # /// followed by the block name pvm to open, and # /// alone to close. Everything inside is TOML.

#!/usr/bin/env pvx
# /// pvm
# dependencies = [
#   "DBI",
#   "JSON::PP >= 4.0",
# ]
# perl_version = "5.30"
# description = "Database export script"
# ///

use DBI;
use JSON::PP;
# ... rest of script

The block can appear anywhere in the file, but convention is at the top, immediately after the shebang. PVX strips the leading # (note the space) from each line before parsing.

This format has one field that the POD format does not: exclude_newer. It accepts an ISO 8601 timestamp and tells PVX to ignore any module releases published after that date, giving you time-pinned reproducibility without a lockfile.

#!/usr/bin/env pvx
# /// pvm
# dependencies = ["Moo", "Type::Tiny"]
# perl_version = "5.36"
# exclude_newer = "2024-01-01T00:00:00Z"
# ///

POD format

The POD format uses a standard =begin / =end block with the section name pvm. The content inside is the same TOML, but without comment prefixes. This is often easier to read in scripts that already have POD documentation.

#!/usr/bin/env pvx

=begin pvm
dependencies = [
  "DBI",
  "JSON::PP >= 4.0",
]
perl_version = "5.30"
description = "Database export script"
=end pvm
=cut

use DBI;
use JSON::PP;
# ... rest of script

The =cut after =end pvm is required for well-formed POD and tells Perl's parser to return to code mode. Omitting it will cause warnings from any tool that processes your POD.

Fields

dependencies

An array of module names to install before the script runs. Each entry is a string. Version constraints use the >= operator, separated from the module name by a space.

dependencies = [
  "DBI",
  "Moo >= 2.0",
  "JSON::PP >= 4.0",
]
perl_version

The minimum Perl version the script requires. PVX will refuse to run the script under an older Perl and will select an appropriate version if multiple are available.

perl_version = "5.36"
description

A short human-readable description of what the script does. Optional. Not used by PVX at runtime — it is documentation for anyone reading the file.

description = "Export database tables to JSON"
exclude_newer (comment format only)

An ISO 8601 timestamp. PVX will not install any module version released after this date. This gives scripts a form of time-based reproducibility: the exact same module versions will be resolved on any machine, on any date, without requiring a lockfile.

exclude_newer = "2024-06-01T00:00:00Z"

How it works

When you run pvx script.pl, PVX reads the file and checks for inline metadata in both formats. The comment format is checked first; if no # /// pvm block is found, PVX looks for a =begin pvm block.

If metadata is found and declares dependencies, PVX installs them into the script's execution environment before running the script. The installation respects the isolation level in effect — with --isolation clean, dependencies are installed into a fresh, empty environment that is discarded after the run.

# Run with full isolation — dependencies installed fresh, nothing from your
# system or project environment leaks in
pvx --isolation clean script.pl

This combination — inline metadata plus clean isolation — is what makes a script truly self-contained. You can copy the file to any machine with PVX installed and run it without any prior setup.

See the Isolation Levels guide for a full explanation of how the three isolation levels interact with dependency installation.

Auto-dependency detection

Inline metadata is not required. When no metadata block is present, PVX can still infer dependencies by scanning the script for use and require statements using regex heuristics. This works well for straightforward cases.

# No metadata block needed — PVX detects DBI and Moo from the use statements
#!/usr/bin/env pvx
use DBI;
use Moo;

my $dbh = DBI->connect(...);

For scripts with dynamic requires, conditional loading, or complex import patterns, the regex approach can miss dependencies or pick up false positives. In those cases, psc analyze provides more precise detection by parsing the script with a full tree-sitter AST instead of pattern matching on text.

psc analyze script.pl

The analysis output lists the modules psc identified. You can use that list to write an accurate dependencies block in your script's metadata.

Choosing a format

Both formats parse the same TOML fields and behave identically at runtime, with the single exception of exclude_newer, which is only available in the comment format.

Use the comment format if you want time-pinned reproducibility or if your script has no other POD. Use the POD format if your script has existing documentation and you want the metadata to sit alongside it in a familiar Perl idiom.

Do not use both formats in the same file. PVX uses the first one it finds and ignores any subsequent block.