healthcloud-cli 3.2.5__tar.gz → 3.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 (70) hide show
  1. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/PKG-INFO +146 -12
  2. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/README.md +145 -11
  3. healthcloud_cli-3.4.0/healthcloud_cli/ams/agents/__init__.py +5 -0
  4. healthcloud_cli-3.4.0/healthcloud_cli/ams/agents/commands.py +83 -0
  5. healthcloud_cli-3.4.0/healthcloud_cli/ams/agents/group.py +18 -0
  6. healthcloud_cli-3.4.0/healthcloud_cli/ams/agents/operations.py +72 -0
  7. healthcloud_cli-3.4.0/healthcloud_cli/ams/agents/projections.py +37 -0
  8. healthcloud_cli-3.4.0/healthcloud_cli/ams/auth.py +91 -0
  9. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/commands.py +2 -0
  10. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/compatibility.py +6 -4
  11. healthcloud_cli-3.4.0/healthcloud_cli/ams/context.py +212 -0
  12. healthcloud_cli-3.4.0/healthcloud_cli/ams/departments/__init__.py +5 -0
  13. healthcloud_cli-3.4.0/healthcloud_cli/ams/departments/commands.py +61 -0
  14. healthcloud_cli-3.4.0/healthcloud_cli/ams/departments/group.py +18 -0
  15. healthcloud_cli-3.4.0/healthcloud_cli/ams/departments/operations.py +71 -0
  16. healthcloud_cli-3.4.0/healthcloud_cli/ams/departments/projections.py +19 -0
  17. healthcloud_cli-3.4.0/healthcloud_cli/ams/domain_errors.py +9 -0
  18. healthcloud_cli-3.4.0/healthcloud_cli/ams/errors.py +97 -0
  19. healthcloud_cli-3.4.0/healthcloud_cli/ams/guardrails/__init__.py +5 -0
  20. healthcloud_cli-3.4.0/healthcloud_cli/ams/guardrails/commands.py +57 -0
  21. healthcloud_cli-3.4.0/healthcloud_cli/ams/guardrails/group.py +18 -0
  22. healthcloud_cli-3.4.0/healthcloud_cli/ams/guardrails/operations.py +67 -0
  23. healthcloud_cli-3.4.0/healthcloud_cli/ams/guardrails/projections.py +18 -0
  24. healthcloud_cli-3.4.0/healthcloud_cli/ams/organization/__init__.py +5 -0
  25. healthcloud_cli-3.4.0/healthcloud_cli/ams/organization/commands.py +29 -0
  26. healthcloud_cli-3.4.0/healthcloud_cli/ams/organization/group.py +17 -0
  27. healthcloud_cli-3.4.0/healthcloud_cli/ams/organization/operations.py +33 -0
  28. healthcloud_cli-3.4.0/healthcloud_cli/ams/organization/projections.py +29 -0
  29. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/output.py +17 -3
  30. healthcloud_cli-3.4.0/healthcloud_cli/ams/policies/__init__.py +5 -0
  31. healthcloud_cli-3.4.0/healthcloud_cli/ams/policies/commands.py +57 -0
  32. healthcloud_cli-3.4.0/healthcloud_cli/ams/policies/group.py +18 -0
  33. healthcloud_cli-3.4.0/healthcloud_cli/ams/policies/operations.py +67 -0
  34. healthcloud_cli-3.4.0/healthcloud_cli/ams/policies/projections.py +21 -0
  35. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/registry.py +14 -0
  36. healthcloud_cli-3.4.0/healthcloud_cli/ams/results.py +31 -0
  37. healthcloud_cli-3.4.0/healthcloud_cli/ams/session.py +345 -0
  38. healthcloud_cli-3.4.0/healthcloud_cli/ams/skills/__init__.py +5 -0
  39. healthcloud_cli-3.4.0/healthcloud_cli/ams/skills/commands.py +57 -0
  40. healthcloud_cli-3.4.0/healthcloud_cli/ams/skills/group.py +18 -0
  41. healthcloud_cli-3.4.0/healthcloud_cli/ams/skills/operations.py +67 -0
  42. healthcloud_cli-3.4.0/healthcloud_cli/ams/skills/projections.py +19 -0
  43. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/target.py +14 -0
  44. healthcloud_cli-3.4.0/healthcloud_cli/ams/teams/__init__.py +5 -0
  45. healthcloud_cli-3.4.0/healthcloud_cli/ams/teams/commands.py +57 -0
  46. healthcloud_cli-3.4.0/healthcloud_cli/ams/teams/group.py +18 -0
  47. healthcloud_cli-3.4.0/healthcloud_cli/ams/teams/operations.py +67 -0
  48. healthcloud_cli-3.4.0/healthcloud_cli/ams/teams/projections.py +21 -0
  49. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/windows_files.py +51 -2
  50. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/cli.py +61 -1
  51. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/pyproject.toml +1 -1
  52. healthcloud_cli-3.2.5/healthcloud_cli/ams/auth.py +0 -49
  53. healthcloud_cli-3.2.5/healthcloud_cli/ams/context.py +0 -74
  54. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/.gitignore +0 -0
  55. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/hatch_build.py +0 -0
  56. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/__init__.py +0 -0
  57. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/__init__.py +0 -0
  58. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/group.py +0 -0
  59. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/tuning.py +0 -0
  60. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/ams/ui.py +0 -0
  61. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/context.py +0 -0
  62. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/contracts/__init__.py +0 -0
  63. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/contracts/mcp-tools.manifest.json +0 -0
  64. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/main.py +0 -0
  65. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/manifest.py +0 -0
  66. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/output.py +0 -0
  67. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/params.py +0 -0
  68. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/healthcloud_cli/util.py +0 -0
  69. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/scripts/__init__.py +0 -0
  70. {healthcloud_cli-3.2.5 → healthcloud_cli-3.4.0}/scripts/bundle_manifest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: healthcloud-cli
