Metadata-Version: 2.4
Name: virtualitics-cli
Version: 2.0.4
Summary: A command line interface for initializing, packaging, and deploying Custom Apps to the Virtualitics AI Platform from a local development environment.
Author-email: Virtualitics Engineering <engineering@virtualitics.com>
License: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.14
Requires-Dist: art>=6.1
Requires-Dist: build>=1.2.1
Requires-Dist: requests>=2.31.0
Requires-Dist: typer>=0.19.2
Provides-Extra: dev
Requires-Dist: ruff>=0.9.0; extra == 'dev'
Provides-Extra: optional
Requires-Dist: virtualitics-sdk>=1.26.0; extra == 'optional'
Provides-Extra: test
Requires-Dist: pip-audit>=2.7.0; extra == 'test'
Requires-Dist: pytest-cov>=5.0.0; extra == 'test'
Requires-Dist: pytest>=8.1.1; extra == 'test'
Description-Content-Type: text/markdown

# Virtualitics AI Platform CLI

A command line interface for initializing, packaging, and deploying Custom Apps to the Virtualitics AI Platform (VAIP) from a local development environment.

## Installation

```console
pip install virtualitics-cli
```

Requires Python >= 3.14.

## Quick Start

```console
vaip config                          # Configure connection to a VAIP instance
vaip init -n my_app -d "My app" -y   # Scaffold a new VAIP app
cd my_app
vaip build                           # Build a wheel
vaip deploy                          # Deploy to the VAIP instance
```

## Versioning

