cinna-cli 0.5.2__tar.gz → 0.6.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 (128) hide show
  1. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/PKG-INFO +44 -3
  2. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/README.md +43 -2
  3. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/README.md +11 -1
  4. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/account_workspace/account_workspace.md +10 -2
  5. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/account_workspace/account_workspace_acceptance.md +7 -3
  6. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/account_workspace/account_workspace_tech.md +3 -1
  7. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_addons/agent_addons.md +14 -0
  8. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_addons/agent_addons_acceptance.md +26 -1
  9. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_addons/agent_addons_tech.md +14 -2
  10. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_management/agent_management.md +19 -2
  11. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_management/agent_management_tech.md +7 -1
  12. cinna_cli-0.6.0/docs/features/artifacts/artifacts.md +138 -0
  13. cinna_cli-0.6.0/docs/features/artifacts/artifacts_acceptance.md +140 -0
  14. cinna_cli-0.6.0/docs/features/artifacts/artifacts_tech.md +124 -0
  15. cinna_cli-0.6.0/docs/features/dashboards/dashboards.md +224 -0
  16. cinna_cli-0.6.0/docs/features/dashboards/dashboards_acceptance.md +271 -0
  17. cinna_cli-0.6.0/docs/features/dashboards/dashboards_tech.md +239 -0
  18. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/delegation/delegation.md +2 -1
  19. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/live_sync/live_sync.md +13 -0
  20. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/live_sync/live_sync_acceptance.md +24 -0
  21. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/live_sync/live_sync_tech.md +21 -4
  22. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/local_agent_import/local_agent_import.md +10 -3
  23. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/local_agent_import/local_agent_import_tech.md +2 -1
  24. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/remote_chat/remote_chat.md +13 -0
  25. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/pyproject.toml +1 -1
  26. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/account.py +210 -3
  27. cinna_cli-0.6.0/src/cinna/artifacts.py +971 -0
  28. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/chat.py +84 -4
  29. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/client.py +315 -13
  30. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/config.py +11 -0
  31. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/context.py +113 -0
  32. cinna_cli-0.6.0/src/cinna/dashboards.py +1648 -0
  33. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/errors.py +50 -0
  34. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/kit_contract.py +1 -0
  35. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/local_import.py +47 -1
  36. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/main.py +83 -3
  37. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/sync_session.py +86 -2
  38. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +26 -1
  39. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/templates/CLAUDE.md.template +16 -4
  40. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_account.py +138 -3
  41. cinna_cli-0.6.0/tests/test_artifacts.py +645 -0
  42. cinna_cli-0.6.0/tests/test_dashboards.py +768 -0
  43. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_kit_contract.py +1 -0
  44. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_sync_session.py +85 -0
  45. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/uv.lock +1 -1
  46. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/.claude/commands/cinna-cli.feature.doc.md +0 -0
  47. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/.github/workflows/publish.yml +0 -0
  48. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/.gitignore +0 -0
  49. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/LICENSE.md +0 -0
  50. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_api/agent_api.md +0 -0
  51. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_api/agent_api_acceptance.md +0 -0
  52. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_api/agent_api_tech.md +0 -0
  53. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_management/agent_management_acceptance.md +0 -0
  54. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_schedules/agent_schedules.md +0 -0
  55. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
  56. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
  57. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +0 -0
  58. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +0 -0
  59. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +0 -0
  60. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/delegation/delegation_acceptance.md +0 -0
  61. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/delegation/delegation_tech.md +0 -0
  62. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/doctor/doctor.md +0 -0
  63. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/doctor/doctor_acceptance.md +0 -0
  64. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/doctor/doctor_tech.md +0 -0
  65. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/git_versioning/git_versioning.md +0 -0
  66. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/git_versioning/git_versioning_acceptance.md +0 -0
  67. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/git_versioning/git_versioning_tech.md +0 -0
  68. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/improvement_requests/improvement_requests.md +0 -0
  69. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
  70. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
  71. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/local_agent_import/local_agent_import_acceptance.md +0 -0
  72. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/mcp_integration/mcp_integration.md +0 -0
  73. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
  74. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
  75. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
  76. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/remote_chat/remote_chat_tech.md +0 -0
  77. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/remote_exec/remote_exec.md +0 -0
  78. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
  79. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/remote_exec/remote_exec_tech.md +0 -0
  80. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/team_library/team_library.md +0 -0
  81. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/team_library/team_library_acceptance.md +0 -0
  82. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/features/team_library/team_library_tech.md +0 -0
  83. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/interface.md +0 -0
  84. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/docs/mutagen_capabilities.md +0 -0
  85. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/scripts/check_docs_references.py +0 -0
  86. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/__init__.py +0 -0
  87. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/auth.py +0 -0
  88. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/bootstrap.py +0 -0
  89. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/cli_version.py +0 -0
  90. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/console.py +0 -0
  91. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/delegation.py +0 -0
  92. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/doctor.py +0 -0
  93. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/git_versioning.py +0 -0
  94. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/improve.py +0 -0
  95. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/library.py +0 -0
  96. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/logging.py +0 -0
  97. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/mcp_proxy.py +0 -0
  98. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/mutagen_runtime.py +0 -0
  99. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/scenarios.py +0 -0
  100. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/sync.py +0 -0
  101. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/sync_ssh_shim.py +0 -0
  102. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/sync_tui.py +0 -0
  103. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/templates/CHAT_TESTING.md +0 -0
  104. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/templates/GIT_VERSIONING.md +0 -0
  105. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/src/cinna/templates/__init__.py +0 -0
  106. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/__init__.py +0 -0
  107. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/conftest.py +0 -0
  108. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_agent_api_access.py +0 -0
  109. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_auth.py +0 -0
  110. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_bootstrap.py +0 -0
  111. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_chat.py +0 -0
  112. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_cli_version.py +0 -0
  113. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_client.py +0 -0
  114. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_config.py +0 -0
  115. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_console.py +0 -0
  116. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_context.py +0 -0
  117. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_delegation.py +0 -0
  118. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_doctor.py +0 -0
  119. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_git_versioning.py +0 -0
  120. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_improve.py +0 -0
  121. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_library.py +0 -0
  122. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_local_import.py +0 -0
  123. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_main.py +0 -0
  124. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_mutagen_runtime.py +0 -0
  125. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_onboarding.py +0 -0
  126. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_scenarios.py +0 -0
  127. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/tests/test_sync.py +0 -0
  128. {cinna_cli-0.5.2 → cinna_cli-0.6.0}/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.5.2