3
- Version: 3.2.5
3
+ Version: 3.4.0
4
4
  Summary: Command-line interface for the HealthCloud MCP gateway, generated from the shared MCP tool manifest.
5
5
  Project-URL: Issues, https://github.com/healthcloudservices/healthcloud-services/issues
6
6
  Project-URL: Repository, https://github.com/healthcloudservices/healthcloud-services
@@ -69,10 +69,10 @@ _HC_COMPLETE=bash_complete COMP_WORDS="hc ams inspect --o" COMP_CWORD=3 uv run -
69
69
  ```
70
70
 
71
71
  Operational validation starts only when Click invokes an AMS leaf callback.
72
- `discover` then validates its explicit environment and tenant arguments; the
73
- other leaves require `--target-file` and apply the existing root, target, and
74
- authentication checks described below. Deferring preflight does not relax
75
- those safety requirements.
72
+ `discover`, `agents`, `departments`, `teams`, `organization`, `skills`, and `policies` then
73
+ validate their explicit environment and tenant arguments; the remaining leaves
74
+ require `--target-file`. All apply the root, target, and authentication checks
75
+ described below. Deferring preflight does not relax those safety requirements.
76
76
 
77
77
  The bundled tool manifest (`healthcloud_cli/contracts/mcp-tools.manifest.json`)
78
78
  is a copy of `packages/cli/contracts/mcp-tools.manifest.json`. Re-sync it
@@ -280,13 +280,147 @@ uv sync --frozen --extra dev
280
280
  uv run --frozen hc ams --help
281
281
  ```
282
282
 
283
- Bootstrap candidate IDs with the public SDK discovery endpoint. Both flags are
284
- required; no MCP defaults, admin login, or automatic candidate selection are used:
283
+ Create one private tenant-admin session before interactive AMS work, inspect its
284
+ non-secret binding, and remove it when finished:
285
+
286
+ ```bash
287
+ uv run --frozen hc login --environment dev --tenant-id TENANT_ID
288
+ uv run --frozen hc login --status
289
+ uv run --frozen hc logout
290
+ ```
291
+
292
+ `hc login` reads `HC_AMS_ADMIN_EMAIL` and `HC_AMS_ADMIN_PASSWORD` when present,
293
+ otherwise it prompts (with hidden password input). It stores only the email,
294
+ tenant/environment binding, access/refresh tokens and expiry in the strict
295
+ owner-only `~/.healthcloud/cli/ams-session.json`; it never stores the password.
296
+ AMS commands may omit both target flags while this session is present, except
297
+ that a `prod` session still needs `--environment prod` on every command. Explicit
298
+ flags and target files must match it exactly. A session within 60 seconds of
299
+ expiry is refreshed once before the operation, under a lock so concurrent
300
+ commands never spend the same refresh token (a second command waits up to 30 seconds, then stops), and
301
+ atomically replaced. Refresh
302
+ failure stops without an AMS call or password fallback and asks for a new login.
303
+ If the refreshed session cannot be saved, the current command still finishes and
304
+ prints a warning to run `hc login` again.
305
+
306
+ `~/.healthcloud` and `~/.healthcloud/cli` must be directories you own with mode
307
+ 700; the CLI creates them that way, and refuses (naming the directory and the
308
+ `chmod 700` fix) if an existing one is shared. `hc logout` deletes the local file
309
+ only and reports `"server_session_revoked": false`: issued tokens stay valid
310
+ until they expire.
311
+
312
+ Bootstrap candidate IDs with the public SDK discovery endpoint. Without a saved
313
+ session both flags are required; a saved session supplies both together. No MCP
314
+ defaults, admin login, or automatic candidate selection are used:
285
315
 