This project follows [PEP 440](https://peps.python.org/pep-0440/) and is versioned
independently from the Virtualitics AI Platform. Patch versions bump on bug fixes
and small improvements; minor and major bumps are for feature releases and
breaking changes respectively.

Compatible with VAIP 1.65+.

## What's New

### 2.0.4

- **Better error messages** — `vaip status` (and other backend-touching commands) now translate the cryptic "No organization ID. Please login to your instance." backend response into actionable advice: log into the UI in a browser first to sync your organization. The 404 path also distinguishes "endpoint missing" (true upgrade hint) from "backend exception mapped to 404" (surfaces the real error) instead of always recommending an upgrade. `{"error": "Invalid API token"}` (non-canonical 401 shape) is also translated to a friendly hint pointing at `vaip edit-context` instead of dumping raw JSON.
- **`vaip config --yes` no longer bricks the CLI** — Previously, `vaip config -N x -H y --yes` (without `-T`/`-U` and without saved defaults) created a 0-byte config file before failing the prompt; every subsequent `vaip` command then errored with "configuration file is invalid" until the file was deleted by hand. Config writes are now atomic (tempfile + `Path.replace`) and `--yes` refuses upfront when there's nothing to default to.
- **Token masking by default in `vaip show-context`** — Bearer tokens render as `<3-prefix>...<4-suffix>` (e.g. `eyJ...M_ng`) so a casual paste into Slack or a screenshot doesn't leak a live token. Pass `--show-token`/`-s` for plaintext (e.g. when debugging an auth failure). The internal `default_config_data` key is also filtered out of the output.
- **`vaip publish` is honest about being unimplemented** — The backend `/cli/publish/` endpoint is a stub that returns "under construction"; previous vaip versions surfaced this as a `✓` success and exit 0, fooling agents into thinking they had published. `vaip publish` now refuses upfront with `EXIT_VALIDATION_ERROR` (3) and `data.reason: "not_implemented"`.
- **`vaip init` rejects uppercase project names** — `vaip init -n MyCoolApp` used to silently lowercase to `mycoolapp`, leaving the printed "Next steps: cd MyCoolApp" hint broken. Now the validator rejects it explicitly: *"Project names must be lowercase. Try 'mycoolapp'."*
- **`vaip destroy --module-name`** — Added as an alias for `--project-name`; vaip used to refuse `--module-name` even though `module` is the noun the backend uses.
- **`vaip skill status`** — New subcommand for an in-band check of whether the bundled Claude Code skill is installed at the default path. Exit 0 when installed, 6 when not (no need to invoke `vaip doctor` just for this).
- **Quieter `vaip build`** — Default mode prints `Building VAIP App wheel...` then `✓ Successfully built VAIP App.` (the basic user was previously drowning in `setuptools` chatter). On failure, the captured output is replayed so the user can actually diagnose. Pass `--verbose` to stream the full build output live; `--json` mode also captures cleanly with the last 2 KB of output under `data.output_tail` on error.
- **Bare `vaip` shows `--help`** — Previously exited 2 with the bare `Missing command` panel; now prints the full help so a fresh user can see the command surface.
- **Canonical `--json` envelope everywhere** — `vaip init`, `vaip build`, and `vaip validate` now emit the same `{status, code, message, data}` shape as the rest of the CLI. `vaip --json init -n bad-name` returns a structured error envelope instead of a Rich-formatted panel that agents couldn't parse.
- **Build subprocess output stays off stdout under `--json`** — `python -m build` chatter goes to stderr so the JSON envelope on stdout is clean for agent consumption.
- **Public release notes pipeline** — The publish workflow now hard-fails if `README_PYPI.md` is missing a `### X.Y.Z` entry for the version being shipped (this is what caused 2.0.2 and 2.0.3 to ship without "What's New" bullets). The auto-generated `CHANGELOG.md` also no longer drops PRs that mention "sonarqube" in their commit body — only the subject is checked.

### 2.0.3

- **`vaip doctor`** — New diagnostic command. Reports the running version + Python interpreter, every `vaip` on PATH (catches the multi-Python footgun where `pip show` and `vaip.exe` resolve to different installs), bundled Claude skill state, and config. `--json` emits the canonical envelope.
- **PATH self-check** — Every command now warns once on stderr if multiple `vaip` executables are detected on PATH; suggests `vaip doctor` for details. Skipped under `--json` and via `VAIP_SKIP_HEALTH_CHECK=1`.

### 2.0.2

- **Public-facing release hygiene** — sdist trimmed to ship only what end-users need; example hostnames in docs are now generic placeholders.

### 2.0.1

- **Bundled Claude Code skill** — Run `vaip skill install` to give Claude Code the ability to drive `vaip` for you.
- **Agent-friendly CLI surface** — Canonical `--json` envelope (`{status, code, message, data}`) on every command, granular exit codes (`0` ok, `2` config, `3` validation, `4` network, `5` auth, `6` not-found, `7` backend error), `--json` support added to `use-context` / `delete-context` / `edit-context`, `edit-context` non-interactive via `--host`/`--token`/`--username` flags, `destroy --yes` is a real flag (no prompt).
- **`vaip status` improvements** — Human-readable text output (was raw JSON); `--json` output unchanged.
- **`vaip init` scaffold fix** — Generated `App(name=...)` now uses the snake_case `--project-name` so the produced app passes backend validation regardless of what's in `--description`.

### 2.0.0

- **Redesigned `vaip init`** — Scaffolds a working two-step app (CSV upload → results dashboard with table and chart), ready to build and deploy immediately.
- **New `vaip validate` command** — Pre-flight checks for `pyproject.toml`, package structure, Python syntax, and App/Flow definitions before building.
- **Structured JSON output** — Global `--json` flag on all commands for scripting and agent consumption.
- **Fully non-interactive `vaip config`** — Provide all flags (`-N`, `-H`, `-T`, `-U`) to skip prompts; use `--yes` to accept stored defaults.
- **Independent versioning** — CLI versions are no longer coupled to VAIP platform versions.

## Usage

```console
$ vaip [OPTIONS] COMMAND [ARGS]...
```

**Options**:

* `--version`
* `--verbose / --no-verbose`: [default: no-verbose]
* `--json`: Emit JSON output for programmatic/agent consumption
* `--install-completion`: Install completion for the current shell.
* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
* `--help`: Show this message and exit.

## Commands

### `vaip config`

Create or update a configuration file for connecting to a VAIP instance.
Requires a friendly name, host URL, API token, and username. Supports multiple named contexts.

```console
$ vaip config [OPTIONS]
```

* `-N, --name TEXT`: Friendly name for the VAIP instance (e.g., `my-vaip`) [required]
* `-H, --host TEXT`: Backend hostname (e.g., `https://your-vaip-instance.example.com`) [required]
* `-T, --token TEXT`: API token for authentication
* `-U, --username TEXT`: Username associated with API token
* `-y, --yes`: Accept default token/username without prompting (refuses upfront if neither flags nor saved defaults are available)

### `vaip use-context`

Switch the active context for deployment.

```console
$ vaip use-context CONTEXT_NAME
```

### `vaip show-context`

Display the configured contexts. Bearer tokens are masked by default
(`<3-prefix>...<4-suffix>`) so a casual paste doesn't leak a live token;
the internal `default_config_data` key is also filtered out.

```console
$ vaip show-context [OPTIONS]
```

* `-s, --show-token`: Print bearer tokens in plaintext (use only when you actually need the value, e.g. debugging an auth failure).

### `vaip delete-context`

Delete a specific context from the configuration file.

```console
$ vaip delete-context CONTEXT_NAME
```

### `vaip edit-context`

Modify a specific context in the configuration file.

```console
$ vaip edit-context CONTEXT_NAME
```

### `vaip init`

Scaffold a new VAIP app with a working two-step example (CSV upload + results dashboard).
All parameters can be provided as flags for fully non-interactive usage.

```console
$ vaip init [OPTIONS]
```

* `-n, --project-name TEXT`: Project name, lowercase snake_case only [required, prompted if omitted]
* `-d, --description TEXT`: App description [required, prompted if omitted]
* `-v, --version TEXT`: App version [default: 0.1.0]
* `-a, --authors TEXT`: Author name [default: git user.name]
* `-y, --yes`: Skip confirmation prompt

### `vaip validate`

Validate a VAIP app project before building. Checks pyproject.toml, package structure,
Python syntax, and App/Flow definition.

```console
$ vaip validate
```

### `vaip status`

Check connection to the configured VAIP instance and list installed modules.

```console
$ vaip status
```

### `vaip build`

Build a Python wheel file from the `pyproject.toml` in the current directory.
Quiet by default — only prints `Building VAIP App wheel...` and the
success/failure summary. The full build output is captured and replayed
on failure so you can diagnose what broke. Pass the global `--verbose`
flag to stream `python -m build`'s output live.

```console
$ vaip build
$ vaip --verbose build      # stream the full build output
```

### `vaip deploy`

Deploy the VAIP App wheel to the configured VAIP instance.

```console
$ vaip deploy [OPTIONS]
```

* `-f, --file TEXT`: Path to the wheel file (defaults to `./dist/*.whl`)

### `vaip destroy`

Delete a VAIP module and all its apps from the instance.

```console
$ vaip destroy [OPTIONS]
```

* `-n, --project-name, --module-name TEXT`: Project name to delete [required]
* `-y, --yes`: Confirm deletion [required]

### `vaip publish`

*(Not currently implemented.)* The backend `/cli/publish/` endpoint is a
stub. `vaip publish` refuses upfront with `EXIT_VALIDATION_ERROR` (3)
and `data.reason: "not_implemented"` in `--json` mode so agents can
branch correctly.

```console
$ vaip publish
```

### `vaip doctor`

Diagnose this `vaip` install. Reports the running version + Python
interpreter, every `vaip` executable found on PATH (catches the
multi-Python footgun where `pip show` and `vaip` resolve to different
installs), bundled Claude skill state, and config. `--json` emits the
canonical envelope.

```console
$ vaip doctor
```

### `vaip skill install`

Install the bundled Claude Code skill so an AI agent (e.g. Claude Code) can drive `vaip` end-to-end via a structured contract. The skill ships with the wheel and gets copied to `~/.claude/skills/vaip/SKILL.md` on install.

```console
$ vaip skill install [OPTIONS]
```

* `-d, --dest PATH`: Override install path (defaults to `~/.claude/skills/vaip/SKILL.md`)
* `-f, --force`: Overwrite an existing `SKILL.md` if present

Once installed, Claude Code's Skill tool sees the `vaip` skill and can scaffold, validate, build, deploy, and manage VAIP apps using the canonical `--json` envelope and granular exit codes.

### `vaip skill status`

Report whether the bundled Claude Code skill is installed at the default
path. Exit 0 when installed, 6 when not.

```console
$ vaip skill status
```

---

# Changelog


## Unreleased


### Other

- [FEATURE] Add vaip doctor + startup PATH health check
  * chore: apply ruff format pass (no behavior change)
  Pre-existing format drift on release/2.0.3; ruff format on the clean
  baseline reformats both files. Splitting this out so the doctor PR
  diff isn't polluted with cosmetic churn.
  * feat: add vaip doctor + startup PATH health check
  After PRED users on Windows hit a stale-install footgun (multiple
  Pythons each with a vaip.exe shim, PATH order picking the wrong one,
  pip show reporting a different version than what actually runs),
  vaip needs a way to surface the mismatch before users get stuck on
  opaque tracebacks like `Parameter.make_metavar() missing 1 required
  positional argument: 'ctx'` (typer/click skew from a .x install
  silently kept on PATH).
  This adds two pieces:
  1. `vaip doctor` — diagnostic subcommand. Reports the running version
  and Python, every `vaip` executable found on PATH (warning when
  more than one), the bundled Claude skill state, and the config
  file. Honors --json with the canonical envelope.
  2. Startup self-check — runs from @app.callback on every command. If
  PATH exposes >1 vaip executable, prints a single stderr line
  pointing the user at `vaip doctor`. Skipped under --json (don't
  pollute structured output), under VAIP_SKIP_HEALTH_CHECK=1
  (escape hatch), and when the user is already running `vaip
  doctor` (no double-warn).
  Helpers `_executable_extensions(os_name=...)` and
  `_find_vaip_on_path(os_name=...)` accept an optional os_name override
  so the Windows PATHEXT branch is testable on Mac/Linux without the
  WindowsPath instantiation issue.
  19 new tests (pytest -q): 130 passed, coverage 93%.
- Fix rollover prev-tag picker so release notes diff against the actual previous release
  git tag --sort=-version:refname doesn't unify v-prefixed and unprefixed
  tags into one ranking. With the CLI's mixed history (legacy v1.x tags +
  modern X.Y.Z tags), the legacy v-prefixed tags can outrank newer ones
  and become PREV_TAG — then `gh release create --notes-start-tag` diffs
  against ancient history, producing huge / nonsensical release notes
  (observed: rollover picking v1.26.0 instead of 1.54.1 / 2.0.x).
  Drop v-prefixed tags from the candidate set (legacy format; no future
  tag will use it) and use `sort -V -r` for deterministic descending
  version order. Verified locally against the live tag list — for
  CURRENT=2.0.3 the new picker returns 2.0.2 as expected.

