Metadata-Version: 2.4
Name: virtualitics-cli
Version: 2.0.3
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.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

### `vaip use-context`

Switch the active context for deployment.

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

### `vaip show-context`

Display the current configuration file.

```console
$ vaip show-context
```

### `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, 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.

```console
$ vaip build
```

### `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 TEXT`: Project name to delete [required]
* `-y, --yes`: Confirm deletion [required]

### `vaip publish`

Publish a VAIP App to other users in your organization. *(Not currently implemented.)*

```console
$ vaip publish
```

### `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.

---

# Changelog


## 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).

## 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