286
316
  ```bash
287
317
  uv run --frozen hc ams discover --environment dev --tenant-id TENANT_ID
288
318
  ```
289
319
 
320
+ Read-only agent listing, inspection and export take flags instead of a target
321
+ file; both may be omitted together when a matching saved session exists. Without
322
+ one, they log in as tenant admin using `HC_AMS_ADMIN_EMAIL` /
323
+ `HC_AMS_ADMIN_PASSWORD`. Stdout carries bounded metadata only; `adapter` shows
324
+ whether an agent runs from the `file` or `db` config adapter (`unknown` when the
325
+ server does not say). `export` first re-checks the agent/tenant identity, then
326
+ saves the server's own JSON/YAML export byte-for-byte to a new owner-only `--out`
327
+ file, refusing (and leaving no file) when the body is not valid UTF-8. `export`
328
+ needs an AMS build that serves `/agents/{agent_id}/configuration/export`; that
329
+ route is not yet deployed to dev:
330
+
331
+ ```bash
332
+ uv run --frozen hc ams agents list --environment dev --tenant-id TENANT_ID
333
+ uv run --frozen hc ams agents get --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID
334
+ uv run --frozen hc ams agents export --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID \
335
+ --format yaml --out agent.yaml
336
+ ```
337
+
338
+ Read-only department listing and inspection use the same explicit tenant-admin
339
+ targeting. They emit one versioned JSON result on stdout; failures emit one
340
+ categorized JSON result on stderr. Department descriptions, creator identities,
341
+ and evaluation-suite content are never included in the projection:
342
+
343
+ ```bash
344
+ uv run --frozen hc ams departments list --environment dev --tenant-id TENANT_ID
345
+ uv run --frozen hc ams departments get --environment dev --tenant-id TENANT_ID \
346
+ --department-id DEPARTMENT_ID
347
+ ```
348
+
349
+ Department reads are complete and unpaged. The CLI does not expose department
350
+ create, update, delete, activate, or deactivate operations.
351
+
352
+ Read-only team listing and inspection are also complete and unpaged. The bounded
353
+ projection omits descriptions and creator identities; it includes evaluation-suite
354
+ IDs but never suite content. The CLI does not expose team mutations:
355
+
356
+ ```bash
357
+ uv run --frozen hc ams teams list --environment dev --tenant-id TENANT_ID
358
+ uv run --frozen hc ams teams get --environment dev --tenant-id TENANT_ID --team-id TEAM_ID
359
+ ```
360
+
361
+ The organization hierarchy is the backend-built department/team/agent tree. The
362
+ CLI preserves its order and nesting without issuing entity reads, joining records,
363
+ or inferring missing relationships:
364
+
365
+ ```bash
366
+ uv run --frozen hc ams organization hierarchy --environment dev --tenant-id TENANT_ID
367
+ ```
368
+
369
+ Read-only skill catalog listing and inspection are complete and unpaged. The
370
+ bounded projection includes only skill IDs, names, tool IDs, and audit timestamps;
371
+ it omits descriptions, tenant identity, creator identity, arbitrary response
372
+ fields, and tool details. Tool-ID order is preserved:
373
+
374
+ ```bash
375
+ uv run --frozen hc ams skills list --environment dev --tenant-id TENANT_ID
376
+ uv run --frozen hc ams skills get --environment dev --tenant-id TENANT_ID --skill-id SKILL_ID
377
+ ```
378
+
379
+ The CLI does not create, update, delete, or assign skills. Skill commands do not
380
+ read guardrails. They make exactly one catalog GET after tenant-admin login
381
+ and does not paginate, retry, cache, join, or infer additional catalog data.
382
+ Skill reads refuse target files, root credentials or base URLs, raw output,
383
+ blank or dot-segment skill IDs, and installed SDKs that do not provide both
384
+ skill list and get methods. These local refusals happen before tenant-admin login
385
+ or catalog reads.
386
+
387
+ Read-only policy catalog listing and inspection are complete and unpaged. The
388
+ bounded projection includes only policy IDs, names, department IDs, guardrail
389
+ IDs, active status, and audit timestamps. It never emits protected policy
390
+ content, descriptions, tenant or creator identity, arbitrary response fields,
391
+ or joined department/guardrail details. Guardrail-ID order is preserved:
392
+
393
+ ```bash
394
+ uv run --frozen hc ams policies list --environment dev --tenant-id TENANT_ID
395
+ uv run --frozen hc ams policies get --environment dev --tenant-id TENANT_ID --policy-id POLICY_ID
396
+ ```
397
+
398
+ The CLI does not export protected policy content; create, update, delete, or
399
+ assign policies; or join guardrail records. It makes exactly one catalog GET after
400
+ tenant-admin login and does not paginate, retry, cache, join, or infer referenced
401
+ resources. Policy reads refuse target files, root credentials or base URLs, raw
402
+ output, blank or dot-segment policy IDs, and installed SDKs without both policy
403
+ methods before tenant-admin login or catalog reads. Successful reads make no
404
+ assignment, deployment, serving, enforcement, or effectiveness claim.
405
+
406
+ Read-only guardrail catalog listing and inspection are complete and unpaged.
407
+ The bounded projection includes only guardrail IDs, names, and audit timestamps.
408
+ It never emits protected rules, descriptions, tenant or creator identity,
409
+ arbitrary response fields, or joined policy/agent details:
410
+
411
+ ```bash
412
+ uv run --frozen hc ams guardrails list --environment dev --tenant-id TENANT_ID
413
+ uv run --frozen hc ams guardrails get --environment dev --tenant-id TENANT_ID \
414
+ --guardrail-id GUARDRAIL_ID
415
+ ```
416
+
417
+ The CLI does not export rules; create, update, delete, assign, deploy, or enforce
418
+ guardrails; or claim they are active or effective. It makes exactly one catalog
419
+ GET after tenant-admin login and does not paginate, retry, cache, join, or infer
420
+ additional data. Guardrail reads refuse target files, root credentials or base
421
+ URLs, raw output, blank or dot-segment guardrail IDs, and installed SDKs without
422
+ both guardrail methods before tenant-admin login or catalog reads.
423
+
290
424
  Every other command requires an explicit local, non-secret target JSON file.
