Commands¶
Reference for every Phast command. phast help <command> shows the same information in
the terminal.
Global options¶
Available on every command:
| Option | Description |
|---|---|
-q, --quiet |
Suppress all output except errors |
-v, --verbose |
Increase verbosity: -v phase timings, -vv per-package detail |
--cache-dir <dir> |
Override the cache directory (env: PHAST_CACHE_DIR) |
-V, --version |
Print the Phast version |
-h, --help |
Print help |
Unless noted otherwise, commands exit 0 on success and non-zero on failure.
Project options¶
Two more options recur throughout, and mean the same thing wherever they appear:
| Option | Description |
|---|---|
--project-dir <dir> |
Directory to operate in; defaults to the current directory |
--member <path> |
Workspace member to target, by its path relative to the workspace root |
--member names a member by path, exactly as phast workspace prints it
(packages/logger), not by package name. It requires an active workspace; outside one
it fails with:
A path matching no member fails with 'packages/loger' is not a workspace member, so a
typo is reported rather than quietly resolved to something nearby.
Standing inside a member's directory means the same as naming it. Every command that
takes --member scopes itself to the member containing the working directory when the
flag is absent, so passing the flag and running from the right place are interchangeable.
Not every command takes both. lock, autoload, audit, and migrate take
--project-dir but no --member: the lock, the installed tree, and an advisory report
each cover the project as a whole. init, cache, and cli take neither; init names
its directory as a positional argument, and the other two operate outside any project.
phast init [path]¶
Scaffold a new package: package name, type, description, PHP version, and optional
src//tests/ directories with PSR-4 mappings derived from the package name. Writes
phast.toml into path (created if needed; defaults to the current directory). Fails
if a phast.toml already exists there.
Anything not given as an option is prompted for. A prompt is skipped when its option is
supplied, so phast init --name acme/blog still asks for the rest. Prompting is skipped
altogether with --yes, or whenever stdin is not a terminal, which is what makes init
usable from a script, a Dockerfile, or a CI job.
| Option | Description |
|---|---|
--name <vendor/package> |
Package name. Defaults to $USER/<directory> |
--type <type> |
library, project, or phast-plugin. Defaults to library |
--description <text> |
Package description. Omitted from the manifest when not given |
--php <constraint> |
PHP version constraint, e.g. ^8.4. Defaults to the detected PHP |
--no-src |
Do not create src/ |
--no-tests |
Do not create tests/ |
-y, --yes |
Take the defaults for anything not given, without prompting |
Package names are normalized to lowercase, as everywhere else in Phast.
The PHP constraint is the one field with no fallback. When no runtime can be detected
and --php was not given, a run that cannot prompt fails and writes nothing rather than
guessing a version that would surface as a resolution failure later.
phast install¶
Install dependencies. Resolves only when phast.lock is missing or out of sync with
phast.toml; otherwise installs straight from the lock. Downloads missing packages,
generates vendor/autoload.php and vendor/bin/ stubs, and fires install lifecycle
scripts.
| Option | Description |
|---|---|
--no-dev |
Skip installing [require-dev] packages; the lock still records them |
--dry-run |
Show what would be installed; write nothing and run no hooks |
--no-scripts |
Skip all lifecycle script hooks |
-a, --classmap-authoritative |
Trust the classmap exclusively; drop the PSR filesystem fallback |
--registry-url <url> |
Resolve against this single registry, ignoring the manifest repositories |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Only download one workspace member's dependency closure |
phast update [packages...]¶
Re-resolve to the newest versions allowed by phast.toml. With package arguments, all
other locked packages are pinned to their current versions, so only the named ones
(and packages only they need) can change; naming a package not present in the lock is
an error. Re-downloads changed packages and regenerates the autoloader.
| Option | Description |
|---|---|
--no-dev |
Skip installing [require-dev] packages; the lock still records them |
--dry-run |
Show what would change; write nothing and run no hooks |
--no-scripts |
Skip all lifecycle script hooks |
--registry-url <url> |
Resolve against this single registry |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Take the package list from one workspace member |
In a workspace, --member (or a working directory inside one) supplies the package list
when none was named: phast update --member packages/api updates what that member
requires, and nothing else. Naming packages explicitly wins over the member, being the
more specific answer. Either way the lock stays workspace-wide, so this chooses which
packages may move, not which part of the lock is rewritten.
phast add <packages...>¶
Add dependencies and install. Each argument is vendor/package or
vendor/package:constraint. Updates the constraint in place when the package is already
present; fails if it is present in the opposite section.
Given no constraint, Phast resolves the whole project with the package required and
records ^major.minor of the version that came out, so the constraint written is one that
resolves against everything else you depend on. Platform packages (php, ext-*) are
recorded as *: they describe the environment, not a version range to track. A package
that nothing installs, because another one carries it through [provide] or [replace],
is recorded as * too: there is no version to derive a range from, and an interface
states what must be available rather than a range to follow.
| Option | Description |
|---|---|
--dev |
Add to [require-dev] instead of [require] |
--no-install |
Update phast.toml only; skip the install |
--registry-url <url> |
Resolve against this single registry |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Target a workspace member's manifest |
Inside a workspace¶
A workspace root declares no dependencies of its own, so add at the root is refused:
Within a member, what gets recorded depends on what the workspace already answers for the package:
- The root pins it in
[workspace.dependencies]. The member records{ workspace = true }rather than spelling out a constraint the root owns. Giving an explicit constraint that disagrees with the pin is refused, not narrowed: only the root decides what every member gets. Dropping the constraint inherits it; changing it means changing the pin at the root. - A sibling member declares it. The constraint is derived as
^major.minorfrom that member's[package].version, with no registry involved: the package is satisfied from the source tree, so there is nothing to look up. An explicit constraint wins, being the more specific answer. A sibling that declares no version is an error rather than a guess, naming the manifest to fix.
--no-install skips the download, not the network: choosing a version requires a
registry. When there is nothing to choose, nothing is asked. That covers a constraint
given on the command line, a package the root pins, and a sibling member, so
phast add psr/log:^3.0 --no-install stays fully offline, and so does adding a pinned or
sibling package.
phast remove <packages...>¶
Remove dependencies and install. Validates that every named package exists before modifying anything; drops a section that becomes empty.
| Option | Description |
|---|---|
--dev |
Remove from [require-dev] instead of [require] |
--no-install |
Update phast.toml only; skip the install |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Target a workspace member's manifest |
Like add, remove is refused at a workspace root, which has no dependencies of its own
to remove: target a member with --member <path>.
phast lock¶
Resolve and write phast.lock without downloading anything or generating the
autoloader. A no-op when the lock is already in sync.
| Option | Description |
|---|---|
--refresh |
Re-resolve even when the lock is fresh, keeping locked versions where possible |
--registry-url <url> |
Resolve against this single registry |
--project-dir <dir> |
Project directory (default: current directory) |
phast outdated¶
Compare locked versions against the newest available in the registry. Each outdated
package is classified patch, minor, or major (major also means: outside your
current constraint). Exits 1 when anything is outdated, making it usable as a CI
gate.
| Option | Description |
|---|---|
--no-dev |
Check production packages only |
--format <text\|json> |
Output format (default text) |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Restrict to a workspace member's direct requirements |
There is deliberately no --registry-url. outdated always asks the repositories the
manifest declares, so the report describes the registries the project actually resolves
against.
phast autoload¶
Regenerate the autoloader from phast.lock, offline: rebuild vendor/autoload.php
and the maps under vendor/phast/, and relink vendor/bin/ stubs, using the
installed tree as-is. No resolution, no downloads. Errors when no lock file exists.
Fires the pre-autoload-dump and post-autoload-dump script hooks.
| Option | Description |
|---|---|
--no-dev |
Exclude dev autoload rules |
--no-scripts |
Skip all lifecycle script hooks |
-a, --classmap-authoritative |
Trust the classmap exclusively; drop the PSR filesystem fallback |
--project-dir <dir> |
Project directory (default: current directory) |
phast run [script] [-- args...]¶
Run a named script from [scripts]. Without a script name, lists all available
scripts. Arguments after -- are appended to the last command of the script.
| Option | Description |
|---|---|
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Run the script from a member's manifest, in its directory |
phast show [packages...]¶
Show installed packages from phast.lock (offline). Without arguments, lists every
package; with arguments, shows a detail view per package including its requirements.
| Option | Description |
|---|---|
--format <text\|json> |
Output format (default text) |
--no-dev |
Exclude [require-dev] packages |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Restrict to a workspace member's direct requirements |
--member narrows the listing the same way outdated does: to that member's direct
requirements, the entries it can act on in its own manifest.
phast why <package>¶
Show which packages require the given package, reading only phast.lock (offline).
| Option | Description |
|---|---|
--tree |
Render all reverse-dependency paths up to the root |
--no-dev |
Trace production dependencies only |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Scope to a workspace member's manifest |
In a workspace, a requirement is attributed to the member that declared it rather than to
the root, which requires nothing of its own; each member's manifest is read separately so
the constraint keeps the name of whoever wrote it. --member answers for that member
only, since what a sibling requires does not answer what this member requires.
phast audit¶
Check locked packages against a security advisory database. Exits 1 when advisories
are found (after filters), 0 when clean. Always workspace-wide in a workspace.
| Option | Description |
|---|---|
--provider <packagist\|osv> |
Advisory source (default packagist) |
--no-dev |
Audit production packages only |
--since <YYYY-MM-DD> |
Only advisories published on or after this date |
--ignore <ID> |
Suppress an advisory by ID (repeatable, case-insensitive) |
--format <text\|json> |
Output format (default text) |
--project-dir <dir> |
Project directory (default: current directory) |
phast workspace¶
Show the workspace root and all discovered members (name, version, path), marking the
member containing the current directory. The paths it prints are exactly what --member
takes elsewhere.
| Option | Description |
|---|---|
--project-dir <dir> |
Project directory (default: current directory) |
phast fmt¶
Format phast.toml in canonical style, preserving comments. In a workspace this covers
the root and every member: each member is reported under its [package].name, the root
under root, which is the only name a manifest declaring no package can be given.
--member narrows it to one, and so does running the command from inside a member.
| Option | Description |
|---|---|
--check |
Verify only: exit 1 with a diff if formatting would change |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Format one workspace member's manifest |
phast validate¶
Check phast.toml offline: parse and type validation (the same pipeline install
uses), plus whole-manifest lints such as duplicate dependencies, self-dependencies,
dangling repository references, and orphaned [sources] entries. No network, no
resolution, no writes; a fast smoke test for CI, pre-commit hooks, or hand-editing.
Exits 1 on any error; warnings are reported but pass unless --strict.
In a workspace it checks the root and every member. Checking only the root would report
nothing, since in a workspace all dependencies live in members; the root is checked as
what it declares, its own [[repositories]] and [sources]. --member narrows the run
to one manifest, and so does running the command from inside a member. A standalone
project has no members, so it stays a single check.
Each finding from a member is prefixed with that member's [package].name, so one run
covering several manifests says which one it is talking about. Findings from the root, and
from a standalone project, carry no prefix: there is nothing to tell them apart from.
| Option | Description |
|---|---|
--strict |
Treat warnings as errors (exit non-zero on any) |
--project-dir <dir> |
Project directory (default: current directory) |
--member <path> |
Validate one workspace member's manifest |
phast migrate¶
Convert a composer.json to phast.toml, extracting inline credentials to auth.toml.
Non-destructive; see
Migrating from Composer.
| Option | Description |
|---|---|
--force |
Overwrite an existing phast.toml |
--project-dir <dir> |
Project directory (default: current directory) |
phast cache <size|clear>¶
Inspect or clear the disk cache.
| Option | Description |
|---|---|
-m, --metadata-only |
Only the metadata layer |
-a, --archives-only |
Only the archive layer |
phast cli <version|update>¶
Manage the Phast executable itself.
phast cli version¶
Print the running build's identity and provenance: the version and release name,
the build time, the commit it was built from, the rustc version, and the target
triple. Useful when reporting an issue. (The top-level --version prints just the
version line.)
phast cli update¶
Update Phast in place to the latest release. It checks the latest published release and, if it is newer than the running build, downloads the binary for your platform, verifies its sha256 checksum, and atomically replaces the running executable. If you are already on the latest version it does nothing.
Linux and macOS only. If the binary lives somewhere you cannot write (for example a root-owned directory), the update reports a permission error and points you at the install script to reinstall with the right permissions.