## 2.0.2


### Other

- Sanitize public sdist + tighten changelog generator
  This is a public-PyPI-hygiene patch. The CLI is published to PyPI but
  the project is for enterprise/DoD users; the sdist tarball was
  shipping internal CI workflows, agent tooling configs, SonarQube
  internals, and badge-laden READMEs that don't belong in a public
  distribution.
  Sdist trimmed via hatchling exclude list (pyproject.toml). The PyPI
  wheel was already clean; only the source tarball is affected.
  Removed from sdist:
  README.md (badges with sonarqube.virtualitics.com URLs)
  .github/ (PR templates, CI workflows, internal bot references)
  .claude/ (local Claude Code agent config)
  scripts/ (internal tooling)
  sonar-project.properties (SonarQube project config)
  .python-version (build env detail)
  README_PYPI.md updates:
  - Drop the "Weekly automated releases" line — internal CI cadence,
  not user-facing.
  - Replace the example hostname predict-api-dev.virtualitics.com
  with a generic placeholder so the docs don't reveal an internal
  dev host.
  - Bump VAIP compatibility from 1.60+ to 1.65+ (the status endpoint
  requires 1.65 minimum and the bundled skill targets that surface).
  - Split "What's New in 2.0" into per-version sections (2.0.2 / 2.0.1
  / 2.0.0) so users can see what each release actually changed.
  - Move the bundled-skill bullet into the 2.0.1 section where it
  actually shipped.
  scripts/generate_changelog.py:
  - Strip trailing "(#NN)" GHE PR refs from titles since they don't
  link to anything publicly.
  - Make the prefix-skip case-insensitive and broaden it to cover
  [CICD], [CI], [PIPELINE] in addition to [CI/CD].
  - Regenerate CHANGELOG.md with the new generator (just drops the
  trailing PR-number suffixes from the existing 2.0.1 section).
  113 tests pass; ruff clean. Verified the built sdist contains only
  README_PYPI.md, CHANGELOG.md, pyproject.toml, source, tests, uv.lock,
  and .gitignore — no internal artifacts.