291
425
  Only the canonical HTTPS AMS origin for that environment is accepted; custom
292
426
  origins require separately verified support. Target and nested qualification keys
@@ -355,11 +489,11 @@ configuration and recheck it before apply and selected-field restore. This is an
355
489
  operator prerequisite: the current CLI does not automatically attest deployment
356
490
  or all-writer coverage, and the record adds no server authorization boundary.
357
491
 
358
- Credentials enter only through `HC_AMS_ADMIN_EMAIL` and `HC_AMS_ADMIN_PASSWORD`
359
- in the current process, intended for composition with the existing
360
- `onepassword-env` wrapper. Each invocation logs in through `HCTenantAdminSDK`;
361
- tokens remain private in memory. There is no token cache, fallback identity,
362
- secret argument, resolved env file, or browser credential bridge. Root MCP
492
+ For CI and scripts with no saved session, credentials may still enter through
493
+ `HC_AMS_ADMIN_EMAIL` and `HC_AMS_ADMIN_PASSWORD` in the current process, intended
494
+ for composition with the existing `onepassword-env` wrapper; that compatibility
495
+ path logs in once per invocation. There is no fallback identity, secret argument,
496
+ arbitrary token import, resolved env file, keychain, or browser credential bridge. Root MCP
363
497
  `--access-token`, `--base-url`, their environment equivalents, and token/base-URL
364
498
  profiles are refused for AMS. Root profile environment values do not select AMS.
365
499
 
@@ -51,10 +51,10 @@ _HC_COMPLETE=bash_complete COMP_WORDS="hc ams inspect --o" COMP_CWORD=3 uv run -
51
51
  ```
52
52
 
53
53
  Operational validation starts only when Click invokes an AMS leaf callback.
54
- `discover` then validates its explicit environment and tenant arguments; the
55
- other leaves require `--target-file` and apply the existing root, target, and
56
- authentication checks described below. Deferring preflight does not relax
57
- those safety requirements.
54
+ `discover`, `agents`, `departments`, `teams`, `organization`, `skills`, and `policies` then
55
+ validate their explicit environment and tenant arguments; the remaining leaves
56
+ require `--target-file`. All apply the root, target, and authentication checks
57
+ described below. Deferring preflight does not relax those safety requirements.
58
58
 
59
59
  The bundled tool manifest (`healthcloud_cli/contracts/mcp-tools.manifest.json`)
60
60
  is a copy of `packages/cli/contracts/mcp-tools.manifest.json`. Re-sync it
@@ -262,13 +262,147 @@ uv sync --frozen --extra dev
262
262
  uv run --frozen hc ams --help
263
263
  ```