3
+ Version: 0.6.0
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
@@ -405,6 +405,8 @@ At a terminal that preview is shown and confirmed before the press; `--yes`, `--
405
405
 
406
406
  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).
407
407
 
408
+ **A publish from the CLI is never refused as stale.** It goes through the agent path, which records the package's latest revision as the new one's base (`cinna skills revisions` shows it under **Based on**), so another maintainer's newer version is built on, not checked against. Cinna Desktop's Share instead sends the base its folder holds, and core refuses that share (`stale_base`) when a co-maintainer has shared a newer version since; the CLI never makes that call.
409
+
408
410
  ### `cinna skills catalog [--search Q] [--mine] [--json]`
409
411
 
410
412
  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.
@@ -417,7 +419,7 @@ One package in full: its display name, visibility, newest version and package UU
417
419
 
418
420
  ### `cinna skills revisions <package> [--json]`
419
421
 
420
- 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.
422
+ A package's revisions, **newest first**: number, version, release date, size, **Shared by** (who published it, `—` once that account is deleted), **Based on** (`Version N`, the revision the sharer's copy held; `—` for a first revision, a share from an older Cinna Desktop, and any revision published before core recorded it) and the release notes given at publish time. A server older than skill co-maintainers sends neither field and both columns read `—`. `--json` passes the server's `published_by` and `base_revision_number` through untouched. 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.
421
423
 
422
424
  ### `cinna skills files <package> [--revision N] [--json]`
423
425
 
@@ -575,7 +577,7 @@ Hand work to a remote agent as a **durable delegation** — a platform task with
575
577
 
576
578
  - `create --id KEY --target AGENT_UUID --title T --brief B [--execute]` — the identity is `--target` + `--id`: a retry with the same pair returns the original task (changed title/brief are ignored) and never launches execution twice (the backend deduplicates). Prints the backend **task id** the other commands take, and whether it executes. `--depth 2 --root ROOT` makes a sub-delegation (`--root` is required at depth 2, not allowed at depth 1); `--group` tags related work.
577
579
  - `status TASK_ID` — one read: state, latest result, and any open question with its result id. A requester agent should end its turn and let Cinna Desktop deliver the result rather than poll.
578
- - `report [TASK_ID] --status in_progress|blocked|done|failed --summary S` — `blocked` needs `--question`; `--audience user` when a human must decide (default `requester`). `--artifact` (repeatable) is a JSON object with non-empty `kind` (backend accepts `file` or `link`), `name` and an `http(s)` `ref` — upload local files separately. `--body` adds detail.
580
+ - `report [TASK_ID] --status in_progress|blocked|done|failed --summary S` — `blocked` needs `--question`; `--audience user` when a human must decide (default `requester`). `--artifact` (repeatable) is a JSON object with non-empty `kind` (backend accepts `file` or `link`), `name` and an `http(s)` `ref` — upload local files separately (`cinna artifacts publish` prints such a link). `--body` adds detail.
579
581
  - **Inside a cloud task**, omit `TASK_ID`: the report goes to the current task using `AGENT_AUTH_TOKEN`, `BACKEND_URL`, `ENV_ID` and the session in `CINNA_SESSION_CONTEXT_PATH` (default `./session_context.json`); the backend checks the session belongs to this environment's agent and owner.
580
582
  - `reply TASK_ID --result-id ID --message M` — answer a blocked question.
581
583
  - `--json` (after the subcommand) prints `{"result": "ok", "delegation": {…}}`.
@@ -590,6 +592,45 @@ cinna delegation report --status done --summary 'Finished' # inside the clou
590
592
 
591
593
  Inside cloud tasks, cinna-core's MCP task server also offers a `handover_report` tool for the same report.
592
594
 
595
+ ### `cinna artifacts publish | list | show | open | download | share | link | delete`
596
+
597
+ Publish a folder or a file to Cinna as an **artifact** — a frozen, versioned copy people open on a Cinna page (a folder with `index.html` runs as a sandboxed static site; other files open in viewers). Run from the account workspace. Every verb first checks the server's artifacts capability and refuses with "This Cinna server does not support artifacts" when it is missing.
598
+
599
+ - `publish PATH [--title T] [--description D] [--entry index.html] [--note N]` — zips the path and prints the page link. `.git/`, `.env*`, `credentials/`, `app-data/`, `.claude/`, `.cinna/`, `node_modules/`, key files and symlinks are never uploaded; the command lists what it left out. Private until shared. Publishing the same path again adds a revision to the same artifact (same link) — the CLI remembers `path → id` in `.cinna/artifacts.json`; `--update ID` names the artifact, `--new` publishes a separate one.
600
+ - `list [--shared]` · `show ID` (revisions, people, links) · `open ID` (browser) · `download ID [--rev N] [-o DIR]` (default `artifacts/<short-id>/`). Ids may be shortened to a unique prefix.
601
+ - `share ID [--instance | --private] [--user EMAIL]… [--remove-user EMAIL]…` and `link create ID [--expires 7d|12h|2w|never] [--code] [--label L] [--rev N]` — **a person runs these, in a terminal**: they refuse under `--json`, `--no-input` or without a TTY (code `needs_person`), and the account workspace's `.claude/settings.json` denies them to Claude Code. `link create` prints the link (and code) once.
602
+ - `link revoke ID LINK` (link id or token prefix, from `show`) · `delete ID` — both confirm; `--yes` is required with `--json`.
603
+ - `--json` (after the subcommand): NDJSON progress lines, then `{"result": "ok", "artifact_id", "action", "revision", "url", "excluded", …}`.
604
+
605
+ ```bash
606
+ cinna artifacts publish ./reports/q3 --title "Q3 churn"
607
+ cinna artifacts publish ./reports/q3 # revision 2, same link
608
+ cinna artifacts share 3fa85f64 --user bob@example.com
609
+ cinna artifacts link create 3fa85f64 --expires 7d --code
610
+ ```
611
+
612
+ The printed page link is a valid `link` artifact for `cinna delegation report --artifact`.
613
+
614
+ ### `cinna dashboards list | show | check | run | ref | boards | board create | add | tiles | tile …`
615
+
616
+ Build, check, run and place an agent's **dashboard components**: folders in the agent's `dashboards/<name>/` (`component.json` plus endpoint scripts or a page) that the owner keeps as live tiles on a dashboard. Run from the account workspace. `AGENT` is a name, slug, id, or `.` for the agent workspace you are in. Every verb first checks the server's dashboards capability. While an environment wakes, verbs retry for `--timeout` seconds (default 120). Verbs that read the agent's cloud copy push your pending local edits first (`--no-flush` skips that).
617
+
618
+ - `list AGENT` · `show AGENT NAME [--skill S]` (`--skill` names a component a skill ships, on every verb that takes `NAME`): the components as the environment sees them (checker findings, where each is placed, including "Cinna Desktop") and the **reference** line `cinna://component/bundle:<bundle_id>/<name>` that offers one.
619
+ - `check AGENT [NAME] [--run]`: the owner's card checks; `--run` also runs each source in the environment through `cinna exec` (never locally). Exits 1 on any error.
620
+ - `run AGENT NAME [--endpoint E] [--param k=v]… [--params @file.json]`: run it exactly as a tile would. `--json` prints `{result, value, params_resolved, notice, fetched_at}`, where `value` is exactly what a tile receives.
621
+ - `ref AGENT NAME [--check]`: print only the reference line; `--check` asks core whether it resolves.
622
+ - `boards` · `board create NAME` · `add AGENT NAME --board B [--new-board] [--size s|m|l|wide|tall] [--param k=v] [--title T] [--max-age 15m] [--every 5m|manual] [--after-turn]`: the owner's web dashboards; place a tile (refused for a component with errors or a full dashboard).
623
+ - `tiles BOARD` · `tile data|refresh|params|call|edit|remove BOARD TILE …`: read and drive tiles. `tile params --set/--clear/--reset` sends the whole set; `tile edit` changes title (`''` resets it), size and refresh overrides. Tiles on Cinna Desktop's dashboards are read-only here.
624
+ - `cinna chat --agent AGENT --board B [--focus TILE] [--context @file.json] [--new-chat] "…"`: talk in the agent's dashboard chat, where it has its tile tools.
625
+
626
+ ```bash
627
+ cinna dashboards check crm-agent deals --run
628
+ cinna dashboards run crm-agent deals --param stage=won
629
+ cinna dashboards ref crm-agent deals --check
630
+ cinna dashboards add crm-agent deals --board Sales --size l
631
+ cinna chat --agent crm-agent --board Sales --focus deals "Show only won deals"
632
+ ```
633
+
593
634
  ### `cinna dev`
