vcf-super-cli 0.2.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. vcf_super_cli-0.4.0/CHANGELOG.md +125 -0
  2. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/PKG-INFO +3 -2
  3. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/README.md +2 -1
  4. vcf_super_cli-0.4.0/docs/commands.md +112 -0
  5. vcf_super_cli-0.4.0/docs/design.md +89 -0
  6. vcf_super_cli-0.4.0/docs/index.md +46 -0
  7. vcf_super_cli-0.4.0/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.3-ergonomics-design.md +232 -0
  8. vcf_super_cli-0.4.0/docs/usage.md +101 -0
  9. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/pyproject.toml +15 -1
  10. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_builder.py +1 -1
  11. vcf_super_cli-0.4.0/tests/test_complete.py +100 -0
  12. vcf_super_cli-0.4.0/tests/test_complete_cache.py +107 -0
  13. vcf_super_cli-0.4.0/tests/test_complete_dynamic.py +205 -0
  14. vcf_super_cli-0.4.0/tests/test_filter_pagination_cli.py +174 -0
  15. vcf_super_cli-0.4.0/tests/test_filters.py +66 -0
  16. vcf_super_cli-0.4.0/tests/test_paginate.py +54 -0
  17. vcf_super_cli-0.4.0/tests/test_pyvmomi_events.py +138 -0
  18. vcf_super_cli-0.4.0/tests/test_pyvmomi_inventory.py +96 -0
  19. vcf_super_cli-0.4.0/tests/test_pyvmomi_perf.py +138 -0
  20. vcf_super_cli-0.4.0/tests/test_pyvmomi_tasks.py +62 -0
  21. vcf_super_cli-0.4.0/tests/test_resources.py +115 -0
  22. vcf_super_cli-0.4.0/tests/test_vmomi.py +131 -0
  23. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/uv.lock +1 -1
  24. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/_version.py +1 -1
  25. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/app.py +14 -1
  26. vcf_super_cli-0.4.0/vsc/connect/vmomi.py +102 -0
  27. vcf_super_cli-0.4.0/vsc/gen/builder.py +434 -0
  28. vcf_super_cli-0.4.0/vsc/gen/complete.py +62 -0
  29. vcf_super_cli-0.4.0/vsc/gen/complete_cache.py +128 -0
  30. vcf_super_cli-0.4.0/vsc/gen/complete_dynamic.py +145 -0
  31. vcf_super_cli-0.4.0/vsc/gen/filters.py +88 -0
  32. vcf_super_cli-0.4.0/vsc/gen/paginate.py +40 -0
  33. vcf_super_cli-0.4.0/vsc/gen/resources.py +135 -0
  34. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/errors.py +26 -0
  35. vcf_super_cli-0.4.0/vsc/pyvmomi/__init__.py +7 -0
  36. vcf_super_cli-0.4.0/vsc/pyvmomi/events.py +98 -0
  37. vcf_super_cli-0.4.0/vsc/pyvmomi/inventory.py +91 -0
  38. vcf_super_cli-0.4.0/vsc/pyvmomi/perf.py +118 -0
  39. vcf_super_cli-0.4.0/vsc/pyvmomi/runner.py +51 -0
  40. vcf_super_cli-0.4.0/vsc/pyvmomi/tasks.py +41 -0
  41. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/skill/assets/SKILL.md +17 -0
  42. vcf_super_cli-0.2.0/CHANGELOG.md +0 -60
  43. vcf_super_cli-0.2.0/docs/commands.md +0 -64
  44. vcf_super_cli-0.2.0/docs/design.md +0 -31
  45. vcf_super_cli-0.2.0/docs/index.md +0 -38
  46. vcf_super_cli-0.2.0/docs/usage.md +0 -55
  47. vcf_super_cli-0.2.0/vsc/gen/builder.py +0 -243
  48. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/docs.yml +0 -0
  49. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/e2e.yml +0 -0
  50. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/lint.yml +0 -0
  51. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/release.yml +0 -0
  52. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.github/workflows/test.yml +0 -0
  53. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/.gitignore +0 -0
  54. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/AGENTS.md +0 -0
  55. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/LICENSE +0 -0
  56. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/install.md +0 -0
  57. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/profiles.md +0 -0
  58. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/superpowers/specs/2026-06-05-vcf-super-cli-design.md +0 -0
  59. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.2-writes-design.md +0 -0
  60. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/docs/writes.md +0 -0
  61. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/mkdocs.yml +0 -0
  62. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/__init__.py +0 -0
  63. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/README.md +0 -0
  64. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/__init__.py +0 -0
  65. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/conftest.py +0 -0
  66. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/e2e/test_read_smoke.py +0 -0
  67. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_cli.py +0 -0
  68. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_config.py +0 -0
  69. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_discover.py +0 -0
  70. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_errors.py +0 -0
  71. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_params.py +0 -0
  72. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_preview.py +0 -0
  73. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_render.py +0 -0
  74. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_session.py +0 -0
  75. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_skill.py +0 -0
  76. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_version.py +0 -0
  77. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/tests/test_write_errors.py +0 -0
  78. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/__init__.py +0 -0
  79. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/__init__.py +0 -0
  80. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/profiles.py +0 -0
  81. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/cli/skill.py +0 -0
  82. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/config/__init__.py +0 -0
  83. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/config/schema.py +0 -0
  84. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/config/store.py +0 -0
  85. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/connect/__init__.py +0 -0
  86. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/connect/session.py +0 -0
  87. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/connect/targets.py +0 -0
  88. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/__init__.py +0 -0
  89. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/discover.py +0 -0
  90. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/model.py +0 -0
  91. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/params.py +0 -0
  92. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/gen/preview.py +0 -0
  93. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/logging_config.py +0 -0
  94. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/__init__.py +0 -0
  95. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/exit_codes.py +0 -0
  96. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/output/render.py +0 -0
  97. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/skill/__init__.py +0 -0
  98. {vcf_super_cli-0.2.0 → vcf_super_cli-0.4.0}/vsc/skill/export.py +0 -0
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ All notable changes to `vcf-super-cli` are documented here. The format is based
4
+ on [Keep a Changelog](https://keepachangelog.com/), and from v1.0.0 the project
5
+ will follow [Semantic Versioning](https://semver.org/). While on `0.x`, minor
6
+ versions may include breaking changes.
7
+
8
+ ## [Unreleased]
9
+
10
+ ## v0.4.0 — 2026-06-08
11
+
12
+ Live resource-id completion. Pressing `<TAB>` on an id-typed argument can now
13
+ suggest **real ids** from the live inventory — strictly opt-in, cached, and
14
+ fail-soft. The agent-facing contract is unchanged: stable JSON, error envelope,
15
+ exit codes, dry-run-by-default writes, and `--help` stays fully offline.
16
+
17
+ ### Added
18
+
19
+ - **Live resource-id completion** (#44), opt-in via `VSC_COMPLETE_DYNAMIC=1` and
20
+ off by default. When enabled, `<TAB>` on an id arg/option suggests live ids for
21
+ the resource type (VMs, hosts, clusters, datacenters, datastores, resource
22
+ pools), with each resource's name shown as completion help. Built on:
23
+ - a resource-type → list-op **registry** derived purely by introspecting the
24
+ SDK metadata (#45), no network;
25
+ - a **TTL cache** under the platform cache dir, keyed by
26
+ profile/backend/resource-type (default 60s, `VSC_COMPLETE_TTL` override) (#46);
27
+ - a **dynamic completer** whose fetch is time-bounded (`VSC_COMPLETE_TIMEOUT`,
28
+ default 2s) and blanket fail-soft — any error, missing auth, or timeout
29
+ yields no suggestions and is never cached (#47).
30
+ - Static/offline completion (enums, output formats, profiles, filter enums) is
31
+ unchanged and remains the default. `--help` and command execution never open a
32
+ connection for completion.
33
+
34
+ ### Notes
35
+
36
+ - Live completion is a human convenience only; agents should keep using `list`
37
+ to discover ids and must not depend on completion for correctness (#48).
38
+
39
+ ## v0.3.0 — 2026-06-05
40
+
41
+ Ergonomics: friendlier filtering and paging, offline shell completion, and a
42
+ pyVmomi fallback surface for gaps the REST/vAPI layer doesn't cover. The
43
+ agent-facing contract is unchanged — stable JSON, error envelope, exit codes,
44
+ and writes still dry-run by default.
45
+
46
+ ### Added
47
+
48
+ - **Static shell completion** (#32), fully offline — never opens a connection.
49
+ Completes enum option choices, output formats, configured profile names, and
50
+ `list` filter enum values. Install with `vsc --install-completion`. (Live
51
+ resource-id completion is intentionally deferred to a later release.)
52
+ - **Per-field filter flags** (#33). `list` commands flatten the SDK filter spec
53
+ into typed `--<field>` options (repeatable for list/set fields; enum choices
54
+ validated and completed), e.g. `vsc vsphere vm list --power-states POWERED_ON
55
+ --names web-1`. The raw `--filter '<json>'` blob stays as a base layer that
56
+ per-field flags override.
57
+ - **Pagination helpers** (#33): `--all` follows the NSX cursor across pages;
58
+ `--max-items N` caps the total; `--limit N` is a client-side cap for
59
+ non-paginated (vSphere) lists. Without `--all`, a paginated `list` returns one
60
+ page and surfaces its `cursor` for manual paging.
61
+ - **pyVmomi fallback commands** (read-only) under `vsc vsphere`, for areas the
62
+ REST/vAPI surface lacks — same JSON / error-envelope / exit-code contract:
63
+ - `perf vm|host --metric <group.name>` — performance counters via the
64
+ PerformanceManager (#34).
65
+ - `events list [--vm|--host] [--since 1h]` and `tasks list` — recent events
66
+ and recent/running tasks (#35).
67
+ - `inventory vm|host [--props <path>]…` — a PropertyCollector property walk
68
+ (#36).
69
+
70
+ ### Changed
71
+
72
+ - Documentation and the bundled agent `SKILL.md` describe completion, the filter
73
+ and paging flags, and the pyVmomi fallback surface.
74
+
75
+ ## v0.2.0 — 2026-06-05
76
+
77
+ First PyPI release. Adds the **write** surface on top of the v0.1 read-only
78
+ foundation, with a dry-run-by-default safety model.
79
+
80
+ ### Added
81
+
82
+ - **Write operations** (`POST`/`PUT`/`PATCH`/`DELETE`) are generated from the
83
+ `vcf-sdk` vAPI metadata alongside reads (#20). Clean, collision-free verbs:
84
+ `POST ?action=X` → `X`; otherwise `PUT` → `set`, `PATCH` → `patch`,
85
+ `DELETE` → `delete` (force variants prefixed `force-`); `POST` without an
86
+ action keeps its operation id.
87
+ - **Dry-run by default + `--apply`** (#21). Every write previews the resolved
88
+ request and changes nothing — and opens **no connection** — unless `--apply`
89
+ is passed. `--apply` is the only gate (no interactive prompt), so the CLI
90
+ stays fully scriptable. New `vsc/gen/preview.py` builds the request plan.
91
+ - **Stable write envelope**, identical across modes — branch on `applied`:
92
+ - dry-run: `{ "applied": false, "request": { method, url, path_params, query, body, … }, "apply_hint": … }`
93
+ - applied: `{ "applied": true, "request": { … }, "result": <sdk response> }`
94
+ - **Body/spec construction from JSON** for both conventions: NSX's named request
95
+ body and vCenter's spec parameters, built into the SDK binding structs.
96
+ - **Catalog expansion**: vSphere `resource-pool` and the VM power/hardware leaves
97
+ (`power`, `cpu`, `memory`, `disk`, `ethernet`); NSX `ip-pools`,
98
+ `dhcp-server-configs`, `dhcp-relay-configs`, and Tier-1 `locale-services`.
99
+ - **`CONFLICT` (7) / `UNAVAILABLE` (8)** exit-code coverage for write errors
100
+ (#22), derived from the error-type map so it can't drift.
101
+
102
+ ### Changed
103
+
104
+ - A malformed or incomplete write body is reported during the dry-run as the
105
+ structured usage envelope (exit `2`) — before any connection — not as a
106
+ traceback.
107
+ - Documentation and the bundled agent `SKILL.md` describe the write surface and
108
+ the `--apply` safety model (#23).
109
+
110
+ ## v0.1.0 — 2026-06-05
111
+
112
+ Initial read-only foundation (not published to PyPI).
113
+
114
+ ### Added
115
+
116
+ - Command tree **generated dynamically** by introspecting the installed `vcf-sdk`
117
+ vAPI bindings (`VapiInterface` services + `OperationRestMetadata`), fully
118
+ offline — `--help` works without a server or credentials.
119
+ - Read (`GET`) commands for vSphere/vCenter (`vsc vsphere …`) and NSX Policy
120
+ (`vsc nsx …`), split into two product groups.
121
+ - Named **profiles** with env-var overrides and optional OS-keyring secret
122
+ storage; `--profile/-p` selection.
123
+ - **Output contract**: JSON by default, `--output table` for humans; a stable
124
+ error envelope on stderr and documented, frozen **exit codes**.
125
+ - Bundled agent **Skill** shipped in the wheel, exportable via `vsc skill export`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vcf-super-cli
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Modern, agent-friendly CLI for VMware Cloud Foundation 9, with a command tree generated dynamically from the vcf-sdk vAPI bindings.
5
5
  Project-URL: Homepage, https://github.com/thomaschristory/vcf-super-cli
6
6
  Project-URL: Documentation, https://thomaschristory.github.io/vcf-super-cli/
@@ -73,7 +73,8 @@ $ vsc vsphere power stop vm-42 --apply # writes are dry-run without --app
73
73
  | vSphere / vCenter read (`vsc vsphere …`) | ✅ v0.1 |
74
74
  | NSX **Policy API** read (`vsc nsx …`) | ✅ v0.1 |
75
75
  | Writes — dry-run by default + `--apply` (`vsc vsphere …` / `vsc nsx …`) | ✅ v0.2 |
76
- | Dynamic shell completion, per-field filter flags, pyVmomi fallback | v0.3 (planned) |
76
+ | Ergonomics — offline shell completion, per-field filter flags + paging, pyVmomi fallback (`perf`/`events`/`tasks`/`inventory`) | v0.3 |
77
+ | Live resource-id completion | planned |
77
78
  | NSX Manager / Global-Manager, SDDC Manager, Operations, LCM | deferred |
78
79
 
79
80
  ## Install
@@ -41,7 +41,8 @@ $ vsc vsphere power stop vm-42 --apply # writes are dry-run without --app
41
41
  | vSphere / vCenter read (`vsc vsphere …`) | ✅ v0.1 |
42
42
  | NSX **Policy API** read (`vsc nsx …`) | ✅ v0.1 |
43
43
  | Writes — dry-run by default + `--apply` (`vsc vsphere …` / `vsc nsx …`) | ✅ v0.2 |
44
- | Dynamic shell completion, per-field filter flags, pyVmomi fallback | v0.3 (planned) |
44
+ | Ergonomics — offline shell completion, per-field filter flags + paging, pyVmomi fallback (`perf`/`events`/`tasks`/`inventory`) | v0.3 |
45
+ | Live resource-id completion | planned |
45
46
  | NSX Manager / Global-Manager, SDDC Manager, Operations, LCM | deferred |
46
47
 
47
48
  ## Install
@@ -0,0 +1,112 @@
1
+ # Commands
2
+
3
+ The `vsphere` and `nsx` command trees are **generated from the `vcf-sdk` vAPI
4
+ bindings**, so `vsc --help` (and each sub-`--help`) is always the authoritative,
5
+ version-accurate reference. This page is an overview.
6
+
7
+ Generated leaves expose both **read** and **write** verbs:
8
+
9
+ - `list` — list resources (optional `--filter '<json>'`)
10
+ - `get <id>` — fetch one resource by id, **where the SDK provides a by-id GET**
11
+ (e.g. `vm`, `cluster`, `datacenter`, `datastore`; some leaves such as `host`,
12
+ `folder`, and `network` are `list`-only)
13
+ - writes — `create`, `delete`, `set` (PUT upsert), `patch`, and action verbs
14
+ (e.g. `start`/`stop`/`reset` under `power`). **Writes are dry-run by default and
15
+ require `--apply`** — see [Writes](writes.md).
16
+
17
+ ## `vsc vsphere …` (vCenter)
18
+
19
+ Generated from `com.vmware.vcenter`:
20
+
21
+ | Group | Examples |
22
+ |-------|----------|
23
+ | `vm` | `vsc vsphere vm list`, `vsc vsphere vm get <vm>`, `vsc vsphere vm delete <vm> --apply` |
24
+ | `host` | `vsc vsphere host list`, `vsc vsphere host disconnect <host> --apply` |
25
+ | `cluster` | `vsc vsphere cluster list` |
26
+ | `datacenter` | `vsc vsphere datacenter list` |
27
+ | `datastore` | `vsc vsphere datastore list` |
28
+ | `folder` | `vsc vsphere folder list` |
29
+ | `network` | `vsc vsphere network list` |
30
+ | `resource-pool` | `vsc vsphere resource-pool create --spec '<json>' --apply` |
31
+ | `power` | `vsc vsphere power start\|stop\|reset\|suspend <vm> --apply` |
32
+ | `cpu` / `memory` / `disk` / `ethernet` | VM hardware reads + writes, e.g. `vsc vsphere cpu update <vm> --spec '<json>' --apply` |
33
+ | `perf` / `events` / `tasks` / `inventory` | **pyVmomi fallback** (read-only) — perf counters, recent events, recent/running tasks, and a property walk the REST/vAPI surface lacks (see below) |
34
+
35
+ ### pyVmomi fallback commands
36
+
37
+ A few inventory/performance areas are only reachable over the older pyVmomi SOAP
38
+ API. Those commands live under `vsc vsphere` alongside the generated ones, are
39
+ **read-only** (no `--apply`), and emit the same JSON / error envelope / exit codes.
40
+
41
+ - `vsc vsphere perf vm <vm> [--metric group.name]… [--max-samples N]` — performance
42
+ counters via the PerformanceManager. Metrics are `group.name` (e.g. `cpu.usage`)
43
+ or `group.name.rollup` (e.g. `cpu.usage.average`); repeat `--metric` for several.
44
+ - `vsc vsphere perf host <host> …` — the same for an ESXi host.
45
+ - `vsc vsphere events list [--vm <vm> | --host <host>] [--since 1h] [--max-count N]`
46
+ — recent events via the EventManager. `--since` takes a duration (`30s`/`15m`/
47
+ `2h`/`1d`); scope to at most one entity.
48
+ - `vsc vsphere tasks list [--max-count N]` — recent and running tasks via the
49
+ TaskManager.
50
+ - `vsc vsphere inventory vm <vm> [--props path]…` / `inventory host <host> …` —
51
+ managed-object properties the REST list ops omit (device tree, custom attributes),
52
+ via the PropertyCollector. `--props` is repeatable (e.g. `--props config.hardware`);
53
+ with none, a small per-type summary set is returned.
54
+
55
+ ## `vsc nsx …` (NSX Policy)
56
+
57
+ Generated from `vcf.nsx.policy`:
58
+
59
+ | Group | Examples |
60
+ |-------|----------|
61
+ | `segments` | `vsc nsx segments list`, `vsc nsx segments set <id> --segment '<json>' --apply` |
62
+ | `tier0s` / `tier1s` | `vsc nsx tier1s list`, `vsc nsx tier1s set <id> --tier1 '<json>' --apply` |
63
+ | `services` | `vsc nsx services list` |
64
+ | `groups` | `vsc nsx groups list`, `vsc nsx groups delete <domain> <group> --apply` |
65
+ | `security-policies` | `vsc nsx security-policies list` |
66
+ | `gateway-policies` | `vsc nsx gateway-policies list` |
67
+ | `ip-pools` | `vsc nsx ip-pools set <id> --ip-address-pool '<json>' --apply` |
68
+ | `dhcp-server-configs` / `dhcp-relay-configs` | DHCP config reads + writes |
69
+ | `locale-services` | Tier-1 locale services reads + writes |
70
+
71
+ ## Curated commands
72
+
73
+ - `vsc profiles …` — manage connection profiles (see [Profiles](profiles.md))
74
+ - `vsc skill export <dir> [--apply]` — export the bundled agent Skill
75
+ - `vsc --version`
76
+
77
+ ## Filtering
78
+
79
+ `list` commands expose each field of the SDK filter spec as its own typed flag.
80
+ List-valued fields are repeatable; enum fields validate their choices and
81
+ tab-complete:
82
+
83
+ ```sh
84
+ vsc --profile prod vsphere vm list --power-states POWERED_ON --names web-1 --names web-2
85
+ ```
86
+
87
+ The raw JSON spec is still accepted as a base layer / escape hatch; per-field
88
+ flags merge **over** it (a flag wins over the same key in the blob):
89
+
90
+ ```sh
91
+ vsc --profile prod vsphere vm list --filter '{"clusters": ["domain-c1"]}' --power-states POWERED_ON
92
+ ```
93
+
94
+ ## Pagination
95
+
96
+ `list` commands accept paging flags:
97
+
98
+ | Flag | Effect |
99
+ |------|--------|
100
+ | `--all` | follow the cursor and return **every** page (paginated backends, e.g. NSX) |
101
+ | `--max-items N` | cap the total number of items returned |
102
+ | `--limit N` | client-side cap for non-paginated (vSphere) lists |
103
+
104
+ ```sh
105
+ vsc --profile prod nsx segments list --all # every page, concatenated
106
+ vsc --profile prod nsx segments list --page-size 50 # one page (cursor surfaced for manual paging)
107
+ vsc --profile prod vsphere vm list --limit 20 # first 20 only
108
+ ```
109
+
110
+ Without `--all`, a paginated `list` returns one page and surfaces the `cursor` so
111
+ an agent can drive pagination itself. On non-paginated backends (vSphere) `--all`
112
+ is a no-op — the output stays a plain array.
@@ -0,0 +1,89 @@
1
+ # Design
2
+
3
+ `vsc` builds its command tree by **statically introspecting the installed
4
+ `vcf-sdk` vAPI bindings**. Both `vmware-vcenter` and `vcf-nsx` are vAPI stub
5
+ libraries on the shared `vmware-vapi-runtime`:
6
+
7
+ - Each service is a `VapiInterface` subclass (`VM`, `Host`, `Cluster`,
8
+ `Datastore`, `Network`, …).
9
+ - Every operation carries `OperationRestMetadata` (`http_method`,
10
+ `path_variables`, `query_parameters`).
11
+ - Parameters are typed via `StructType` binding types.
12
+
13
+ A single generator walks those classes and emits one Typer command per
14
+ operation. `http_method == "GET"` ⇒ a read command; other verbs ⇒ writes
15
+ (dry-run by default + `--apply`). Because the metadata ships inside the SDK, the
16
+ tree builds **offline** — no server needed to render `--help`.
17
+
18
+ Top-level grouping:
19
+
20
+ - `vsc vsphere …` → `com.vmware.vcenter` (+ selected `com.vmware.esx`)
21
+ - `vsc nsx …` → `vcf.nsx.policy` (NSX Policy API)
22
+
23
+ The introspected intermediate representation is a list of `Operation` objects,
24
+ each carrying typed `Param`s (`vsc/gen/model.py`). Everything below is built from
25
+ that one model.
26
+
27
+ ## Ergonomics (v0.3)
28
+
29
+ These build on the introspected `Param` model without changing the agent
30
+ contract.
31
+
32
+ ### Offline shell completion
33
+
34
+ Completion values come entirely from the model and local config — **never a
35
+ network call**, so `<TAB>` stays fast and `--help` stays offline. Enum options
36
+ complete from their fixed choices, `--output` from the output formats, and
37
+ `--profile` from configured profile names (`vsc/gen/complete.py`). Completing a
38
+ live resource id would require a connection and is deferred to a later release.
39
+
40
+ ### Per-field filter flags
41
+
42
+ A `list` operation takes a single `filter` parameter that is a struct
43
+ (`VM.FilterSpec` etc.). The generator flattens that struct into typed
44
+ `--<field>` options (repeatable for list/set fields; enum fields validate and
45
+ complete their choices). The raw `--filter '<json>'` blob remains as a base
46
+ layer that per-field flags merge **over** (`vsc/gen/filters.py`).
47
+
48
+ ### Pagination
49
+
50
+ `list` commands gain `--all` (follow the NSX cursor across pages), `--max-items`
51
+ (cap the total), and `--limit` (client-side cap for non-paginated vSphere
52
+ lists). Without `--all`, a paginated list returns one page and surfaces its
53
+ `cursor` for manual paging; on non-cursor backends `--all` is a no-op and the
54
+ output stays a plain array (`vsc/gen/paginate.py`).
55
+
56
+ ## pyVmomi fallback (v0.3)
57
+
58
+ A few inventory/performance areas are only reachable through the older pyVmomi
59
+ SOAP API. Those are **hand-written read-only commands** mounted under
60
+ `vsc vsphere` alongside the generated ones, sharing the same output contract:
61
+
62
+ - a separate connection path, `connect_vmomi()` via `SmartConnect`, reusing the
63
+ resolved vSphere credentials and honouring `--insecure` like the REST path
64
+ (`vsc/connect/vmomi.py`);
65
+ - `vmomi_jsonable()` collapses managed objects to their moref and data objects to
66
+ dicts so results render like any other;
67
+ - a shared `run_read()` runner maps pyVmomi faults onto the same error envelope
68
+ and exit codes (`vsc/pyvmomi/runner.py`).
69
+
70
+ Commands: `perf` (PerformanceManager counters), `events` / `tasks`
71
+ (Event/Task managers), and `inventory` (a PropertyCollector property walk). All
72
+ are reads — no `--apply`.
73
+
74
+ ## Design specs
75
+
76
+ The authoritative, milestone-by-milestone designs live in the repository:
77
+
78
+ - [v0.1 — dynamic read-only CLI](https://github.com/thomaschristory/vcf-super-cli/blob/main/docs/superpowers/specs/2026-06-05-vcf-super-cli-design.md)
79
+ - [v0.2 — writes](https://github.com/thomaschristory/vcf-super-cli/blob/main/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.2-writes-design.md)
80
+ - [v0.3 — ergonomics](https://github.com/thomaschristory/vcf-super-cli/blob/main/docs/superpowers/specs/2026-06-05-vcf-super-cli-v0.3-ergonomics-design.md)
81
+
82
+ ## Contracts
83
+
84
+ - **Output:** JSON by default; `--output table` for humans.
85
+ - **Errors:** stable envelope `{ "error": { code, message, kind, details } }`.
86
+ - **Exit codes:** documented `IntEnum` (`0` ok, `1` generic, `2` usage, `3` auth,
87
+ `4` not-found, `5` connection, `6` config, `7` conflict, `8` unavailable).
88
+ - **Writes:** dry-run by default; `--apply` is the only gate and a dry-run opens
89
+ no connection.
@@ -0,0 +1,46 @@
1
+ # vcf-super-cli
2
+
3
+ A modern, agent-friendly CLI for **VMware Cloud Foundation 9** whose command tree
4
+ is **generated dynamically** from the official [`vcf-sdk`](https://pypi.org/project/vcf-sdk/)
5
+ vAPI bindings.
6
+
7
+ ```console
8
+ $ vsc vsphere vm list --power-states POWERED_ON --profile prod
9
+ $ vsc nsx segments list --all --output table
10
+ $ vsc vsphere perf vm vm-42 --metric cpu.usage
11
+ ```
12
+
13
+ !!! warning "Pre-1.0"
14
+ Reads and writes are both available. **Writes are dry-run by default** —
15
+ nothing changes without `--apply`. See [Writes](writes.md). While on `0.x`,
16
+ minor versions may include breaking changes.
17
+
18
+ ## Highlights
19
+
20
+ - **Mirrors the real API** — commands come from the SDK's vAPI metadata, covering
21
+ vCenter and NSX from one generator.
22
+ - **Ergonomic** — offline tab-completion (enums, formats, profiles, filter
23
+ choices), per-field `--<field>` filter flags, and paging (`--all` /
24
+ `--max-items` / `--limit`).
25
+ - **pyVmomi fallback** — read-only `perf`, `events`, `tasks`, and `inventory`
26
+ commands for areas the REST/vAPI surface doesn't cover.
27
+ - **Safe by default** — writes are dry-run unless `--apply`; a dry-run never connects.
28
+ - **Agent-friendly** — JSON output, stable error envelope, documented exit codes,
29
+ bundled agent Skill.
30
+
31
+ See the [Design](design.md) for how dynamic generation works, and
32
+ [Commands](commands.md) for the full surface.
33
+
34
+ ## Install
35
+
36
+ ```sh
37
+ uv tool install vcf-super-cli # or: pip install vcf-super-cli
38
+ vsc --install-completion # optional: offline shell completion
39
+ ```
40
+
41
+ From source:
42
+
43
+ ```sh
44
+ uv sync
45
+ uv run vsc --help
46
+ ```
@@ -0,0 +1,232 @@
1
+ # vcf-super-cli v0.3 — Ergonomics (design)
2
+
3
+ **Status:** approved 2026-06-05. Milestone: `v0.3 — ergonomics` (#3).
4
+ **Predecessors:** [v0.1 design](2026-06-05-vcf-super-cli-design.md), [v0.2 writes design](2026-06-05-vcf-super-cli-v0.2-writes-design.md).
5
+
6
+ ## Goal
7
+
8
+ Make the dynamically-generated CLI pleasant to drive by hand and still trivially
9
+ scriptable by an agent. Three independent feature areas, each shipped as its own
10
+ issue/PR under milestone #3:
11
+
12
+ 1. **Static shell completion** — offline tab-completion of enums, output formats,
13
+ profile names, and per-field filter choices.
14
+ 2. **Filter & pagination helpers** — per-field filter flags (augmenting today's
15
+ `--filter '<json>'` blob) and friendlier paging (`--all`, `--max-items`,
16
+ `--limit`).
17
+ 3. **pyVmomi fallback commands** — hand-written read commands (perf, events/tasks,
18
+ inventory walk) for gaps the vAPI/REST surface doesn't cover.
19
+
20
+ The agent contract is unchanged and non-negotiable: stable JSON output, the
21
+ documented error envelope, the frozen exit codes, and **writes stay dry-run by
22
+ default**. The pyVmomi additions are all reads, so they need no `--apply` gate.
23
+
24
+ ## Locked decisions (from brainstorm, 2026-06-05)
25
+
26
+ - **Completion is static/offline only.** No feature in v0.3 hits the API during
27
+ `<TAB>`. Live resource-ID completion (e.g. completing `<vm>` from a live list)
28
+ is explicitly **deferred to v0.4**; the param model already carries
29
+ `resource_types` so it remains a clean future extension.
30
+ - **Filter flags augment, never replace, `--filter`.** Raw `--filter '<json>'`
31
+ stays as the base layer / escape hatch; generated per-field flags merge over it.
32
+ - **Pagination:** `--all` auto-follows the NSX cursor; `--max-items` caps total;
33
+ `--limit` is a client-side cap for non-cursor (vSphere) lists. NSX
34
+ `--page-size`/`--cursor` already exist as generated query options.
35
+ - **pyVmomi covers all three families** (perf, events/tasks, inventory walk),
36
+ built on a small shared `SmartConnect` foundation.
37
+
38
+ ---
39
+
40
+ ## Feature 1 — Static shell completion
41
+
42
+ ### Behaviour
43
+
44
+ Completion values are derived entirely from the introspected `Param` model plus
45
+ local config — never from a network call. `--help` and completion both stay fully
46
+ offline.
47
+
48
+ Completed surfaces:
49
+
50
+ | Surface | Source | Example |
51
+ |---------|--------|---------|
52
+ | enum option | `param.enum_values` | `--power-state POWERED_<TAB>` → `POWERED_ON`, `POWERED_OFF` |
53
+ | `--output` / `-o` | `OutputFormat` members | `-o <TAB>` → `json`, `table` |
54
+ | global `--profile` / `-p` | profile names from `load_config()` (offline file read) | `-p <TAB>` → `prod`, `lab` |
55
+ | per-field filter enum flags | reuse enum completer (feature 2) | `--filter-power-state <TAB>` |
56
+
57
+ Resource-ID args (the `<vm>` positionals) are **not** completed in v0.3.
58
+
59
+ ### Implementation
60
+
61
+ - New module **`vsc/gen/complete.py`** — pure completer factories:
62
+ - `enum_completer(values: list[str]) -> Callable[[str], list[str]]`
63
+ - `profile_completer() -> Callable[[str], list[str]]`
64
+ Each takes the Click `incomplete` string and returns the prefix-filtered
65
+ candidate list. No `ctx`/`param` dependency → unit-testable without a shell.
66
+ - **`vsc/gen/builder.py`** `_build_signature`: when a non-path option is built for
67
+ an `ENUM` param, pass `autocompletion=enum_completer(param.enum_values)` to
68
+ `typer.Option`. When building `--output`, attach an `OutputFormat` completer.
69
+ - **`vsc/cli/app.py`** main callback: attach `profile_completer()` to `--profile`.
70
+ - Root app already sets `add_completion=True`; document `vsc --install-completion`
71
+ and `vsc --show-completion` in `docs/usage.md`.
72
+
73
+ ### Tests
74
+
75
+ `tests/test_complete.py` — completer factories return correctly prefix-filtered
76
+ lists, empty `incomplete` returns all, no match returns `[]`. A smoke test asserts
77
+ generated enum options carry an `autocompletion` callback.
78
+
79
+ ---
80
+
81
+ ## Feature 2 — Filter & pagination helpers
82
+
83
+ ### Per-field filter flags
84
+
85
+ vCenter list operations take a single `filter` parameter that is a `STRUCT`
86
+ (`VM.FilterSpec` etc.). Today the user must pass it as `--filter '<json>'`. v0.3
87
+ additionally flattens that struct's fields into typed options.
88
+
89
+ - **Detection:** on a read/list op, a parameter that is `ParamKind.STRUCT` **and
90
+ named `filter`** is flattened. Write-body structs are *not* flattened — they
91
+ stay JSON (`--spec`, `--segment`, …).
92
+ - **Generated flags:** each struct field → `--<field>` (kebab-cased). `list`/`set`
93
+ fields are **repeatable** (`list[str]` annotation, multiple uses accumulate).
94
+ `enum` fields carry choices in help + the enum completer.
95
+ - **Precedence / merge:** raw `--filter '<json>'` provides the base dict; per-field
96
+ flags merge **over** it (a flag wins over the same key in the blob). The merged
97
+ dict is coerced into the FilterSpec via the existing
98
+ `coerce_struct`/`coerce_value` path — no new coercion logic.
99
+ - **Collisions:** a flattened field whose flag would collide with a reserved option
100
+ (`--output`, `--apply`) or another option is suffixed using the existing
101
+ `_sig_name` mechanism.
102
+
103
+ New helper **`vsc/gen/filters.py`**:
104
+ - `flatten_filter(param: Param) -> list[Param]` — child params for each struct field
105
+ (built with `param_from_type` on `struct_type.get_field(name)`).
106
+ - `assemble_filter(base_json, field_values, param) -> Any` — merge base + per-field
107
+ values and coerce into the struct. Pure; unit-testable.
108
+
109
+ `builder.py` consumes these: `_build_signature` emits the child options (tracking
110
+ them so `_collect_kwargs` knows to reassemble rather than pass them through), and
111
+ `_collect_kwargs` calls `assemble_filter` to rebuild the single `filter` kwarg.
112
+
113
+ ### Pagination
114
+
115
+ NSX Policy list ops return a `*ListResult` struct (`.results`, `.cursor`,
116
+ `.result_count`) and already expose `cursor`/`page_size` as generated query
117
+ options. vSphere REST list ops return a plain list and do not paginate.
118
+
119
+ Injected options on **list verbs** (`op.cli_verb == "list"`):
120
+
121
+ | Flag | Applies to | Effect |
122
+ |------|-----------|--------|
123
+ | `--all` | cursor lists (NSX) | follow `.cursor` across pages, concatenate `.results`, stop at `--max-items` |
124
+ | `--max-items N` | all lists | hard cap on returned items (post-fetch for vSphere, loop-stop for `--all`) |
125
+ | `--limit N` | non-cursor lists (vSphere) | client-side slice of the returned list |
126
+
127
+ Without `--all`, a cursor list returns one page **including** its `cursor`, so an
128
+ agent can paginate manually. `--all` and `--max-items` interact: the follow loop
129
+ stops once `--max-items` items are collected.
130
+
131
+ New helper **`vsc/gen/paginate.py`**:
132
+ - `follow_cursor(fetch_page, *, max_items) -> list` — `fetch_page(cursor) ->
133
+ (results, next_cursor)`; loops until `next_cursor` is empty/repeated or the cap
134
+ is hit. Pure given the `fetch_page` callable (the callable closes over the SDK
135
+ method in `builder.py`); unit-testable with a fake pager.
136
+
137
+ `builder.py` `make_command` detects a cursor-bearing result and, when `--all` is
138
+ set, drives `follow_cursor`; otherwise applies `--limit`/`--max-items` slicing
139
+ before `emit()`.
140
+
141
+ ### Tests
142
+
143
+ `tests/test_filters.py` — flatten produces one child per field with correct
144
+ kind/required/repeatable; assemble merges blob + flags with flags winning; enum
145
+ field carries choices. `tests/test_paginate.py` — `follow_cursor` concatenates,
146
+ respects `max_items`, terminates on empty/duplicate cursor. Builder integration
147
+ tests assert a vCenter list op grows `--<field>` options and an NSX list op grows
148
+ `--all`/`--max-items`.
149
+
150
+ ---
151
+
152
+ ## Feature 3 — pyVmomi fallback commands
153
+
154
+ Hand-written read commands for gaps the vAPI/REST surface doesn't cover, mounted
155
+ into the existing `vsc vsphere` tree and emitted through the existing
156
+ `emit()`/error-envelope contract.
157
+
158
+ ### Foundation
159
+
160
+ - New module **`vsc/connect/vmomi.py`**:
161
+ - `connect_vmomi() -> ServiceInstance` via `pyVim.connect.SmartConnect`, reusing
162
+ `resolve_target("vsphere")` for server/user/password and an `ssl` context that
163
+ honours `verify` (unverified context when `insecure`). Cached like the vAPI
164
+ connections (`reset_cache()` clears it); `Disconnect` registered at process
165
+ exit.
166
+ - `vmomi_jsonable(obj) -> Any` — convert managed-object / data-object trees into
167
+ plain JSON-able dicts (`moref` → `{"type", "value"}`, data objects → field
168
+ dicts, leave scalars/datetimes alone) so `emit()` renders them like any other
169
+ result.
170
+ - New package **`vsc/pyvmomi/`**, one module per family, each exposing a
171
+ `typer.Typer`. `vsc/cli/app.py` adds them onto the vsphere group
172
+ (`vsphere_group.add_typer(perf_app, name="perf")`, …). pyVmomi errors
173
+ (`vim.fault.*`, connection failures) map into the existing envelope/exit codes
174
+ (auth→3, not-found→4, connection→5) via a small adapter reusing
175
+ `vsc/output/errors.py` patterns.
176
+
177
+ ### Commands
178
+
179
+ - **`vsc vsphere perf`** — real-time/historical counters via `PerformanceManager`.
180
+ e.g. `perf vm <vm> --metric cpu.usage [--interval 20s]`,
181
+ `perf host <host> --metric mem.usage`. Resolves the counter id, queries
182
+ `QueryPerf`, emits timestamped samples.
183
+ - **`vsc vsphere events`** — recent events via `EventManager` with `--since`/entity
184
+ filters. **`vsc vsphere tasks`** — running + recent tasks via `TaskManager`.
185
+ - **`vsc vsphere inventory`** — `PropertyCollector` walk for properties the REST
186
+ list ops omit (device tree, custom attributes, relationships), e.g.
187
+ `inventory vm <vm> --props config.hardware`.
188
+
189
+ All are reads → no `--apply`. They accept the same `--output json|table` and obey
190
+ `--profile`.
191
+
192
+ ### Tests
193
+
194
+ pyVmomi has no offline-introspection trick like the vAPI bindings, so tests mock
195
+ `SmartConnect`/`ServiceInstance` and the relevant managers. `tests/test_vmomi.py`
196
+ covers `vmomi_jsonable` conversion (moref/data-object/scalar) purely;
197
+ `tests/test_pyvmomi_*.py` drive each command against a fake `ServiceInstance`
198
+ asserting the emitted JSON shape and error mapping. No live vCenter required.
199
+
200
+ ---
201
+
202
+ ## Cross-cutting
203
+
204
+ - Each feature PR updates the relevant `docs/` page(s) and the bundled
205
+ `vsc/skill/assets/SKILL.md` for any new surface.
206
+ - `ruff`, `mypy --strict`, `pytest`, and `mkdocs --strict` stay green; CI green
207
+ before merge.
208
+ - Every PR gets an adversarial **refute-before-accept** review pass; findings are
209
+ fixed before merge.
210
+
211
+ ## Issues (milestone #3)
212
+
213
+ | Issue | Title | Depends on |
214
+ |-------|-------|-----------|
215
+ | Epic | v0.3 — ergonomics | — |
216
+ | A | Static shell completion (enums, `--output`, `--profile`, filter choices) | — |
217
+ | B | Filter & pagination helpers (per-field flags + `--all`/`--max-items`/`--limit`) | — |
218
+ | C | pyVmomi fallback foundation + `vsc vsphere perf` | — |
219
+ | D | pyVmomi events & tasks | C |
220
+ | E | pyVmomi inventory walk | C |
221
+ | F | v0.3 docs/SKILL roundup + release prep | A–E |
222
+
223
+ Build order: A and B in parallel; C → (D, E); F last. The release tag (`vX.Y.Z`)
224
+ is pushed **manually by the maintainer** after F merges — `release.yml` keeps **no
225
+ `environment:` block** (the PyPI trusted publisher is registered with a blank
226
+ environment; they must stay matched).
227
+
228
+ ## Out of scope (v0.3)
229
+
230
+ - Live resource-ID completion (hits the API) — v0.4 candidate.
231
+ - pyVmomi *writes* — the fallback surface is read-only this milestone.
232
+ - NSX Manager / Global-Manager APIs (still Policy-only).