cinna-cli 0.4.0__tar.gz → 0.4.1__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 (106) hide show
  1. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/PKG-INFO +153 -3
  2. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/README.md +152 -2
  3. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/README.md +2 -1
  4. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/account_workspace/account_workspace_acceptance.md +8 -1
  5. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/account_workspace/account_workspace_tech.md +7 -1
  6. cinna_cli-0.4.1/docs/features/agent_addons/agent_addons.md +346 -0
  7. cinna_cli-0.4.1/docs/features/agent_addons/agent_addons_acceptance.md +639 -0
  8. cinna_cli-0.4.1/docs/features/agent_addons/agent_addons_tech.md +416 -0
  9. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_api/agent_api.md +17 -0
  10. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_api/agent_api_tech.md +3 -1
  11. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_management/agent_management.md +26 -1
  12. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_management/agent_management_acceptance.md +39 -4
  13. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_management/agent_management_tech.md +30 -4
  14. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +13 -1
  15. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/pyproject.toml +1 -1
  16. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/account.py +1990 -7
  17. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/cli_version.py +76 -3
  18. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/client.py +388 -1
  19. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/errors.py +37 -0
  20. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/main.py +476 -0
  21. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/sync.py +4 -0
  22. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +31 -0
  23. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/templates/CLAUDE.md.template +9 -5
  24. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_account.py +2015 -1
  25. cinna_cli-0.4.1/tests/test_cli_version.py +150 -0
  26. cinna_cli-0.4.1/tests/test_client.py +471 -0
  27. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/uv.lock +1 -1
  28. cinna_cli-0.4.0/tests/test_cli_version.py +0 -55
  29. cinna_cli-0.4.0/tests/test_client.py +0 -196
  30. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/.claude/commands/cinna-cli.feature.doc.md +0 -0
  31. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/.github/workflows/publish.yml +0 -0
  32. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/.gitignore +0 -0
  33. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/LICENSE.md +0 -0
  34. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/account_workspace/account_workspace.md +0 -0
  35. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_api/agent_api_acceptance.md +0 -0
  36. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_schedules/agent_schedules.md +0 -0
  37. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
  38. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
  39. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +0 -0
  40. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +0 -0
  41. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/doctor/doctor.md +0 -0
  42. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/doctor/doctor_acceptance.md +0 -0
  43. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/doctor/doctor_tech.md +0 -0
  44. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/git_versioning/git_versioning.md +0 -0
  45. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/git_versioning/git_versioning_acceptance.md +0 -0
  46. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/git_versioning/git_versioning_tech.md +0 -0
  47. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/improvement_requests/improvement_requests.md +0 -0
  48. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
  49. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
  50. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/live_sync/live_sync.md +0 -0
  51. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/live_sync/live_sync_acceptance.md +0 -0
  52. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/live_sync/live_sync_tech.md +0 -0
  53. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/local_agent_import/local_agent_import.md +0 -0
  54. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/local_agent_import/local_agent_import_acceptance.md +0 -0
  55. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/local_agent_import/local_agent_import_tech.md +0 -0
  56. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/mcp_integration/mcp_integration.md +0 -0
  57. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
  58. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
  59. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/remote_chat/remote_chat.md +0 -0
  60. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
  61. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/remote_chat/remote_chat_tech.md +0 -0
  62. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/remote_exec/remote_exec.md +0 -0
  63. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
  64. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/features/remote_exec/remote_exec_tech.md +0 -0
  65. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/interface.md +0 -0
  66. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/docs/mutagen_capabilities.md +0 -0
  67. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/scripts/check_docs_references.py +0 -0
  68. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/__init__.py +0 -0
  69. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/auth.py +0 -0
  70. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/bootstrap.py +0 -0
  71. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/chat.py +0 -0
  72. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/config.py +0 -0
  73. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/console.py +0 -0
  74. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/context.py +0 -0
  75. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/doctor.py +0 -0
  76. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/git_versioning.py +0 -0
  77. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/improve.py +0 -0
  78. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/kit_contract.py +0 -0
  79. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/local_import.py +0 -0
  80. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/logging.py +0 -0
  81. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/mcp_proxy.py +0 -0
  82. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/mutagen_runtime.py +0 -0
  83. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/sync_session.py +0 -0
  84. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/sync_ssh_shim.py +0 -0
  85. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/sync_tui.py +0 -0
  86. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/templates/CHAT_TESTING.md +0 -0
  87. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/templates/GIT_VERSIONING.md +0 -0
  88. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/src/cinna/templates/__init__.py +0 -0
  89. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/__init__.py +0 -0
  90. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/conftest.py +0 -0
  91. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_auth.py +0 -0
  92. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_bootstrap.py +0 -0
  93. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_chat.py +0 -0
  94. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_config.py +0 -0
  95. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_context.py +0 -0
  96. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_doctor.py +0 -0
  97. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_git_versioning.py +0 -0
  98. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_improve.py +0 -0
  99. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_kit_contract.py +0 -0
  100. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_local_import.py +0 -0
  101. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_main.py +0 -0
  102. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_mutagen_runtime.py +0 -0
  103. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_onboarding.py +0 -0
  104. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_sync.py +0 -0
  105. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_sync_session.py +0 -0
  106. {cinna_cli-0.4.0 → cinna_cli-0.4.1}/tests/test_sync_ssh_shim.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cinna-cli
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Local development CLI for Cinna Core agents
5
5
  Project-URL: Homepage, https://github.com/opencinna/cinna-cli
6
6
  Project-URL: Repository, https://github.com/opencinna/cinna-cli
@@ -176,13 +176,13 @@ cinna account set-token 'curl -sL https://your-platform.com/api/cli-setup/accoun
176
176
 
177
177
  ### `cinna account agents`
178
178
 
