@xmtp/convos-cli 0.1.0

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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +572 -0
  3. package/bin/dev.js +4 -0
  4. package/bin/run.js +4 -0
  5. package/dist/baseCommand.d.ts +46 -0
  6. package/dist/baseCommand.js +171 -0
  7. package/dist/commands/agent/serve.d.ts +67 -0
  8. package/dist/commands/agent/serve.js +662 -0
  9. package/dist/commands/conversation/add-members.d.ts +19 -0
  10. package/dist/commands/conversation/add-members.js +39 -0
  11. package/dist/commands/conversation/consent-state.d.ts +18 -0
  12. package/dist/commands/conversation/consent-state.js +24 -0
  13. package/dist/commands/conversation/download-attachment.d.ts +28 -0
  14. package/dist/commands/conversation/download-attachment.js +164 -0
  15. package/dist/commands/conversation/explode.d.ts +24 -0
  16. package/dist/commands/conversation/explode.js +156 -0
  17. package/dist/commands/conversation/info.d.ts +22 -0
  18. package/dist/commands/conversation/info.js +79 -0
  19. package/dist/commands/conversation/invite.d.ts +26 -0
  20. package/dist/commands/conversation/invite.js +137 -0
  21. package/dist/commands/conversation/lock.d.ts +24 -0
  22. package/dist/commands/conversation/lock.js +98 -0
  23. package/dist/commands/conversation/members.d.ts +22 -0
  24. package/dist/commands/conversation/members.js +39 -0
  25. package/dist/commands/conversation/messages.d.ts +31 -0
  26. package/dist/commands/conversation/messages.js +141 -0
  27. package/dist/commands/conversation/permissions.d.ts +18 -0
  28. package/dist/commands/conversation/permissions.js +33 -0
  29. package/dist/commands/conversation/profiles.d.ts +22 -0
  30. package/dist/commands/conversation/profiles.js +80 -0
  31. package/dist/commands/conversation/remove-members.d.ts +19 -0
  32. package/dist/commands/conversation/remove-members.js +36 -0
  33. package/dist/commands/conversation/send-attachment.d.ts +30 -0
  34. package/dist/commands/conversation/send-attachment.js +187 -0
  35. package/dist/commands/conversation/send-reaction.d.ts +21 -0
  36. package/dist/commands/conversation/send-reaction.js +38 -0
  37. package/dist/commands/conversation/send-remote-attachment.d.ts +30 -0
  38. package/dist/commands/conversation/send-remote-attachment.js +96 -0
  39. package/dist/commands/conversation/send-reply.d.ts +32 -0
  40. package/dist/commands/conversation/send-reply.js +170 -0
  41. package/dist/commands/conversation/send-text.d.ts +24 -0
  42. package/dist/commands/conversation/send-text.js +64 -0
  43. package/dist/commands/conversation/stream.d.ts +24 -0
  44. package/dist/commands/conversation/stream.js +81 -0
  45. package/dist/commands/conversation/sync.d.ts +18 -0
  46. package/dist/commands/conversation/sync.js +25 -0
  47. package/dist/commands/conversation/update-consent.d.ts +19 -0
  48. package/dist/commands/conversation/update-consent.js +35 -0
  49. package/dist/commands/conversation/update-description.d.ts +19 -0
  50. package/dist/commands/conversation/update-description.js +28 -0
  51. package/dist/commands/conversation/update-name.d.ts +19 -0
  52. package/dist/commands/conversation/update-name.js +29 -0
  53. package/dist/commands/conversation/update-profile.d.ts +24 -0
  54. package/dist/commands/conversation/update-profile.js +97 -0
  55. package/dist/commands/conversations/create.d.ts +26 -0
  56. package/dist/commands/conversations/create.js +165 -0
  57. package/dist/commands/conversations/join.d.ts +27 -0
  58. package/dist/commands/conversations/join.js +232 -0
  59. package/dist/commands/conversations/list.d.ts +20 -0
  60. package/dist/commands/conversations/list.js +109 -0
  61. package/dist/commands/conversations/process-join-requests.d.ts +26 -0
  62. package/dist/commands/conversations/process-join-requests.js +261 -0
  63. package/dist/commands/conversations/sync.d.ts +19 -0
  64. package/dist/commands/conversations/sync.js +50 -0
  65. package/dist/commands/identity/create.d.ts +21 -0
  66. package/dist/commands/identity/create.js +56 -0
  67. package/dist/commands/identity/info.d.ts +22 -0
  68. package/dist/commands/identity/info.js +63 -0
  69. package/dist/commands/identity/list.d.ts +19 -0
  70. package/dist/commands/identity/list.js +59 -0
  71. package/dist/commands/identity/remove.d.ts +23 -0
  72. package/dist/commands/identity/remove.js +51 -0
  73. package/dist/commands/init.d.ts +16 -0
  74. package/dist/commands/init.js +91 -0
  75. package/dist/commands/reset.d.ts +17 -0
  76. package/dist/commands/reset.js +93 -0
  77. package/dist/help.d.ts +4 -0
  78. package/dist/help.js +31 -0
  79. package/dist/index.d.ts +9 -0
  80. package/dist/index.js +15 -0
  81. package/dist/utils/client.d.ts +8 -0
  82. package/dist/utils/client.js +58 -0
  83. package/dist/utils/config.d.ts +15 -0
  84. package/dist/utils/config.js +1 -0
  85. package/dist/utils/identities.d.ts +49 -0
  86. package/dist/utils/identities.js +92 -0
  87. package/dist/utils/invite.d.ts +70 -0
  88. package/dist/utils/invite.js +339 -0
  89. package/dist/utils/metadata.d.ts +39 -0
  90. package/dist/utils/metadata.js +180 -0
  91. package/dist/utils/mime.d.ts +2 -0
  92. package/dist/utils/mime.js +42 -0
  93. package/dist/utils/random.d.ts +5 -0
  94. package/dist/utils/random.js +19 -0
  95. package/dist/utils/upload.d.ts +14 -0
  96. package/dist/utils/upload.js +51 -0
  97. package/dist/utils/xmtp.d.ts +45 -0
  98. package/dist/utils/xmtp.js +298 -0
  99. package/oclif.manifest.json +5562 -0
  100. package/package.json +124 -0
  101. package/skills/convos-cli/SKILL.md +588 -0