264
264
 
265
- Bootstrap candidate IDs with the public SDK discovery endpoint. Both flags are
266
- required; no MCP defaults, admin login, or automatic candidate selection are used:
265
+ Create one private tenant-admin session before interactive AMS work, inspect its
266
+ non-secret binding, and remove it when finished:
267
+
268
+ ```bash
269
+ uv run --frozen hc login --environment dev --tenant-id TENANT_ID
270
+ uv run --frozen hc login --status
271
+ uv run --frozen hc logout
272
+ ```
273
+
274
+ `hc login` reads `HC_AMS_ADMIN_EMAIL` and `HC_AMS_ADMIN_PASSWORD` when present,
275
+ otherwise it prompts (with hidden password input). It stores only the email,
276
+ tenant/environment binding, access/refresh tokens and expiry in the strict
277
+ owner-only `~/.healthcloud/cli/ams-session.json`; it never stores the password.
278
+ AMS commands may omit both target flags while this session is present, except
279
+ that a `prod` session still needs `--environment prod` on every command. Explicit
280
+ flags and target files must match it exactly. A session within 60 seconds of
281
+ expiry is refreshed once before the operation, under a lock so concurrent
282
+ commands never spend the same refresh token (a second command waits up to 30 seconds, then stops), and
283
+ atomically replaced. Refresh
284
+ failure stops without an AMS call or password fallback and asks for a new login.
285
+ If the refreshed session cannot be saved, the current command still finishes and
286
+ prints a warning to run `hc login` again.
287
+
288
+ `~/.healthcloud` and `~/.healthcloud/cli` must be directories you own with mode
289
+ 700; the CLI creates them that way, and refuses (naming the directory and the
290
+ `chmod 700` fix) if an existing one is shared. `hc logout` deletes the local file
291
+ only and reports `"server_session_revoked": false`: issued tokens stay valid
292
+ until they expire.
293
+
294
+ Bootstrap candidate IDs with the public SDK discovery endpoint. Without a saved
295
+ session both flags are required; a saved session supplies both together. No MCP
296
+ defaults, admin login, or automatic candidate selection are used:
267
297
 
268
298
  ```bash
269
299
  uv run --frozen hc ams discover --environment dev --tenant-id TENANT_ID
270
300
  ```
271
301
 