594
635
 
595
636
  Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
@@ -368,6 +368,8 @@ At a terminal that preview is shown and confirmed before the press; `--yes`, `--
368
368
 
369
369
  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).
370
370
 
371
+ **A publish from the CLI is never refused as stale.** It goes through the agent path, which records the package's latest revision as the new one's base (`cinna skills revisions` shows it under **Based on**), so another maintainer's newer version is built on, not checked against. Cinna Desktop's Share instead sends the base its folder holds, and core refuses that share (`stale_base`) when a co-maintainer has shared a newer version since; the CLI never makes that call.
372
+
371
373
  ### `cinna skills catalog [--search Q] [--mine] [--json]`
372
374
 
373
375
  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.
@@ -380,7 +382,7 @@ One package in full: its display name, visibility, newest version and package UU
380
382
 
381
383
  ### `cinna skills revisions <package> [--json]`
382
384
 
383
- 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.
385
+ A package's revisions, **newest first**: number, version, release date, size, **Shared by** (who published it, `—` once that account is deleted), **Based on** (`Version N`, the revision the sharer's copy held; `—` for a first revision, a share from an older Cinna Desktop, and any revision published before core recorded it) and the release notes given at publish time. A server older than skill co-maintainers sends neither field and both columns read `—`. `--json` passes the server's `published_by` and `base_revision_number` through untouched. 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.
384
386
 
385
387
  ### `cinna skills files <package> [--revision N] [--json]`
386
388
 
@@ -538,7 +540,7 @@ Hand work to a remote agent as a **durable delegation** — a platform task with
538
540
 
539
541
  - `create --id KEY --target AGENT_UUID --title T --brief B [--execute]` — the identity is `--target` + `--id`: a retry with the same pair returns the original task (changed title/brief are ignored) and never launches execution twice (the backend deduplicates). Prints the backend **task id** the other commands take, and whether it executes. `--depth 2 --root ROOT` makes a sub-delegation (`--root` is required at depth 2, not allowed at depth 1); `--group` tags related work.
540
542
  - `status TASK_ID` — one read: state, latest result, and any open question with its result id. A requester agent should end its turn and let Cinna Desktop deliver the result rather than poll.
541
- - `report [TASK_ID] --status in_progress|blocked|done|failed --summary S` — `blocked` needs `--question`; `--audience user` when a human must decide (default `requester`). `--artifact` (repeatable) is a JSON object with non-empty `kind` (backend accepts `file` or `link`), `name` and an `http(s)` `ref` — upload local files separately. `--body` adds detail.
543
+ - `report [TASK_ID] --status in_progress|blocked|done|failed --summary S` — `blocked` needs `--question`; `--audience user` when a human must decide (default `requester`). `--artifact` (repeatable) is a JSON object with non-empty `kind` (backend accepts `file` or `link`), `name` and an `http(s)` `ref` — upload local files separately (`cinna artifacts publish` prints such a link). `--body` adds detail.
542
544
  - **Inside a cloud task**, omit `TASK_ID`: the report goes to the current task using `AGENT_AUTH_TOKEN`, `BACKEND_URL`, `ENV_ID` and the session in `CINNA_SESSION_CONTEXT_PATH` (default `./session_context.json`); the backend checks the session belongs to this environment's agent and owner.
543
545
  - `reply TASK_ID --result-id ID --message M` — answer a blocked question.
544
546
  - `--json` (after the subcommand) prints `{"result": "ok", "delegation": {…}}`.
@@ -553,6 +555,45 @@ cinna delegation report --status done --summary 'Finished' # inside the clou
553
555
 
554
556
  Inside cloud tasks, cinna-core's MCP task server also offers a `handover_report` tool for the same report.
555
557
 
558
+ ### `cinna artifacts publish | list | show | open | download | share | link | delete`
559
+
560
+ Publish a folder or a file to Cinna as an **artifact** — a frozen, versioned copy people open on a Cinna page (a folder with `index.html` runs as a sandboxed static site; other files open in viewers). Run from the account workspace. Every verb first checks the server's artifacts capability and refuses with "This Cinna server does not support artifacts" when it is missing.
561
+
562
+ - `publish PATH [--title T] [--description D] [--entry index.html] [--note N]` — zips the path and prints the page link. `.git/`, `.env*`, `credentials/`, `app-data/`, `.claude/`, `.cinna/`, `node_modules/`, key files and symlinks are never uploaded; the command lists what it left out. Private until shared. Publishing the same path again adds a revision to the same artifact (same link) — the CLI remembers `path → id` in `.cinna/artifacts.json`; `--update ID` names the artifact, `--new` publishes a separate one.
563
+ - `list [--shared]` · `show ID` (revisions, people, links) · `open ID` (browser) · `download ID [--rev N] [-o DIR]` (default `artifacts/<short-id>/`). Ids may be shortened to a unique prefix.
564
+ - `share ID [--instance | --private] [--user EMAIL]… [--remove-user EMAIL]…` and `link create ID [--expires 7d|12h|2w|never] [--code] [--label L] [--rev N]` — **a person runs these, in a terminal**: they refuse under `--json`, `--no-input` or without a TTY (code `needs_person`), and the account workspace's `.claude/settings.json` denies them to Claude Code. `link create` prints the link (and code) once.
565
+ - `link revoke ID LINK` (link id or token prefix, from `show`) · `delete ID` — both confirm; `--yes` is required with `--json`.
566
+ - `--json` (after the subcommand): NDJSON progress lines, then `{"result": "ok", "artifact_id", "action", "revision", "url", "excluded", …}`.
567
+
568
+ ```bash
569
+ cinna artifacts publish ./reports/q3 --title "Q3 churn"
570
+ cinna artifacts publish ./reports/q3 # revision 2, same link
571
+ cinna artifacts share 3fa85f64 --user bob@example.com
572
+ cinna artifacts link create 3fa85f64 --expires 7d --code
573
+ ```
574
+
575
+ The printed page link is a valid `link` artifact for `cinna delegation report --artifact`.
576
+
577
+ ### `cinna dashboards list | show | check | run | ref | boards | board create | add | tiles | tile …`
578
+
579
+ Build, check, run and place an agent's **dashboard components**: folders in the agent's `dashboards/<name>/` (`component.json` plus endpoint scripts or a page) that the owner keeps as live tiles on a dashboard. Run from the account workspace. `AGENT` is a name, slug, id, or `.` for the agent workspace you are in. Every verb first checks the server's dashboards capability. While an environment wakes, verbs retry for `--timeout` seconds (default 120). Verbs that read the agent's cloud copy push your pending local edits first (`--no-flush` skips that).
580
+
581
+ - `list AGENT` · `show AGENT NAME [--skill S]` (`--skill` names a component a skill ships, on every verb that takes `NAME`): the components as the environment sees them (checker findings, where each is placed, including "Cinna Desktop") and the **reference** line `cinna://component/bundle:<bundle_id>/<name>` that offers one.
582
+ - `check AGENT [NAME] [--run]`: the owner's card checks; `--run` also runs each source in the environment through `cinna exec` (never locally). Exits 1 on any error.
583
+ - `run AGENT NAME [--endpoint E] [--param k=v]… [--params @file.json]`: run it exactly as a tile would. `--json` prints `{result, value, params_resolved, notice, fetched_at}`, where `value` is exactly what a tile receives.
584
+ - `ref AGENT NAME [--check]`: print only the reference line; `--check` asks core whether it resolves.
585
+ - `boards` · `board create NAME` · `add AGENT NAME --board B [--new-board] [--size s|m|l|wide|tall] [--param k=v] [--title T] [--max-age 15m] [--every 5m|manual] [--after-turn]`: the owner's web dashboards; place a tile (refused for a component with errors or a full dashboard).
586
+ - `tiles BOARD` · `tile data|refresh|params|call|edit|remove BOARD TILE …`: read and drive tiles. `tile params --set/--clear/--reset` sends the whole set; `tile edit` changes title (`''` resets it), size and refresh overrides. Tiles on Cinna Desktop's dashboards are read-only here.
587
+ - `cinna chat --agent AGENT --board B [--focus TILE] [--context @file.json] [--new-chat] "…"`: talk in the agent's dashboard chat, where it has its tile tools.
588
+
589
+ ```bash
590
+ cinna dashboards check crm-agent deals --run
591
+ cinna dashboards run crm-agent deals --param stage=won
592
+ cinna dashboards ref crm-agent deals --check
593
+ cinna dashboards add crm-agent deals --board Sales --size l
594
+ cinna chat --agent crm-agent --board Sales --focus deals "Show only won deals"
595
+ ```
596
+
556
597
  ### `cinna dev`