@@ -0,0 +1,588 @@
1
+ ---
2
+ name: convos-cli
3
+ description: Use when working with Convos messaging - privacy-focused ephemeral messaging with per-conversation identities, invites, profiles, and group management via the convos CLI tool
4
+ ---
5
+
6
+ # Convos CLI
7
+
8
+ The Convos CLI (`convos`) is a command-line tool for privacy-focused ephemeral messaging built on [XMTP](https://xmtp.org). Unlike standard XMTP, Convos creates a **unique identity per conversation** so conversations cannot be linked or correlated.
9
+
10
+ Key differences from standard XMTP:
11
+
12
+ - **Per-conversation identities**: Each conversation gets its own wallet, inbox, and database
13
+ - **No global wallet key**: Identities are created automatically when creating/joining conversations
14
+ - **Invite system**: Serverless QR code + URL invites for joining conversations
15
+ - **Per-conversation profiles**: Different display name and avatar in each conversation
16
+ - **Explode**: Permanently destroy a conversation and all cryptographic keys
17
+ - **Lock**: Prevent new members from being added
18
+
19
+ ## Prerequisites
20
+
21
+ ### Initialize Configuration
22
+
23
+ ```bash
24
+ # generate config and save to default path (~/.convos/.env)
25
+ convos init
26
+
27
+ # output config to console instead of writing to file
28
+ convos init --stdout
29
+
30
+ # initialize for production environment
31
+ convos init --env production
32
+
33
+ # overwrite existing config
34
+ convos init --force
35
+ ```
36
+
37
+ This creates a `.env` file with:
38
+
39
+ - `CONVOS_ENV` - Network environment (local, dev, production)
40
+ - `CONVOS_UPLOAD_PROVIDER` - Upload provider for attachments (e.g., `pinata`)
41
+ - `CONVOS_UPLOAD_PROVIDER_TOKEN` - Authentication token for upload provider
42
+ - `CONVOS_UPLOAD_PROVIDER_GATEWAY` - Custom gateway URL for upload provider
43
+
44
+ **Note:** Unlike standard XMTP, there is no global wallet key. Each conversation creates its own identity stored in `~/.convos/identities/`.
45
+
46
+ ### Configuration Loading Priority
47
+
48
+ 1. CLI flags (highest priority)
49
+ 2. Explicit `--env-file <path>`
50
+ 3. `.env` in the current working directory
51
+ 4. `~/.convos/.env` (global default)
52
+
53
+ ## Command Structure
54
+
55
+ ```
56
+ convos [TOPIC] [COMMAND] [ARGUMENTS] [FLAGS]
57
+ ```
58
+
59
+ ### Topics
60
+
61
+ | Topic | Purpose |
62
+ | ----- | ------- |
63
+ | `agent` | Agent mode — long-running sessions with streaming I/O |
64
+ | `identity` | Manage per-conversation identities (inboxes) |
65
+ | `conversations` | List, create, join, and stream conversations |
66
+ | `conversation` | Interact with a specific conversation |
67
+
68
+ ### Standalone Commands
69
+
70
+ | Command | Purpose |
71
+ | ------- | ------- |
72
+ | `init` | Initialize configuration and directory structure |
73
+ | `reset` | Delete all identities and conversation data (preserves .env) |
74
+
75
+ ## Output Modes
76
+
77
+ All commands support `--json` for machine-readable JSON output:
78
+
79
+ ```bash
80
+ convos conversations list --json
81
+ ```
82
+
83
+ Use `--verbose` to see detailed client initialization logs. When combined with `--json`, verbose output goes to stderr:
84
+
85
+ ```bash
86
+ convos identity info <id> --verbose
87
+ convos conversations list --json --verbose 2>/dev/null
88
+ ```
89
+
90
+ ## Common Workflows
91
+
92
+ ### Create a Conversation
93
+
94
+ ```bash
95
+ # create a conversation (auto-creates a per-conversation identity)
96
+ convos conversations create --name "My Group" --profile-name "Alice"
97
+
98
+ # create with admin-only permissions
99
+ convos conversations create --name "Announcement Channel" --permissions admin-only
100
+
101
+ # create and capture the conversation ID
102
+ CONV_ID=$(convos conversations create --name "Test" --json | jq -r '.conversationId')
103
+ ```
104
+
105
+ ### Send Messages
106
+
107
+ ```bash
108
+ # send a text message
109
+ convos conversation send-text <conversation-id> "Hello, world!"
110
+
111
+ # send a reaction
112
+ convos conversation send-reaction <conversation-id> <message-id> add "👍"
113
+ # remove a reaction
114
+ convos conversation send-reaction <conversation-id> <message-id> remove "👍"
115
+
116
+ # send a reply referencing another message
117
+ convos conversation send-reply <conversation-id> <message-id> "Replying to you"
118
+
119
+ # reply with a photo
120
+ convos conversation send-reply <conversation-id> <message-id> --file ./photo.jpg
121
+
122
+ # reply with a large file (auto-uploaded via provider)
123
+ convos conversation send-reply <conversation-id> <message-id> --file ./video.mp4
124
+ ```
125
+
126
+ ### Send Attachments
127
+
128
+ ```bash
129
+ # send a photo (small files ≤1MB sent inline, large files auto-uploaded via provider)
130
+ convos conversation send-attachment <conversation-id> ./photo.jpg
131
+
132
+ # force remote upload even for small files
133
+ convos conversation send-attachment <conversation-id> ./photo.jpg --remote
134
+
135
+ # override MIME type
136
+ convos conversation send-attachment <conversation-id> ./file.bin --mime-type image/png
137
+
138
+ # use upload provider via flags (no .env needed)
139
+ convos conversation send-attachment <conversation-id> ./photo.jpg \
140
+ --upload-provider pinata --upload-provider-token <jwt>
141
+
142
+ # encrypt only — outputs encrypted file + decryption keys for manual upload
143
+ convos conversation send-attachment <conversation-id> ./photo.jpg --encrypt
144
+
145
+ # send a pre-uploaded encrypted file with decryption keys
146
+ convos conversation send-remote-attachment <conversation-id> <url> \
147
+ --content-digest <hex> --secret <base64> --salt <base64> \
148
+ --nonce <base64> --content-length <bytes> --filename photo.jpg
149
+
150
+ # download an attachment (handles both inline and remote transparently)
151
+ convos conversation download-attachment <conversation-id> <message-id>
152
+
153
+ # download to a specific path
154
+ convos conversation download-attachment <conversation-id> <message-id> --output ./photo.jpg
155
+
156
+ # save encrypted payload without decrypting
157
+ convos conversation download-attachment <conversation-id> <message-id> --raw
158
+ ```
159
+
160
+ To enable automatic upload for large files, configure a provider in your `.env`:
161
+
162
+ ```bash
163
+ CONVOS_UPLOAD_PROVIDER=pinata
164
+ CONVOS_UPLOAD_PROVIDER_TOKEN=<your-pinata-jwt>
165
+ # Optional: custom gateway URL
166
+ CONVOS_UPLOAD_PROVIDER_GATEWAY=https://your-gateway.mypinata.cloud
167
+ ```
168
+
169
+ Supported upload providers: `pinata`
170
+
171
+ ### Read Messages
172
+
173
+ ```bash
174
+ # list messages (default: descending order)
175
+ convos conversation messages <conversation-id>
176
+ # sync from network and limit results
177
+ convos conversation messages <conversation-id> --sync --limit 10
178
+ ```
179
+
180
+ ### Stream Messages in Real-Time
181
+
182
+ ```bash
183
+ # stream messages from a single conversation
184
+ convos conversation stream <conversation-id>
185
+ # stop after 60 seconds
186
+ convos conversation stream <conversation-id> --timeout 60
187
+ ```
188
+
189
+ ### List Conversations
190
+
191
+ ```bash
192
+ # list all conversations across all identities
193
+ convos conversations list
194
+ # sync from network before listing
195
+ convos conversations list --sync
196
+ ```
197
+
198
+ ### Invite System
199
+
200
+ Convos uses a serverless invite system. The creator generates a cryptographic invite URL; the person joining must open the URL in the Convos app (or scan the QR code); then the creator processes the join request to add them to the group.
201
+
202
+ **Important: Adding someone to a conversation is a multi-step process:**
203
+
204
+ 1. **Generate an invite** (creator side) — produces a URL and QR code
205
+ 2. **Person opens the invite URL in Convos or scans the QR code** — this sends a join request to the creator via DM
206
+ 3. **Creator processes the join request** — this validates the request and adds the person to the group
207
+
208
+ The creator must process join requests *after* the person has opened/scanned the invite. If you don't know when that will happen, use `--watch` with a timeout to stream and process requests as they arrive.
209
+
210
+ #### Create an Invite
211
+
212
+ ```bash
213
+ # generate invite — displays QR code in terminal
214
+ convos conversation invite <conversation-id>
215
+
216
+ # generate invite with 1-hour expiry
217
+ convos conversation invite <conversation-id> --expires-in 3600
218
+
219
+ # single-use invite
220
+ convos conversation invite <conversation-id> --single-use
221
+
222
+ # JSON output (suppresses QR code)
223
+ convos conversation invite <conversation-id> --json
224
+
225
+ # capture invite URL for scripting
226
+ INVITE_URL=$(convos conversation invite <conversation-id> --json | jq -r '.url')
227
+ ```
228
+
229
+ #### Person Joins via Invite
230
+
231
+ The person being invited must open the invite URL in the Convos app or scan the QR code with Convos. This can be done:
232
+
233
+ - **On iOS**: Open the URL in Safari (redirects to Convos app) or scan the QR code from within the app
234
+ - **Via CLI**: Use `convos conversations join`
235
+
236
+ ```bash
237
+ # join using a raw invite slug
238
+ convos conversations join <invite-slug>
239
+
240
+ # join using a full invite URL
241
+ convos conversations join "https://dev.convos.org/v2?i=<slug>"
242
+
243
+ # join with a display name
244
+ convos conversations join <slug> --profile-name "Bob"
245
+
246
+ # send join request without waiting for acceptance
247
+ convos conversations join <slug> --no-wait
248
+
249
+ # wait up to 2 minutes for acceptance
250
+ convos conversations join <slug> --timeout 120
251
+ ```
252
+
253
+ #### Process Join Requests (Creator Side)
254
+
255
+ After the person has opened/scanned the invite, the creator must process the join request:
256
+
257
+ ```bash
258
+ # process all pending join requests (use when you know the invite has already been opened)
259
+ convos conversations process-join-requests
260
+
261
+ # process for a specific conversation only
262
+ convos conversations process-join-requests --conversation <id>
263
+
264
+ # watch for join requests with a timeout (use when you don't know when the invite will be opened)
265
+ convos conversations process-join-requests --watch --conversation <id>
266
+ # note: use ctrl-c or a timeout to stop watching
267
+
268
+ # continuously watch for all join requests (keep running in background)
269
+ convos conversations process-join-requests --watch
270
+ ```
271
+
272
+ ### Per-Conversation Profiles
273
+
274
+ Each conversation has independent profiles — you can have a different name and avatar in each.
275
+
276
+ ```bash
277
+ # set display name
278
+ convos conversation update-profile <conversation-id> --name "Alice"
279
+
280
+ # set name and avatar
281
+ convos conversation update-profile <conversation-id> --name "Alice" --image "https://example.com/avatar.jpg"
282
+
283
+ # go anonymous (clear profile)
284
+ convos conversation update-profile <conversation-id> --name "" --image ""
285
+
286
+ # view all member profiles
287
+ convos conversation profiles <conversation-id>
288
+ convos conversation profiles <conversation-id> --json
289
+ ```
290
+
291
+ ### Identity Management
292
+
293
+ Identities are created automatically when creating/joining conversations, but you can manage them directly.
294
+
295
+ ```bash
296
+ # list all identities
297
+ convos identity list
298
+
299
+ # create an identity manually
300
+ convos identity create --label "Work Chat" --profile-name "Alice"
301
+
302
+ # view identity details (connects to XMTP to show inbox ID)
303
+ convos identity info <identity-id>
304
+
305
+ # remove an identity (destroys all keys — irreversible)
306
+ convos identity remove <identity-id> --force
307
+ ```
308
+
309
+ ### Reset All Data
310
+
311
+ Delete all identities and conversation data. The `.env` configuration is preserved.
312
+
313
+ ```bash
314
+ # reset with confirmation prompt
315
+ convos reset
316
+
317
+ # reset without confirmation
318
+ convos reset --force
319
+ ```
320
+
321
+ ### Group Management
322
+
323
+ ```bash
324
+ # view members
325
+ convos conversation members <conversation-id>
326
+
327
+ # add members by inbox ID
328
+ convos conversation add-members <conversation-id> <inbox-id>
329
+
330
+ # remove members
331
+ convos conversation remove-members <conversation-id> <inbox-id>
332
+
333
+ # update group name
334
+ convos conversation update-name <conversation-id> "New Name"
335
+
336
+ # update group description
337
+ convos conversation update-description <conversation-id> "New description"
338
+
339
+ # view permissions
340
+ convos conversation permissions <conversation-id>
341
+ ```
342
+
343
+ ### Lock a Conversation
344
+
345
+ Prevent new members from joining by setting the addMember permission to deny. This also invalidates all existing invites. Only super admins can lock/unlock.
346
+
347
+ ```bash
348
+ # lock
349
+ convos conversation lock <conversation-id>
350
+
351
+ # unlock (previously shared invites remain invalid — generate new ones)
352
+ convos conversation lock <conversation-id> --unlock
353
+ ```
354
+
355
+ ### Explode a Conversation
356
+
357
+ Permanently destroy a conversation and all its cryptographic keys. Sends an ExplodeSettings notification to all members (so iOS and other clients can trigger their cleanup), updates group metadata with the expiration timestamp, removes all members, then destroys the local identity. **Irreversible.**
358
+
359
+ ```bash
360
+ # explode immediately
361
+ convos conversation explode <conversation-id> --force
362
+
363
+ # schedule explosion for a future date (ISO8601)
364
+ convos conversation explode <conversation-id> --scheduled "2025-03-01T00:00:00Z"
365
+ ```
366
+
367
+ When scheduled, the ExplodeSettings message is sent with a future `expiresAt` date. Members are notified but not removed — clients handle cleanup when the time arrives. When immediate (no `--scheduled`), members are removed and the local identity is destroyed right away.
368
+
369
+ ### Sync Data from Network
370
+
371
+ ```bash
372
+ # sync conversation list
373
+ convos conversations sync
374
+
375
+ # sync a single conversation
376
+ convos conversation sync <conversation-id>
377
+ ```
378
+
379
+ ## Agent Mode
380
+
381
+ The `agent serve` command runs a long-running process that combines conversation creation, message streaming, join request processing, and stdin command handling — ideal for AI agents and bots.
382
+
383
+ ### Quick Start (Agent)
384
+
385
+ ```bash
386
+ # create a new conversation and start serving
387
+ convos agent serve --name "My Bot" --profile-name "Assistant"
388
+
389
+ # attach to an existing conversation
390
+ convos agent serve <conversation-id>
391
+
392
+ # create with admin-only permissions
393
+ convos agent serve --name "Agent" --permissions admin-only
394
+ ```
395
+
396
+ ### Protocol
397
+
398
+ The agent uses an **ndjson** (newline-delimited JSON) protocol:
399
+
400
+ - **stdout**: Events (one JSON object per line)
401
+ - **stdin**: Commands (one JSON object per line)
402
+ - **stderr**: QR code + diagnostic logs
403
+
404
+ #### Events (stdout)
405
+
406
+ | Event | Description | Key Fields |
407
+ | ----- | ----------- | ---------- |
408
+ | `ready` | Session started | `conversationId`, `inviteUrl`, `inboxId` |
409
+ | `message` | New message received | `id`, `senderInboxId`, `content`, `contentType`, `sentAt` |
410
+ | `member_joined` | Member joined via invite | `inboxId`, `conversationId` |
411
+ | `sent` | Message sent confirmation | `id`, `text`, `replyTo` (optional) |
412
+ | `error` | Error occurred | `message` |
413
+
414
+ #### Commands (stdin)
415
+
416
+ ```jsonl
417
+ {"type":"send","text":"Hello, world!"}
418
+ {"type":"send","text":"Replying to you","replyTo":"<message-id>"}
419
+ {"type":"react","messageId":"<message-id>","emoji":"👍"}
420
+ {"type":"react","messageId":"<message-id>","emoji":"👍","action":"remove"}
421
+ {"type":"attach","file":"./photo.jpg"}
422
+ {"type":"attach","file":"./photo.jpg","replyTo":"<message-id>"}
423
+ {"type":"attach","file":"./photo.jpg","mimeType":"image/jpeg"}
424
+ {"type":"remote-attach","url":"https://...","contentDigest":"<hex>","secret":"<base64>","salt":"<base64>","nonce":"<base64>","contentLength":12345,"filename":"photo.jpg"}
425
+ {"type":"stop"}
426
+ ```
427
+
428
+ | Command | Required Fields | Optional Fields |
429
+ | ------- | --------------- | --------------- |
430
+ | `send` | `text` | `replyTo` |
431
+ | `react` | `messageId`, `emoji` | `action` (`add`/`remove`, default: `add`) |
432
+ | `attach` | `file` (local path) | `mimeType`, `replyTo` |
433
+ | `remote-attach` | `url`, `contentDigest`, `secret`, `salt`, `nonce`, `contentLength` | `filename`, `scheme` |
434
+ | `stop` | — | — |
435
+
436
+ Small attachments (≤1MB) are sent inline. Larger files are auto-encrypted and uploaded via the configured upload provider (e.g., Pinata).
437
+
438
+ ### How It Works
439
+
440
+ When started, `agent serve`:
441
+
442
+ 1. **Creates or attaches** to a conversation
443
+ 2. **Displays QR code** invite on stderr (so users can scan and join)
444
+ 3. **Emits `ready` event** with conversation ID, invite URL, and identity info
445
+ 4. **Processes pending join requests** from before the agent started
446
+ 5. **Streams messages** — emits `message` events as they arrive in real-time
447
+ 6. **Streams DM join requests** — automatically adds new members and emits `member_joined`
448
+ 7. **Reads stdin** — accepts `send` and `stop` commands
449
+
450
+ All of these run concurrently. The agent stays alive until `SIGINT`, `SIGTERM`, stdin close, or a `stop` command.
451
+
452
+ ### Example: Agent Integration
453
+
454
+ ```bash
455
+ # Start the agent, pipe commands in, read events out
456
+ convos agent serve --name "Bot" --profile-name "AI Assistant" | while IFS= read -r event; do
457
+ type=$(echo "$event" | jq -r '.event')
458
+ case "$type" in
459
+ ready)
460
+ echo "Bot ready! Invite URL: $(echo "$event" | jq -r '.inviteUrl')" >&2
461
+ ;;
462
+ message)
463
+ content=$(echo "$event" | jq -r '.content')
464
+ echo "Received: $content" >&2
465
+ # Send a reply (write JSON command to agent's stdin)
466
+ msg_id=$(echo "$event" | jq -r '.id')
467
+ echo "{\"type\":\"send\",\"text\":\"You said: $content\",\"replyTo\":\"$msg_id\"}"
468
+ ;;
469
+ member_joined)
470
+ inbox=$(echo "$event" | jq -r '.inboxId')
471
+ echo "New member: $inbox" >&2
472
+ echo "{\"type\":\"send\",\"text\":\"Welcome!\"}"
473
+ ;;
474
+ esac
475
+ done
476
+ ```
477
+
478
+ ### Agent Flags
479
+
480
+ | Flag | Description |
481
+ | ---- | ----------- |
482
+ | `--name` | Conversation name (when creating new) |
483
+ | `--description` | Conversation description (when creating new) |
484
+ | `--permissions` | `all-members` or `admin-only` (when creating new) |
485
+ | `--profile-name` | Display name for this conversation |
486
+ | `--identity` | Use an existing unlinked identity |
487
+ | `--label` | Local label for the identity |
488
+ | `--no-invite` | Skip generating an invite (attach mode) |
489
+
490
+ ## Important Concepts
491
+
492
+ ### Per-Conversation Identities
493
+
494
+ Every conversation has its own:
495
+
496
+ - **Wallet key** (secp256k1 private key)
497
+ - **DB encryption key** (32-byte key)
498
+ - **XMTP inbox** (unique inbox ID)
499
+ - **Local database** (SQLite)
500
+
501
+ Identities are stored in `~/.convos/identities/<id>.json`. Databases are stored in `~/.convos/db/<env>/<id>.db3`.
502
+
503
+ ### Invite Flow
504
+
505
+ 1. **Creator** generates an invite URL/QR code (contains encrypted conversation token + creator's inbox ID)
506
+ 2. **Person opens the invite URL in Convos app** (or scans the QR code) — this creates a per-conversation identity and sends a DM join request to the creator
507
+ 3. **Creator processes the join request** — validates the invite signature, decrypts the conversation token, and adds the person to the group
508
+ 4. Person is now a member with their own isolated identity
509
+
510
+ **Key point:** Step 3 must happen *after* step 2. The creator must either run `process-join-requests` after the invite has been opened, or use `--watch` to stream and process requests as they arrive.
511
+
512
+ ### Consent States
513
+
514
+ | State | Meaning |
515
+ | ----- | ------- |
516
+ | `allowed` | Messages are welcome |
517
+ | `denied` | Messages are blocked |
518
+ | `unknown` | No decision made |
519
+
520
+ ### Environment Networks
521
+
522
+ | Network | Use Case |
523
+ | ------- | -------- |
524
+ | `local` | Local XMTP node |
525
+ | `dev` | Development/testing (default) |
526
+ | `production` | Production use |
527
+
528
+ ### Data Directory
529
+
530
+ ```
531
+ ~/.convos/
532
+ ├── .env # Global config (env only)
533
+ ├── identities/
534
+ │ ├── <id-1>.json # Identity: wallet key, db key, conversation link
535
+ │ └── <id-2>.json
536
+ └── db/
537
+ └── dev/ # XMTP databases by environment
538
+ ├── <id-1>.db3
539
+ └── <id-2>.db3
540
+ ```
541
+
542
+ ## Error Handling
543
+
544
+ 1. **Not initialized**: Run `convos init` to create configuration
545
+ 2. **No identities**: Create a conversation or identity first
546
+ 3. **Identity not found**: Use `convos identity list` to see available identities
547
+ 4. **Conversation not found**: Sync first with `convos conversations sync`
548
+ 5. **Permission denied**: Check group permissions with `convos conversation permissions`
549
+ 6. **Invite expired or invalid**: Generate a new invite with `convos conversation invite`
550
+
551
+ ## Complete Example
552
+
553
+ ```bash
554
+ # 1. initialize (first time only)
555
+ convos init --env dev
556
+
557
+ # 2. create a conversation
558
+ CONV=$(convos conversations create --name "Project Team" --profile-name "Alice" --json)
559
+ CONV_ID=$(echo "$CONV" | jq -r '.conversationId')
560
+
561
+ # 3. generate an invite for others to join
562
+ convos conversation invite "$CONV_ID"
563
+
564
+ # 4. wait for the person to open the invite URL or scan the QR code,
565
+ # then process their join request
566
+ convos conversations process-join-requests --conversation "$CONV_ID"
567
+
568
+ # OR: if you don't know when they'll open it, watch for requests
569
+ # convos conversations process-join-requests --watch --conversation "$CONV_ID"
570
+
571
+ # 5. send a message
572
+ convos conversation send-text "$CONV_ID" "Welcome to the team!"
573
+
574
+ # 6. stream messages
575
+ convos conversation stream "$CONV_ID" --timeout 300
576
+ ```
577
+
578
+ ## Tips
579
+
580
+ 1. **Always display the full QR code**: The `conversation invite` and `conversations create` commands output a scannable QR code rendered in Unicode block characters followed by the invite URL. When showing the user the result, you **must** display the complete, unmodified command output so the QR code renders correctly in the terminal. Do not summarize, truncate, or omit the QR code — it is the primary way users share invites. Always show the full stdout output to the user. When running `agent serve`, the QR code is saved as a PNG file (path in the `qrCodePath` field of the `ready` event) — display it to the user using the read tool so they can scan it.
581
+ 2. **Never use markdown in messages**: Convos does not render markdown. When sending messages (via `send-text`, `send-reply`, or agent `send` commands), always use plain text. Do not use markdown formatting like `**bold**`, `*italic*`, `# headings`, `` `code` ``, `[links](url)`, or bullet lists with `- ` or `* `. Write naturally in plain text instead.
582
+ 3. **Identities are automatic**: You rarely need to manage them directly — creating/joining conversations handles it
583
+ 4. **Use JSON output for scripting**: Add `--json` flag when extracting data programmatically
584
+ 5. **Sync before reading**: Add `--sync` flag when reading messages to ensure fresh data
585
+ 6. **Process join requests after invite is opened**: After generating an invite, wait for the person to open/scan it, then run `process-join-requests`. If you don't know when they'll open it, use `--watch` to stream requests as they arrive
586
+ 7. **Lock before exploding**: Lock a conversation first to prevent new joins, then explode when ready
587
+ 8. **Dangerous operations require --force**: Commands like `explode`, `identity remove`, and `lock` prompt for confirmation unless `--force` is passed
588
+ 9. **Check command help**: Run `convos <command> --help` for full flag documentation