ente-cli 0.1.0 → 0.4.54
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.
- package/README.md +274 -0
- package/dist/SKILL.md +344 -0
- package/dist/cli.mjs +16485 -0
- package/dist/cli.mjs.map +7 -0
- package/package.json +28 -15
- package/dist/api.d.ts +0 -221
- package/dist/api.js +0 -182
- package/dist/commands/activity.d.ts +0 -16
- package/dist/commands/activity.js +0 -345
- package/dist/commands/auth.d.ts +0 -7
- package/dist/commands/auth.js +0 -86
- package/dist/commands/entity.d.ts +0 -13
- package/dist/commands/entity.js +0 -205
- package/dist/commands/search.d.ts +0 -3
- package/dist/commands/search.js +0 -13
- package/dist/commands/task.d.ts +0 -3
- package/dist/commands/task.js +0 -305
- package/dist/commands/update.d.ts +0 -7
- package/dist/commands/update.js +0 -104
- package/dist/deadline.d.ts +0 -6
- package/dist/deadline.js +0 -171
- package/dist/format.d.ts +0 -21
- package/dist/format.js +0 -287
- package/dist/guide/index.d.ts +0 -2
- package/dist/guide/index.js +0 -27
- package/dist/guide/topics.d.ts +0 -5
- package/dist/guide/topics.js +0 -208
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -54
- package/dist/resolve.d.ts +0 -11
- package/dist/resolve.js +0 -66
- package/dist/utils.d.ts +0 -4
- package/dist/utils.js +0 -18
package/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# ente CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for [ente](https://github.com/valentindoering/ente) — a
|
|
4
|
+
relationship manager and knowledge graph (people, orgs, projects, topics and the
|
|
5
|
+
notes/calls/tasks/conversations logged against them).
|
|
6
|
+
|
|
7
|
+
TypeScript, built on [clipanion](https://clipanion.dev/), bundled with esbuild
|
|
8
|
+
into a single self-contained file, published to npm and Ivo's Gitea npm registry. It talks
|
|
9
|
+
to ente's external REST API (`/api/external/*`) with a static Bearer API key.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -g ente-cli
|
|
15
|
+
# Ivo's registry remains available with the same package and version:
|
|
16
|
+
npm i -g ente-cli --registry https://gitea.i.ivo-zilkenat.de/api/packages/ivo.zilkenat/npm
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
Connect a normal CLI profile with browser approval. Replace the site URL and
|
|
22
|
+
email with your Ente deployment and account:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ente auth pair --url https://your-deployment.convex.site --email you@example.com
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The CLI prints a review link and six-digit code. Open the link, sign in, inspect
|
|
29
|
+
the requested profile, enter the code, and approve it. The CLI generates its
|
|
30
|
+
key locally and sends its hash and short prefix to Ente. The full key stays
|
|
31
|
+
local and never appears in the browser or terminal output. Keep the CLI running
|
|
32
|
+
until it verifies the new profile.
|
|
33
|
+
|
|
34
|
+
To connect an operator to one agent, use that agent's ID from `ente agent list`
|
|
35
|
+
and name the local profile:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ente auth pair --url https://your-deployment.convex.site --email you@example.com \
|
|
39
|
+
--profile mail-operator --agent <agent-id> --access write
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For up to ten operators, put the exact agent IDs and local profile names in a
|
|
43
|
+
JSON file outside a repository:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
[
|
|
47
|
+
{ "profile": "operator-a", "agentId": "<agent-id-a>", "access": "write" },
|
|
48
|
+
{ "profile": "operator-b", "agentId": "<agent-id-b>", "access": "read" }
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ente auth pair --url https://your-deployment.convex.site --email you@example.com \
|
|
54
|
+
--manifest /private/path/operators.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
One Ente approval page lists all profiles and scopes. Existing local profiles
|
|
58
|
+
are never overwritten unless `--replace` is supplied. A stopped or expired CLI
|
|
59
|
+
run must be started again. Pending requests expire after ten minutes.
|
|
60
|
+
Both agent access modes can read and change Ente data and manage spaces.
|
|
61
|
+
With `read`, the key can inspect the bound agent and inbox, mark inbox threads
|
|
62
|
+
read/unread, and change their workflow state. `write` also permits agent
|
|
63
|
+
changes and sending email. The CLI saves approved profiles before
|
|
64
|
+
activating their keys, so a failed local save cannot leave active keys behind.
|
|
65
|
+
|
|
66
|
+
For headless integrations or a legacy key, generate an API key in the web app
|
|
67
|
+
and enter it directly in the terminal:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
ente auth login --url https://your-deployment.convex.site --key-stdin
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Credentials are stored at `~/.config/ente/config.toml` (`~/Library/Application
|
|
74
|
+
Support/ente/config.toml` on macOS), `0600`. `ENTE_API_URL` and `ENTE_API_KEY`
|
|
75
|
+
override the file only when both are set.
|
|
76
|
+
|
|
77
|
+
An agent operator profile is bound to that agent's configuration, threads,
|
|
78
|
+
scheduled runs, and inbox when enabled:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
ENTE_PROFILE=mail-operator ente agent config show
|
|
82
|
+
ENTE_PROFILE=mail-operator ente agent mail threads --unread
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Operator keys use the CLI's scoped REST commands. They cannot be exchanged for
|
|
86
|
+
the legacy full-account CLI session token, so the bound-agent restriction
|
|
87
|
+
cannot be bypassed through direct Convex commands. Use a normal account key for
|
|
88
|
+
legacy administrative CLI commands that require that session token.
|
|
89
|
+
|
|
90
|
+
Named profiles are optional. Use one only when a person or process deliberately
|
|
91
|
+
switches between several credentials. `--profile` overrides `ENTE_PROFILE`.
|
|
92
|
+
Profiles do not activate from the current directory and are not a security
|
|
93
|
+
boundary. Use a separate OS account or container when operators on the same
|
|
94
|
+
machine must not read each other's keys. Never commit a key or put it in a
|
|
95
|
+
project `.env` file.
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
ente search "Alice"
|
|
101
|
+
ente entity list --type person
|
|
102
|
+
ente entity rename "Lorey Georg" "Georg Lorey"
|
|
103
|
+
ente task list --due today
|
|
104
|
+
ente activity create --type note --title "Meeting notes" --entity "Alice" --space "Personal"
|
|
105
|
+
ente activity get <activityId> --json # includes the body, nested agenda, messages/replies, entities, and attachments
|
|
106
|
+
ente agenda create-point --meeting <meetingActivityId> --title "Decisions" --content="- Ship the smaller scope"
|
|
107
|
+
ente agenda link-task --point <agendaPointId> --task "#12"
|
|
108
|
+
ente activity message add <activityId> --content "Decision: ship the smaller scope first."
|
|
109
|
+
ente activity content read <activityId> --json
|
|
110
|
+
ente activity content edit <activityId> --expected-version 42 --operations-file edits.json --json
|
|
111
|
+
ente canvas boards <entityId>
|
|
112
|
+
ente canvas html create <boardId> --title "Prototype" --file ./prototype.html --operation-id prototype-v1
|
|
113
|
+
ente notification send --subject "Weekly brief" --text "Your brief is ready." --html-file ./brief.html --attachment ./brief.pdf --inline-image chart=./chart.png
|
|
114
|
+
ente agent config show
|
|
115
|
+
ente agent threads
|
|
116
|
+
ente agent mail threads --unread
|
|
117
|
+
ente agent mail send --to person@example.net --subject "Update" --text "Plain fallback" --attachment ./handoff.md --html "<p><strong>Update</strong></p>" --idempotency-key agent-update-2026-09-01
|
|
118
|
+
ente tasks
|
|
119
|
+
ente guide # concepts; `ente guide <topic>`: entities, tasks, activities, search, patterns, limits
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Every command supports `--json`. Run `ente --help` or `ente <group> --help`.
|
|
123
|
+
|
|
124
|
+
### Command groups
|
|
125
|
+
|
|
126
|
+
| Group | Commands |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `auth` | `pair`, `login`, `status`, `logout`, `profiles` |
|
|
129
|
+
| `entity` | `list`, `create`, `get`, `update` (`--name`, `--tag`, `--description`), `rename`, `archive`, `restore` (`--share-parents` plus the returned grant fingerprint), `pin` (`--role`, optional confirmed parent grants), `unpin`, `add-to-space`, `remove-from-space` (`--remove-from-activities` optional) |
|
|
130
|
+
| `activity` | `list`, `create`, `search`, `get`, `update`, `delete`, `restore`, `message add`, `attachment upload` |
|
|
131
|
+
| `agenda` | `create-point`, `link-task` |
|
|
132
|
+
| `task` | `list`, `get`, `create`, `update`, `done`, `assign`, `unassign`, `set-assignees`, `clear-assignees` |
|
|
133
|
+
| `pins` | `list` |
|
|
134
|
+
| `space` | `members` |
|
|
135
|
+
| `recording` | `list`, `show`, `inspect`, `rerun` (admin only) |
|
|
136
|
+
| `canvas` | `boards`, `html list`, `html get`, `html create`, `html update`, `html delete` |
|
|
137
|
+
| `notification` | `send` (emails only your own Ente account address; optional HTML, file attachments, and CID inline images) |
|
|
138
|
+
| `agent` | `list`, `threads`, `show`, `new`, `send`, `config show`, `config set-prompt`, `schedules configure` |
|
|
139
|
+
| `agent mail` | `address`, `threads`, `show`, `send`, `reply`, `mark` |
|
|
140
|
+
| top-level | `search`, `ask`, `query`, `tasks`, `guide` |
|
|
141
|
+
|
|
142
|
+
### Agent operators and inboxes
|
|
143
|
+
|
|
144
|
+
An agent operator key can read and change Ente data, manage spaces, and access
|
|
145
|
+
one bound agent. Agent read includes Ente configuration such as the prompt,
|
|
146
|
+
Ente thread history, scheduled-run configuration, inbox reads, and thread status
|
|
147
|
+
updates when an inbox is active. Agent
|
|
148
|
+
write additionally allows prompt changes, full scheduled-run replacement, and
|
|
149
|
+
inbox send/reply. New write-capable keys also receive `agent:communicate`, which
|
|
150
|
+
lets the bound external agent start, claim, and reply to external threads without
|
|
151
|
+
calling an Ente-hosted model. Ente prompt and scheduled runs configure the Ente agent
|
|
152
|
+
counterpart; they are not instructions or schedules for the external operator.
|
|
153
|
+
The caller cannot select another agent, mailbox, or sender.
|
|
154
|
+
The corresponding scopes are `agent:read`, `agent:write`, `mail:read`, and
|
|
155
|
+
`mail:send`, and `agent:communicate`.
|
|
156
|
+
|
|
157
|
+
The inbox owner must add each exact correspondent in Ente. Unlisted inbound
|
|
158
|
+
mail stays owner-only and never appears in operator REST, MCP, WebMCP, or CLI
|
|
159
|
+
reads. New sends and replies are rejected before persistence unless the final
|
|
160
|
+
recipient is allowed. An administrator-granted unlimited-mail account may
|
|
161
|
+
enable send-to-anyone or receive-from-anyone separately for each mailbox.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
ente agent config show
|
|
165
|
+
ente agent threads --include-archived --limit 20
|
|
166
|
+
ente agent show <ente-thread-id>
|
|
167
|
+
ente agent new "Can I publish this?" --title "Release note" --request text --idempotency-key release-question-1
|
|
168
|
+
ente agent threads --attention agent
|
|
169
|
+
ente agent send <ente-thread-id> "Published." --request ack --request-prompt "Release note published" --idempotency-key release-answer-1
|
|
170
|
+
ente agent config set-prompt --text "Review finance updates."
|
|
171
|
+
ente agent config set-prompt --file ./ente-agent-prompt.md
|
|
172
|
+
ente agent schedules configure --file ./schedules.json
|
|
173
|
+
ente agent mail address
|
|
174
|
+
ente agent mail threads --unread --limit 20
|
|
175
|
+
ente agent mail show <thread-id>
|
|
176
|
+
ente agent mail send --to person@example.net --subject "Update" --text "Plain fallback" --html "<p>Rich update</p>" --attachment ./reference.pdf --idempotency-key update-2026-09-01
|
|
177
|
+
ente agent mail reply <thread-id> --message <message-id> --text-file ./reply.txt --html-file ./reply.html --attachment ./handoff.docx --idempotency-key reply-2026-09-01
|
|
178
|
+
ente agent mail mark <thread-id> --state-version 4 --handled
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`schedules.json` is the complete replacement list. Omitted schedules are
|
|
182
|
+
deleted:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
[
|
|
186
|
+
{
|
|
187
|
+
"enabled": true,
|
|
188
|
+
"prompt": "Review new finance mail and prepare follow-ups.",
|
|
189
|
+
"timeMinutes": 480,
|
|
190
|
+
"timezone": "Europe/Berlin"
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Send and reply support repeatable `--inline-image CONTENT_ID=PATH` alongside
|
|
196
|
+
`--attachment PATH`. Supply HTML containing `<img src="cid:readiness">` and
|
|
197
|
+
`--inline-image readiness=./readiness.png`. The CLI embeds local file bytes in
|
|
198
|
+
the email; use CID images for private charts, never remote URLs or data URIs.
|
|
199
|
+
Inline images must be PNG, JPEG, GIF, or WebP (verified by file signature).
|
|
200
|
+
Content-IDs must be unique, case-sensitive, at most 128 characters, start with a
|
|
201
|
+
letter or number, and contain only letters, numbers, `.`, `_`, `@`, or `-`.
|
|
202
|
+
Every inline image must have a matching HTML CID reference, and every CID
|
|
203
|
+
reference must have an image. Keep the complete report in the required plain-text
|
|
204
|
+
fallback; HTML and images do not replace it.
|
|
205
|
+
|
|
206
|
+
The combined limit is **four files** across ordinary attachments and inline
|
|
207
|
+
images, **750,000 bytes per file** (3,000,000 decoded bytes total). Four Health
|
|
208
|
+
charts fit only when no ordinary attachment is also sent. Five SaaS Spending
|
|
209
|
+
charts exceed this limit and are rejected; inline images do not bypass it.
|
|
210
|
+
For example:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
ente agent mail send --to owner@example.net --subject "Readiness" --text-file ./report.txt --html-file ./report.html --inline-image readiness=./readiness.png --idempotency-key readiness-report-v1
|
|
214
|
+
ente agent mail reply <thread-id> --message <message-id> --text-file ./report.txt --html-file ./report.html --inline-image readiness=./readiness.png --attachment ./notes.txt --idempotency-key readiness-reply-v1
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Retries must keep filenames, MIME types, Content-IDs, file order, and exact file
|
|
218
|
+
bytes unchanged, along with the recipient, text, HTML, and reply target. A changed
|
|
219
|
+
image or CID with the same key is rejected. Reported `sent` means transport
|
|
220
|
+
accepted the message; delivery feedback may later report delivered, delayed,
|
|
221
|
+
bounced, or complained. An unknown delivery outcome must never be retried with a
|
|
222
|
+
new key.
|
|
223
|
+
|
|
224
|
+
`--text` and `--html` accept inline strings. Their `--text-file` and
|
|
225
|
+
`--html-file` counterparts read UTF-8 files. After an ambiguous timeout, retry
|
|
226
|
+
the exact same operation with the same `--idempotency-key`; changing the body,
|
|
227
|
+
recipient, thread, or message requires a new key.
|
|
228
|
+
|
|
229
|
+
### Operational recipes (playbooks)
|
|
230
|
+
|
|
231
|
+
Copy-pasteable, task-oriented recipes live in [`playbooks/`](./playbooks/) — log a
|
|
232
|
+
meeting, rename/merge an entity, work around cross-space linking, etc. Start there
|
|
233
|
+
for "how do I do X".
|
|
234
|
+
|
|
235
|
+
## Develop in-repo / build
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
pnpm setup:cli # from the repository root; once per checkout
|
|
239
|
+
cd cli
|
|
240
|
+
pnpm dev entity list # run from source via tsx
|
|
241
|
+
pnpm typecheck # the build-from-truth gate (tsc --noEmit)
|
|
242
|
+
pnpm build # → dist/cli.mjs (+ dist/SKILL.md)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The CLI is not a workspace member. `pnpm setup:cli` installs its locked
|
|
246
|
+
dependencies separately. The repository's `pnpm check` typechecks and tests the
|
|
247
|
+
CLI without installing dependencies.
|
|
248
|
+
|
|
249
|
+
Publishing is automatic: a push to `main` touching `cli/**` or the publish
|
|
250
|
+
workflow selects a version above both registries, tests and packs once, then
|
|
251
|
+
publishes identical bytes to npm and Gitea. npm uses GitHub OIDC; Gitea keeps
|
|
252
|
+
the existing package token. See `.github/workflows/publish-cli.yml` and
|
|
253
|
+
`docs/cli-architecture.md` for setup and retry instructions.
|
|
254
|
+
|
|
255
|
+
## Architecture
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
core/ brand-neutral: transport (REST/fetch), config, output, errors, update, command base.
|
|
259
|
+
ente/ branded: the /api/external client, types, resolve, deadline, formatters, commands/*.
|
|
260
|
+
cli.ts clipanion bootstrap: registers every command group.
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The dependency direction is one-way — `ente/` imports `core/`, never the reverse.
|
|
264
|
+
See [`CLAUDE.md`](./CLAUDE.md) for the layering rules and conventions.
|
|
265
|
+
|
|
266
|
+
## Public installation and recovery guide
|
|
267
|
+
|
|
268
|
+
This is the Ente private knowledge-graph / relationship-manager CLI at ente.fast,
|
|
269
|
+
not Ente Photos. Follow https://ente.fast/cli.md for new installations, upgrades,
|
|
270
|
+
PATH conflicts, configuration reuse, legacy API keys, and stale authentication.
|
|
271
|
+
Keep working credentials. `ente auth status --json` reports local configuration
|
|
272
|
+
without key material; verify server access with `ente entity list --limit 1 --json`.
|
|
273
|
+
Use `auth login --key-stdin` for hidden local key entry as shown in the guide.
|
|
274
|
+
Never put secrets in chat, repositories, project `.env` files, or logs.
|
package/dist/SKILL.md
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: m:ente-cli
|
|
3
|
+
description: "Cli: drives the ente CRM and knowledge graph over its Convex API as the signed-in user — entities (people, organisations, projects, topics), activities (notes, calls, meetings, tasks), relationships, spaces, full-text and AI-backed search, agent threads and mail, plus Excalidraw canvas prototypes. Use when context about a person, an organisation or a meeting has to be stored, looked up or updated from the shell. Not for splitting shared costs, which is schnorrer-cli."
|
|
4
|
+
when_to_use: "Remembering what was said in a call or meeting; looking up open tasks, deadlines or follow-ups for a person or project; creating or renaming an entity and pinning it to a parent; linking an activity to several entities or moving it between spaces; asking a question over the whole graph and getting citations; reviewing the voice-recording prompt or debugging a failed recording; sending a notification mail or an agent-inbox reply; posting to the in-app feedback board; managing Excalidraw canvas HTML prototypes on an entity board."
|
|
5
|
+
metadata:
|
|
6
|
+
short-description: Relationship memory through ente
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ente-cli - CRM & Relationship Manager CLI Skill
|
|
10
|
+
|
|
11
|
+
ente is a CRM / relationship manager that tracks people, organisations, projects, and topics (entities), and logs notes, calls, tasks, and conversations (activities) against them. This skill wraps the `ente` CLI so an agent can manage entities, activities, tasks, pins, and search end-to-end.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
This is Ente’s private knowledge-graph / relationship-manager CLI at ente.fast,
|
|
18
|
+
not Ente Photos. For installation, upgrades, PATH conflicts, and recovery, follow
|
|
19
|
+
https://ente.fast/cli.md. Honor an explicit CLI request even if MCP works;
|
|
20
|
+
otherwise MCP remains the default for ordinary remote access.
|
|
21
|
+
|
|
22
|
+
Reuse an existing working configuration. Never generate another key merely
|
|
23
|
+
because the CLI was upgraded. Check locally, then verify with a read-only call:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ente auth status --json
|
|
27
|
+
ente entity list --limit 1 --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Auth status reports local configuration, not server validity. Only the read
|
|
31
|
+
verifies access. Upgrade older releases if --json prints text.
|
|
32
|
+
If no valid profile exists, run `ente auth pair --url <convex-site-url> --email
|
|
33
|
+
<account-email>`. Open the printed Ente review link for the user and ask them to
|
|
34
|
+
compare and enter the printed six-digit code on that page. Keep the command
|
|
35
|
+
running until it reports verification. The CLI creates the key locally and
|
|
36
|
+
passes its hash and short prefix to Ente. The full key stays local.
|
|
37
|
+
Never collect secrets in chat, repositories,
|
|
38
|
+
project `.env` files, or logs. A legacy key can still be entered with
|
|
39
|
+
`ente auth login --url <convex-site-url> --key-stdin`.
|
|
40
|
+
|
|
41
|
+
The current CLI reuses the legacy config.toml in the platform user configuration
|
|
42
|
+
directory (macOS: ~/Library/Application Support/ente; Linux: ~/.config/ente).
|
|
43
|
+
The complete ENTE_API_URL / ENTE_API_KEY environment pair overrides that file.
|
|
44
|
+
Partial overrides fail closed. Check the selected profile and stale environment
|
|
45
|
+
overrides before replacing credentials. Never print their secret values.
|
|
46
|
+
|
|
47
|
+
For an agent operator, pair a named profile with the exact agent ID and scope.
|
|
48
|
+
For several operators, pass a JSON manifest with up to ten `{ profile, agentId,
|
|
49
|
+
access }` entries. The Ente page shows the complete set for one approval:
|
|
50
|
+
|
|
51
|
+
Both agent access modes can read and change Ente data and manage spaces. With
|
|
52
|
+
`read`, the key can inspect the bound agent and inbox, mark inbox threads
|
|
53
|
+
read/unread, and change their workflow state. `write` also permits agent
|
|
54
|
+
changes and email sending.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ente auth pair --url <convex-site-url> --email <account-email> --profile operator-name --agent <agent-id> --access write
|
|
58
|
+
ente auth pair --url <convex-site-url> --email <account-email> --manifest /private/path/operators.json
|
|
59
|
+
ENTE_PROFILE=operator-name ente agent config show --json
|
|
60
|
+
ENTE_PROFILE=operator-name ente agent mail threads --unread --json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Operator keys use scoped REST commands and cannot mint the legacy full-account
|
|
64
|
+
CLI session token. This preserves the one-agent boundary. A normal account key
|
|
65
|
+
is still required for legacy administrative commands that call Convex
|
|
66
|
+
functions directly.
|
|
67
|
+
|
|
68
|
+
Named profiles are optional and only needed to switch deliberately between
|
|
69
|
+
credentials. `--profile` overrides `ENTE_PROFILE`. Profiles do not activate
|
|
70
|
+
from the current directory and do not isolate secrets from other processes
|
|
71
|
+
running as the same operating-system user. Use a separate OS account or
|
|
72
|
+
container when that isolation matters. Never store a key in the repository or
|
|
73
|
+
a project `.env` file.
|
|
74
|
+
|
|
75
|
+
Verify server access with `ente entity list --limit 1 --json`.
|
|
76
|
+
|
|
77
|
+
Install / upgrade the CLI from npm:
|
|
78
|
+
```bash
|
|
79
|
+
npm i -g ente-cli --registry https://registry.npmjs.org
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Important Notes
|
|
85
|
+
|
|
86
|
+
- **Install targets**: The CLI writes this skill to supported app paths when they exist: `~/.claude/skills/ente-cli/SKILL.md` and `~/.codex/skills/ente-cli/SKILL.md`.
|
|
87
|
+
- **Proactive triggers**: meeting logs, inbox context capture, promise/follow-up tracking, and people/org/project memory updates.
|
|
88
|
+
- **Entity resolution**: Entity names are resolved via fuzzy search. Pass a name or a Convex ID (long alphanumeric string, 20+ chars). If ambiguous, the CLI lists matches and asks for a specific ID.
|
|
89
|
+
- **Space resolution**: Space names work the same way — exact match first, then substring. Use `--space` to target a workspace.
|
|
90
|
+
- **--json flag**: Every command supports `--json`. Always use `--json` when parsing output programmatically.
|
|
91
|
+
- **Deadlines**: stored as millisecond epochs; displayed as `DD.MM.YYYY` / `DD.MM.YYYY, HH:MM` in Europe/Berlin.
|
|
92
|
+
- **Entity types**: `person`, `org`, `project`, `topic`
|
|
93
|
+
- **Activity types**: `note`, `conversation`, `meeting`, `call`, `message`, `task`
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Quick Reference
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Auth
|
|
101
|
+
ente auth pair --url <convex-site-url> --email <account-email> [--profile <name>] [--agent <agent-id> --access read|write] [--manifest <json-file>]
|
|
102
|
+
ente auth login --url <convex-site-url> --key-stdin
|
|
103
|
+
ente auth status
|
|
104
|
+
ente auth logout
|
|
105
|
+
ente auth profiles
|
|
106
|
+
|
|
107
|
+
# Search & ask
|
|
108
|
+
ente search "Alice" [--type person] [--json]
|
|
109
|
+
ente ask "What's open with Erik?" [--space <name>] [--entity <name>] [--evidence] [--json] # AI answer + citations
|
|
110
|
+
ente query "QM system export" [--space <name>] [--entity <name>] [--verbose] [--json] # evidence only, no AI
|
|
111
|
+
|
|
112
|
+
# Entities
|
|
113
|
+
ente entity list [--query <text>] [--type person|org|project|topic] [--space <name>] [--limit 50] [--archived] [--json]
|
|
114
|
+
ente entity create "Jane Doe" --type person [--pin "Acme Corp"] [--share-parents --parent-space-grant-fingerprint <token>] [--alias "JD"] [--attr email=jane@acme.com] [--tag vip] [--description "CTO at Acme"] [--json]
|
|
115
|
+
ente entity get "Jane Doe" [--json]
|
|
116
|
+
ente entity update "Jane Doe" [--name "Jane A. Doe"] [--alias-add "Jane"] [--alias-remove "JD"] [--attr role=CEO] [--tag vip] [--description "…"] [--json]
|
|
117
|
+
ente entity rename "Lorey Georg" "Georg Lorey" # change display name
|
|
118
|
+
ente entity pin "Jane Doe" "Acme Corp" [--role "student"] [--share-parents --parent-space-grant-fingerprint <token>] [--json]
|
|
119
|
+
ente entity unpin "Jane Doe" "Acme Corp" [--json]
|
|
120
|
+
ente entity add-to-space "Jane Doe" --space "Team" [--share-parents --parent-space-grant-fingerprint <token>] [--json]
|
|
121
|
+
ente entity remove-from-space "Jane Doe" --space "Team" [--remove-from-activities] [--json]
|
|
122
|
+
ente pins list "Jane Doe" [--json] # relationships whose endpoints are accessible
|
|
123
|
+
ente entity archive "Old Project" [--json]
|
|
124
|
+
ente entity restore <entity-id> [--share-parents --parent-space-grant-fingerprint <token>] [--json]
|
|
125
|
+
|
|
126
|
+
# Activities
|
|
127
|
+
ente activity list [--entity "Jane"] [--type note|conversation|meeting|call|message|task] [--attr k=v] [--only-open] [--hours 168] [--limit 20] [--sort deadline|created] [--json]
|
|
128
|
+
ente activity create --type meeting --title "Call with Jane" --entity "Jane Doe" --entity "Acme Corp" --space "Work" [--content "…"] [--content-file ./notes.md] [--auto-link --entity-space-grant-fingerprint <token>] [--json]
|
|
129
|
+
ente activity search "quarterly review" [--entity "Acme"] [--type meeting] [--limit 10] [--json]
|
|
130
|
+
ente activity get <activityId> [--json] # complete detail: body, full nested agenda, messages/replies, entities, attachments
|
|
131
|
+
ente activity update <activityId> [--title "…"] [--content "…"] [--entity-add "Bob"] [--entity-remove "Carol"] [--json]
|
|
132
|
+
ente activity content read <activityId> [--cursor <cursor> --expected-version <version>] [--max-blocks 20] [--max-characters 12000] [--json]
|
|
133
|
+
ente activity content edit <activityId> --expected-version <version> --operations-file ./operations.json [--json]
|
|
134
|
+
ente activity message add <activityId> (--content "…" | --content-file ./update.md) [--json]
|
|
135
|
+
ente activity attachment upload <activityId> ./file.pdf [--name "Contract.pdf"] [--json]
|
|
136
|
+
ente activity delete <activityId> [--json]
|
|
137
|
+
ente activity restore <activityId> [--json]
|
|
138
|
+
|
|
139
|
+
# Meeting agendas
|
|
140
|
+
ente agenda create-point --meeting <meetingActivityId> --title "Decisions" [--content="…"] [--content-file ./decisions.md] [--parent <agendaPointId>] [--after <agendaPointId>] [--json]
|
|
141
|
+
ente agenda link-task --point <agendaPointId> --task <activityId|#N> [--json]
|
|
142
|
+
|
|
143
|
+
# Excalidraw Canvas HTML prototypes
|
|
144
|
+
ente canvas boards <entityId> [--json]
|
|
145
|
+
ente canvas html list <boardId> [--json]
|
|
146
|
+
ente canvas html get <boardId> <elementId> [--json]
|
|
147
|
+
ente canvas html create <boardId> --title "Prototype" --file <html-path|-> [--placement below_content|empty_region|manual] [--direction right|down|left|up] [--x N --y N] [--width N --height N] [--operation-id <stable-key>] [--json]
|
|
148
|
+
ente canvas html update <boardId> <elementId> --file <html-path|-> [--title "Revised"] [--x N --y N] [--width N --height N] [--json]
|
|
149
|
+
ente canvas html delete <boardId> <elementId> [--json]
|
|
150
|
+
|
|
151
|
+
# Tasks (activities with type=task; short-ids #N, deadlines, assignees)
|
|
152
|
+
ente tasks [--json] # open tasks by deadline
|
|
153
|
+
ente task list [--due today|overdue|all] [--entity "Jane"] [--json]
|
|
154
|
+
ente task get 5 [--space "Work"] [--json]
|
|
155
|
+
ente task create "Reply to Jane" --entity "Jane Doe" --space "Personal" [--deadline "2026-05-23 14:00"] [--content "…"] [--assignee <userId-or-email>] [--json]
|
|
156
|
+
ente task update 5 [--title "…"] [--content "…"] [--deadline "2026-05-26"] [--attr k=v] [--json]
|
|
157
|
+
ente task done 5 [--json]
|
|
158
|
+
ente space members --space "Work" [--json]
|
|
159
|
+
ente task assign 5 <userId-or-email> [--json]
|
|
160
|
+
ente task unassign 5 <userId-or-email> [--json]
|
|
161
|
+
ente task set-assignees 5 --assignee <userId-or-email> [--json]
|
|
162
|
+
ente notification send --subject "Weekly brief" --text "Your brief is ready." [--html-file ./brief.html] [--attachment ./handoff.md] [--image ./attachment.png] [--inline-image chart=./chart.png] [--json]
|
|
163
|
+
ente task clear-assignees 5 [--json]
|
|
164
|
+
|
|
165
|
+
# Bound Ente agent (operator key)
|
|
166
|
+
ente agent config show [--json]
|
|
167
|
+
ente agent threads [--include-archived] [--limit 20] [--json]
|
|
168
|
+
ente agent threads [--attention user|agent|none] [--json]
|
|
169
|
+
ente agent show <ente-thread-id> [--json]
|
|
170
|
+
ente agent new "Question" [--title "Title"] [--request none|ack|text] [--request-prompt "Prompt"] [--html-file ./message.html] [--idempotency-key <stable-key>] [--json]
|
|
171
|
+
ente agent send <ente-thread-id> "Reply" [--request none|ack|text] [--request-prompt "Prompt"] [--html-file ./message.html] [--idempotency-key <stable-key>] [--json]
|
|
172
|
+
ente agent config set-prompt (--text "New prompt" | --file ./prompt.md) [--json]
|
|
173
|
+
ente agent schedules configure --file ./schedules.json [--json]
|
|
174
|
+
|
|
175
|
+
# Agent inbox (available when the bound agent has an active inbox)
|
|
176
|
+
# The owner must first add each exact correspondent in Ente inbox settings.
|
|
177
|
+
# Unlisted inbound mail is owner-only and is absent from CLI reads. Sends and
|
|
178
|
+
# replies fail unless the final recipient is allowlisted (or an admin-granted
|
|
179
|
+
# unlimited account enabled send-to-anyone for this mailbox).
|
|
180
|
+
ente agent mail address [--profile <name>] [--json]
|
|
181
|
+
ente agent mail threads [--profile <name>] [--unread] [--needs-reply] [--limit 20] [--cursor <cursor>] [--json]
|
|
182
|
+
ente agent mail show <thread-id> [--cursor <cursor>] [--json]
|
|
183
|
+
ente agent mail send --to person@example.net --subject "Update" --text "Plain fallback" [--html "<p>Rich update</p>" | --html-file ./message.html] [--attachment ./reference.pdf] [--idempotency-key <stable-key>] [--json]
|
|
184
|
+
ente agent mail reply <thread-id> --message <message-id> (--text "Reply" | --text-file ./reply.txt) [--html "<p>Reply</p>" | --html-file ./reply.html] [--attachment ./handoff.docx] [--idempotency-key <stable-key>] [--json]
|
|
185
|
+
ente agent mail mark <thread-id> --state-version <version> (--handled | --open | --needs-reply | --read | --unread) [--json]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Agent operator credentials are bound server-side to one exact agent. Agent read
|
|
189
|
+
adds `agent:read` and, while the inbox is active, `mail:read`. Agent write adds
|
|
190
|
+
`agent:write`, `agent:communicate`, and, while the inbox is active, `mail:send`.
|
|
191
|
+
Use `agent threads --attention agent`, then `agent send`; the CLI claims the current
|
|
192
|
+
attention version before replying so concurrent cron workers cannot duplicate work.
|
|
193
|
+
The prompt and
|
|
194
|
+
scheduled runs are Ente configuration for the Ente agent counterpart. They are
|
|
195
|
+
not work instructions or scheduler settings for the external operator. The key
|
|
196
|
+
cannot select a different agent, mailbox, or sender. All authenticated commands
|
|
197
|
+
accept optional `--profile <name>`; use `ENTE_PROFILE` only when a launcher must
|
|
198
|
+
select one of several stored credentials.
|
|
199
|
+
Send and reply support repeatable `--inline-image CONTENT_ID=PATH` alongside
|
|
200
|
+
`--attachment PATH`. Supply HTML containing `<img src="cid:readiness">` and
|
|
201
|
+
`--inline-image readiness=./readiness.png`. The CLI embeds local file bytes in
|
|
202
|
+
the email; use CID images for private charts, never remote URLs or data URIs.
|
|
203
|
+
Inline images must be PNG, JPEG, GIF, or WebP (verified by file signature).
|
|
204
|
+
Content-IDs must be unique, case-sensitive, at most 128 characters, start with a
|
|
205
|
+
letter or number, and contain only letters, numbers, `.`, `_`, `@`, or `-`.
|
|
206
|
+
Every inline image must have a matching HTML CID reference, and every CID
|
|
207
|
+
reference must have an image. Keep the complete report in the required plain-text
|
|
208
|
+
fallback; HTML and images do not replace it.
|
|
209
|
+
|
|
210
|
+
The combined limit is **four files** across ordinary attachments and inline
|
|
211
|
+
images, **750,000 bytes per file** (3,000,000 decoded bytes total). Four Health
|
|
212
|
+
charts fit only when no ordinary attachment is also sent. Five SaaS Spending
|
|
213
|
+
charts exceed this limit and are rejected; inline images do not bypass it.
|
|
214
|
+
For example:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
ente agent mail send --to owner@example.net --subject "Readiness" --text-file ./report.txt --html-file ./report.html --inline-image readiness=./readiness.png --idempotency-key readiness-report-v1
|
|
218
|
+
ente agent mail reply <thread-id> --message <message-id> --text-file ./report.txt --html-file ./report.html --inline-image readiness=./readiness.png --attachment ./notes.txt --idempotency-key readiness-reply-v1
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Retries must keep filenames, MIME types, Content-IDs, file order, and exact file
|
|
222
|
+
bytes unchanged, along with the recipient, text, HTML, and reply target. A changed
|
|
223
|
+
image or CID with the same key is rejected. Reported `sent` means transport
|
|
224
|
+
accepted the message; delivery feedback may later report delivered, delayed,
|
|
225
|
+
bounced, or complained. An unknown delivery outcome must never be retried with a
|
|
226
|
+
new key.
|
|
227
|
+
|
|
228
|
+
`--text` and `--html` accept inline strings; use the
|
|
229
|
+
corresponding `--*-file` option for UTF-8 files. If a send or reply returns an
|
|
230
|
+
ambiguous timeout, retry the exact operation with the same
|
|
231
|
+
`--idempotency-key`. Use a new key whenever any payload field changes.
|
|
232
|
+
|
|
233
|
+
For a focused update to an existing long body, use `activity content read`, select the returned
|
|
234
|
+
version-scoped block targets, then call `activity content edit` with the exact `contentVersion`.
|
|
235
|
+
The operations file is either a JSON array or `{ "operations": [...] }`; supported operation types
|
|
236
|
+
are `replaceBlock`, `insertBefore`, `insertAfter`, `deleteBlock`, `replaceText`, and `append`.
|
|
237
|
+
Use `replaceText` with a unique exact Markdown substring for file-patch-like changes inside a large
|
|
238
|
+
block. Partial Markdown edits preserve existing rich blocks but do not create interactive HTML
|
|
239
|
+
blocks; use the dedicated HTML block commands for those. If the version conflicts, reread and
|
|
240
|
+
reconsider the edit; never overwrite newer content.
|
|
241
|
+
Keep `activity update --content-file` for deliberate whole-document imports or rewrites, and use
|
|
242
|
+
`activity message add` for chronological progress that belongs outside the main body.
|
|
243
|
+
|
|
244
|
+
For a Canvas prototype, resolve the entity ID, call `canvas boards`, and insert the source with a
|
|
245
|
+
stable `--operation-id`. Prefer `below_content` for a predictable append or `empty_region` when the
|
|
246
|
+
board is already dense. The create result includes `reviewPath`. Update and delete perform a fresh
|
|
247
|
+
read and fail on a concurrent human edit. These remote scene changes sync into open boards, but do
|
|
248
|
+
not enter another browser's local undo stack.
|
|
249
|
+
|
|
250
|
+
### Recording (voice) pipeline
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
ente recording-pipeline show [--json] # prompt + dictionary
|
|
254
|
+
ente recording-pipeline set-prompt "<text>" | --file <path|-> | --edit
|
|
255
|
+
ente recording-pipeline reset-prompt # clear → starter regenerated next run
|
|
256
|
+
ente recording-pipeline set-dictionary "<text>" | --file <path|-> | --edit
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- Edits **your own** user's voice-pipeline settings (same functions as web Settings → Voice pipeline).
|
|
260
|
+
- `set-prompt`/`set-dictionary` are full replacements; exactly one source (`<value>` | `--file` | `--edit`).
|
|
261
|
+
|
|
262
|
+
### Own models (own-models plan only)
|
|
263
|
+
|
|
264
|
+
Users on the hidden €0 own-models plan bring OpenAI-compatible endpoints (e.g. LiteLLM). Ente then sends their content only to those endpoints; a feature stays disabled until its slot has a model.
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
ente models status # slots, connections, web search
|
|
268
|
+
printf %s "$KEY" | ente models connection add LiteLLM --base-url https://llm.example.eu/v1 --api-key-stdin
|
|
269
|
+
ente models set agent LiteLLM claude-sonnet # slots: agent ask recording transcription summary small
|
|
270
|
+
ente models test [slot] # real request; agent/ask/recording must call a tool
|
|
271
|
+
ente models web-search on|off # Linkup web search for the agent (default off)
|
|
272
|
+
ente users set-plan <email> own_models # admin: move a user onto the plan
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Recordings (admin — debug a failing recording)
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
ente recording list [--user <email|id>] [--status error] [--limit 20] [--json]
|
|
279
|
+
ente recording show <recordingId> [--json] # status, error, transcript size, activity count
|
|
280
|
+
ente recording inspect <recordingId> [--json] # audio container: brands, fragmented?, box counts
|
|
281
|
+
ente recording rerun <recordingId> [--wait] [--timeout 900] [--json]
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
- Admin-only (`api.admin.recordings.*`); the ordinary `recordings.*` API is creator-scoped.
|
|
285
|
+
- `rerun` never modifies or deletes the stored audio; `--wait` polls to `done`/`error` and exits non-zero on failure.
|
|
286
|
+
- `--status` scans newest-first and says so when the sweep is capped.
|
|
287
|
+
|
|
288
|
+
### Feedback board
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
ente feedback send "<summary>" [--category missing|confusing|wrong_data|workflow|bug|other] \
|
|
292
|
+
[--situation "…"] [--goal "…"] [--need-to-see "…"] [--need-to-do "…"] [--reason "…"] [--workaround "…"]
|
|
293
|
+
ente feedback list [--status offen|todo|bearbeitung|klaerung|abgeschlossen|spaeter] [--text "…"] [--limit 50] [--json]
|
|
294
|
+
ente feedback view <feedbackId> [--json] # full item + comment thread
|
|
295
|
+
ente feedback comment <feedbackId> "<body>" # reply on the thread
|
|
296
|
+
ente feedback status <feedbackId> <status> # move to a board column
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
- Same in-app feedback board as the web composer (`convex/feedback.ts` public functions) — no GitHub issue.
|
|
300
|
+
- Every authenticated user may submit and manage the shared board. Screenshots/attachments are web-composer only.
|
|
301
|
+
|
|
302
|
+
### Deadline formats
|
|
303
|
+
|
|
304
|
+
| Input | Interpretation |
|
|
305
|
+
|---|---|
|
|
306
|
+
| `tomorrow` / `tomorrow 14:00` | Tomorrow 22:00 / 14:00 Berlin |
|
|
307
|
+
| `today` / `today 14:00` | Today 22:00 / 14:00 Berlin |
|
|
308
|
+
| `+3 days` / `+1 week` | Relative, 22:00 Berlin |
|
|
309
|
+
| `2026-03-15` / `2026-03-15 14:00` | That date (22:00 default) Berlin |
|
|
310
|
+
| `2026-03-15T14:00:00+01:00` | ISO 8601 with offset |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Limits & boundaries
|
|
315
|
+
|
|
316
|
+
- **Rename is supported**: `ente entity rename "<old>" "<new>"` (or `entity update <id> --name`).
|
|
317
|
+
- **Relationships are spaceless**, but access propagates upward through the active graph. Every active parent and ancestor must belong to every active child space. A failed write returns the exact grant preview and `grantFingerprint`; retry with `--share-parents --parent-space-grant-fingerprint <token>` so concurrent graph changes cannot expand the approved grant set.
|
|
318
|
+
- **Activity ownership stays inside its space.** An entity cannot leave a space while it owns activities there. Use `--remove-from-activities` to remove all affected ownership links when every activity has another owner.
|
|
319
|
+
- **Cross-space linking is bounded**: an activity can only link entities already in its target space. A failed write returns the exact entity-space grant fingerprint. Retry with `--auto-link --entity-space-grant-fingerprint <token>` only after confirming that exact grant set; concurrent hierarchy changes fail closed.
|
|
320
|
+
- **Merging duplicates and adding an entity to a shared space are web-UI only** — there is no API endpoint. Run `ente guide limits` for the full rundown.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Workflow pattern
|
|
325
|
+
|
|
326
|
+
1. **Check auth** — `ente auth status` before data commands.
|
|
327
|
+
2. **Search first** — `ente search "name" --json` before creating entities (avoid duplicates).
|
|
328
|
+
3. **Identify entities** — find the most specific existing owners; inspect direct children when a broad project or organization matches.
|
|
329
|
+
4. **Determine space** — personal for private notes, shared for team-visible activities.
|
|
330
|
+
5. **Operate** — entity/activity/task CRUD (prefer `--json` for parsing).
|
|
331
|
+
6. **Link precisely** — link independently central entities, prefer specific children, and omit ancestors that are implied by hierarchy.
|
|
332
|
+
7. **Confirm** — re-run the relevant list/get after writes to verify.
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Error handling
|
|
337
|
+
|
|
338
|
+
- **"Not authenticated"**: run `ente auth login`.
|
|
339
|
+
- **"Ambiguous match"**: multiple entities match — use the full Convex ID.
|
|
340
|
+
- **"No entity found"**: check spelling, try `ente search`.
|
|
341
|
+
- **"not in '<space>'" (cross-space)**: confirm the listed grants, then retry with `--auto-link --entity-space-grant-fingerprint <token>`, or add the entity to that space in the web UI.
|
|
342
|
+
- **"Cannot remove all entities"**: an activity must keep at least one entity.
|
|
343
|
+
|
|
344
|
+
Parent relationships are spaceless. Both entities must be accessible. Every active parent and ancestor must belong to every active child space. Commands that would expand parent access fail with a grant preview and fingerprint. Retry with both confirmation flags. A parent cannot be removed from a space while an active child remains there.
|