557
598
 
558
599
  Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
@@ -25,7 +25,8 @@ The platform also handles scheduling, triggers, email, A2A, MCP serving, and ses
25
25
  - Testing credential integrations (keys and tokens stay on the platform, exercised by the remote env)
26
26
  - Installing and validating Python / system packages
27
27
  - Writing and iterating on prompt files
28
- - Building webapp dashboards and data endpoints
28
+ - Building the agent's webapp and data endpoints
29
+ - Building dashboard components, checking and running them in the env, placing them as tiles
29
30
  - Preparing output files and reports
30
31
 
31
32
  ### What stays in the cloud
@@ -56,6 +57,10 @@ The live-sync rewrite removes the local container entirely. Mutagen keeps the wo
56
57
 
57
58
  A self-contained automation unit on the platform. Has a name, template, scripts, prompts, webapp, credentials, and a knowledge base. Identified by `agent_id`.
58
59
 
60
+ ### Dashboard component
61
+
62
+ A live view an agent keeps in `dashboards/<name>/` (`component.json` plus endpoint scripts or a page). The owner places it as a **tile** on a dashboard (core's web dashboards, or Cinna Desktop); the agent offers it by printing its reference `cinna://component/bundle:<bundle_id>/<name>`. It runs only in the agent's environment. See [Dashboards](features/dashboards/dashboards.md).
63
+
59
64
  ### Environment
60
65
 
61
66
  The runtime container configuration for an agent on the platform. Each agent has exactly one environment. Environments may be suspended when idle; opening a sync WebSocket auto-activates them.
@@ -297,6 +302,8 @@ authoring convention.
297
302
  | **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) |
298
303
  | **Git versioning** | `cinna git` (link, status, commit, push, pull, log, checkout, unlink) — external remotes with your own credentials, `cinna` remotes with the CLI token | [business](features/git_versioning/git_versioning.md) · [tech](features/git_versioning/git_versioning_tech.md) · [acceptance](features/git_versioning/git_versioning_acceptance.md) |
299
304
  | **Durable delegation** | `cinna delegation` (create — idempotent per target + `--id`, status, report — owner side with a task id or from inside a cloud task, reply), gated on the backend capability version | [business](features/delegation/delegation.md) · [tech](features/delegation/delegation_tech.md) · [acceptance](features/delegation/delegation_acceptance.md) |
305
+ | **Artifacts** | `cinna artifacts` (publish — republishes a remembered path, `--new` / `--update`; list, show, open, download, share and link create — person-only, refused without a TTY; link revoke, delete), gated on the server's artifacts capability | [business](features/artifacts/artifacts.md) · [tech](features/artifacts/artifacts_tech.md) · [acceptance](features/artifacts/artifacts_acceptance.md) |
306
+ | **Dashboards** | `cinna dashboards` (list, show, check `[--run]`, run, ref `[--check]` — an agent's components as its environment sees them; boards, board create, add, tiles, tile data / refresh / params / call / edit / remove — tiles on the owner's web dashboards, desktop dashboards read-only) · `cinna chat --board [--focus] [--context] [--new-chat]` (dashboard chat with the tile tools); gated on the server's dashboards capability | [business](features/dashboards/dashboards.md) · [tech](features/dashboards/dashboards_tech.md) · [acceptance](features/dashboards/dashboards_acceptance.md) |
300
307
  | **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) |
301
308
  | **Local agent import** | `cinna agent import` (Local Agent Kit → cloud agent), incl. the versioned folder contract (`.cinna-kit/layout.json`), `publications.json` and `content_hash` | [business](features/local_agent_import/local_agent_import.md) · [tech](features/local_agent_import/local_agent_import_tech.md) · [acceptance](features/local_agent_import/local_agent_import_acceptance.md) |