302
+ Read-only agent listing, inspection and export take flags instead of a target
303
+ file; both may be omitted together when a matching saved session exists. Without
304
+ one, they log in as tenant admin using `HC_AMS_ADMIN_EMAIL` /
305
+ `HC_AMS_ADMIN_PASSWORD`. Stdout carries bounded metadata only; `adapter` shows
306
+ whether an agent runs from the `file` or `db` config adapter (`unknown` when the
307
+ server does not say). `export` first re-checks the agent/tenant identity, then
308
+ saves the server's own JSON/YAML export byte-for-byte to a new owner-only `--out`
309
+ file, refusing (and leaving no file) when the body is not valid UTF-8. `export`
310
+ needs an AMS build that serves `/agents/{agent_id}/configuration/export`; that
311
+ route is not yet deployed to dev:
312
+
313
+ ```bash
314
+ uv run --frozen hc ams agents list --environment dev --tenant-id TENANT_ID
315
+ uv run --frozen hc ams agents get --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID
316
+ uv run --frozen hc ams agents export --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID \
317
+ --format yaml --out agent.yaml
318
+ ```
319
+
320
+ Read-only department listing and inspection use the same explicit tenant-admin
321
+ targeting. They emit one versioned JSON result on stdout; failures emit one
322
+ categorized JSON result on stderr. Department descriptions, creator identities,
323
+ and evaluation-suite content are never included in the projection:
324
+
325
+ ```bash
326
+ uv run --frozen hc ams departments list --environment dev --tenant-id TENANT_ID
327
+ uv run --frozen hc ams departments get --environment dev --tenant-id TENANT_ID \
328
+ --department-id DEPARTMENT_ID
329
+ ```
330
+
331
+ Department reads are complete and unpaged. The CLI does not expose department
332
+ create, update, delete, activate, or deactivate operations.
333
+
334
+ Read-only team listing and inspection are also complete and unpaged. The bounded
335
+ projection omits descriptions and creator identities; it includes evaluation-suite
336
+ IDs but never suite content. The CLI does not expose team mutations:
337
+
338
+ ```bash
339
+ uv run --frozen hc ams teams list --environment dev --tenant-id TENANT_ID
340
+ uv run --frozen hc ams teams get --environment dev --tenant-id TENANT_ID --team-id TEAM_ID
341
+ ```
342
+
343
+ The organization hierarchy is the backend-built department/team/agent tree. The
344
+ CLI preserves its order and nesting without issuing entity reads, joining records,
345
+ or inferring missing relationships:
346
+
347
+ ```bash
348
+ uv run --frozen hc ams organization hierarchy --environment dev --tenant-id TENANT_ID
349
+ ```
350
+
351
+ Read-only skill catalog listing and inspection are complete and unpaged. The
352
+ bounded projection includes only skill IDs, names, tool IDs, and audit timestamps;
353
+ it omits descriptions, tenant identity, creator identity, arbitrary response
354
+ fields, and tool details. Tool-ID order is preserved:
355
+
356
+ ```bash
357
+ uv run --frozen hc ams skills list --environment dev --tenant-id TENANT_ID
358
+ uv run --frozen hc ams skills get --environment dev --tenant-id TENANT_ID --skill-id SKILL_ID
359
+ ```
360
+
361
+ The CLI does not create, update, delete, or assign skills. Skill commands do not
362
+ read guardrails. They make exactly one catalog GET after tenant-admin login
363
+ and does not paginate, retry, cache, join, or infer additional catalog data.
364
+ Skill reads refuse target files, root credentials or base URLs, raw output,
365
+ blank or dot-segment skill IDs, and installed SDKs that do not provide both
366
+ skill list and get methods. These local refusals happen before tenant-admin login
367
+ or catalog reads.
368
+
369
+ Read-only policy catalog listing and inspection are complete and unpaged. The
370
+ bounded projection includes only policy IDs, names, department IDs, guardrail
371
+ IDs, active status, and audit timestamps. It never emits protected policy
372
+ content, descriptions, tenant or creator identity, arbitrary response fields,
373
+ or joined department/guardrail details. Guardrail-ID order is preserved:
374
+
375
+ ```bash
376
+ uv run --frozen hc ams policies list --environment dev --tenant-id TENANT_ID
377
+ uv run --frozen hc ams policies get --environment dev --tenant-id TENANT_ID --policy-id POLICY_ID
378
+ ```
379
+
380
+ The CLI does not export protected policy content; create, update, delete, or
381
+ assign policies; or join guardrail records. It makes exactly one catalog GET after
382
+ tenant-admin login and does not paginate, retry, cache, join, or infer referenced
383
+ resources. Policy reads refuse target files, root credentials or base URLs, raw
384
+ output, blank or dot-segment policy IDs, and installed SDKs without both policy
385
+ methods before tenant-admin login or catalog reads. Successful reads make no
386
+ assignment, deployment, serving, enforcement, or effectiveness claim.
387
+
388
+ Read-only guardrail catalog listing and inspection are complete and unpaged.
389
+ The bounded projection includes only guardrail IDs, names, and audit timestamps.
390
+ It never emits protected rules, descriptions, tenant or creator identity,
391
+ arbitrary response fields, or joined policy/agent details:
392
+
393
+ ```bash
394
+ uv run --frozen hc ams guardrails list --environment dev --tenant-id TENANT_ID
395
+ uv run --frozen hc ams guardrails get --environment dev --tenant-id TENANT_ID \
396
+ --guardrail-id GUARDRAIL_ID
397
+ ```
398
+
399
+ The CLI does not export rules; create, update, delete, assign, deploy, or enforce
400
+ guardrails; or claim they are active or effective. It makes exactly one catalog
401
+ GET after tenant-admin login and does not paginate, retry, cache, join, or infer
402
+ additional data. Guardrail reads refuse target files, root credentials or base
403
+ URLs, raw output, blank or dot-segment guardrail IDs, and installed SDKs without
404
+ both guardrail methods before tenant-admin login or catalog reads.
405
+
272
406
  Every other command requires an explicit local, non-secret target JSON file.
273
407
  Only the canonical HTTPS AMS origin for that environment is accepted; custom
274
408
  origins require separately verified support. Target and nested qualification keys
@@ -337,11 +471,11 @@ configuration and recheck it before apply and selected-field restore. This is an
337
471
  operator prerequisite: the current CLI does not automatically attest deployment
338
472
  or all-writer coverage, and the record adds no server authorization boundary.
339
473
 
