VSA Monolith Framework

Commands

Run vsa anywhere inside a project — it finds the project by the nearest vsa.json in the folder or above.

Terminal
vsa help                 # every command, grouped
vsa <command> --help     # one command

Starting a project

CommandWhat it does
new <folder> A new project from the template: named, app renamed, a fresh git repository with a first commit, then setup. Takes --name=, --app=, --port=, --remote=, --no-setup, --template=
init [--yes] Adopt an app that already has the VSA structure but predates the CLI: writes its vsa.json from what the project already says. No template baseline is recorded, because a hand-migrated project was never taken from one
setup Fresh clone to running app: tools, GitHub access, .env, composer, APP_KEY, git hooks, doctor. Safe to re-run
doctor [--offline] CLI version, tools, PHP extensions, .env keys (values never printed), hooks, and whether the database, XAuth and CDN are reachable
self-update [--check], version git pull in ~/.vsa/cli, and the version this project needs. A project names it in vsa.json ("cli": ">=1.1.0"); an older CLI refuses to run and says to self-update

Pages and apps

CommandWhat it does
pages [app]Page → blade → module id → menu place, for the whole app
make:page <app> <Name>Component Page JS and blade, the app-file entry, a menu place and translation keys. A list page (search, filters, ListView, add/edit dialog, row menu) and a form page (one record, loaded on open and saved in place) also get a controller, a model and routes/api/<key>.php. Takes --type=list|form|blank, --api/--no-api, --nav=, --module=, --title=, --icon=, --dry-run
rename:page <app> <Old> <New>Files, global, root id, app file and every reference. Shows a plan first
rm:page <app> <Name>Removes the page JS, blade, app-file entry and menu place, and refuses while other files still use it. Takes --api, --yes, --force
make:app <key>A new app: app file, VSApp.config.js, script bundle and home page, with the look and base bundles copied from an existing app. Takes --title=, --from=<app>, --dry-run
rename:app <old> <new>App key, URL, folders and bundle. Shows a plan first

Quality

CommandWhat it does
check The rules; exit 1 on any error, and the project's pre-commit hook runs it. Takes --only=, --skip=, --strict, --quiet, --json, --list
upgrade [--yes] Update this project's framework files from the template — the files the template's vsa.json says it owns. A file you never edited is updated; one you both changed is left alone with the incoming version beside it as <file>.vsa-new. Nothing is written without --yes. Takes --from=<commit>, --only=<path,…>, --template=
permissions [app]Page modules, button codes, server checks, and the codes that are only ever checked in the browser

Build and run

CommandWhat it does
serve [port]
alias: dev
PHP server on 127.0.0.1, with browser reload and a check on every save. Before starting, the app is booted once and its shells rendered to verify the asset mode: --built stops if the app still renders raw tags, raw mode warns if it serves public/dist, and --built lists source files changed since the last build. Takes --no-reload, --no-check, --queue, --built, --no-asset-probe
build [app] This project's JS and CSS into content-hashed, minified chunks with source maps and a manifest.json in public/dist (esbuild). CDN files are never inlined — they stay version-pinned tags. The shell serves the chunks once the manifest exists. Takes --check, --no-minify, --no-sourcemap
cache:bust [--show] A new asset epoch in config/vsa.php, so raw-mode files are refetched after a deploy. Built chunks do not need it — their name is their hash. Takes --dry-run, --no-commit; vsa check fails a bumped epoch that was never staged

XAuth

A VSA app declares its apps, modules, permissions and reports in code. These commands push that catalog to the identity console and tell you when the two have drifted apart.

CommandWhat it does
xauth:register [--dry-run] The first push: project, apps, modules, permissions, reports, plan, a development subscription and its root. Writes XAUTH_APP_ID and XAUTH_BASE_URL into .env and points you at the console for the project credential. --no-tenancy pushes the product only; --token= replaces the sign-in prompt, which is what CI needs
xauth:sync [--dry-run] Push the code's catalog — modules, permissions, reports, the report filter dictionary and features — matched by code, deleting nothing. Report presentation is a three-way merge, so a console-side edit survives the next sync. With --console= this is the go-live import; --plan= selects the plan, --rotate-secret re-issues the application secret
xauth:diffThe code against the console, by code; exit 1 on any difference
xauth:export [--out=]The manifest file for the production console — no ids, no secrets
xauth:pull [--dry-run]The other half of the sync: takes the console's report presentation — name, category, filters, order, hidden, actions — back into config/apps. Identity and wiring stay the code's and are never rewritten

Reports

A report is two declarations in the app file and one handler class; the Reports page is the whole contract. These commands write and read those declarations — nothing is live until vsa xauth:sync pushes them.

CommandWhat it does
make:report <app> <code> A Report Center report: the app-file entry and a handler stub. The code is the render type and never changes afterwards — the console matches on it, so a new code is a new report. Takes --name=, --module=, --category=, --params=a|b, --permission=MOD.NAME or --open-access, --actions=, --hidden, --no-handler, --dry-run
reports [app]The Report Center reports the code declares
rename:report <app> <code> "<New name>"The display name only; the code stays
report:permission <app> <code> <MOD.NAME | --own | --open-access>Gate a report by an existing permission code, by its own report.<code>, or not at all

Translations

i18n:missing, i18n:sync, i18n:unused, i18n:review, i18n:import and i18n:normalize have a page of their own — Translations.

Adding a command

A new command is one file in commands/ with a meta block; bin/vsa.js picks it up from there. VSA_ROOT=<dir> points the CLI at another tree, which is how commands get tested against a copy of a project. To release: bump version in package.json, commit and push — developers get it with vsa self-update. When a project starts depending on a new command, raise its vsa.json cli constraint.