302
309
  | **Team library** | `cinna library` (list, get, share, update, status, grants, suggest) — kit folders in `Local/<slug>/` shared through cinna-core-hosted git with the account token; `cinna agent import --from-library` | [business](features/team_library/team_library.md) · [tech](features/team_library/team_library_tech.md) · [acceptance](features/team_library/team_library_acceptance.md) |
@@ -525,6 +532,8 @@ The platform's send-message route (`POST /sessions/{id}/messages/stream`) return
525
532
  3. Records the current message count as a cursor, then sends the message (`file_ids` carry the attachments).
526
533
  4. **Polls** `GET /sessions/{id}/messages?offset=<cursor>` (messages are ordered ascending by `sequence_number`, so `offset` is the cursor) and `GET …/messages/streaming-status` (`{is_streaming}`) until the turn settles — `is_streaming` false with no message flagged `streaming_in_progress`. A start-grace window covers env wake / queueing before the turn begins; an overall `--timeout` bounds the wait.
527
534
 
535
+ With `--board`, the session is the agent's **dashboard chat** (`POST /dashboards/{id}/chat`), each message carries `dashboard_context`, and a `tiles` event before `done` reports tiles whose params the turn changed (see [Dashboards](features/dashboards/dashboards.md)).
536
+
528
537
  Each finalized message is emitted as one NDJSON line (`session` / `upload` / `message` / `status` / `done`); the in-progress assistant message is held back (its content is still growing) and emitted once final. Every `message` also carries the agent's reasoning/tool trace under **`events`** — the normalized `streaming_events` (thinking blocks, `tool` calls with their full `tool_input` payloads, tool results), with the bookkeeping/`attachment` entries stripped (attachments are surfaced separately). The final coalesced text stays in `content`; the trace shows *how* the agent got there. `--no-events` drops the trace; `--pretty` swaps NDJSON for a Rich transcript. Ctrl-C calls `POST …/messages/interrupt` and exits 130.
529
538
 
530
539
  ### Attachments
@@ -625,6 +634,7 @@ When a CLI token expires (or is revoked) the normal remedy is `cinna set-token <
625
634
  | POST | `/api/v1/cli/account/agents/{id}/mint` | Account token | Mint a per-agent CLI token (`cinna agent sync`, `cinna doctor` re-mint) |
626
635
  | POST | `/api/v1/cli/account/api-proxy` | Account token | Buffered JSON escape hatch — `cinna api`, and the transport for every `cinna chat` session/message call |
627
636
  | POST | `/api/v1/cli/account/files/upload` | Account token | Multipart upload for `cinna chat --file` (the proxy can't carry multipart) |
637
+ | (proxy) | `dashboards/capabilities`, `agents/{id}/dashboards/…`, `dashboards/…`, `dashboards/{id}/blocks/{block}/component/…`, `dashboards/{id}/chat`, `dashboards/component-refs/resolve` | Account token | `cinna dashboards` and `cinna chat --board`, all through the api-proxy; never the `…/frame` routes (core's proxy refuses them) |
628
638
  | GET/PATCH | `/api/v1/cli/account/improvement-requests[/{id}[/archive]]` | Account token | Improvement requests received on the agents the account owns (`cinna improve`); the `/archive` route returns a binary ZIP the JSON-only proxy can't carry |
629
639
 
630
640
  `cinna chat` reaches the conversation API **through** the api-proxy (so these are inner routes, not CLI routes): `POST /sessions/`, `GET /sessions/{id}`, `GET /sessions/{id}/messages`, `POST /sessions/{id}/messages/stream`, `GET /sessions/{id}/messages/streaming-status`, `POST /sessions/{id}/messages/interrupt`, and `GET /files/{id}/download`.
@@ -178,7 +178,13 @@ each with its own token, registry entry, and Mutagen session.
178
178
  - `cinna account refresh-context` re-downloads `context/` and regenerates the
179
179
  orchestrator `CLAUDE.md`, the MCP wiring, and every synced child's per-agent
180
180
  `CLAUDE.md` from the bundled templates — so a CLI upgrade's new commands reach
181
- existing workspaces without a re-setup.
181
+ existing workspaces without a re-setup. A child whose config predates the
182
+ `bundle_id` field gets it filled in first (best effort), so its guide's
183
+ Dashboard components section carries the agent's own reference prefix.
184
+ - The orchestrator `CLAUDE.md` has a **Dashboards** command table and a
185
+ "Which one?" note (webapp / dashboard component / artifact). The Artifacts
186
+ heading says "a frozen copy", and its starter points at
187
+ `artifact-sqlite-report/`. See [Dashboards](../dashboards/dashboards.md).
182
188
 
183
189
  ## Business rules / guardrails
184
190
 
@@ -219,7 +225,9 @@ each with its own token, registry entry, and Mutagen session.
219
225
  orchestrator/child `CLAUDE.md` regeneration is offline and per-workspace, so one
220
226
  unreadable child never aborts the rest.
221
227
  - **User-edited config is preserved.** `.claude/settings.json` is create-if-absent
222
- (never clobbered on refresh); `.mcp.json` / `opencode.json` and the generated
228
+ (never clobbered on refresh; an existing file only gains the two `deny` rules
229
+ that keep `cinna artifacts share` / `link create` away from Claude Code — see
230
+ [Artifacts](../artifacts/artifacts.md)); `.mcp.json` / `opencode.json` and the generated
223
231
  `CLAUDE.md` are auto-generated infra and are overwritten.
224
232
  - **Child checkouts are standard.** A synced child is identical to a `cinna setup`
225
233
  checkout — same Model-A layout, same auto-link when git-versioned, same
@@ -53,7 +53,9 @@ the silent-secret and scope-drift bugs live.
53
53
  `account_token` / `platform_url` / `frontend_url` / `machine_name`; an empty
54
54
  `agents/` dir; an orchestrator `CLAUDE.md` (contains "Account Workspace");
55
55
  `.claude/settings.json` with `permissions.allow == ["Bash(cinna:*)",
56
- "mcp__platform-knowledge"]` and `enableAllProjectMcpServers: true`; `.mcp.json`
56
+ "mcp__platform-knowledge"]`, `permissions.deny == ["Bash(cinna artifacts
57
+ share:*)", "Bash(cinna artifacts link create:*)"]` and
58
+ `enableAllProjectMcpServers: true`; `.mcp.json`
57
59
  + `opencode.json` pointing the proxy at `.cinna/account.json` via
58
60
  `CINNA_ACCOUNT_CONFIG`; a `context/` tree; and the next-step hints
59
61
  (`cinna account agents`, `cinna agent sync`).
@@ -226,7 +228,8 @@ the silent-secret and scope-drift bugs live.
226
228
  - **Expected:** `context/` is replaced (old-only files gone); the orchestrator
227
229
  `CLAUDE.md`, `.mcp.json`/`opencode.json`, and **every** synced child's
228
230
  per-agent `CLAUDE.md` are regenerated from the bundled templates; a