179
- List the agents your account can access (run from inside the account workspace). For each agent: display name + ID, building rights (`✓ can build`, `view-only`, or `foreign install` — installed bundles are publisher-managed and can't be synced), whether a remote environment is active, and whether a local workspace already exists under `agents/`.
179
+ List the agents your account can access (run from inside the account workspace). Ids are printed in full at any terminal width (the column folds rather than ellipsizing — a truncated UUID is a copy-paste trap that the API answers `404 not found` for). For each agent: display name + ID, building rights (`✓ can build`, `view-only`, or `foreign install` — installed bundles are publisher-managed and can't be synced), whether a remote environment is active, and whether a local workspace already exists under `agents/`.
180
180
 
181
181
  ### `cinna account status`
182
182
 
183
183
  One-shot summary of the account workspace: platform/frontend URLs, machine name, synced-agent count, and an account-token probe (`valid token` / `expired token` / `no connection`) — the account-level counterpart of `cinna status`.
184
184
 
185
- Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there. Likewise it compares the installed **cinna-cli version** with the one the platform pins (its `/.well-known/cinna-desktop` discovery document) and suggests `uv tool install cinna-cli==<pin>` when behind; no pin means "unknown", not an error.
185
+ Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there. Likewise it compares the installed **cinna-cli version** with the one the platform pins (its `/.well-known/cinna-desktop` discovery document) and suggests `uv tool install cinna-cli==<pin>` when behind; no pin means "unknown", not an error. An **editable install** (`uv tool install -e`, `pip install -e`) is detected and labelled instead of compared — its metadata records the version it was installed at and never changes again, so pinning that number against the platform's would report skew that does not exist and explain missing features with a version that is not the code being run. It renders as `0.2.5 (editable checkout of /path/to/cinna-cli)`, with the pin named as context, and neither `cinna account status` nor `cinna doctor` nudges an upgrade.
186
186
 
187
187
  With `--json` it prints a single line: `{"result":"ok","workspace",…,"token":"valid|expired|unreachable","synced_agents":N,"agents":[…],"context_package":{"local","remote","state"},"cli":{"installed","required","state"}}`.
188
188
 
@@ -295,6 +295,150 @@ The folder rules come from the kit's versioned contract, `.cinna-kit/layout.json
295
295
 
296
296
  Where the folder has been published is recorded in `publications.json`, a **sibling** of `cinna-agent.json` holding one entry per Cinna instance — it cannot live inside the manifest, because each entry records a hash of the exported tree and the manifest is part of that tree. Every step is idempotent (agent by the entry whose `platform_url` matches the instance you are logged into, credentials by name, schedules by name), so a partial import is resumed with `--update` instead of duplicating anything. The publication is recorded **only after the push settled** — `--no-push`, a failed flush, or remaining conflicts leave it unrecorded, and the next run is a plain `--update`. A legacy `cloud` block is migrated into the ledger on the first write and is still read until then, so an older folder's `--update` never creates a second agent. `--dry-run` makes no platform call and writes nothing.
297
297
 
298
+ ### `cinna skills list <agent> [--json]`
299
+
300
+ List everything an agent carries beyond its prompt, as one deduplicated list. An **addon** is either an installed plugin or a `skills/<name>/` folder — a `SKILL.md` plus its files that the engine loads on demand. The two overlap (a skill installed from the catalog is *also* a plugin link), and the platform owns the dedupe rule, so this prints the server's projection rather than folding the halves itself: one row per addon, with its kind, source (`marketplace` / `bundle` / `catalog` / `local`), status, name and version.
301
+
302
+ ```bash
303
+ cinna skills list crm-agent
304
+ ```
305
+
306
+ ```
307
+ Agent: CRM Agent
308
+ Addons (3)
309
+ ┏━━━┳━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
310
+ ┃ # ┃ Kind ┃ Source ┃ Status ┃ Name ┃ Version ┃
311
+ ┡━━━╇━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
312
+ │ 1 │ plugin │ marketplace │ ● ok │ pdf-tools (PDF Tools) │ 2.1.0 │
313
+ │ 2 │ skill │ catalog │ ● ok │ dad-jokes (Dad Jokes) │ 1.0.0 → 1.1.0 │
314
+ │ 3 │ skill │ local │ ● ok │ report · published │ 1.0.1 │
315
+ │ 4 │ skill │ local │ ! secrets │ draft │ │
316
+ └───┴────────┴─────────────┴───────────┴───────────────────────┴───────────────┘
317
+ 1 plugin(s), 3 skill(s) (2 of them this agent's own).
318
+
319
+ ! 1 installed addon(s) have a newer revision: dad-jokes
320
+ Update with: cinna skills update crm-agent dad-jokes
321
+
322
+ ! draft (secrets): This skill holds files that look like key material.
323
+ .env
324
+
325
+ A blank version means no `version:` in the skill's SKILL.md — or an index built
326
+ before skills carried one, which fills in after the next refresh or publish.
327
+ ```
328
+
329
+ The **Version** column is what the agent carries (`installed_version`), and an installed addon whose catalog has moved since reads `1.0.0 → 1.1.0` with the update command named under the table — an agent sitting two revisions back must not look identical to a current one. `Name` is the engine-facing folder name — the string `cinna skills publish` takes back — with the display name beside it when they differ. `· published` marks a local skill that already has a catalog package, so a re-publish appends a revision instead of creating a second package. The status column carries the server's own code (`secrets`, `oversized`, `shadowed` for warnings; `missing_description`, `name_mismatch`, `source_unavailable`, `orphan` for errors), and the platform's own sentence for each flagged row — with the offending files for `secrets` — follows under the table. Reads the server's cache, so it never wakes a sleeping environment: when the skill half could not be read the plugin rows still list and the reason is printed (`env_not_running`, `adapter_error`, `parse_error`) instead of a short list looking complete. `--json` prints the raw payload (`addons`, `counts`, `skills_error`) for a script or a local coding agent.
330
+
331
+ ### `cinna skills publish <agent> <name> [--visibility public|private|users] [--grant EMAIL ...] [--version V] [--notes TEXT] [--package-id ID] [--dry-run] [--yes] [--json]`
332
+
333
+ Publish one of the agent's own skills to the instance skills catalog, where other agents can install it. `<name>` is the folder name from `cinna skills list`. Requires the `agent-developer` role on an agent that is not a foreign install; the skill must be clean (no parse error, no files that look like key material).
334
+
335
+ **It publishes the agent's cloud workspace, not the folder you are standing in.** The files are read from the remote workspace on disk — which is why a *suspended* environment publishes fine, no container needs to be running — but an edit that has not synced yet is not there. Run `cinna sync push` first, or you publish the older remote copy as a revision you cannot take back.
336
+
337
+ **Without `--visibility` the package is private and nobody else sees it.** Say `--visibility public` for the catalog, or `--visibility users` with `--grant` for named people.
338
+
339
+ **The version is derived, not typed.** A skill's version is a line in its own `SKILL.md`, and the platform continues the series for you: the header's version if it has not been published yet, otherwise the next one after the newest release (the last run of digits incremented — `1.0.0`→`1.0.1`, `v3`→`v4`), or `1.0.0` for a skill nobody has versioned. The resolved version is written back into `skills/<name>/SKILL.md` on the environment before the snapshot, so the published bytes carry their own version and your next `cinna sync` brings the stamped header down. `--version` overrides it verbatim for one revision — after which the series continues from *that*. `--dry-run` prints the version, package id and revision number a publish would take, from the same code that will take them, and publishes nothing.
340
+
341
+ ```bash
342
+ cinna skills publish crm-agent report --dry-run
343
+ cinna skills publish crm-agent report --visibility public \
344
+ --notes "Adds the quarterly rollup"
345
+ cinna skills publish crm-agent report --visibility users \
346
+ --grant alice@example.com --grant bob@example.com
347
+ ```
348
+
349
+ ```
350
+ Re-publish report from CRM Agent
351
+ Version: 1.0.2 (header 1.0.1, latest published 1.0.1)
352
+ Package: com.example.skill.report
353
+ Revision: 3
354
+ Publish? [Y/n]:
355
+ ```
356
+
357
+ At a terminal that preview is shown and confirmed before the press; `--yes`, `--json` and `--no-input` publish straight away. `--package-id` is optional too — omitted, it is derived as `<reversed host>.skill.<name>`, with your own publisher slug appended when that id is already taken on the instance (`--dry-run` says when that happened, since a hex tail in your own package id has no other explanation).
358
+
359
+ A re-publish appends a revision to the same package, so `--package-id` (the reverse-DNS id, e.g. `com.acme.report`) is only for a first publish — a mismatch on a later one is refused rather than silently ignored. `--visibility` is likewise honoured on a first publish and on an explicit change; omitting it leaves the package as it is. `--grant` requires `--visibility users` and the CLI refuses the combination otherwise, before anything is written. The server would accept it — it stores the grant either way — but a package that is private or public never consults its grant list, so the publish would report success and share nothing, and a revision cannot be taken back. Within a `users` package `--grant` is strictly **additive**: it adds the addresses it names and never revokes the ones it omits (revoking is `cinna skills revoke`, so a stale publish cannot silently remove access), and an unknown address fails the whole publish rather than half-sharing it. On success the package id, revision and its version, visibility and catalog URL are printed, plus a line saying the version landed in `skills/<name>/SKILL.md` — and a warning instead when it could not be written there, because the header and the catalog have then diverged and the next publish continues from the catalog. A refusal is printed as the platform's own sentence with its code (`not_developer`, `foreign_install`, `no_environment`, `workspace_unavailable`, `package_id_immutable`, `skill_contains_secrets` — which also lists the offending files), and `--json` prints `{revision, package, catalog_url, skill_md_updated}` instead of the human block (`--dry-run --json` prints the preview payload).
360
+
361
+ ### `cinna skills catalog [--search Q] [--mine] [--json]`
362
+
363
+ Browse the instance skills catalog: one row per package this account may see, with its reverse-DNS **package id**, display name, visibility and newest version. The package id — `com.acme.pdf-report`, not a UUID — is the reference every other package verb takes. A delisted package is marked as such beside its visibility.
364
+
365
+ The route takes no parameters — it answers the whole visible catalogue — so `--search` (matching the id, name and description) and `--mine` (packages this account can manage) narrow the rows locally. `--json` prints the filtered rows, not the raw envelope.
366
+
367
+ ### `cinna skills show <package> [--revision N] [--json]`
368
+
369
+ One package in full: its display name, visibility, newest version and package UUID, then the revision table, then the `SKILL.md` of the newest revision (or the one `--revision` names). `<package>` is a package id, a display name, or a UUID. The `SKILL.md` is printed as plain text — it is someone else's file and may contain anything — and a content fetch that fails degrades the output rather than the command, because the package detail is the answer.
370
+
371
+ ### `cinna skills revisions <package> [--json]`
372
+
373
+ A package's revisions, **newest first**: number, version, release date, size and the release notes given at publish time. A revision is immutable, so this is the question a publisher has before every publish — what is already out there — and the answer to it no longer requires `cinna api` and a UUID. Pair it with `cinna skills publish <agent> <name> --dry-run`, which says what the *next* one would be.
374
+
375
+ ### `cinna skills files <package> [--revision N] [--json]`
376
+
377
+ The files one revision ships, with sizes and the total. A file list the server truncated says so.
378
+
379
+ There is no `download` verb, for two reasons: installing a skill lands it in the agent's own workspace, which sync brings down to the local mirror — so the bytes already arrive that way — and the archive routes are binary, which the JSON-only escape hatch could not carry anyway.
380
+
381
+ ### `cinna skills install <agent> <package> [--revision N] [--conversation-only|--building-only] [--json]`
382
+
383
+ Install a catalog package onto an agent. `<agent>` is a name, slug or id; `<package>` is a package id, display name or UUID — the CLI resolves both, so neither a raw UUID nor a hand-built JSON body is needed.
384
+
385
+ ```bash
386
+ cinna skills catalog --search jokes
387
+ cinna skills install crm-agent localhost.skill.dad-jokes
388
+ cinna skills install crm-agent localhost.skill.dad-jokes --revision 2 --conversation-only
389
+ ```
390
+
391
+ Omitting `--revision` installs whatever the catalog calls latest *now*; because a revision is immutable, the install pins bytes that will not change under the agent until `cinna skills update` moves it. Both modes are on unless `--conversation-only` / `--building-only` narrows where the skill is offered (naming both is refused — it would install a skill offered nowhere).
392
+
393
+ Installing something the agent already carries is answered as a sentence, not a JSON body: the platform's own `already_installed` refusal followed by either "already at the newest revision" or the `cinna skills update` that closes the gap. The `--json` error code stays `already_installed`.
394
+
395
+ ### `cinna skills update <agent> <name> [--json]`
396
+
397
+ Move an installed skill to the package's newest revision, printing the version it moved from and to. `<name>` is the name `cinna skills list` prints; the plugin-link id the route actually addresses is resolved internally. The listing's `has_update` is *reported*, never used to skip the call — it comes from a cache, and the upgrade route is what decides.
398
+
399
+ Every install / uninstall / update / toggle also pushes the change into the agent's running environments, and that push can partly fail while the call itself succeeds. When it does, the command says how many environments did not take it and points at `cinna agent restart-env` — a green check alone would hide the one case where the catalog and the live agent disagree. An environment built *before* the feature existed is reported separately (`unsupported_syncs`): the link write is complete and there is nothing to retry, so that half points at `cinna agent rebuild-env` and suppresses the plain success line rather than offering a restart that cannot change the outcome.
400
+
401
+ ### `cinna skills uninstall <agent> <name> [--yes] [--json]`
402
+
403
+ Remove an agent's copy of an installed skill. The package and its revisions are untouched — what the agent held was a copy, not a reference. Asks first unless `--yes`. An agent's *own* `skills/<name>/` folder is not an install, and the command says so rather than failing on a route that could never match it.
404
+
405
+ ### `cinna skills toggle <agent> <name> [--enable|--disable] [--conversation-mode|--no-conversation-mode] [--building-mode|--no-building-mode] [--json]`
406
+
407
+ Enable or disable an installed skill, or change where it is offered, without removing it. Every switch defaults to "leave it alone" and only the ones named are sent, so `--disable` cannot silently reset the mode flags a previous call set. Naming no switch at all is a usage error rather than a no-op call. A disabled install still lists and still reports its version — it is not an error — so `cinna skills list` marks it `· disabled`.
408
+
409
+ ### `cinna skills refresh <agent> [--json]`
410
+
411
+ Rebuild the agent's addon index, reporting how many skills were indexed. Everything else in this group reads the platform's cache — which is what makes it safe against a sleeping environment, and what makes a stale index possible. This is the remedy when `cinna skills list` reports `parse_error`, or shows no version for a skill whose `SKILL.md` has one. It is *not* the remedy for every reason code — see below. The plugin half is refreshed too where the platform offers that route; a platform without it still refreshes the skill half and says so.
412
+
413
+ A refresh that still could not read the index answers **200 with the reason** — so this prints the reason and the remedy *that reason* has, rather than a green check. The four codes do not share a fix, and both `skills refresh` and `skills list` route through the same table:
414
+
415
+ | Code | What it means | The fix it prints |
416
+ |---|---|---|
417
+ | `env_not_running` | The environment is asleep | Send it a message, or refresh again, to wake it |
418
+ | `adapter_error` | Up, but not answering | `cinna agent restart-env <agent>` |
419
+ | `adapter_unsupported` | Built before agent skills existed; no skills endpoint to answer | `cinna agent rebuild-env <agent>` |
420
+ | `parse_error` | Answered, but the index did not parse | `cinna skills refresh <agent>` |
421
+
422
+ An unrecognised code gets a generic "refresh again, then check the logs" and names **no** verb: a code whose fix this build cannot name is one where guessing a verb sends the caller round a loop that cannot close. That is exactly what the old copy did to `adapter_unsupported` — it printed one hardcoded remedy for every code, so a container missing the route was told to restart (which re-runs the same image) or, in `skills list`, to "Rebuild it with: cinna skills refresh" (which re-reads a route that is not there, and calls a refresh a rebuild).
423
+
424
+ ### `cinna skills grants <package> [--json]` · `grant <package> --user EMAIL` · `revoke <package> --user EMAIL [--yes]`
425
+
426
+ Who is named on a package, and adding or removing one. Visibility and grants belong to the *package*, not to a revision, so changing them costs no new revision — this is the post-publish half of `cinna skills publish --grant`.
427
+
428
+ A grant list is stored on any package but only *consulted* on one whose visibility is `users`, so `grants` prints the visibility beside the rows and both verbs warn when the list is being ignored. `revoke` takes the same email `grant` did and resolves it to the user id the route actually addresses; an address that was never granted is a sentence naming who actually is.
429
+
430
+ ### `cinna skills visibility <package> <public|private|users> [--json]`
431
+
432
+ Change who may see a package after it was published. Switching to `users` with nobody named warns that it shares the package with nobody — the one setting that looks like sharing and is not.
433
+
434
+ ### `cinna skills delist <package> [--yes] [--json]`
435
+
436
+ Take a package out of the catalog. Agents that already installed it keep what they have: a revision they hold is a copy, not a reference. Asks first unless `--yes`. Deleting a package outright is still web-UI only.
437
+
438
+ ### `cinna skills relist <package> [--json]`
439
+
440
+ Put a delisted package back. `delist` has no inverse route of its own — without this verb the only way back would be `cinna api`, which would make delisting the one door in this group that opens only outwards.
441
+
298
442
  ### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
299
443
 
300
444
  Wire one agent to another's REST API from the account workspace. Resolves both agents (name, slug, or ID), mints a producer API token, and attaches it to the consumer as a credential — which rides the consumer's normal credential sync into its remote environment, so no key ever touches your machine. Prints the credential ID, token prefix, base URL, and spec URL. `--read-only` restricts the consumer to read-only access; `--label` names the credential. Backend errors surface verbatim: 400 if the producer's REST API is disabled, 403/404 for ownership violations.
@@ -319,8 +463,13 @@ Generic escape hatch into the platform API, authenticated with the account token
319
463
  - The inner response is passed through verbatim: the body prints to stdout (pretty-printed for JSON) and the exit code is `0` for 2xx and `1` for an inner 4xx/5xx — so it composes in shell pipelines.
320
464
  - When the escape hatch itself refuses the call, the detail prints to stderr and the exit code is `2`: policy denials (credentials, user management, admin, CLI, MFA/auth, and streaming routes are excluded — shown as `blocked by platform policy: …`), rate limiting (429, with the Retry-After delay), and request/response size caps (413/502).
321
465
 
466
+ `<path>` accepts the same **agent references** the rest of the CLI does: a segment straight after `agents/` that is not already a UUID is resolved against the account's agent listing, and the substitution is announced on stderr so a `--json` stdout stream stays pure. Resolution is sugar and never breaks the hatch — a reference that does not resolve (or an agent listing that cannot be reached) leaves the path exactly as typed, because `agents/` is a route prefix as well as a collection.
467
+
468
+ A path segment carrying an **elided id** (`agents/f0506e24-3740-4fe3…/addons`, copied out of a table cell too narrow to print it whole) is refused locally, before any request. The API would answer `404 Agent not found` for it, which reads as a missing agent rather than a mangled id.
469
+
322
470
  ```bash
323
471
  cinna api GET agents
472
+ cinna api GET agents/crm-agent/addons # resolved to agents/<uuid>/addons
324
473
  cinna api GET agents --query limit=5
325
474
  cinna api PATCH agents/3fa85f64-5717-4562-b3fc-2c963f66afa6 --json '{"description": "updated"}'
326
475
  cinna api POST tasks --data @task.json
@@ -417,6 +566,7 @@ It detects and fixes:
417
566
  - **Orphaned sessions** — `cinna-*` sessions with no registry entry at all. Terminated.
418
567
  - **Active sessions** — the healthy, still-watching sessions left over from past `cinna dev` runs. They are not broken, but they keep the shared Mutagen daemon busy and are recreated on demand, so doctor offers to clear them as a separate step.
419
568
  - **Expired tokens** — for **account-managed** workspaces (those under an account root), the CLI token is re-minted automatically through the parent account token, no pasting required. **Standalone** workspaces (set up via `cinna setup`) can only be refreshed with a pasted setup token, so they are reported with a `cinna set-token` hint rather than changed.
569
+ - **cinna-cli behind the platform pin** — one report-only finding per platform whose discovery document pins a different version. An **editable install** raises none: its recorded version is a snapshot of the day it was installed, not the code that runs, so "behind the pin" would be a confident wrong answer — and one that invites missing features to be misdiagnosed as version skew.
420
570
  - **Expired account token** — a sub-agent token can only be re-minted while the **account** token that mints it is still valid. When the account token has itself expired, doctor probes it once and surfaces a single _"renew the account token — run `cinna login`"_ finding (listing the blocked sub-agents) instead of a pile of re-mints that would all fail with 401. Run `cinna login`, then re-run `cinna doctor` to re-mint the dependents.
421
571
 
422
572
  ```bash
@@ -139,13 +139,13 @@ cinna account set-token 'curl -sL https://your-platform.com/api/cli-setup/accoun
139
139
 
140
140
  ### `cinna account agents`
141
141
 
142
- List the agents your account can access (run from inside the account workspace). For each agent: display name + ID, building rights (`✓ can build`, `view-only`, or `foreign install` — installed bundles are publisher-managed and can't be synced), whether a remote environment is active, and whether a local workspace already exists under `agents/`.
142
+ List the agents your account can access (run from inside the account workspace). Ids are printed in full at any terminal width (the column folds rather than ellipsizing — a truncated UUID is a copy-paste trap that the API answers `404 not found` for). For each agent: display name + ID, building rights (`✓ can build`, `view-only`, or `foreign install` — installed bundles are publisher-managed and can't be synced), whether a remote environment is active, and whether a local workspace already exists under `agents/`.
143
143
 
144
144
  ### `cinna account status`
145
145
 
146
146
  One-shot summary of the account workspace: platform/frontend URLs, machine name, synced-agent count, and an account-token probe (`valid token` / `expired token` / `no connection`) — the account-level counterpart of `cinna status`.
147
147
 
148
- Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there. Likewise it compares the installed **cinna-cli version** with the one the platform pins (its `/.well-known/cinna-desktop` discovery document) and suggests `uv tool install cinna-cli==<pin>` when behind; no pin means "unknown", not an error.
148
+ Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there. Likewise it compares the installed **cinna-cli version** with the one the platform pins (its `/.well-known/cinna-desktop` discovery document) and suggests `uv tool install cinna-cli==<pin>` when behind; no pin means "unknown", not an error. An **editable install** (`uv tool install -e`, `pip install -e`) is detected and labelled instead of compared — its metadata records the version it was installed at and never changes again, so pinning that number against the platform's would report skew that does not exist and explain missing features with a version that is not the code being run. It renders as `0.2.5 (editable checkout of /path/to/cinna-cli)`, with the pin named as context, and neither `cinna account status` nor `cinna doctor` nudges an upgrade.
149
149
 
150
150
  With `--json` it prints a single line: `{"result":"ok","workspace",…,"token":"valid|expired|unreachable","synced_agents":N,"agents":[…],"context_package":{"local","remote","state"},"cli":{"installed","required","state"}}`.
151
151
 
@@ -258,6 +258,150 @@ The folder rules come from the kit's versioned contract, `.cinna-kit/layout.json
258
258
 
259
259
  Where the folder has been published is recorded in `publications.json`, a **sibling** of `cinna-agent.json` holding one entry per Cinna instance — it cannot live inside the manifest, because each entry records a hash of the exported tree and the manifest is part of that tree. Every step is idempotent (agent by the entry whose `platform_url` matches the instance you are logged into, credentials by name, schedules by name), so a partial import is resumed with `--update` instead of duplicating anything. The publication is recorded **only after the push settled** — `--no-push`, a failed flush, or remaining conflicts leave it unrecorded, and the next run is a plain `--update`. A legacy `cloud` block is migrated into the ledger on the first write and is still read until then, so an older folder's `--update` never creates a second agent. `--dry-run` makes no platform call and writes nothing.
260
260
 
261
+ ### `cinna skills list <agent> [--json]`
262
+
263
+ List everything an agent carries beyond its prompt, as one deduplicated list. An **addon** is either an installed plugin or a `skills/<name>/` folder — a `SKILL.md` plus its files that the engine loads on demand. The two overlap (a skill installed from the catalog is *also* a plugin link), and the platform owns the dedupe rule, so this prints the server's projection rather than folding the halves itself: one row per addon, with its kind, source (`marketplace` / `bundle` / `catalog` / `local`), status, name and version.
264
+
265
+ ```bash
266
+ cinna skills list crm-agent
267
+ ```
268
+
269
+ ```
270
+ Agent: CRM Agent
271
+ Addons (3)
272
+ ┏━━━┳━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
273
+ ┃ # ┃ Kind ┃ Source ┃ Status ┃ Name ┃ Version ┃
274
+ ┡━━━╇━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
275
+ │ 1 │ plugin │ marketplace │ ● ok │ pdf-tools (PDF Tools) │ 2.1.0 │
276
+ │ 2 │ skill │ catalog │ ● ok │ dad-jokes (Dad Jokes) │ 1.0.0 → 1.1.0 │
277
+ │ 3 │ skill │ local │ ● ok │ report · published │ 1.0.1 │
278
+ │ 4 │ skill │ local │ ! secrets │ draft │ │
279
+ └───┴────────┴─────────────┴───────────┴───────────────────────┴───────────────┘
280
+ 1 plugin(s), 3 skill(s) (2 of them this agent's own).
281
+
282
+ ! 1 installed addon(s) have a newer revision: dad-jokes
283
+ Update with: cinna skills update crm-agent dad-jokes
284
+
285
+ ! draft (secrets): This skill holds files that look like key material.
286
+ .env
287
+
288
+ A blank version means no `version:` in the skill's SKILL.md — or an index built
289
+ before skills carried one, which fills in after the next refresh or publish.
290
+ ```
291
+
292
+ The **Version** column is what the agent carries (`installed_version`), and an installed addon whose catalog has moved since reads `1.0.0 → 1.1.0` with the update command named under the table — an agent sitting two revisions back must not look identical to a current one. `Name` is the engine-facing folder name — the string `cinna skills publish` takes back — with the display name beside it when they differ. `· published` marks a local skill that already has a catalog package, so a re-publish appends a revision instead of creating a second package. The status column carries the server's own code (`secrets`, `oversized`, `shadowed` for warnings; `missing_description`, `name_mismatch`, `source_unavailable`, `orphan` for errors), and the platform's own sentence for each flagged row — with the offending files for `secrets` — follows under the table. Reads the server's cache, so it never wakes a sleeping environment: when the skill half could not be read the plugin rows still list and the reason is printed (`env_not_running`, `adapter_error`, `parse_error`) instead of a short list looking complete. `--json` prints the raw payload (`addons`, `counts`, `skills_error`) for a script or a local coding agent.
293
+
294
+ ### `cinna skills publish <agent> <name> [--visibility public|private|users] [--grant EMAIL ...] [--version V] [--notes TEXT] [--package-id ID] [--dry-run] [--yes] [--json]`
295
+
296
+ Publish one of the agent's own skills to the instance skills catalog, where other agents can install it. `<name>` is the folder name from `cinna skills list`. Requires the `agent-developer` role on an agent that is not a foreign install; the skill must be clean (no parse error, no files that look like key material).
297
+
298
+ **It publishes the agent's cloud workspace, not the folder you are standing in.** The files are read from the remote workspace on disk — which is why a *suspended* environment publishes fine, no container needs to be running — but an edit that has not synced yet is not there. Run `cinna sync push` first, or you publish the older remote copy as a revision you cannot take back.
299
+
300
+ **Without `--visibility` the package is private and nobody else sees it.** Say `--visibility public` for the catalog, or `--visibility users` with `--grant` for named people.
301
+
302
+ **The version is derived, not typed.** A skill's version is a line in its own `SKILL.md`, and the platform continues the series for you: the header's version if it has not been published yet, otherwise the next one after the newest release (the last run of digits incremented — `1.0.0`→`1.0.1`, `v3`→`v4`), or `1.0.0` for a skill nobody has versioned. The resolved version is written back into `skills/<name>/SKILL.md` on the environment before the snapshot, so the published bytes carry their own version and your next `cinna sync` brings the stamped header down. `--version` overrides it verbatim for one revision — after which the series continues from *that*. `--dry-run` prints the version, package id and revision number a publish would take, from the same code that will take them, and publishes nothing.
303
+
304
+ ```bash
305
+ cinna skills publish crm-agent report --dry-run
306
+ cinna skills publish crm-agent report --visibility public \
307
+ --notes "Adds the quarterly rollup"
308
+ cinna skills publish crm-agent report --visibility users \
309
+ --grant alice@example.com --grant bob@example.com
310
+ ```
311
+
312
+ ```
313
+ Re-publish report from CRM Agent
314
+ Version: 1.0.2 (header 1.0.1, latest published 1.0.1)
315
+ Package: com.example.skill.report
316
+ Revision: 3
317
+ Publish? [Y/n]:
318
+ ```
319
+
320
+ At a terminal that preview is shown and confirmed before the press; `--yes`, `--json` and `--no-input` publish straight away. `--package-id` is optional too — omitted, it is derived as `<reversed host>.skill.<name>`, with your own publisher slug appended when that id is already taken on the instance (`--dry-run` says when that happened, since a hex tail in your own package id has no other explanation).
321
+
322
+ A re-publish appends a revision to the same package, so `--package-id` (the reverse-DNS id, e.g. `com.acme.report`) is only for a first publish — a mismatch on a later one is refused rather than silently ignored. `--visibility` is likewise honoured on a first publish and on an explicit change; omitting it leaves the package as it is. `--grant` requires `--visibility users` and the CLI refuses the combination otherwise, before anything is written. The server would accept it — it stores the grant either way — but a package that is private or public never consults its grant list, so the publish would report success and share nothing, and a revision cannot be taken back. Within a `users` package `--grant` is strictly **additive**: it adds the addresses it names and never revokes the ones it omits (revoking is `cinna skills revoke`, so a stale publish cannot silently remove access), and an unknown address fails the whole publish rather than half-sharing it. On success the package id, revision and its version, visibility and catalog URL are printed, plus a line saying the version landed in `skills/<name>/SKILL.md` — and a warning instead when it could not be written there, because the header and the catalog have then diverged and the next publish continues from the catalog. A refusal is printed as the platform's own sentence with its code (`not_developer`, `foreign_install`, `no_environment`, `workspace_unavailable`, `package_id_immutable`, `skill_contains_secrets` — which also lists the offending files), and `--json` prints `{revision, package, catalog_url, skill_md_updated}` instead of the human block (`--dry-run --json` prints the preview payload).
323
+
324
+ ### `cinna skills catalog [--search Q] [--mine] [--json]`
325
+
326
+ Browse the instance skills catalog: one row per package this account may see, with its reverse-DNS **package id**, display name, visibility and newest version. The package id — `com.acme.pdf-report`, not a UUID — is the reference every other package verb takes. A delisted package is marked as such beside its visibility.
327
+
328
+ The route takes no parameters — it answers the whole visible catalogue — so `--search` (matching the id, name and description) and `--mine` (packages this account can manage) narrow the rows locally. `--json` prints the filtered rows, not the raw envelope.
329
+
330
+ ### `cinna skills show <package> [--revision N] [--json]`
331
+
332
+ One package in full: its display name, visibility, newest version and package UUID, then the revision table, then the `SKILL.md` of the newest revision (or the one `--revision` names). `<package>` is a package id, a display name, or a UUID. The `SKILL.md` is printed as plain text — it is someone else's file and may contain anything — and a content fetch that fails degrades the output rather than the command, because the package detail is the answer.
333
+
334
+ ### `cinna skills revisions <package> [--json]`
335
+
336
+ A package's revisions, **newest first**: number, version, release date, size and the release notes given at publish time. A revision is immutable, so this is the question a publisher has before every publish — what is already out there — and the answer to it no longer requires `cinna api` and a UUID. Pair it with `cinna skills publish <agent> <name> --dry-run`, which says what the *next* one would be.
337
+
338
+ ### `cinna skills files <package> [--revision N] [--json]`
339
+
340
+ The files one revision ships, with sizes and the total. A file list the server truncated says so.
341
+
342
+ There is no `download` verb, for two reasons: installing a skill lands it in the agent's own workspace, which sync brings down to the local mirror — so the bytes already arrive that way — and the archive routes are binary, which the JSON-only escape hatch could not carry anyway.
343
+
344
+ ### `cinna skills install <agent> <package> [--revision N] [--conversation-only|--building-only] [--json]`
345
+
346
+ Install a catalog package onto an agent. `<agent>` is a name, slug or id; `<package>` is a package id, display name or UUID — the CLI resolves both, so neither a raw UUID nor a hand-built JSON body is needed.
347
+
348
+ ```bash
349
+ cinna skills catalog --search jokes
350
+ cinna skills install crm-agent localhost.skill.dad-jokes
351
+ cinna skills install crm-agent localhost.skill.dad-jokes --revision 2 --conversation-only
352
+ ```
353
+
354
+ Omitting `--revision` installs whatever the catalog calls latest *now*; because a revision is immutable, the install pins bytes that will not change under the agent until `cinna skills update` moves it. Both modes are on unless `--conversation-only` / `--building-only` narrows where the skill is offered (naming both is refused — it would install a skill offered nowhere).
355
+
356
+ Installing something the agent already carries is answered as a sentence, not a JSON body: the platform's own `already_installed` refusal followed by either "already at the newest revision" or the `cinna skills update` that closes the gap. The `--json` error code stays `already_installed`.
357
+
358
+ ### `cinna skills update <agent> <name> [--json]`
359
+
360
+ Move an installed skill to the package's newest revision, printing the version it moved from and to. `<name>` is the name `cinna skills list` prints; the plugin-link id the route actually addresses is resolved internally. The listing's `has_update` is *reported*, never used to skip the call — it comes from a cache, and the upgrade route is what decides.
361
+
362
+ Every install / uninstall / update / toggle also pushes the change into the agent's running environments, and that push can partly fail while the call itself succeeds. When it does, the command says how many environments did not take it and points at `cinna agent restart-env` — a green check alone would hide the one case where the catalog and the live agent disagree. An environment built *before* the feature existed is reported separately (`unsupported_syncs`): the link write is complete and there is nothing to retry, so that half points at `cinna agent rebuild-env` and suppresses the plain success line rather than offering a restart that cannot change the outcome.
363
+
364
+ ### `cinna skills uninstall <agent> <name> [--yes] [--json]`
365
+
366
+ Remove an agent's copy of an installed skill. The package and its revisions are untouched — what the agent held was a copy, not a reference. Asks first unless `--yes`. An agent's *own* `skills/<name>/` folder is not an install, and the command says so rather than failing on a route that could never match it.
367
+
368
+ ### `cinna skills toggle <agent> <name> [--enable|--disable] [--conversation-mode|--no-conversation-mode] [--building-mode|--no-building-mode] [--json]`
369
+
370
+ Enable or disable an installed skill, or change where it is offered, without removing it. Every switch defaults to "leave it alone" and only the ones named are sent, so `--disable` cannot silently reset the mode flags a previous call set. Naming no switch at all is a usage error rather than a no-op call. A disabled install still lists and still reports its version — it is not an error — so `cinna skills list` marks it `· disabled`.
371
+
372
+ ### `cinna skills refresh <agent> [--json]`
373
+
374
+ Rebuild the agent's addon index, reporting how many skills were indexed. Everything else in this group reads the platform's cache — which is what makes it safe against a sleeping environment, and what makes a stale index possible. This is the remedy when `cinna skills list` reports `parse_error`, or shows no version for a skill whose `SKILL.md` has one. It is *not* the remedy for every reason code — see below. The plugin half is refreshed too where the platform offers that route; a platform without it still refreshes the skill half and says so.
375
+
376
+ A refresh that still could not read the index answers **200 with the reason** — so this prints the reason and the remedy *that reason* has, rather than a green check. The four codes do not share a fix, and both `skills refresh` and `skills list` route through the same table:
377
+
378
+ | Code | What it means | The fix it prints |
379
+ |---|---|---|
380
+ | `env_not_running` | The environment is asleep | Send it a message, or refresh again, to wake it |
381
+ | `adapter_error` | Up, but not answering | `cinna agent restart-env <agent>` |
382
+ | `adapter_unsupported` | Built before agent skills existed; no skills endpoint to answer | `cinna agent rebuild-env <agent>` |
383
+ | `parse_error` | Answered, but the index did not parse | `cinna skills refresh <agent>` |
384
+
385
+ An unrecognised code gets a generic "refresh again, then check the logs" and names **no** verb: a code whose fix this build cannot name is one where guessing a verb sends the caller round a loop that cannot close. That is exactly what the old copy did to `adapter_unsupported` — it printed one hardcoded remedy for every code, so a container missing the route was told to restart (which re-runs the same image) or, in `skills list`, to "Rebuild it with: cinna skills refresh" (which re-reads a route that is not there, and calls a refresh a rebuild).
386
+
387
+ ### `cinna skills grants <package> [--json]` · `grant <package> --user EMAIL` · `revoke <package> --user EMAIL [--yes]`
388
+
389
+ Who is named on a package, and adding or removing one. Visibility and grants belong to the *package*, not to a revision, so changing them costs no new revision — this is the post-publish half of `cinna skills publish --grant`.
390
+
391
+ A grant list is stored on any package but only *consulted* on one whose visibility is `users`, so `grants` prints the visibility beside the rows and both verbs warn when the list is being ignored. `revoke` takes the same email `grant` did and resolves it to the user id the route actually addresses; an address that was never granted is a sentence naming who actually is.
392
+
393
+ ### `cinna skills visibility <package> <public|private|users> [--json]`
394
+
395
+ Change who may see a package after it was published. Switching to `users` with nobody named warns that it shares the package with nobody — the one setting that looks like sharing and is not.
396
+
397
+ ### `cinna skills delist <package> [--yes] [--json]`
398
+
399
+ Take a package out of the catalog. Agents that already installed it keep what they have: a revision they hold is a copy, not a reference. Asks first unless `--yes`. Deleting a package outright is still web-UI only.
400
+
401
+ ### `cinna skills relist <package> [--json]`
402
+
403
+ Put a delisted package back. `delist` has no inverse route of its own — without this verb the only way back would be `cinna api`, which would make delisting the one door in this group that opens only outwards.
404
+
261
405
  ### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
262
406
 
263
407
  Wire one agent to another's REST API from the account workspace. Resolves both agents (name, slug, or ID), mints a producer API token, and attaches it to the consumer as a credential — which rides the consumer's normal credential sync into its remote environment, so no key ever touches your machine. Prints the credential ID, token prefix, base URL, and spec URL. `--read-only` restricts the consumer to read-only access; `--label` names the credential. Backend errors surface verbatim: 400 if the producer's REST API is disabled, 403/404 for ownership violations.
@@ -282,8 +426,13 @@ Generic escape hatch into the platform API, authenticated with the account token
282
426
  - The inner response is passed through verbatim: the body prints to stdout (pretty-printed for JSON) and the exit code is `0` for 2xx and `1` for an inner 4xx/5xx — so it composes in shell pipelines.
283
427
  - When the escape hatch itself refuses the call, the detail prints to stderr and the exit code is `2`: policy denials (credentials, user management, admin, CLI, MFA/auth, and streaming routes are excluded — shown as `blocked by platform policy: …`), rate limiting (429, with the Retry-After delay), and request/response size caps (413/502).
284
428
 
429
+ `<path>` accepts the same **agent references** the rest of the CLI does: a segment straight after `agents/` that is not already a UUID is resolved against the account's agent listing, and the substitution is announced on stderr so a `--json` stdout stream stays pure. Resolution is sugar and never breaks the hatch — a reference that does not resolve (or an agent listing that cannot be reached) leaves the path exactly as typed, because `agents/` is a route prefix as well as a collection.
430
+
431
+ A path segment carrying an **elided id** (`agents/f0506e24-3740-4fe3…/addons`, copied out of a table cell too narrow to print it whole) is refused locally, before any request. The API would answer `404 Agent not found` for it, which reads as a missing agent rather than a mangled id.
432
+
285
433
  ```bash
286
434
  cinna api GET agents
435
+ cinna api GET agents/crm-agent/addons # resolved to agents/<uuid>/addons
287
436
  cinna api GET agents --query limit=5
288
437
  cinna api PATCH agents/3fa85f64-5717-4562-b3fc-2c963f66afa6 --json '{"description": "updated"}'
289
438
  cinna api POST tasks --data @task.json
@@ -380,6 +529,7 @@ It detects and fixes:
380
529
  - **Orphaned sessions** — `cinna-*` sessions with no registry entry at all. Terminated.
381
530
  - **Active sessions** — the healthy, still-watching sessions left over from past `cinna dev` runs. They are not broken, but they keep the shared Mutagen daemon busy and are recreated on demand, so doctor offers to clear them as a separate step.
382
531
  - **Expired tokens** — for **account-managed** workspaces (those under an account root), the CLI token is re-minted automatically through the parent account token, no pasting required. **Standalone** workspaces (set up via `cinna setup`) can only be refreshed with a pasted setup token, so they are reported with a `cinna set-token` hint rather than changed.
532
+ - **cinna-cli behind the platform pin** — one report-only finding per platform whose discovery document pins a different version. An **editable install** raises none: its recorded version is a snapshot of the day it was installed, not the code that runs, so "behind the pin" would be a confident wrong answer — and one that invites missing features to be misdiagnosed as version skew.
383
533
  - **Expired account token** — a sub-agent token can only be re-minted while the **account** token that mints it is still valid. When the account token has itself expired, doctor probes it once and surfaces a single _"renew the account token — run `cinna login`"_ finding (listing the blocked sub-agents) instead of a pile of re-mints that would all fail with 401. Run `cinna login`, then re-run `cinna doctor` to re-mint the dependents.
384
534
 
385
535
  ```bash
@@ -286,12 +286,13 @@ authoring convention.
286
286
  |---|---|---|
287
287
  | **Bootstrap & onboarding** | `cinna setup` / `set-token` / `login` / `list` / `status` / `disconnect[-all]` / `completion` / `dev` / `redev`; the no-TTY contract (`--no-input`, `--json`, exit codes, `CINNA_MUTAGEN_BIN`) | [business](features/bootstrap_onboarding/bootstrap_onboarding.md) · [tech](features/bootstrap_onboarding/bootstrap_onboarding_tech.md) · [acceptance](features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md) |
288
288
  | **Account workspace** | `cinna account` (setup, set-token, agents, status, refresh-context, user-workspace, credentials); desktop-managed workspaces | [business](features/account_workspace/account_workspace.md) · [tech](features/account_workspace/account_workspace_tech.md) · [acceptance](features/account_workspace/account_workspace_acceptance.md) |
289
- | **Agent management** | `cinna agent` (sync, unsync, create, restart-env, show, status) | [business](features/agent_management/agent_management.md) · [tech](features/agent_management/agent_management_tech.md) · [acceptance](features/agent_management/agent_management_acceptance.md) |
289
+ | **Agent management** | `cinna agent` (sync, unsync, create, restart-env, rebuild-env, show, status) | [business](features/agent_management/agent_management.md) · [tech](features/agent_management/agent_management_tech.md) · [acceptance](features/agent_management/agent_management_acceptance.md) |
290
290
  | **Agent schedules** | `cinna agent schedule` (list, generate, create, update, run, logs, delete) | [business](features/agent_schedules/agent_schedules.md) · [tech](features/agent_schedules/agent_schedules_tech.md) · [acceptance](features/agent_schedules/agent_schedules_acceptance.md) |
291
291
  | **Live sync** | `cinna sync` (status, conflicts, push, pull, resolve) + Mutagen transport | [business](features/live_sync/live_sync.md) · [tech](features/live_sync/live_sync_tech.md) · [acceptance](features/live_sync/live_sync_acceptance.md) |
292
292
  | **Remote exec** | `cinna exec` | [business](features/remote_exec/remote_exec.md) · [tech](features/remote_exec/remote_exec_tech.md) · [acceptance](features/remote_exec/remote_exec_acceptance.md) |
293
293
  | **Remote chat** | `cinna chat` | [business](features/remote_chat/remote_chat.md) · [tech](features/remote_chat/remote_chat_tech.md) · [acceptance](features/remote_chat/remote_chat_acceptance.md) |
294
294
  | **Agent API** | `cinna agent-api` (enable, refresh, spec, call) · `cinna api` · `cinna connect agent-api` | [business](features/agent_api/agent_api.md) · [tech](features/agent_api/agent_api_tech.md) · [acceptance](features/agent_api/agent_api_acceptance.md) |
295
+ | **Agent addons** | `cinna skills` (list, publish, install, uninstall, update, toggle, refresh, catalog, show, revisions, files, grants, grant, revoke, visibility, delist, relist) — the whole skills lifecycle: what an agent carries, publishing one of its `skills/<name>/` folders to the catalog, installing a package on another agent and keeping it current, and sharing after the fact | [business](features/agent_addons/agent_addons.md) · [tech](features/agent_addons/agent_addons_tech.md) · [acceptance](features/agent_addons/agent_addons_acceptance.md) |
295
296
  | **MCP integration** | `cinna connect mcp` · `cinna mcp-proxy` (knowledge stdio server) | [business](features/mcp_integration/mcp_integration.md) · [tech](features/mcp_integration/mcp_integration_tech.md) · [acceptance](features/mcp_integration/mcp_integration_acceptance.md) |
296
297
  | **Git versioning** | `cinna git` (link, status, commit, push, pull, log, checkout, unlink) | [business](features/git_versioning/git_versioning.md) · [tech](features/git_versioning/git_versioning_tech.md) · [acceptance](features/git_versioning/git_versioning_acceptance.md) |
297
298
  | **Improvement requests** | `cinna improve` (list, show, download, status) | [business](features/improvement_requests/improvement_requests.md) · [tech](features/improvement_requests/improvement_requests_tech.md) · [acceptance](features/improvement_requests/improvement_requests_acceptance.md) |
@@ -353,8 +353,15 @@ the silent-secret and scope-drift bugs live.
353
353
  as a `cinna-cli` table row and a `uv tool install cinna-cli==<pin>` hint when
354
354
  behind; `doctor` lists the platform under "manual action needed" only when
355
355
  the pin differs.
356
+ - **Editable install:** on a `uv tool install -e` checkout, `cli.editable` is
357
+ the checkout path, `cli.state` is `unknown` even though the platform publishes
358
+ a pin, the human row reads `0.2.5 (editable checkout of …) (platform pins
359
+ 0.4.0)`, and neither `status` nor `doctor` suggests an upgrade. This is the
360
+ common case for anyone developing cinna-cli, and the "behind the pin" warning
361
+ it used to raise explained missing features with skew that did not exist.
356
362
  - **Watch for:** a missing discovery document turning into an error; `doctor`
357
- nagging when no pin is published; the JSON line missing `cli`.
363
+ nagging when no pin is published; the JSON line missing `cli`; an editable
364
+ checkout reported as `behind`.
358
365
 
359
366
  ## Cross-cutting invariants (must hold across all scenarios)
360
367
 
@@ -168,7 +168,13 @@ Related (documented here as integration points): `cinna agent sync` →
168
168
  to `cinna login`).
169
169
  - `src/cinna/cli_version.py:cli_version_status()` — `GET
170
170
  {origin}/.well-known/cinna-desktop` → `local_dev.cinna_cli_version`, compared
171
- with the running `__version__` (`current` / `behind` / `ahead` / `unknown`).
171
+ with the running `__version__` (`current` / `behind` / `ahead` / `unknown`),
172
+ plus `editable`: the checkout path when this is an editable install
173
+ (`editable_install_source()`), which forces `state` to `unknown` and renders
174
+ as `0.2.5 (editable checkout of <path>)` with the pin named as context. An
175
+ editable install's version is the day it was installed, so comparing it would
176
+ report skew that does not exist — and invite missing features to be
177
+ misdiagnosed as version skew.
172
178
  - `src/cinna/account.py:probe_account_token()` — cheap `GET /account/agents`;
173
179
  2xx → valid, 401 → expired, else → unreachable.
174
180
  - `src/cinna/account.py:run_agent_sync()` — resolve via `_resolve_account_agent`,