## 2.0.1


### CI/CD

- Replace weekly cron with manual rollover, add GH App token + auto release notes
  - Drop cron schedule; manual workflow_dispatch only (releases are infrequent)
  - Drop publish-outgoing job; PyPI publish stays separate via build-and-publish.yml
  - Add actions/create-github-app-token@v2 for default-branch flip perms
  - Add gh release create --generate-notes --latest for outgoing version
  - Make tag/release/branch creation idempotent for safe re-runs
  - Rename file weekly-rollover.yml -> release-rollover.yml since cron is gone

### Other

- Bundle Claude Code skill + vaip skill install subcommand
  * Bundle Claude Code skill + vaip skill install
  Ship a SKILL.md inside the wheel at virtualitics_cli/skill/SKILL.md
  and add a `vaip skill install` subcommand that copies it into
  ~/.claude/skills/vaip/SKILL.md (or --dest <path>).
  The skill teaches Claude Code's Skill tool how to drive vaip end-to-end:
  the canonical --json envelope, all granular exit codes (introduced in
  the previous PR), per-command flags, common workflows, and known
  backend gotchas (e.g. the lru_cache + empty-store 404 issues on VAIP
  <= 1.65). Designed against the post-#30 CLI surface so an agent gets
  a deterministic API to reason against.
  `vaip skill install` flags:
  --dest/-d  Override target path
  --force/-f Overwrite an existing SKILL.md
  Refuses with EXIT_VALIDATION_ERROR if the destination exists and
  --force isn't set. Emits the canonical --json envelope with
  {path, bytes} on success.
  Tests: 5 new (default path, custom dest, exists+no-force, exists+force,
  JSON envelope). All 112 tests pass; ruff clean. Confirmed SKILL.md is
  included in the built wheel.
  * Fix test_verbose_flag exit-code assertion for new error codes
  show-context with no config now returns EXIT_CONFIG_ERROR (2), not
  generic 1, so the (0, 1) assertion fails on CI where there's no
  config. Update to (EXIT_OK, EXIT_CONFIG_ERROR).
  * Document vaip skill install in READMEs
  Add a section under Commands and a What's New bullet so users discover
  the bundled Claude Code skill that ships with 2.0.1+.
  Mirror the same change in README_PYPI.md (the user-facing PyPI README).
  * Address review feedback on bundled skill PR
  - skill install: drop redundant Path() cast (dest is already Optional[Path]).
  - skill install: include `data={"bundled": str(bundled)}` on the
  bundled-not-found error path for consistency with the other _error_exit
  calls in the same function.
  - skill install: wrap target.parent.mkdir in try/except → _error_exit so
  passing --dest with a regular-file path component yields a clean envelope
  instead of a Python NotADirectoryError traceback.
  - SKILL.md: warn agents loud-and-clear that show-context's `data` includes
  bearer tokens at data.<context>.token — do not echo verbatim.
  - SKILL.md: drop the "VAIP ≤ 1.65" version-claim wording around the
  lru_cache and empty-backend bugs. The lru_cache fix is not in
  flight (tell users to restart the backend container); the empty-store
  one is the same situation. Skill should not assert version-bound fixes
  it can't guarantee.
  Adds test_skill_install_parent_is_file covering the new mkdir-failure path.
  113 tests pass; ruff clean.
  ---------