229
- user-edited `.claude/settings.json` is left untouched; the run reports how many
231
+ user-edited `.claude/settings.json` keeps every entry and only gains the
232
+ artifact `deny` rules it lacks; the run reports how many
230
233
  child workspaces were regenerated.
231
234
  - **Watch for (non-destructive rule):** simulate a download failure (point at an
232
235
  unreachable platform) and confirm the **old `context/` survives** and the
@@ -388,7 +391,8 @@ the silent-secret and scope-drift bugs live.
388
391
  - **Single-use setup token is never burned on a doomed run** — guards precede the
389
392
  exchange.
390
393
  - **No silent clobber** — `refresh-context` removes the old `context/` only after
391
- a successful download; user-edited `.claude/settings.json` is never overwritten.
394
+ a successful download; user-edited `.claude/settings.json` is never overwritten
395
+ (only the missing artifact `deny` rules are merged in).
392
396
  - **No state drift between commands** — a synced child is a standard per-agent
393
397
  workspace (registry + config) indistinguishable from `cinna setup`; the active
394
398
  user workspace is the single source for both the listing scope and the create
@@ -344,7 +344,9 @@ All consumed by `src/cinna/client.py:AccountClient` with the account token
344
344
  `CLAUDE.md` regeneration is independent of the download.
345
345
  (`tests/test_account.py:test_refresh_context_failure_preserves_old_tree`)
346
346
  - **Settings is create-if-absent** — `_write_account_claude_settings` never
347
- clobbers a user-edited `.claude/settings.json`.
347
+ clobbers a user-edited `.claude/settings.json`; it only appends the missing
348
+ `artifacts.person_only_deny_rules()` to `permissions.deny`, and leaves a file
349
+ that is not a JSON object alone.
348
350
  (`tests/test_account.py:test_refresh_context_preserves_user_claude_settings`)
349
351
  - **Per-agent URL is rejected** — `parse_account_setup_input` must not silently
350
352
  accept a `/cli-setup/<token>` (non-account) URL.
@@ -242,6 +242,20 @@ change without cutting a new one:
242
242
  it never revokes (revoking is its own verb in the web UI, so a stale
243
243
  invocation cannot silently remove access). An unknown address fails the whole
244
244
  publish rather than half-sharing it.