340
- Credentials enter only through `HC_AMS_ADMIN_EMAIL` and `HC_AMS_ADMIN_PASSWORD`
341
- in the current process, intended for composition with the existing
342
- `onepassword-env` wrapper. Each invocation logs in through `HCTenantAdminSDK`;
343
- tokens remain private in memory. There is no token cache, fallback identity,
344
- secret argument, resolved env file, or browser credential bridge. Root MCP
474
+ For CI and scripts with no saved session, credentials may still enter through
475
+ `HC_AMS_ADMIN_EMAIL` and `HC_AMS_ADMIN_PASSWORD` in the current process, intended
476
+ for composition with the existing `onepassword-env` wrapper; that compatibility
477
+ path logs in once per invocation. There is no fallback identity, secret argument,
478
+ arbitrary token import, resolved env file, keychain, or browser credential bridge. Root MCP
345
479
  `--access-token`, `--base-url`, their environment equivalents, and token/base-URL
346
480
  profiles are refused for AMS. Root profile environment values do not select AMS.
347
481
 
@@ -0,0 +1,5 @@
1
+ """Read-only AMS agent command domain."""
2
+
3
+ from .group import create_agents_group
4
+
5
+ __all__ = ["create_agents_group"]
@@ -0,0 +1,83 @@
1
+ """Fresh Click adapters for read-only agent operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+ from ..compatibility import safe_errors
10
+ from ..context import AmsDependencies, pass_ams, preflight_agents
11
+ from ..output import metadata
12
+ from ..session import resolve_tenant_target
13
+ from .operations import AgentReadRefused, export_agent, get_agent, list_agents
14
+
15
+
16
+ def _environment(function):
17
+ return click.option("--environment", type=click.Choice(["dev", "uat", "prod"]))(function)
18
+
19
+
20
+ def _tenant(function):
21
+ return click.option("--tenant-id")(function)
22
+
23
+
24
+ def _agent(function):
25
+ return click.option("--agent-id", required=True)(function)
26
+
27
+
28
+ def _run(operation, *args):
29
+ try:
30
+ return operation(*args)
31
+ except AgentReadRefused as exc:
32
+ raise click.ClickException(str(exc)) from None
33
+
34
+
35
+ def create_list_command(dependencies: AmsDependencies) -> click.Command:
36
+ @click.command("list")
37
+ @_environment
38
+ @_tenant
39
+ @pass_ams
40
+ @safe_errors
41
+ def command(invocation, environment: str | None, tenant_id: str | None) -> None:
42
+ preflight_agents(invocation)
43
+ metadata(_run(list_agents, dependencies, resolve_tenant_target(environment, tenant_id)))
44
+
45
+ return command
46
+
47
+
48
+ def create_get_command(dependencies: AmsDependencies) -> click.Command:
49
+ @click.command("get")
50
+ @_environment
51
+ @_tenant
52
+ @_agent
53
+ @pass_ams
54
+ @safe_errors
55
+ def command(invocation, environment: str | None, tenant_id: str | None, agent_id: str) -> None:
56
+ preflight_agents(invocation)
57
+ metadata(_run(get_agent, dependencies, resolve_tenant_target(environment, tenant_id), agent_id))
58
+
59
+ return command
60
+
61
+
62
+ def create_export_command(dependencies: AmsDependencies) -> click.Command:
63
+ @click.command("export")
64
+ @_environment
65
+ @_tenant
66
+ @_agent
67
+ @click.option("--out", required=True, type=click.Path(path_type=Path, dir_okay=False))
68
+ @click.option("--format", "export_format", type=click.Choice(["json", "yaml"]), default="json", show_default=True)
69
+ @pass_ams
70
+ @safe_errors
71
+ def command(
72
+ invocation,
73
+ environment: str | None,
74
+ tenant_id: str | None,
75
+ agent_id: str,
76
+ out: Path,
77
+ export_format: str,
78
+ ) -> None:
79
+ preflight_agents(invocation)
80
+ target = resolve_tenant_target(environment, tenant_id)
81
+ metadata(_run(export_agent, dependencies, target, agent_id, out, export_format))
82
+
83
+ return command
@@ -0,0 +1,18 @@
1
+ """Fresh agent-domain group assembly."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from ..context import AmsDependencies
8
+ from .commands import create_export_command, create_get_command, create_list_command
9
+
10
+
11
+ def create_agents_group(dependencies: AmsDependencies) -> click.Group:
12
+ @click.group("agents")
13
+ def group() -> None:
14
+ """Read-only agent listing, inspection and export for one explicit tenant."""
15
+
16
+ for factory in (create_list_command, create_get_command, create_export_command):
17
+ group.add_command(factory(dependencies))
18
+ return group
@@ -0,0 +1,72 @@
1
+ """Click-free orchestration for agent list, get, and exact-text export."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ from pathlib import Path
7
+ from typing import Literal
8
+
9
+ from ..context import AmsDependencies
10
+ from ..output import private_text_output
11
+ from ..target import TenantTarget
12
+ from .projections import detail, summary
13
+
14
+
15
+ class AgentReadRefused(ValueError):
16
+ """A stable refusal safe for the CLI adapter to expose."""
17
+
18
+
19
+ def _agent_id(value: str) -> str:
20
+ if not isinstance(value, str) or not value.strip():
21
+ raise AgentReadRefused("Explicit agent ID required.")
22
+ return value
23
+
24
+
25
+ def list_agents(dependencies: AmsDependencies, target: TenantTarget) -> dict:
26
+ with dependencies.authenticated_sdk_factory(target) as sdk:
27
+ agents = sdk.ams_admin.list_agents()
28
+ if any(agent.tenant_id != target.tenant_id for agent in agents):
29
+ raise AgentReadRefused("Agent/tenant identity mismatch; stopped.")
30
+ return {
31
+ "environment": target.environment,
32
+ "tenant_id": target.tenant_id,
33
+ "agents": [summary(agent) for agent in agents],
34
+ }
35
+
36
+
37
+ def _verified_agent(sdk, target: TenantTarget, agent_id: str):
38
+ agent = sdk.ams_admin.get_agent(agent_id)
39
+ if agent is None or agent.id != agent_id or agent.tenant_id != target.tenant_id:
40
+ raise AgentReadRefused("Agent/tenant identity mismatch; stopped.")
41
+ return agent
42
+
43
+
44
+ def get_agent(dependencies: AmsDependencies, target: TenantTarget, agent_id: str) -> dict:
45
+ agent_id = _agent_id(agent_id)
46
+ with dependencies.authenticated_sdk_factory(target) as sdk:
47
+ agent = _verified_agent(sdk, target, agent_id)
48
+ return {"environment": target.environment, "tenant_id": target.tenant_id} | detail(agent)
49
+
50
+
51
+ def export_agent(
52
+ dependencies: AmsDependencies,
53
+ target: TenantTarget,
54
+ agent_id: str,
55
+ output: Path,
56
+ export_format: Literal["json", "yaml"],
57
+ ) -> dict:
58
+ agent_id = _agent_id(agent_id)
59
+ if export_format not in ("json", "yaml"):
60
+ raise AgentReadRefused("Export format must be json or yaml.")
61
+ with private_text_output(output) as write, dependencies.authenticated_sdk_factory(target) as sdk:
62
+ _verified_agent(sdk, target, agent_id)
63
+ text = sdk.ams_admin.export_agent_configuration(agent_id, format=export_format)
64
+ write(text)
65
+ return {
66
+ "environment": target.environment,
67
+ "tenant_id": target.tenant_id,
68
+ "agent_id": agent_id,
69
+ "path": str(output),
70
+ "format": export_format,
71
+ "sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
72
+ }
@@ -0,0 +1,37 @@
1
+ """Explicit bounded projections for agent reads."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from healthcloud.models.ams_tuning import AgentConfig
6
+
7
+ from ..output import digest
8
+
9
+ ASSIGNED_REFERENCES = ("tool_ids", "skill_ids", "guardrail_ids", "policy_ids", "data_source_ids")
10
+
11
+
12
+ def _metadata_string(value: object) -> str:
13
+ return value if isinstance(value, str) and value.strip() else "unknown"
14
+
15
+
16
+ def summary(agent: AgentConfig) -> dict:
17
+ runtime = agent.runtime_configuration if isinstance(agent.runtime_configuration, dict) else {}
18
+ return {
19
+ "id": agent.id,
20
+ "name": agent.name,
21
+ "key": agent.key,
22
+ "type": agent.type,
23
+ "status": agent.status,
24
+ "active": agent.active,
25
+ "config_version": agent.config_version,
26
+ "adapter": _metadata_string(runtime.get("adapter")),
27
+ "runtime": _metadata_string(runtime.get("runtime")),
28
+ }
29
+
30
+
31
+ def detail(agent: AgentConfig) -> dict:
32
+ values = {"instructions": agent.instructions, "greetings": agent.greetings}
33
+ return summary(agent) | {
34
+ "fields": sorted(values),
35
+ "field_hashes": {key: digest(value) for key, value in values.items()},
36
+ "assigned_references": {key: getattr(agent, key) for key in ASSIGNED_REFERENCES},
37
+ }