- Make CLI agent-friendly: --json everywhere, granular exit codes, non-interactive flags
  * Make CLI agent-friendly: --json everywhere, granular exit codes, non-interactive flags
  Audit of vaip 2.0.x for use as a Claude skill surfaced several blockers
  for agent-driven invocation. This consolidates the fixes.
  Canonical --json envelope
  All commands now emit {status, code, message, data} when --json is set.
  Backend payloads (deploy/destroy/publish/status) are nested under "data"
  rather than spread at the top level (which used to vary per command).
  This is the only intentionally-breaking change for agents that may have
  parsed .0.1 --json output.
  Granular exit codes
  EXIT_OK=0, EXIT_GENERIC=1, EXIT_CONFIG_ERROR=2, EXIT_VALIDATION_ERROR=3,
  EXIT_NETWORK_ERROR=4, EXIT_AUTH_ERROR=5, EXIT_NOT_FOUND=6,
  EXIT_BACKEND_ERROR=7. Network errors (timeout/refused) and HTTP 4xx/5xx
  from the backend now map to specific codes so agents can branch.
  use-context, delete-context, edit-context support --json
  These three commands previously had no --json mode at all. Now they emit
  the canonical envelope with relevant data (current_context, deleted name
  + dangling-warning flag, updated_fields list, etc.).
  edit-context goes non-interactive
  Adds --host/-H, --token/-T, --username/-U flags. Pass any combination to
  update those fields in one shot. Without flags + text mode, falls back
  to the legacy interactive prompt. With --json, at least one flag is
  required (no prompts in JSON mode).
  destroy --yes is a real flag
  Was marked required with prompt=True, so even in --json mode it would
  prompt for a y/n response. Now defaults to False; the command refuses
  with EXIT_VALIDATION_ERROR if --yes is omitted. Project name still
  prompts in text mode if --project-name not provided.
  deploy is more deterministic
  Wheel resolution sorts dist/*.whl lexicographically and picks the last
  match (previously: next() over an unordered glob). Warns in text mode
  when multiple wheels exist. Pass --file to disambiguate.
  get_current_context cleans up
  Replaced ad-hoc print + raise with _error_exit calls so config errors
  now go through the canonical envelope in --json mode.
  Tests
  All 107 tests pass. Test assertions updated to reference the new exit
  code constants. Added new tests covering the JSON envelope shape on
  use-context / delete-context / edit-context, the --field-flag path on
  edit-context, and the JSON-mode-requires-flag guard.
  * Update virtualitics_cli/virtualitics_cli.py
  ---------
- Refresh CLAUDE.md and exclude from PyPI sdist
  Update CLAUDE.md to reflect current CLI state:
  - Add `validate` and `status` commands to CLI Commands table
  - Document global `--json` flag in Common Commands section
  - Add JSON envelope details to Key Patterns (note schema variance by command)
  Exclude CLAUDE.md from PyPI sdist via hatchling config. File is dev-only
  documentation for Claude Code; shouldn't ship to users. Wheel already excludes
  it (not in package dir).
  Part of agent-friendliness audit. CLAUDE.md remains git-tracked for dev use.
- Use json.loads(r.text) in status to match existing test mocks
  #27 introduced r.json() in the status text path; existing test mocks
  set mock_response.text = json.dumps(...) but not mock_response.json,
  so the mock returned a MagicMock (causing test_status_success to fail
  on master). Switch to json.loads(r.text) — matches print_response
  pattern and the existing tests pass without mock changes.
- Fix init scaffold App name + status text-mode formatting
  - init scaffold's app.py.template was setting `App(name="$description")`,
  which produces an unvalidated name. The backend strips spaces and
  rejects dots, digits, and most punctuation, so any app description
  with those characters fails to register on deploy. Switch to
  `App(name="$project_name")` — project_name is already snake_case-
  validated by app_name_callback in `vaip init -n`.
  - status text mode previously dumped raw JSON via print_response. Add
  a small human-readable summary (host/org/installed modules) and
  leave --json output untouched.
  Surfaced during 2.0.1 smoke test against backend 1.65.0; verified
  locally end-to-end (init -> build -> deploy -> status -> destroy).

## 2.0.0


### Other

- Feat/idea: CLI 2.0 — scaffold redesign, agent-friendliness, validate, changelog
  * chore: bump version to 2.0.0 and add production classifier
  Decouples CLI versioning from VAIP platform versions. The CLI now follows
  its own PEP 440 versioning with weekly patch releases.
  * ci: add workflow_call trigger with auto_publish support
  Allows the weekly rollover workflow to call build-and-publish with
  auto_publish=true, bypassing the manual approval gate for automated
  weekly releases.
  * ci: add weekly rollover workflow
  Runs Monday 06:00 UTC. Publishes the outgoing release to PyPI,
  creates a new patch release branch, bumps the version in pyproject.toml,
  updates dependencies via uv lock --upgrade, and retargets open PRs.
  * docs: add versioning and what's new sections to READMEs
  Documents the independent versioning scheme (PEP 440, weekly patch
  releases) and VAIP 1.60+ compatibility.
  * fix: isolate test_config_w_input from filesystem state
  The test relied on real filesystem state and failed when a config file
  already existed with a 'predict-dev' context. Uses tmp_path/monkeypatch
  for isolation, matching the pattern of the other config tests.
  * feat: add basic_app scaffold templates
  Six template files for generating a working two-step VAIP app
  with CSV upload and results visualization.
  * build: include template files in wheel distribution
  * feat: replace init with template-based scaffold
  Generates a working two-step VAIP app (CSV upload + results dashboard).
  All inputs available as CLI flags for non-interactive/agentic usage.
  Templates loaded via importlib.resources from bundled .template files.
  * test: replace init tests for new scaffold behavior
  Tests cover file generation, variable substitution, non-interactive mode,
  defaults, existing directory handling, and Python syntax validity.
  * fix: suppress S607 lint warning for git subprocess call
  * fix: escape special characters in template variable substitution
  Prevents broken Python/TOML syntax when description or authors contain
  double quotes or backslashes.
  * feat: add --json flag, remove build confirmation, fix exit codes
  - Global --json flag emits structured JSON for agent/programmatic use
  - Remove unnecessary confirmation prompt from build command
  - Standardize exit codes: explicit code=0 on success, code=1 on error
  - Extract _error_exit() and _resolve_wheel() helpers to reduce complexity
  - Update tests for new behavior and add JSON output tests
  * feat: auto-generate changelog for PyPI from git history
  - Add scripts/generate_changelog.py that extracts commits between tags,
  filters internal references (Jira tickets, internal URLs, emails),
  and groups by conventional commit type
  - Weekly rollover generates CHANGELOG.md and commits it
  - Build workflow appends changelog to PyPI readme before packaging
  - Update README_PYPI.md for 2.0 changes (new init flags, no build
  confirmation, --json flag)
  * feat: add vaip validate command, make config non-interactive, fix template
  - Add `vaip validate` pre-flight check: pyproject.toml, package structure,
  Python syntax, and App/Flow definition with --json support
  - Make `vaip config` fully non-interactive when -N/-H/-T/-U all provided
  - Add --yes/-y flag to config to accept stored defaults without prompting
  - Extract _resolve_credentials() to reduce config complexity
  - Error cleanly in --json mode instead of hanging on stdin prompts
  - Fix generated README template referencing removed --yes build flag
  - 17 new tests (95 total), coverage at 94%
  * fix: isolate flaky test_use_context_no_config_file from filesystem state
  The test relied on real filesystem state, causing it to pass locally
  (where a config file exists) but fail on CI (where none exists).
  * fix: upgrade pygments and requests to resolve CVEs
  - pygments 2.19.2 → 2.20.0 (-4539)
  - requests 2.32.5 → 2.33.1 (-25645)
  * docs: update What's New in 2.0 and sync README command docs
  Expand release notes to cover scaffold redesign, validate command,
  JSON output, and non-interactive config. Update stale init/build
  docs in internal README to match current CLI behavior.
  * fix: filter internal tooling references from public changelog
  Add content-based skip filter to changelog generator so commits
  about internal infrastructure (SonarQube, etc.) don't appear in
  the public PyPI changelog.
  * feat: add status command and parse standardized backend responses
  - Add `vaip status` command calling POST /cli/status/ for connection
  health checks and installed module listing
  - Update print_response() to parse the new standardized backend format:
  - JSON mode: flat output merging backend response with HTTP code,
  agent-friendly without double-wrapping
  - Human mode: shows ✓/✗ icon + message, with data fields formatted
  below (labels from snake_case keys)
  - Falls back to raw JSON/text for legacy or non-JSON responses
  - Remove redundant "X failed with status N" error lines from deploy,
  destroy, and publish — print_response now handles error display
  - Update tests with new backend response format and add status tests
  * docs: add vaip status command to READMEs
  * fix: extract _print_structured to reduce print_response complexity
  Ruff C901/PLR0912: too many branches. Extract human-mode formatting
  into a helper function.
  ---------

## 1.54.1


### Other

- Handle HTTP timeouts and non-JSON responses gracefully
  - Catch requests.exceptions.Timeout in deploy, destroy, and publish
  with a helpful message suggesting the operation may still be processing
  - Add print_response() helper that gracefully handles non-JSON server
  responses (HTML error pages, plain text) instead of crashing
  - Check r.ok for xx status codes before attempting to parse response
  - Add error handling to publish (previously had no try/except at all)
  - Add 7 new tests: deploy timeout, deploy server error, deploy non-JSON
  success, destroy timeout, publish timeout, publish connection error,
  publish server error
- Remove stale TODOs, auto-convert hyphens in destroy, improve 404 message
  - Remove 8 stale TODO comments (pipx, blueprint docs, setuptools-scm,
  vaip-init detection, authors list)
  - Auto-convert hyphens to underscores in destroy project_name so users
  can copy the name directly from pyproject.toml
  - Improve destroy 404 error to mention the project name may not exist
  - Add test for hyphen-to-underscore conversion
- [SECURITY] - Update dependencies to resolve CVEs.
- Update version to 1.38.0

## 1.37.0


### Other

- Update deps