245
+ - **A CLI publish is never refused as stale.** It goes through the agent path
246
+ (`POST /agents/{id}/skills/{name}/publish`), which records the package's
247
+ latest revision as the new revision's base, so a version a co-maintainer
248
+ shared in the meantime is built on rather than checked against. Cinna
249
+ Desktop's Share sends the base its folder holds instead, and core refuses
250
+ that share (`stale_base`) when a co-maintainer has shared a newer version
251
+ since. The CLI never calls the desktop upload, so it never sees
252
+ `stale_base` or `editable_package_exists`.
253
+ - **Each revision names who shared it and what it was based on.**
254
+ `cinna skills revisions` and `cinna skills show` print **Shared by** (the
255
+ publisher's display name, `—` once the account is deleted) and **Based on**
256
+ (`Version N`, the revision the sharer's copy held; `—` for a first revision,
257
+ a share from an older Cinna Desktop, and any revision published before core
258
+ recorded it). An older server sends neither field and both read `—`.
245
259
  - **Publishing reads the cloud workspace.** The files come from the agent's
246
260
  remote workspace on disk — which is why a suspended environment publishes
247
261
  normally — so an unsynced local edit is simply not in the revision. Push
@@ -516,11 +516,36 @@ cinna skills revisions <PACKAGE_ID>
516
516
  ```
517
517
 
518
518
  **Expected** — one row per revision, newest first, with number, version, release
519
- date, size and the `release_notes` given at publish time.
519
+ date, size, **Shared by**, **Based on** and the `release_notes` given at publish
520
+ time.
520
521
 
521
522
  **Watch for** — a permanently blank notes column (the field is `release_notes`,
522
523
  not `notes`); ascending order, which buries the answer at the bottom.
523
524
 
525
+ ### 20a. Each revision names who shared it and what it was based on
526
+
527
+ **Goal** — the revision table shows the sharer and the base core recorded, and a
528
+ CLI re-publish is recorded as built on the latest revision. Needs core
529
+ d08c10d8 and 465c43c2 (revisions carrying `published_by` and
530
+ `base_revision_number`).
531
+
532
+ **Steps** — after scenarios 4 and 5 have published `acceptance-demo` twice:
533
+
534
+ ```bash
535
+ cinna skills revisions <PACKAGE_ID>
536
+ cinna skills revisions <PACKAGE_ID> --json
537
+ ```
538
+
539
+ **Expected** — the table has **Shared by** and **Based on** columns. Revision 2
540
+ reads **Based on** `Version 1`; revision 1 reads `—` there (a first revision has
541
+ no base). Both rows name the publisher's display name under **Shared by**.
542
+ `--json` carries the server's `published_by` (`{user_id, display_name}`) and
543
+ `base_revision_number` (`1` on revision 2, `null` on revision 1) as sent.
544
+
545
+ **Watch for** — revision 2 with no base (the agent publish path did not record
546
+ one); a crash or a blank cell instead of `—` on a revision published before
547
+ core recorded these fields.
548
+
524
549
  ### 21. Sharing changes after the publish
525
550
 
526
551
  **Steps**
@@ -139,6 +139,10 @@
139
139
  - `src/cinna/account.py:_revision_rows()` — revisions sorted newest first. The
140
140
  question this list answers is "what is the newest"; a payload that happened to
141
141
  be ascending would put the answer at the bottom.
142
+ - `src/cinna/account.py:_revision_shared_by()` / `_revision_based_on()` — the
143
+ revision table's **Shared by** (`published_by.display_name`) and **Based on**
144
+ (`Version <base_revision_number>`) cells, each `—` when the field is null or
145
+ absent.
142
146
  - `src/cinna/account.py:_addon_link()` — the row's `link` sub-object. Every
143
147
  fact about an *install* rather than a package lives there: `id` (the link id
144
148
  the plugin routes address), `installed_version`, `latest_version`,
@@ -242,11 +246,19 @@ hatch, `POST /api/v1/cli/account/api-proxy`:
242
246
  `grant_emails`, `package_id`). Returns the new revision; consumed fields are
243
247
  `package_id` (the package's UUID), `revision_number`, `version` and
244
248
  `frontmatter` (the last one only to tell whether the version reached the
245
- workspace's `SKILL.md`).
249
+ workspace's `SKILL.md`). Core records the package's previous latest revision
250
+ as the new one's `base_revision_number`; this route never answers
251
+ `stale_base` or `editable_package_exists` — those come only from the desktop
252
+ upload (`POST /skills/packages/publish`), which the CLI does not call.
246
253
  - `GET /api/v1/skills/packages/{package_id}` — consumed fields are the
247
254
  reverse-DNS `package_id` string, `display_name`, `visibility`,
248
255
  `latest_version` and `revisions[]` (`revision_number`, `version`,
249
- `created_at`, `release_notes` — **not** `notes` — and `size_bytes`).
256
+ `created_at`, `release_notes` — **not** `notes` — `size_bytes`,
257
+ `published_by` `{user_id, display_name}` (null once the account is deleted)
258
+ and `base_revision_number` (int or null)). The last two are absent on a
259
+ server older than skill co-maintainers; `_revision_shared_by()` and
260
+ `_revision_based_on()` render a missing or null value as `—`, and `--json`
261
+ passes both through untouched.
250
262
  - `POST /api/v1/agents/{agent_id}/skills/install` — `SkillInstallRequest`:
251
263
  `package_id` (the package **UUID**), optional `revision_number`,
252
264
  `conversation_mode`, `building_mode`. Answers `PluginSyncResponse`; 409
@@ -66,10 +66,14 @@ This doc covers the agent **lifecycle** verbs. The CRON automation subgroup
66
66
  conflicts against untouched remote originals. A session that cannot start is a
67
67
  warning naming `cinna sync push --agent <slug>` to run *before* editing — never
68
68
  a failed sync.
69
- 3. Re-running sync for an agent **already synced** in that folder is refused
69
+ 3. Sync also records the agent's **bundle id** and the environment's **platform
70
+ skills** in the workspace config. The per-agent `CLAUDE.md` uses them for its
71
+ Dashboard components section: the agent's own `cinna://component/bundle:<id>/`
72
+ prefix, and the path to the account's kit copy of the `cinna-dashboards` skill.
73
+ 4. Re-running sync for an agent **already synced** in that folder is refused
70
74
  (with a `cinna agent unsync` / `cinna set-token` hint) — it never silently
71
75
  re-clones or duplicates.
72
- 4. The workspace lands at `agents/<slug>/<subdir>/` (the Model-A nested layout;
76
+ 5. The workspace lands at `agents/<slug>/<subdir>/` (the Model-A nested layout;
73
77
  `<subdir>` is the slug unless the agent is git-versioned with another subdir) —
74
78
  the command prints the exact path. Afterward `cd` there and `cinna dev`, or stay
75
79
  at the account root and use `cinna sync push --agent <slug>` /
@@ -92,6 +96,12 @@ This doc covers the agent **lifecycle** verbs. The CRON automation subgroup
92
96
  re-materializes the backend scaffold and can clobber local changes, so the
93
97
  builder is told to `cinna sync push` first.
94
98
  3. It blocks until the container is back, then prints the post-restart status.
99
+ 4. If the agent is synced on this machine, it then **reconnects the sync
100
+ session** ("Sync session reconnected."). The old container took the remote
101
+ Mutagen agent with it, and without this the next `cinna sync push` failed
102
+ until Mutagen's own retry came round. A refused token is re-minted through
103
+ the account. A session that doesn't come back is a warning naming
104
+ `cinna sync push` / `cinna doctor`, never a failed restart.
95
105
 
96
106
  ### Rebuild an environment that predates a feature
97
107
  1. `cinna agent rebuild-env <agent_ref> [--yes]` is **not** a louder restart.
@@ -120,6 +130,8 @@ This doc covers the agent **lifecycle** verbs. The CRON automation subgroup
120
130
  row states; the server inside can still be starting, and a chat sent into that
121
131
  window sat streaming with no reply. It ends with "ready to chat", or a warning
122
132
  that the environment has not answered yet.
133
+ 7. Like `restart-env`, it then reconnects this machine's sync session for the
134
+ agent (see [Live Sync](../live_sync/live_sync.md)).
123
135
 
124
136
  ### Inspect what's live
125
137
  1. `cinna agent show <agent_ref>` prints the **effective prompts** (each under its
@@ -132,6 +144,11 @@ This doc covers the agent **lifecycle** verbs. The CRON automation subgroup
132
144
  `credentials:` block names. A credential shared by someone else whose slot
133
145
  this account cannot see reads `slot: unknown`, never `no slot`. When the
134
146
  agent's credential listing cannot be read, it falls back to name + type.
147
+ - A **Dashboard components** section (count, errors, tiles) when the server
148
+ has dashboards. It is read only while the environment is running, because
149
+ listing components would wake a suspended one; otherwise a dim note says the
150
+ environment's state. It never fails `show`. See
151
+ [Dashboards](../dashboards/dashboards.md).
135
152
 
136
153
  ### Choose the model per mode
137
154
  1. `cinna agent model show <agent_ref> [--json]` prints, for conversation and
@@ -66,7 +66,13 @@ The group/subgroup objects are `src/cinna/main.py:agent()` and
66
66
  - `src/cinna/account.py:run_agent_restart_env()` — resolve the agent; if a local
67
67
  child workspace exists (`resolve_child_workspace()`), read `sync_session.status()`
68
68
  and warn+confirm when `pending_to_remote > 0` or `conflict_count > 0`; then
69
- `AccountClient.restart_agent_env()` (blocking) and print status.
69
+ `AccountClient.restart_agent_env()` (blocking) and print status, then
70
+ `_reconnect_sync_session()`.
71
+ - `src/cinna/account.py:_reconnect_sync_session()` — shared by restart and
72
+ rebuild: for the child workspace of this agent (`list_child_workspaces()`)
73
+ whose session exists, `sync_session.heal_session(child_cfg, child)` (a forced
74
+ reconnect, plus a token re-mint on a 401/403). It prints "Sync session
75
+ reconnected." or a warning, and never raises.
70
76
  - `src/cinna/account.py:run_agent_rebuild_env()` — the same shape as
71
77
  `run_agent_restart_env()` plus a second gate: the D2 unsynced-changes
72
78
  warn+confirm, then a `Rebuild …?` confirmation (default No) that `--yes`
@@ -0,0 +1,138 @@
1
+ # Artifacts (`cinna artifacts`)
2
+
3
+ ## Purpose
4
+
5
+ Publish a folder or a file from the user's machine to cinna-core as an
6
+ **artifact** — a frozen, versioned copy people open on a Cinna page — and get a
7
+ link back. The same command group lists, downloads, opens, shares and deletes
8
+ artifacts from a terminal.
9
+
10
+ ## Mental model / core concepts
11
+
12
+ An artifact lives **in cinna-core**, not on the machine and not in an agent
13
+ environment. The CLI is a publisher and a remote control; once published, the
14
+ artifact outlives the folder, the agent and the session it came from.
15
+
16
+ - **Artifact** — a stable identity (title, who can see it, its page link) over a
17
+ sequence of immutable **revisions**. Core renders it: a folder with an
18
+ `index.html` runs as a small static site in a sandbox; anything else is a file
19
+ set with viewers (Markdown, CSV, PDF, images, code). Not to be confused with a
20
+ delegation's `--artifact` — a link attached to a task result (see Integration
21
+ points).
22
+ - **Revision** — one publish of the files. Republishing adds a revision; the
23
+ page link stays the same and shows the latest one, so a shared link never
24
+ breaks when a report is refreshed.
25
+ - **Path memory** — the CLI remembers which artifact each local path was
26
+ published as, per account workspace (`.cinna/artifacts.json`), and republishes
27
+ into it. Nothing is ever written into the published folder itself.
28
+ - **Exclusions** — what never leaves the machine: `.git/`, `.env*` and `*.env`,
29
+ `credentials/`, `credentials.json`, `app-data/`, `.claude/`, `.cinna/` (it
30
+ holds the account token), `node_modules/`, `__pycache__/`, key and token
31
+ files (`*.pem`, `*.key`, `*.p12`, `id_rsa*` and the other SSH key names,
32
+ `.netrc`, `.npmrc`, `.pypirc`), `.DS_Store`, and every symlink. Mandatory, not
33
+ advisory; core drops its own set again on its side.
34
+ - **Who can see it** — "Only you" (private, the default), plus named people,
35
+ plus "Everyone on the instance", plus **public links** (anyone holding the
36
+ link, signed out; optional expiry, optional 4-digit code, revocable,
37
+ optionally pinned to a revision). The CLI never says "public" for
38
+ instance-wide.
39
+ - **Agents publish, people share** — an agent in the account workspace may
40
+ publish (always private) and manage its artifacts, but widening who can see
41
+ one is the user's call: `share` and `link create` are person-only verbs.
42
+
43
+ ## User flows
44
+
45
+ ### Publish a report and get its link
46
+ 1. `cinna artifacts publish ./reports/q3 --title "Q3 churn"` from the account
47
+ workspace (or any folder below it).
48
+ 2. The CLI prints the file count and size, then what it left out
49
+ (`.git/`, `.env`, …), uploads, and prints the page link and the id.
50
+ 3. The artifact is private; the user opens the link (or
51
+ `cinna artifacts open <id>`) to check it.
52
+
53
+ ### Refresh it later, same link
54
+ 4. Run the same `cinna artifacts publish ./reports/q3` again. The CLI says it
55
+ republished — revision 2, same link — because it remembered the path.
56
+ 5. `--update <id>` republishes into a named artifact instead; `--new` publishes
57
+ a separate artifact even though the path is known (the path then points at
58
+ the new one).
59
+
60
+ ### Share it with people
61
+ 6. In their own terminal the user runs
62
+ `cinna artifacts share <id> --user bob@example.com` and/or `--instance`;
63
+ `--private` and `--remove-user` narrow it again. The CLI prints who can see
64
+ it afterwards.
65
+ 7. For someone outside the instance:
66
+ `cinna artifacts link create <id> [--expires 7d] [--code] [--label …] [--rev N]`
67
+ prints the link (and code) **once**. `cinna artifacts show <id>` lists links
68
+ by id and prefix; `cinna artifacts link revoke <id> <link>` kills one.
69
+
70
+ ### Find, fetch, remove
71
+ 8. `cinna artifacts list` (yours) or `list --shared` (shared with you);
72
+ `show <id>` for revisions, people and links.
73
+ 9. `cinna artifacts download <id> [--rev N] [-o DIR]` extracts the files
74
+ (default `artifacts/<short-id>/` under the account root).
75
+ 10. `cinna artifacts delete <id>` asks first; the page and every link stop
76
+ working at once.
77
+
78
+ ## Business rules
79
+
80
+ - **Server support first** — every verb asks core for its artifacts capability
81
+ before anything else; a server without it (or with another version) gets
82
+ "This Cinna server does not support artifacts" and nothing is uploaded.
83
+ - **A secret path named directly is refused** — publishing `.cinna/account.json`,
84
+ a `credentials/` folder or anything inside an excluded folder stops before
85
+ any request; the walk alone would only see paths below what was named.
86
+ - **Exclusions are listed, always** — the human output and the `--json` result
87
+ name every path left out, the CLI's and the server's together.
88
+ - **Refuse what core would refuse, before uploading** — over the file count,
89
+ per-file size or total size the server advertises, the CLI stops locally.
90
+ - **Republish keeps the title** — on a republish only the flags the user passes
91
+ change (`--title`, `--description`, `--entry`, `--note`).
92
+ - **A remembered artifact that was deleted** — the CLI says so and publishes a
93
+ new one; an explicit `--update <id>` that no longer exists fails instead.
94
+ - **Person-only verbs (A8)** — `share` and `link create` refuse under `--json`,
95
+ `--no-input` / `CINNA_NO_INPUT`, or when stdin is not a terminal, with code
96
+ `needs_person`. There is no `--yes` bypass: an agent can pass any flag. The
97
+ generated `.claude/settings.json` also denies `Bash(cinna artifacts share:*)`
98
+ and `Bash(cinna artifacts link create:*)` to Claude Code. The CLI cannot tell
99
+ an agent from a person — this is a speed bump; core enforces A8 on its
100
+ agent-token route.
101
+ - **A script cannot share either** — a script without a terminal is refused the
102
+ same way. Sharing from automation is deliberately not offered in v1; the web
103
+ page's Share dialog is the alternative.
104
+ - **Destructive verbs confirm** — `delete` and `link revoke` ask (default No);
105
+ under `--json` they need `--yes`, under `--no-input` without `--yes` they
106
+ abort.
107
+ - **The link token is shown once** — core returns it only on creation; the
108
+ api-proxy never relays it, and `show` lists links by prefix only.
109
+ - **Owner-only writes** — share, link and delete check ownership first and say
110
+ so plainly rather than relaying a 404.
111
+
112
+ ## Architecture overview
113
+
114
+ ```
115
+ cinna artifacts publish PATH
116
+ → artifacts.py: walk + exclusions → temp zip (stored entries for very compressible files)
117
+ → AccountClient → POST /cli/account/artifacts[/{id}/revisions] (multipart, account token)
118
+ → .cinna/artifacts.json (path → artifact id)
119
+
120
+ cinna artifacts list|show|share|link revoke|delete
121
+ → AccountClient → api-proxy → /api/v1/artifacts…, /users/search
122
+
123
+ cinna artifacts link create | download
124
+ → AccountClient → /cli/account/artifacts/{id}/links | /download
125
+ ```
126
+
127
+ ## Integration points
128
+
129
+ - [Account workspace](../account_workspace/account_workspace.md) — the account
130
+ token, the active user workspace the artifact lands in, and the generated
131
+ `.claude/settings.json` that carries the deny rules.
132
+ - [Durable delegation](../delegation/delegation.md) — the page link a publish
133
+ prints is a valid `link` artifact for
134
+ `cinna delegation report --artifact '{"kind":"link","name":"…","ref":"<url>"}'`.
135
+ - `cinna api` — the generic api-proxy the JSON verbs ride; it refuses link
136
+ creation and blanks link codes, which is why `link create` has its own route.
137
+ - cinna-core's kit skill `cinna-artifacts` — how to build a folder that works
138
+ in the sandbox (bundle libraries, no `localStorage`, relative paths).