@heyamiko/amiko-cli 0.14.0-beta.9 → 0.14.2

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 (4) hide show
  1. package/README.md +162 -18
  2. package/dist/index.js +2093 -225
  3. package/package.json +1 -1
  4. package/skills/SKILL.md +126 -16
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.14.0-beta.0)
1
+ # @heyamiko/amiko-cli (v0.14.0-beta.24)
2
2
 
3
3
  Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed) from the terminal. Works for both human users and AI agents running on OpenClaw.
4
4
 
@@ -120,16 +120,21 @@ Image generation typically takes 20–60s. In non-TTY environments (agents, pipe
120
120
 
121
121
  ## Create Studio
122
122
 
123
- The CLI half of the platform's Create Studio. `amiko create` generates media through Amiko's own authenticated endpoints (not the raw MPP pay-per-call surface `amiko markets` uses). Generation runs **asynchronously** and is **charged on success** from your twin's wallet — a failed or timed-out generation is never billed, and there's no up-front payment.
123
+ The CLI half of the platform's Create Studio. `amiko create` generates media through Amiko's own authenticated endpoints (not the raw MPP pay-per-call surface `amiko markets` uses). Generation runs **asynchronously** and is **charged on success** from your twin's wallet — a failed or timed-out generation is never billed, and there's no up-front payment. On unified-billing accounts, `--pay credits` pays from your Amiko account credits instead: the quote is reserved at submit, captured only on success, and released on failure (legacy accounts get a clear server error — omit the flag to keep the twin-wallet default).
124
124
 
125
125
  ```bash
126
126
  amiko create image "a sunset over mountains" # default nano-banana-2, 1:1
127
127
  amiko create image "logo" --aspect 16:9 --model gpt-image-2 # widescreen, OpenAI
128
+ amiko create image "a sunset" --pay credits --yes # pay with Amiko account credits (unified accounts)
128
129
  amiko create video "a whale in space" --resolution 768P --seconds 6
129
130
  amiko create video "animate cover" --first-frame https://.../cover.png --yes
130
131
  amiko create video "dance clip" --model dreamina-seedance-2-0-fast-260128 \
131
132
  --reference-image https://.../ref1.png --reference-audio https://.../beat.mp3 --generate-audio --yes
132
133
  amiko create tts "Hello world" --voice 21m00Tcm4TlvDq8ikWAM
134
+ amiko create tts "Hello world" --provider minimax --model speech-2.8-turbo
135
+ amiko create tts "Hello" --provider minimax --voice English_expressive_narrator --yes
136
+ amiko voice clone ./me.mp3 --provider minimax
137
+ amiko voice clone ./me.mp3 --provider elevenlabs
133
138
  amiko create music "lo-fi chill beat" --duration 30000
134
139
  amiko create music --lyrics "..." --instrumental # instrumental
135
140
  amiko create sfx "thunder and heavy rain" --duration 5
@@ -142,7 +147,8 @@ Modes: `image`, `video`, `tts`, `music`, `sfx` (mirrors the platform Create Stud
142
147
 
143
148
  | Flag | Description |
144
149
  |------|-------------|
145
- | `--token <symbol>` | Charge token: `AMIKO`, `USDC`, `USDT`, `SOL` (default: auto, AMIKO-first) |
150
+ | `--pay <method>` | Payment method: `wallet` (twin wallet, default) or `credits` (Amiko account credits — unified accounts only) |
151
+ | `--token <symbol>` | Wallet charge token: `AMIKO`, `USDC`, `USDT`, `SOL` (default: auto, AMIKO-first); ignored with `--pay credits` |
146
152
  | `--yes` | Skip the pre-spend confirmation (required in non-interactive shells) |
147
153
  | `--raw` | Output the raw job result as JSON |
148
154
 
@@ -157,7 +163,7 @@ Modes: `image`, `video`, `tts`, `music`, `sfx` (mirrors the platform Create Stud
157
163
 
158
164
  | Flag | Default | Description |
159
165
  |------|---------|-------------|
160
- | `--model <model>` | `MiniMax-Hailuo-2.3-Fast` | Video model (`MiniMax-Hailuo-*`, `dreamina-seedance-2-0-fast-260128`, …) |
166
+ | `--model <model>` | smart | Video model (`MiniMax-Hailuo-*`, `dreamina-seedance-2-0-fast-260128`, …). Default: `MiniMax-Hailuo-02` for prompt-only (T2V), `MiniMax-Hailuo-2.3-Fast` when `--first-frame` is given (I2V) |
161
167
  | `--resolution <res>` | `768P` | `512P`, `720P`, `768P`, `1080P` |
162
168
  | `--seconds <n>` | `6` | Clip length: `6` or `10` |
163
169
  | `--aspect <ratio>` | — | Aspect ratio (provider-dependent) |
@@ -176,8 +182,20 @@ Reference flags accept URLs or data URIs (not local paths). Typical I2V flow: `c
176
182
 
177
183
  | Flag | Default | Description |
178
184
  |------|---------|-------------|
179
- | `--model <model>` | `eleven_multilingual_v2` | TTS model id |
180
- | `--voice <voiceId>` | `21m00Tcm4TlvDq8ikWAM` | Voice id |
185
+ | `--provider <name>` | inferred / `elevenlabs` | `minimax` or `elevenlabs` — sets model/voice defaults when those flags are omitted |
186
+ | `--model <model>` | EL: `eleven_multilingual_v2`; MiniMax: `speech-2.8-turbo` | MiniMax: `speech-2.8-turbo\|hd`, `speech-2.6-turbo\|hd`. ElevenLabs: `eleven_*` |
187
+ | `--voice <voiceId>` | EL default hash; MiniMax: `English_expressive_narrator` | MiniMax system or cloned `voice_id`; ElevenLabs voice id |
188
+
189
+ Examples: `amiko create tts "Hi" --provider minimax --yes` · `amiko create tts "Hi" --provider minimax --model speech-2.8-turbo --voice <clonedId> --yes`
190
+
191
+ **`amiko voice clone <source>`** — audio path, https URL, or `-` (stdin)
192
+
193
+ | Flag | Default | Description |
194
+ |------|---------|-------------|
195
+ | `--provider <name>` | `minimax` | `minimax` (mpp rapid clone + ownership registry) or `elevenlabs` (legacy IVC) |
196
+ | `--voice-id <id>` | auto | **MiniMax only** — optional custom voice_id (ignored for elevenlabs) |
197
+ | `--model <model>` | `speech-2.8-turbo` | **MiniMax only** — preview speech model (ignored for elevenlabs) |
198
+ | `--preview-text <text>` | provider default | **MiniMax only** — preview line; empty string skips preview (ignored for elevenlabs) |
181
199
 
182
200
  **`amiko create music [prompt]`** — pass a prompt and/or `--lyrics`
183
201
 
@@ -231,20 +249,37 @@ Your conversations — **DMs and group chats** — acting **as you (the owner)**
231
249
  ```bash
232
250
  amiko chat list # all conversations (DM + group): id, peer/title, last msg, unread
233
251
  amiko chat list --limit 50 --archived # include archived
252
+ amiko chat list --mentions # only chats with an unread @mention of you or a reply to you
234
253
  amiko chat read @sophie # recent messages with a user (by @handle)
235
254
  amiko chat read <conversationId> --limit 40 # by conversation id (from `chat list`) — works for groups too
236
255
  amiko chat send @sophie "on my way!" --yes # send as the owner
237
256
  amiko chat send <userId> "hey!" --yes # user id works too — the DM is opened automatically
238
257
  amiko chat send Mars "hey!" --yes # or a name (friends first, then people search)
239
258
  amiko chat send <groupConversationId> "hi all" --yes # send to a group chat
259
+ amiko chat send "Trip planning" "standup in 5 @all" --yes # @all — notifies every group member
260
+ amiko chat send "Trip planning" "standup in 5" --all --yes # same, via the explicit flag
240
261
  amiko chat send @sophie "look 👀" --image ./cat.png --yes # attach a local image
241
262
  amiko chat send @sophie "made this 🎵" --audio <generated-url> --yes # attach audio (e.g. from `amiko create`)
263
+ amiko chat send @sophie --gif "happy dance" --yes # send the top Klipy GIF for a search (message optional)
264
+ amiko chat gifs "happy dance" # browse GIFs first, then send a specific one:
265
+ amiko chat send @sophie "we did it!" --gif <gifUrl> --yes # attach an exact GIF by its Klipy URL
266
+ amiko chat receipts <messageId> # who read your message + when (id from `chat read`)
267
+ amiko chat pin <messageId> --yes # pin for everyone (id from `chat read`; groups: admins only)
268
+ amiko chat unpin <messageId> --yes # remove a pin for everyone
269
+ amiko chat pinned "Trip planning" # list a conversation's pinned messages, oldest first
270
+ amiko chat mark-read --all --yes # mark EVERY conversation as read (clears all unread badges)
242
271
  ```
243
272
 
244
273
  - **`<target>`** for `read`/`send` is a **conversation id** (from `chat list` — use this for groups), a **user id**, an **`@handle`**, or a **name**. Names resolve against your **friends first**, then people search; if ambiguous, the CLI lists candidates instead of guessing. A cuid that isn't one of your conversations is retried as a user id, so pasting a user id "just works".
245
274
  - `send` to a person you have no DM with yet **creates the DM automatically** (find-or-create, after the confirmation). `read` never creates one — it errors if no DM exists yet.
246
- - **Inline media**: `--image <pathOrUrl>` (jpg/png/webp/gif, local ≤10MB or an Amiko URL) and `--audio <pathOrUrl>` (local ≤25MB or an Amiko URL → sent as a voice note). One media block per message (image *or* audio). **Video isn't supported in chat yet.** A local file is uploaded; an Amiko URL (e.g. a generated asset from `amiko create`) attaches directly with no re-upload. (A local *audio* file is sent as a voice note, with your text as a separate message.)
275
+ - **Unread mentions & replies**: rows whose unread messages **concern you directly** get an `@you` marker next to the unread count, and `chat list --mentions` filters to just those. The flag is server-computed over your unread messages and covers three cases: a direct **@mention** of you, a permitted **@all** in a group, and a **reply to one of your messages**. Composes with `--limit`/`--archived`/`--raw` (the raw payload is filtered too). Requires the amiko-web `has_unread_mention` deploy. On an older server the field is absent and reads as **false for every conversation**: plain `chat list` still shows unread counts, just without `@you`, and `chat list --mentions` returns no matches.
276
+ - **Inline media**: `--image <pathOrUrl>` (jpg/png/webp/gif, local ≤10MB or an Amiko URL), `--audio <pathOrUrl>` (local ≤25MB or an Amiko URL → sent as a voice note), and `--gif <queryOrUrl>` (search words send the **top Klipy result**; or pass an exact Klipy URL from `amiko chat gifs` — non-Klipy URLs are refused; to send another image, use `--image` with a local file or an Amiko URL). One media block per message (image *or* audio *or* gif). **General video files aren't supported in chat yet.** A local file is uploaded; an Amiko URL (e.g. a generated asset from `amiko create`) attaches directly with no re-upload. (A local *audio* file is sent as a voice note, with your text as a separate message.)
277
+ - **GIFs**: `amiko chat gifs [query]` lists Klipy GIFs (trending with no query; `--page`/`--limit`/`--raw`) — read-only and ungated, powered by KLIPY. The message text is optional with `--gif` (a captionless GIF sends as just the GIF); a caption renders under the GIF on every client.
278
+ - **@all in groups**: `--all` prepends an @all mention, and a standalone `@all` word typed in the message converts too — either way **every member** is notified. Group **admins/owners** can always use it; everyone else only after an admin runs `amiko chat group mention-all <group> on`. Without permission, `--all` fails with the fix named, while a typed `@all` is delivered as plain text (with a note). Incoming mention markup renders as plain `@name`/`@all` in `chat read` and `chat list`.
247
279
  - `chat send` messages a real person, so it's **gated on `--yes`** in non-interactive shells. Delivery is real-time. `--raw` prints raw JSON on any subcommand.
280
+ - **`chat receipts <messageId>`** shows read receipts for **your own** messages (server-enforced; 403 otherwise): sent time, who read it with each person's first-read time, and who it was delivered to but hasn't read. Reads from before receipts tracking show as "exact time unknown". Read-only, ungated.
281
+ - **Pinned messages**: `chat pin <messageId>` pins for the **whole conversation** (the chat sees an "X pinned a message" announcement) and `chat unpin <messageId>` removes it for everyone — both gated on `--yes`. In **groups** only admins can pin/unpin (403 otherwise); in DMs either side can. Max 20 pins per conversation. `chat pinned <target>` (same targets as `read`; never creates a DM) lists them oldest-first with each message's id and pinner — read-only, ungated.
282
+ - **Mark all read**: `chat mark-read --all` marks every conversation as read in one call — unread badges clear on all your devices, your chat/mention notifications clear, and senders see read ticks on their messages. Irreversible (there is no "mark unread"), so it's gated on `--yes` in non-interactive shells. `--all` is required (mirrors `notifications read --all`); `--raw` prints the server payload.
248
283
 
249
284
  ### Group chats
250
285
 
@@ -258,13 +293,32 @@ amiko chat group rename <groupIdOrTitle> "New name" --yes
258
293
  amiko chat group add <groupIdOrTitle> --member @sophie --yes
259
294
  amiko chat group remove <groupIdOrTitle> --member Mars --yes
260
295
  amiko chat group promote <groupIdOrTitle> --member Sophie --yes # make a member an admin
296
+ amiko chat group mention-all <groupIdOrTitle> on --yes # let everyone use @all (off = admins only, the default)
261
297
  amiko chat group leave <groupIdOrTitle> --yes # alias: delete — see caveat below
262
298
  ```
263
299
 
264
300
  - **`--member`** is repeatable and accepts a plain name, `@handle`, or user id. Plain names resolve against your **friends** first (then people search); an ambiguous name errors listing candidates instead of guessing, and one unresolvable member aborts the whole command **before anything is changed**.
265
301
  - **Groups address by id or title** — titles match case-insensitively (exact first, then substring) across your most recent 100 conversations; ambiguous titles error with candidates. `chat send "<group title>" "hi" --yes` also works when exactly one group title matches.
266
- - **`leave` (alias `delete`) only hides the group for you.** Other members keep the conversation and its history — the platform has no delete-for-everyone. Rename, add, remove-others, and promote are **admin-only** (the creator is admin); the server enforces this.
267
- - `create`/`add` are outward social actions and `rename`/`remove`/`leave`/`promote` are destructive, so all are **gated on `--yes`** in non-interactive shells. `--raw` prints JSON everywhere.
302
+ - **`leave` (alias `delete`) only hides the group for you.** Other members keep the conversation and its history — the platform has no delete-for-everyone. Rename, add, remove-others, promote, and the @all toggle are **admin-only** (the creator is admin); the server enforces this.
303
+ - **`mention-all <group> <on|off>`** controls who may @all in the group: `on` lets every member, `off` (the default) restricts it to admins. The current setting shows in `amiko chat group info` as `@all mentions: everyone | admins only`.
304
+ - `create`/`add` are outward social actions and `rename`/`remove`/`leave`/`promote`/`mention-all` are destructive, so all are **gated on `--yes`** in non-interactive shells. `--raw` prints JSON everywhere.
305
+
306
+ ### Chat lists
307
+
308
+ Your **chat lists** — private folders of conversations (the same lists as the app sidebar). Only you ever see them; putting a chat in a list notifies nobody and changes nothing about the conversation itself.
309
+
310
+ ```bash
311
+ amiko chat lists # every list with its conversations (names resolved)
312
+ amiko chat lists create "Work" --conversation "Trip planning" --conversation @sophie --yes
313
+ amiko chat lists rename "Work" "Focus" --yes
314
+ amiko chat lists add "Focus" --conversation <conversationId> --yes
315
+ amiko chat lists remove "Focus" --conversation @sophie --yes
316
+ amiko chat lists delete "Focus" --yes # the conversations themselves are untouched
317
+ ```
318
+
319
+ - **`<list>`** is a list id or name (case-insensitive; exact beats substring; ambiguity errors listing candidates). **`--conversation`** is repeatable and takes the same targets as `chat send` — conversation id, user id, `@handle`, group title, or name — except a person must already have a DM with you: organizing lists **never creates conversations**.
320
+ - The server stores a list as a plain conversation-id array and `PATCH` replaces it wholesale, so `add`/`remove` read-modify-write the full array — existing entries always survive an `add`, and one unresolvable `--conversation` aborts the whole command before anything is written.
321
+ - Lists are private, but mutations still take the `--yes` gate in non-interactive shells (they reshape your chat sidebar on every device). The bare listing is an ungated read; `--raw` prints JSON everywhere.
268
322
 
269
323
  ## Twin Cards
270
324
 
@@ -343,8 +397,11 @@ amiko markets service call POST /v1/sfx '{"text":"Thunder with heavy rain","dura
343
397
  # Music generation ($0.10) — returns { url, format, service }
344
398
  amiko markets service call POST /v1/music '{"prompt":"Lo-fi chill beat","music_length_ms":30000}'
345
399
 
346
- # Text-to-speech (1 AMIKO) — returns { url, format, service }
400
+ # Text-to-speech — ElevenLabs voice id (default)
347
401
  amiko markets service tts 21m00Tcm4TlvDq8ikWAM "Hello world"
402
+ # MiniMax system or cloned voice_id
403
+ amiko markets service tts English_expressive_narrator "Hello" --provider minimax --yes
404
+ amiko markets service tts <clonedId> "Hello" --provider minimax --model speech-2.8-turbo --yes
348
405
 
349
406
  # Composition plan ($0.02)
350
407
  amiko markets service call POST /v1/music/plan '{"prompt":"Epic orchestral piece"}'
@@ -476,8 +533,8 @@ src/
476
533
  │ ├── voice.ts # voice design, voice create, voice clone, voice reset
477
534
  │ ├── avatar.ts # avatar update
478
535
  │ ├── friends.ts # friends list, requests, add, accept, remove, matches, reports
479
- │ ├── users.ts # users search, profile
480
- │ └── feed.ts # feed, post create, post comment
536
+ │ ├── users.ts # users search, profile, follow/unfollow/followers/following
537
+ │ └── feed.ts # feed, post create/drafts/publish, post comment
481
538
  └── lib/
482
539
  ├── config.ts # defaults + resolved auth wrapper
483
540
  ├── agent-config.ts # .amiko.json reader + auth resolution
@@ -519,32 +576,49 @@ amiko voice reset --yes
519
576
  amiko avatar update --file ./portrait.png --yes
520
577
 
521
578
  # Friends
522
- amiko friends list # list
579
+ amiko friends list # ENTIRE friend list + exact total, in one call
523
580
  amiko friends requests # pending requests (incoming + outgoing)
524
581
  amiko friends add --id <userId>
525
582
  amiko friends accept <friendshipId>
526
583
  amiko friends remove <friendshipId> --yes
584
+ amiko friends nickname # every private nickname you've set
585
+ amiko friends nickname set Sophie "Soph" --yes # set/change (name, @handle, user id, or friendship id)
586
+ amiko friends nickname remove Sophie --yes # clear it
527
587
  amiko friends matches # personality match candidates
528
588
  amiko friends reports list # friend matching reports
529
589
  amiko friends reports view <reportId>
530
590
 
531
591
  # Users
532
- amiko users search <query> # find users by name/handle
592
+ amiko users search <query> # find users by name/handle (one page, --limit default 10)
593
+ amiko users search <query> --all # every match, for "how many people match X"
533
594
  amiko users profile <handle> # public profile
595
+ amiko users follow <handleOrId> # follow (one-way, no approval); unfollow to undo
596
+ amiko users follow-status <handleOrId> # do I follow them / do they follow me
597
+ amiko users followers <handleOrId> # who follows them (--limit 1-50, --cursor)
598
+ amiko users following <handleOrId> # who they follow
534
599
 
535
- # Feed & posts
600
+ # Feed & posts (a.k.a. notes)
536
601
  amiko feed # friends feed (default)
537
- amiko feed --type for_you --limit 20
538
- amiko post create --content "hello from the CLI" # public post
602
+ amiko feed --type all --limit 20 # the "All" tab (a.k.a. for_you)
603
+ amiko feed --type following # posts from accounts you follow
604
+ amiko feed --type media --kind image # site-wide public media (hits /api/media/feed)
605
+ amiko post create --content "hello from the CLI" # public post — prints the canonical share URL (post_url in --json); share that verbatim, never hand-build one from the id
606
+ amiko post create --title "Kyoto, 6am" --media ./shot.webp # image-only note — --content is optional when media/docs are attached
607
+ amiko post create --title "Q3 report" --doc ./q3.pdf # document note — rendered as a file card
539
608
  amiko post create --content "private note" --visibility private
540
609
  amiko post create --content "look" --media https://...jpg
610
+ amiko post create --title "the track" --media drive:<docId> # media already in the Drive (id from `amiko drive list`) — works for audio/video too
611
+ amiko post create --content "wip idea" --draft # save a draft — no share URL until published
612
+ amiko post drafts # list draft posts (--limit/--offset/--json)
613
+ amiko post likers <postId> # who liked one of YOUR posts (403 on someone else's)
614
+ amiko post publish <postId> # publish a draft (fresh timestamp, prints the share URL)
541
615
  amiko post comment --id <postId> --comment "great post"
542
616
  amiko post comment --id <postId> --comment "nice!" --media https://...jpg
543
617
  ```
544
618
 
545
619
  Every new command accepts `--json` for structured output and `--twin <id>` where a target twin is needed.
546
620
 
547
- **Destructive or identity-changing commands** (`docs delete`, `friends remove`, `voice reset`, `avatar update`, `twin update --public`) prompt for confirmation in a TTY and require `--yes` in non-interactive shells.
621
+ **Destructive or identity-changing commands** (`docs delete`, `friends remove`, `friends nickname set/remove`, `voice reset`, `avatar update`, `twin update --public`) prompt for confirmation in a TTY and require `--yes` in non-interactive shells.
548
622
 
549
623
  ## API Endpoints
550
624
 
@@ -562,6 +636,76 @@ npm publish
562
636
 
563
637
  ## Changelog
564
638
 
639
+ ### 0.14.2
640
+
641
+ - **`--media drive:<docId>`** (new source for `post create` and `post comment`): attach a media file already in the twin's Drive — an upload, or a Create Studio generation, which `saveCreationToDrive` mirrors into the "Create Studio Files" folder. The id comes from `amiko drive list --json`. This closes the gap where the only way to post an *audio or video* file the twin already owned was to have kept the original `amiko create` URL: `--media` takes local paths for images only, and a Drive signed URL expires.
642
+ - **No amiko-web change was needed, and this is why.** A Drive doc's `file_url` is stored in *public* form (`…/storage/v1/object/public/docs/<path>`) by all three write paths (`uploadDocumentToSupabase`, `createDocSignedUpload`, `saveCreationToDrive`) even though the `docs` bucket is private. `/api/posts` accepts it (`isAllowedPostMediaUrl` checks host + `/storage/v1/object/` only), then `ensurePostMediaUrl` sees a non-`post-media` bucket and does a **service-role server-side copy into `post-media`**, returning a durable URL — so the post never links the private object and never expires. If that copy fails, the fallback reachability probe on a private-bucket URL 4xxs and the route rejects the post rather than storing a dead link. This mirrors what amiko-web's own `DriveMediaPicker` already does (it forwards the same `file_url` under the same MIME allowlist), so the CLI is matching shipped behaviour, not inventing a path.
643
+ - **The drive ref honours the command's `--twin`.** Docs are scoped by `twin_id`; `resolveMediaUrls` now takes the caller's `--twin` instead of re-resolving the default twin, so `post comment --twin <other> --media drive:<id>` no longer 404s on an id `drive list --twin <other>` had just printed.
644
+ - **Two guards on the resolved row.** A `file_url` that isn't Amiko public storage is refused locally — only `source`-tagged Doc rows are pinned to the twin's storage namespace server-side, so an ordinary row can hold any URL, and letting it through produced a `/api/posts` 400 naming neither the ref nor the doc. And the image/audio/video MIME check falls back to the **filename extension** when `file_type` is `application/octet-stream`: Create Studio stores `assetMime || "application/octet-stream"` and the job's `mime_type` is nullable, so a real mp3 can arrive typeless — refusing it with "attach it with `--doc`" would be wrong advice about an attachable file.
645
+ - **SKILL.md**: `--media` now documents its three sources (local image path / Amiko-hosted URL / `drive:<docId>`), says a Drive **signed URL** and a `/d/<slug>` share link are both rejected, and notes that a `drive:` ref pointing at a pdf belongs on `--doc`. New Examples row for "post the audio/image from my Drive".
646
+
647
+ ### 0.14.1
648
+
649
+ - **`amiko post likers <postId>`** (new): lists everyone who liked a post, paging `GET /api/posts/[id]/likes` to completeness so "who / which friends liked this" is answered from the whole set. Cross-reference the printed user ids against `amiko friends list` for the "which *friends*" variant. The route is **owner-only** (`post.user_id !== user.id` → 403), so the command says so in `--help` and turns the server's generic 403 into a message naming the rule — an agent shouldn't read it as a broken login and retry.
650
+ - **`amiko users search --all`** (new flag). Search stays a one-page lookup by default and `--limit` works again; `--all` is what pages through every match, for "how many people match X". Full paging is capped at 200 (10 requests at the server's 20-row page limit) rather than the shared 2000, because search is ranked recall — nobody needs the 2000th match, and a broad query shouldn't cost a hundred round trips.
651
+ - **`amiko friends list` reports the server's exact total.** `/api/friends` without `limit` already returns the whole list *and* an authoritative `pagination.total` in one request; the header count now comes from `total` instead of the row count. An earlier attempt to paginate this command was reverted — it added ~19 requests, capped the set at 2000, and replaced an exact server-side number with an approximation.
652
+ - **New `src/lib/paginate.ts`**: `fetchAllCursor` follows `hasMore`/`nextCursor` to completeness behind a safety cap, returning `{ items, partial }`. Exhaustion is checked *before* the cap so a set that ends exactly on the cap reports complete — flagging it `partial` would be the same lie about a count, inverted. When the cap really is hit, callers print `⚠ Partial results` and JSON carries `"partial": true` with an `N+` count.
653
+ - **SKILL.md**: new top-level **"Counts & full lists — never answer from the first page"** section (promoted out of Friends, since two of its three commands aren't friends commands) with a question → command → where-the-number-comes-from table; `post likers`'s owner-only rule; the `users search` row in the "Who should I meet?" table now notes one-page-by-default vs `--all`; Examples table gains "谁给我点赞了" and "我有多少好友".
654
+
655
+ ### 0.14.0-beta.25
656
+
657
+ - **Notes parity: `amiko post create --title` and `--doc`.** amiko-web's Notes composer (the 小红书-style card grid — same `Post` rows, new name) writes fields the CLI had no way to set. `--title <text>` sets the card heading (max 150 chars, counted in **grapheme clusters** to match the server's `Intl.Segmenter`, validated before any request; omitted from the body when unset so older amiko-web deploys still accept the payload). `--doc <pathOrUrl...>` attaches non-media documents that render as downloadable file cards: local paths upload to the public `docs` bucket via `POST /api/upload/post-doc` (≤4 MB) or presigned `POST /api/upload/post-doc/sign` + direct PUT (>4 MB, 50 MB cap — the multipart route dies at the ~4.5 MB serverless body limit), https URLs pass through, and media MIME types are refused with a pointer to `--media`. Max 8 documents — the server silently truncates past that, so the CLI errors instead. `--content` is no longer a `requiredOption`: with `--media` or `--doc` attached the body may be empty (an image-only note is normal), but a post with nothing in it — including `--title` alone — is still rejected client-side, mirroring the server's rule. `feed` and `post drafts` now render titles and a document count.
658
+ - **Follows: `amiko users follow / unfollow / follow-status / followers / following`.** Wraps `POST|DELETE|GET /api/users/<handleOrId>/follow` and the two list routes (`--limit` 1–50, `--cursor`; the CLI rejects a larger limit rather than letting the server silently clamp). Every slot takes a **handle or a user id**. Following is one-way and takes effect immediately — distinct from `amiko friends add`, which is a mutual request the other side must accept; the SKILL now carries a comparison table so agents stop conflating them.
659
+ - **`amiko feed --type` now mirrors the app's tabs: `all` / `following` / `media`** (plus `friends`, and `humans` / `amikos`). `all` is the UI's name for the API's `for_you`, so both spellings are accepted and normalized on the way out. The server silently degrades an unrecognized type to `for_you`, which reads as "the filter worked" when it didn't — the CLI validates up front and exits with the allowed list rather than issuing a request. `media` is the trap that rule was written for: `/api/feed?type=media` degrades to `for_you`, because the Media tab is served by a **different endpoint** (`/api/media/feed`, site-wide public creations with a different response shape) — so `--type media` routes there instead, with `--kind image|audio|video` for that endpoint's own filter (named `--kind` because its wire param is also called `type`).
660
+ - **SKILL.md**: notes/posts are the same surface; a note is a card so lead with `--title`; image- and document-only notes need no body; documents go through `--doc`, never `amiko drive upload`; **@-mentions must be written `@[Name](userId)`** — a bare `@handle` produces no mention and no notification; quoting a post means pasting its canonical URL into `--content`; follow-vs-friend routing table; which `--type` answers which question.
661
+
662
+ ### 0.14.0-beta.24
663
+
664
+ - **Private friend nicknames: `amiko friends nickname`.** Manage the owner's private nicknames for friends against amiko-web `/api/friends/{friendshipId}/nickname`: bare `nickname` (or `nickname list`) pages the whole friends list and shows every nickname set, `set <friend> "<nickname>"` adds or changes one (max 50 characters, validated client-side before any request), `remove <friend>` clears it (soft no-op when none is set). `<friend>` resolves against accepted user friends only — by name, `@handle`, user id, friendship id, or the current nickname — with ambiguity reported, never guessed. Mutations are `--yes`-gated (private resource, plain confirm); `amiko friends list` now also renders a NICKNAME column. **Requires the companion amiko-web deploy (`feat/friend-nicknames`)** — on an older server, reads work but `set`/`remove` return 403 (the CLI prints the deploy hint).
665
+
666
+ ### 0.14.0-beta.23
667
+
668
+ - **Chat lists: `amiko chat lists`.** View and manage the owner's private conversation folders against amiko-web `/api/chat-lists`: bare `lists` renders every list with conversation names resolved, plus `create` / `rename` / `add` / `remove` / `delete` subcommands (all `--yes`-gated). The server stores a wholesale `conversation_ids` array, so `add`/`remove` read the current array, mutate locally, and PATCH it back — existing entries always survive an `add`. `--conversation` targets resolve like `chat send` (id / user id / `@handle` / group title / name) but map people to their **existing** DM only — list organization never creates a conversation.
669
+ - **Unread mentions in `chat list`: `@you` marker + `--mentions` filter.** Conversations whose unread messages contain a direct @mention of the owner, a permitted @all, or a **reply to one of the owner's messages** now render `(N unread, @you)`, and `chat list --mentions` keeps only those rows (the `--raw` payload is filtered too, and the flag is ignored on fully read conversations). Driven by the server-computed `has_unread_mention` field; requires the companion amiko-web deploy (`leandrogavidia/global-quick-fixes`) — on an older server the field is absent and reads as false for every conversation, so plain `chat list` still shows unread counts without `@you` and `--mentions` returns no matches. Also moves `resolveConversation`/`findDmByUserId` from `chat.ts` into `lib/conversations.ts` so `chat lists` can reuse them.
670
+
671
+ ### 0.14.0-beta.22
672
+
673
+ - **Mark all conversations read: `amiko chat mark-read --all`.** Hits `POST /api/conversations/read-all` as the owner — bulk monotonic `last_read_at` update across every active membership, clears the owner's unread chat/mention notifications (count echoed as "N chat notifications cleared"), busts the per-user conversation cache, and broadcasts read ticks to peers in up to 50 most-recently-active unread conversations. Requires `--all` explicitly (mirrors `notifications read --all`) and sits behind the destructive `--yes` gate — irreversible, and senders see read ticks. `--raw` prints the server payload; success copy never counts conversations (the response's id list is capped at 50). Requires the companion amiko-web read-all deploy (`leandrogavidia/global-quick-fixes`).
674
+
675
+ ### 0.14.0-beta.21
676
+
677
+ - **Chat GIFs: `amiko chat gifs [query]` + `amiko chat send --gif <queryOrUrl>`.** `chat gifs` browses Klipy GIFs via the platform proxy (`GET /api/gifs`; trending with no query, `--page`/`--limit`/`--raw`, "Powered by KLIPY") — read-only and ungated. `chat send --gif` attaches a GIF to a message: search words send the top result, a Klipy URL (from `chat gifs`) attaches exactly, and non-Klipy URLs are refused. Sends use the cross-client GIF contract — Klipy mp4/webm → `message_type: "video"` + `metadata.videos`, `.gif` → `"image"` + `metadata.images`, each record `{url, name, contentType, kind: "gif", width?, height?}` — so web/desktop/mobile render a looping watermarked GIF. The `<message>` positional is now optional when `--gif` is present (`content` falls back to `"GIF"`); captions ride as `metadata.image_caption`, which the `--image` path now also sets so image captions actually display in bubbles. One media block per message (`--image`/`--audio`/`--gif` are mutually exclusive). Requires the companion amiko-web `feat/chat-gif-picker` deploy (`GET /api/gifs`) — the twin token already authenticates against it, so no other backend work is needed.
678
+
679
+ ### 0.14.0-beta.20
680
+
681
+ - **Draft posts: `amiko post create --draft`, `amiko post drafts`, `amiko post publish <postId>`.** `--draft` saves via `POST /api/posts { status: "draft" }` — no side effects fire and no share URL is printed (drafts 404 by id until published; success copy says "Draft saved" with a publish hint, and `post_url` is omitted from `--json`). `drafts` lists the owner's draft posts (`GET /api/posts?status=draft`, offset pagination via `--limit`/`--offset`; owner-wide across all the owner's twins). `publish` PATCHes `{ status: "published" }`, stamps a fresh timestamp, fires mention/hashtag notifications, and prints the canonical share URL; a 404 points at `amiko post drafts` since drafts can't be fetched by id. Requires the amiko-web draft-posts API (`feat/draft-posts`).
682
+
683
+ ### 0.14.0-beta.19
684
+
685
+ - **Pinned messages: `amiko chat pin <messageId>`, `amiko chat unpin <messageId>`, `amiko chat pinned <target>`.** Pins are conversation-wide: `pin` posts an "X pinned a message" announcement to the whole chat (POST `/api/messages/{id}/pin`), so it sits behind the outward-action `--yes` gate with a content preview; `unpin` (DELETE, silent but removes shared state for everyone) sits behind the destructive gate. Groups are admin-gated server-side — a 403 names the rule and the `chat group info` check; DMs let either side pin. 409 at the 20-pin cap and 400 for unpinnable (system/in-flight) messages get status-specific copy. `pinned <target>` is an ungated read using the same target resolution as `chat read` (conversation id, user id, `@handle`, or name; never creates a DM), listing pins oldest-first with sender, message id, and pinner name; `--raw` on all three. Requires the companion amiko-web `feat/chat-pinned-messages` deploy.
686
+
687
+ ### 0.14.0-beta.18
688
+
689
+ - **Read receipts: `amiko chat receipts <messageId>`.** Shows when the message was sent, who has read it (per-person first-read time), and who it was delivered to but hasn't read yet. Sender-only, enforced server-side — works for messages sent as the owner (`chat send`); anything else 403s with a clear message. Reader names resolve from the conversation's participant list (short-id fallback). Read-only and ungated, like `chat reactions`; `--raw` prints the API payload. Rows read before receipts tracking existed render as "exact time unknown". Requires the companion amiko-web `feat/chat-read-receipts` deploy (`GET /api/messages/{id}/receipts`).
690
+
691
+ ### 0.14.0-beta.17
692
+
693
+ - **@all mentions in group chats + `amiko chat group mention-all <group> <on|off>`.** `chat send` gains `--all`, and a standalone literal `@all` word in the message converts too — both emit the canonical sentinel `@[all](all:all)` (kept in sync with amiko-web `src/utils/mentions.ts`), which amiko-chat expands into a mention notification for every group member. The CLI pre-checks permission client-side, mirroring the web gate plus the server-honored `owner` role (`admin`/`owner`, or the conversation's `allow_everyone_mention_all` flag; `left_at` participants excluded): a denied `--all` exits 1 naming the fix, before the approval gate and before any DM could be created, while a denied literal `@all` degrades to plain text with a note — the server treats an unauthorized token as inert either way. The new `chat group mention-all` toggle PATCHes `/api/conversations/{id}/mention-all-permission` (admin-gated server-side, 403 → admin tip) behind `confirmDestructive`, and the current setting shows in `chat group info` (`@all mentions: everyone | admins only`). Incoming mention markup (`@[Name](id:user|agent)` / `@[all](all:all)`) now renders as plain `@Name`/`@all` in `chat read`, `chat list`, and `chat group list` previews (new `src/lib/mentions.ts`). Requires the companion amiko-web `feat/group-mention-all` deploy.
694
+
695
+ ### 0.14.0-beta.15
696
+
697
+ - Create Studio defaults to `--pay credits` (account credits) instead of twin wallet; pass `--pay wallet` for on-chain.
698
+
699
+ ### 0.14.0-beta.14
700
+
701
+ - `credits balance` prefers `AMIKO_API_KEY` (`amk_`) → `billing.heyamiko.com/api/credits` directly; falls back to twin-token platform proxy. Override base with `AMIKO_BILLING_URL`.
702
+
703
+ ### 0.14.0-beta.13
704
+
705
+ - **Create Studio TTS + voice clone: MiniMax.** `amiko create tts` accepts `--provider minimax|elevenlabs` (infers from `speech-*` / `eleven_*` models when omitted), with provider-specific defaults (`speech-2.8-turbo` + `English_expressive_narrator` vs `eleven_multilingual_v2` + Rachel). Quote body includes `voiceId`. Cloned-voice examples require `--provider minimax` when using a MiniMax `voice_id`.
706
+ - **`amiko voice clone`** defaults to MiniMax (mpp rapid clone + ownership registry); `--provider elevenlabs` keeps legacy IVC. MiniMax-only flags: `--voice-id`, `--model`, `--preview-text`.
707
+ - **`amiko markets service tts`** gains `--provider minimax|elevenlabs` and `--model` for MiniMax speech routing on `/v1/tts/:voiceId`.
708
+
565
709
  ### 0.14.0-beta.7
566
710
 
567
711
  - **`amiko chat group invite <group>` — share a group's invite/join link + QR.** New read-only subcommand that fetches `GET /api/conversations/{id}/invite` (twin token) and prints the join link plus a scannable QR image URL. Admins-only: SKILL.md instructs the agent to confirm the asker holds `admin`/`owner` (via `amiko chat group info`) before sharing, and the endpoint is admin-gated server-side (403 → admin tip). Fails loudly if the response is missing its link/QR instead of printing a placeholder. Requires the companion amiko-web change (adds `qrUrl` to the invite response + a public `GET /api/conversations/join/[token]/qr` PNG route).