phi follows the contracts every Node project already obeys. Drop it in alongside npm/yarn/pnpm — separate phi.lock, no conflict.
In a project with a package.json:
phi installphi resolves the full transitive tree, scans every package, and either installs everything (if all safe), prompts on review-flagged ones, or aborts on a blocked one. Outputs phi.lock and phi-report.json.
To add a new dep:
phi install chalk@^5.0.0Updates the dependencies field, scans, installs.
To audit without installing:
phi audit| Command | Aliases | Behavior |
|---|---|---|
phi init | — | Create package.json + .gitignore + README.md. --yes for non-interactive; --force to overwrite. |
phi install [pkg…] | i, a | Scan and install. Args added to package.json (or to devDependencies with --save-dev). |
phi update [pkg…] | u | Re-resolve and install fresh, ignoring phi.lock. |
phi remove <pkg…> | rm | Drop from package.json, prune from lockfile + node_modules. |
phi audit | — | Resolve + scan, no extraction. Writes phi-report.json. |
phi do <script> [args…] | d | Run a script from package.json with node_modules/.bin on PATH. Pre/post hooks honored. |
phi exec <bin> [args…] | x | Run a binary from node_modules/.bin. Like npx, but no auto-install. |
phi dev | build | start | … | — | Direct shortcuts to phi do <name>. Available: dev, build, start, test, lint, preview, prod. |
phi outdated | — | Show direct deps with newer versions available, color-coded by major bump. |
phi why <pkg> | — | Print every dep chain from a root that reaches <pkg>. |
phi cache stat | — | Tarball cache size. |
phi cache clean | — | Prune cache. --older-than 30d default; --all wipes everything. |
phi version | — | Print version, commit, build date. |
phi help | — | Print available commands and flags. |
| Flag | Effect |
|---|---|
--allow-scripts a,b | Run lifecycle scripts only for the named packages. Default: never run. |
--frozen-lockfile | Require phi.lock to exactly cover package.json. CI mode. |
--no-lockfile | Ignore phi.lock and resolve fresh. |
--no-advisories | Skip OSV vulnerability database query (offline mode). |
--json | Emit phi-report.json to stdout, suppress UI. Review-flagged packages cause exit 1. |
--save-dev / -D | Write to devDependencies, moving from dependencies if needed. |
--save-peer | Write to peerDependencies. |
--save-exact / -E | Pin without caret prefix. |
phi.lock is generated on every install. Format mirrors npm's package-lock.json shape with extra score and verdict fields per entry. When present and it covers package.json, phi reuses it without re-resolving.
$XDG_CACHE_HOME/phi/tarballs/ (Linux/macOS) or %LOCALAPPDATA%\phi\tarballs\ (Windows), keyed by sha512 integrity. Repeat installs are near-instant.If your root package.json declares a workspaces field, phi aggregates dependencies from every workspace package, installs the union into the root node_modules/, and links each workspace into node_modules/<workspace-name> as a junction (Windows) or symlink (Unix).
{
"workspaces": ["packages/*"]
}
Both array form and {packages: [...]} object form are supported. Sibling references ("@org/utils": "*" from inside @org/app) bypass the registry — they resolve through the link.
phi reads .npmrc from $HOME and the project root (project wins on conflict). Supported settings:
registry=https://... — default registry override@scope:registry=https://... — scoped registry routing//host/path/:_authToken=... — bearer token, sent as Authorization: Bearer <token> to matching URLs${ENV_VAR} substitution — keep secrets out of committed files//npm.pkg.github.com/:_authToken=${GITHUB_PAT}
@my-org:registry=https://npm.pkg.github.com/
# GitHub Actions
- run: phi install --frozen-lockfile
- run: phi audit --json > phi-report.json
- run: jq '.summary.blocked == 0 and .summary.review == 0' phi-report.json
--frozen-lockfile requires phi.lock to exactly cover package.json; non-zero exit on drift. --json emits the full scan report to stdout and exits non-zero on any blocked or review-flagged package.