Commands
Run vsa anywhere inside a project — it finds the project by the
nearest vsa.json in the folder or above.
vsa help # every command, grouped
vsa <command> --help # one command
Starting a project
| Command | What 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
| Command | What 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
| Command | What 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
| Command | What 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.
| Command | What 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:diff | The 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.
| Command | What 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.