@heyamiko/amiko-cli 0.14.0-beta.23 → 0.14.0-beta.24
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 +9 -2
- package/dist/index.js +195 -1
- package/package.json +1 -1
- package/skills/SKILL.md +13 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @heyamiko/amiko-cli (v0.14.0-beta.
|
|
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
|
|
|
@@ -581,6 +581,9 @@ amiko friends requests # pending requests (incoming + outgoing)
|
|
|
581
581
|
amiko friends add --id <userId>
|
|
582
582
|
amiko friends accept <friendshipId>
|
|
583
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
|
|
584
587
|
amiko friends matches # personality match candidates
|
|
585
588
|
amiko friends reports list # friend matching reports
|
|
586
589
|
amiko friends reports view <reportId>
|
|
@@ -604,7 +607,7 @@ amiko post comment --id <postId> --comment "nice!" --media https://...jpg
|
|
|
604
607
|
|
|
605
608
|
Every new command accepts `--json` for structured output and `--twin <id>` where a target twin is needed.
|
|
606
609
|
|
|
607
|
-
**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.
|
|
610
|
+
**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.
|
|
608
611
|
|
|
609
612
|
## API Endpoints
|
|
610
613
|
|
|
@@ -622,6 +625,10 @@ npm publish
|
|
|
622
625
|
|
|
623
626
|
## Changelog
|
|
624
627
|
|
|
628
|
+
### 0.14.0-beta.24
|
|
629
|
+
|
|
630
|
+
- **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).
|
|
631
|
+
|
|
625
632
|
### 0.14.0-beta.23
|
|
626
633
|
|
|
627
634
|
- **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.
|
package/dist/index.js
CHANGED
|
@@ -30449,6 +30449,196 @@ async function mppFriendReportGenerate(auth, body, opts = {}) {
|
|
|
30449
30449
|
}
|
|
30450
30450
|
}
|
|
30451
30451
|
|
|
30452
|
+
// src/lib/nickname.ts
|
|
30453
|
+
var NICKNAME_MAX_CODE_POINTS = 50;
|
|
30454
|
+
var CONTROL_CHARS = /\p{Cc}/u;
|
|
30455
|
+
function validateNickname(input) {
|
|
30456
|
+
if (CONTROL_CHARS.test(input)) {
|
|
30457
|
+
return {
|
|
30458
|
+
ok: false,
|
|
30459
|
+
error: "invalid_characters",
|
|
30460
|
+
message: "Nickname contains unsupported characters (control characters are not allowed)."
|
|
30461
|
+
};
|
|
30462
|
+
}
|
|
30463
|
+
const trimmed = input.trim();
|
|
30464
|
+
if (!trimmed) {
|
|
30465
|
+
return { ok: false, error: "empty", message: "Nickname is empty." };
|
|
30466
|
+
}
|
|
30467
|
+
if ([...trimmed].length > NICKNAME_MAX_CODE_POINTS) {
|
|
30468
|
+
return {
|
|
30469
|
+
ok: false,
|
|
30470
|
+
error: "too_long",
|
|
30471
|
+
message: `Nickname is too long (max ${NICKNAME_MAX_CODE_POINTS} characters).`
|
|
30472
|
+
};
|
|
30473
|
+
}
|
|
30474
|
+
return { ok: true, value: trimmed };
|
|
30475
|
+
}
|
|
30476
|
+
|
|
30477
|
+
// src/commands/friends-nickname.ts
|
|
30478
|
+
function exitWithError3(e5, fallback2) {
|
|
30479
|
+
console.error(error(e5 instanceof Error ? e5.message : fallback2));
|
|
30480
|
+
process.exit(1);
|
|
30481
|
+
}
|
|
30482
|
+
function friendLabel(f) {
|
|
30483
|
+
const name = f.friend.name ?? f.friend.id;
|
|
30484
|
+
return f.friend.handle ? `${name} (@${f.friend.handle})` : name;
|
|
30485
|
+
}
|
|
30486
|
+
async function fetchUserFriends(auth) {
|
|
30487
|
+
const limit = 100;
|
|
30488
|
+
const all = [];
|
|
30489
|
+
for (let offset = 0;offset < 1e4; offset += limit) {
|
|
30490
|
+
const data = await amikoWebFetch(auth, "/api/friends", {
|
|
30491
|
+
query: { type: "user", limit, offset },
|
|
30492
|
+
timeoutMs: 20000
|
|
30493
|
+
});
|
|
30494
|
+
const page = data.friends ?? [];
|
|
30495
|
+
all.push(...page);
|
|
30496
|
+
if (page.length < limit || data.pagination?.has_more === false)
|
|
30497
|
+
break;
|
|
30498
|
+
}
|
|
30499
|
+
return all;
|
|
30500
|
+
}
|
|
30501
|
+
async function resolveFriend(auth, target) {
|
|
30502
|
+
const t = target.trim();
|
|
30503
|
+
if (!t) {
|
|
30504
|
+
throw new Error("No friend given. Pass a name, @handle, user id, or friendship id.");
|
|
30505
|
+
}
|
|
30506
|
+
const all = await fetchUserFriends(auth);
|
|
30507
|
+
const asId = normalizeUserId(t);
|
|
30508
|
+
const byId = all.find((f) => f.friendship_id === asId || f.friend.id === asId);
|
|
30509
|
+
if (byId)
|
|
30510
|
+
return byId;
|
|
30511
|
+
if (t.startsWith("@")) {
|
|
30512
|
+
const handle = t.slice(1).toLowerCase();
|
|
30513
|
+
const byHandle = all.filter((f) => f.friend.handle?.toLowerCase() === handle);
|
|
30514
|
+
if (byHandle.length === 1)
|
|
30515
|
+
return byHandle[0];
|
|
30516
|
+
throw new Error(`No friend ${t}. Nicknames only work for accepted friends — run \`amiko friends list\`.`);
|
|
30517
|
+
}
|
|
30518
|
+
const lower = t.toLowerCase();
|
|
30519
|
+
const exact = all.filter((f) => f.friend.name?.toLowerCase() === lower || f.friend.handle?.toLowerCase() === lower || f.nickname?.toLowerCase() === lower);
|
|
30520
|
+
const matches = exact.length ? exact : all.filter((f) => f.friend.name?.toLowerCase().includes(lower) || f.friend.handle?.toLowerCase().includes(lower) || f.nickname?.toLowerCase().includes(lower));
|
|
30521
|
+
if (matches.length === 0) {
|
|
30522
|
+
throw new Error(`No friend matching "${t}". Nicknames only work for accepted friends — run \`amiko friends list\`.`);
|
|
30523
|
+
}
|
|
30524
|
+
if (matches.length > 1) {
|
|
30525
|
+
const names = matches.map((f) => `${friendLabel(f)} (${f.friend.id})`).join(", ");
|
|
30526
|
+
throw new Error(`"${t}" is ambiguous — matches: ${names}. Use the friend's @handle or user id.`);
|
|
30527
|
+
}
|
|
30528
|
+
return matches[0];
|
|
30529
|
+
}
|
|
30530
|
+
async function resolveFriendOrExit(auth, target) {
|
|
30531
|
+
try {
|
|
30532
|
+
return await resolveFriend(auth, target);
|
|
30533
|
+
} catch (e5) {
|
|
30534
|
+
exitWithError3(e5, "Could not resolve the friend");
|
|
30535
|
+
}
|
|
30536
|
+
}
|
|
30537
|
+
async function patchNickname(auth, friendshipId, nickname, spinner) {
|
|
30538
|
+
try {
|
|
30539
|
+
const data = await amikoWebFetch(auth, `/api/friends/${encodeURIComponent(friendshipId)}/nickname`, { method: "PATCH", body: { nickname }, timeoutMs: 20000 });
|
|
30540
|
+
spinner?.stop();
|
|
30541
|
+
return data;
|
|
30542
|
+
} catch (e5) {
|
|
30543
|
+
spinner?.stop();
|
|
30544
|
+
if (e5.status === 403) {
|
|
30545
|
+
console.error(error(e5 instanceof Error ? e5.message : "Forbidden"));
|
|
30546
|
+
console.error(dim("Tip: nickname writes need an amiko-web deploy that allows twin tokens to manage nicknames (feat/friend-nicknames)."));
|
|
30547
|
+
process.exit(1);
|
|
30548
|
+
}
|
|
30549
|
+
exitWithError3(e5, "Failed to update the nickname");
|
|
30550
|
+
}
|
|
30551
|
+
}
|
|
30552
|
+
async function runList(opts) {
|
|
30553
|
+
const auth = authOrExit();
|
|
30554
|
+
const spinner = opts.json ? null : ora("Loading nicknames...").start();
|
|
30555
|
+
let all;
|
|
30556
|
+
try {
|
|
30557
|
+
all = await fetchUserFriends(auth);
|
|
30558
|
+
} catch (e5) {
|
|
30559
|
+
spinner?.stop();
|
|
30560
|
+
exitWithError3(e5, "Failed to load friends");
|
|
30561
|
+
}
|
|
30562
|
+
spinner?.stop();
|
|
30563
|
+
const named = all.filter((f) => !!f.nickname);
|
|
30564
|
+
renderOutput({
|
|
30565
|
+
nicknames: named.map((f) => ({
|
|
30566
|
+
nickname: f.nickname ?? null,
|
|
30567
|
+
user_id: f.friend.id,
|
|
30568
|
+
name: f.friend.name ?? null,
|
|
30569
|
+
handle: f.friend.handle ?? null,
|
|
30570
|
+
friendship_id: f.friendship_id
|
|
30571
|
+
}))
|
|
30572
|
+
}, () => {
|
|
30573
|
+
if (named.length === 0) {
|
|
30574
|
+
console.log(dim("No nicknames set."));
|
|
30575
|
+
console.log(dim('Set one with `amiko friends nickname set <friend> "<nickname>"`.'));
|
|
30576
|
+
return;
|
|
30577
|
+
}
|
|
30578
|
+
console.log(heading(`Nicknames (${named.length})`));
|
|
30579
|
+
const rows = [
|
|
30580
|
+
[
|
|
30581
|
+
dim("NICKNAME"),
|
|
30582
|
+
dim("NAME"),
|
|
30583
|
+
dim("HANDLE"),
|
|
30584
|
+
dim("USER ID"),
|
|
30585
|
+
dim("FRIENDSHIP")
|
|
30586
|
+
],
|
|
30587
|
+
...named.map((f) => [
|
|
30588
|
+
f.nickname ?? "-",
|
|
30589
|
+
f.friend.name ?? "-",
|
|
30590
|
+
f.friend.handle ?? "-",
|
|
30591
|
+
f.friend.id,
|
|
30592
|
+
f.friendship_id
|
|
30593
|
+
])
|
|
30594
|
+
];
|
|
30595
|
+
console.log(table(rows));
|
|
30596
|
+
}, { json: opts.json });
|
|
30597
|
+
}
|
|
30598
|
+
function registerFriendsNicknameCommand(nickname) {
|
|
30599
|
+
nickname.option("--json", "Output as JSON").action(runList);
|
|
30600
|
+
nickname.command("list").description("List every nickname you've set").option("--json", "Output as JSON").action((_opts, cmd) => runList(cmd.optsWithGlobals()));
|
|
30601
|
+
nickname.command("set <friend> <nickname>").description("Set or change your private nickname for a friend (name, @handle, user id, or friendship id)").option("--yes", "Skip the confirmation (required in non-interactive shells)").option("--json", "Output as JSON").action(async (target, rawNickname, _opts, cmd) => {
|
|
30602
|
+
const opts = cmd.optsWithGlobals();
|
|
30603
|
+
const checked = validateNickname(rawNickname);
|
|
30604
|
+
if (!checked.ok) {
|
|
30605
|
+
console.error(error(checked.message));
|
|
30606
|
+
if (checked.error === "empty") {
|
|
30607
|
+
console.error(dim(`Tip: to clear a nickname, run \`amiko friends nickname remove ${JSON.stringify(target)}\`.`));
|
|
30608
|
+
}
|
|
30609
|
+
process.exit(1);
|
|
30610
|
+
}
|
|
30611
|
+
const auth = authOrExit();
|
|
30612
|
+
const friend = await resolveFriendOrExit(auth, target);
|
|
30613
|
+
await confirmDestructive({
|
|
30614
|
+
action: friend.nickname ? `Change your private nickname for ${friendLabel(friend)} from "${friend.nickname}" to "${checked.value}"` : `Set your private nickname for ${friendLabel(friend)} to "${checked.value}"`,
|
|
30615
|
+
detail: "Nicknames are private — only you ever see them, in every Amiko app.",
|
|
30616
|
+
yes: opts.yes,
|
|
30617
|
+
commandExample: `amiko friends nickname set ${JSON.stringify(target)} ${JSON.stringify(checked.value)}`
|
|
30618
|
+
});
|
|
30619
|
+
const spinner = opts.json ? null : ora("Saving nickname...").start();
|
|
30620
|
+
const data = await patchNickname(auth, friend.friendship_id, checked.value, spinner);
|
|
30621
|
+
renderOutput(data, (d) => console.log(success(`${friendLabel(friend)} is now "${d.nickname}" to you`)), { json: opts.json });
|
|
30622
|
+
});
|
|
30623
|
+
nickname.command("remove <friend>").description("Remove your private nickname for a friend").option("--yes", "Skip the confirmation (required in non-interactive shells)").option("--json", "Output as JSON").action(async (target, _opts, cmd) => {
|
|
30624
|
+
const opts = cmd.optsWithGlobals();
|
|
30625
|
+
const auth = authOrExit();
|
|
30626
|
+
const friend = await resolveFriendOrExit(auth, target);
|
|
30627
|
+
if (!friend.nickname) {
|
|
30628
|
+
renderOutput({ nickname: null }, () => console.log(dim(`No nickname set for ${friendLabel(friend)}.`)), { json: opts.json });
|
|
30629
|
+
return;
|
|
30630
|
+
}
|
|
30631
|
+
await confirmDestructive({
|
|
30632
|
+
action: `Remove your private nickname "${friend.nickname}" for ${friendLabel(friend)}`,
|
|
30633
|
+
yes: opts.yes,
|
|
30634
|
+
commandExample: `amiko friends nickname remove ${JSON.stringify(target)}`
|
|
30635
|
+
});
|
|
30636
|
+
const spinner = opts.json ? null : ora("Removing nickname...").start();
|
|
30637
|
+
const data = await patchNickname(auth, friend.friendship_id, null, spinner);
|
|
30638
|
+
renderOutput(data, () => console.log(success(`Nickname removed — ${friendLabel(friend)} shows their real name again`)), { json: opts.json });
|
|
30639
|
+
});
|
|
30640
|
+
}
|
|
30641
|
+
|
|
30452
30642
|
// src/commands/friends.ts
|
|
30453
30643
|
async function runPaidRegenerate(args) {
|
|
30454
30644
|
let auth;
|
|
@@ -30577,6 +30767,7 @@ function registerFriendsCommand(program2) {
|
|
|
30577
30767
|
[
|
|
30578
30768
|
dim("USER ID"),
|
|
30579
30769
|
dim("NAME"),
|
|
30770
|
+
dim("NICKNAME"),
|
|
30580
30771
|
dim("HANDLE"),
|
|
30581
30772
|
dim("TYPE"),
|
|
30582
30773
|
dim("FRIENDSHIP"),
|
|
@@ -30585,6 +30776,7 @@ function registerFriendsCommand(program2) {
|
|
|
30585
30776
|
...items.map((f) => [
|
|
30586
30777
|
f.friend.id,
|
|
30587
30778
|
f.friend.name ?? "-",
|
|
30779
|
+
f.nickname ?? "-",
|
|
30588
30780
|
f.friend.handle ?? "-",
|
|
30589
30781
|
f.friend.type,
|
|
30590
30782
|
f.friendship_id,
|
|
@@ -30689,6 +30881,8 @@ Accept with: amiko friends accept <friendshipId>`));
|
|
|
30689
30881
|
process.exit(1);
|
|
30690
30882
|
}
|
|
30691
30883
|
});
|
|
30884
|
+
const nickname = program2.command("nickname").description("Private nicknames for friends — set, remove, list (only you ever see them)");
|
|
30885
|
+
registerFriendsNicknameCommand(nickname);
|
|
30692
30886
|
const reports = program2.command("reports").description("Friend matching reports");
|
|
30693
30887
|
reports.command("list").description("List friend matching reports").option("--page <n>", "Page number", "1").option("--limit <n>", "Page size (1-50)", "20").option("--json", "Output as JSON").action(async (opts) => {
|
|
30694
30888
|
const auth = resolveAuth();
|
|
@@ -32032,7 +32226,7 @@ var twin = program2.command("twin").description("Update twin identity (name, des
|
|
|
32032
32226
|
var drive = program2.command("drive").alias("docs").description("Manage twin drive — upload/download/search files, organise into folders");
|
|
32033
32227
|
var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
|
|
32034
32228
|
var avatar = program2.command("avatar").description("Manage twin avatar image");
|
|
32035
|
-
var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, reports, matches");
|
|
32229
|
+
var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, nicknames, reports, matches");
|
|
32036
32230
|
var users = program2.command("users").description("Search users and view public profiles");
|
|
32037
32231
|
var post = program2.command("post").description("Create posts and comments on the feed");
|
|
32038
32232
|
var review = program2.command("review").description("Review queue — list, approve, or reject twin-drafted comments");
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: amiko-cli
|
|
3
|
-
description: The Amiko CLI lets an agent act on the Amiko platform end-to-end — read platform notifications (friend requests, mentions, system alerts), search and write **cross-agent memories** about the owner (what other agents already know — preferences, decisions, facts), manage Solana/Base wallets (create, swap via Jupiter, bridge USDC via Across, transfer tokens to external addresses), top up and spend credits, generate media — images, video, speech, music, and SFX — via Create Studio (`amiko create`, async and charged on success), call paid MPP marketplace services (X/Twitter search, Amazon product search, TTS/STT, AI chat), manage the twin's identity and drive (files, folders, RAG), voice and avatar, and social graph (friends, posts, comments, feed). Auth is automatic when run from the agent's workspace folder; payments are platform-custodied (no keys on disk). The owner's DMs and group chats are `amiko chat` (list / read / send / mark everything read / see which chats have unread @mentions or replies to the owner (`chat list --mentions`) / organize chats into private folders (`chat lists`) / create + manage group chats AS the owner, including sending GIFs (`chat gifs` search + `chat send --gif`), sharing a group's invite/join link + QR and @all group announcements, 人对人); the agent's OWN sessions are the openhermit gateway's session_list / session_send (AS the agent) — different identities, different surfaces. Use this skill whenever the user asks about their Amiko notifications, chats/DMs, drive, memory, social graph, wallets, credits, or marketplace services — anything that would show up in their Amiko account or cost AMIKO/credits.
|
|
3
|
+
description: The Amiko CLI lets an agent act on the Amiko platform end-to-end — read platform notifications (friend requests, mentions, system alerts), search and write **cross-agent memories** about the owner (what other agents already know — preferences, decisions, facts), manage Solana/Base wallets (create, swap via Jupiter, bridge USDC via Across, transfer tokens to external addresses), top up and spend credits, generate media — images, video, speech, music, and SFX — via Create Studio (`amiko create`, async and charged on success), call paid MPP marketplace services (X/Twitter search, Amazon product search, TTS/STT, AI chat), manage the twin's identity and drive (files, folders, RAG), voice and avatar, and social graph (friends, private friend nicknames, posts, comments, feed). Auth is automatic when run from the agent's workspace folder; payments are platform-custodied (no keys on disk). The owner's DMs and group chats are `amiko chat` (list / read / send / mark everything read / see which chats have unread @mentions or replies to the owner (`chat list --mentions`) / organize chats into private folders (`chat lists`) / create + manage group chats AS the owner, including sending GIFs (`chat gifs` search + `chat send --gif`), sharing a group's invite/join link + QR and @all group announcements, 人对人); the agent's OWN sessions are the openhermit gateway's session_list / session_send (AS the agent) — different identities, different surfaces. Use this skill whenever the user asks about their Amiko notifications, chats/DMs, drive, memory, social graph, wallets, credits, or marketplace services — anything that would show up in their Amiko account or cost AMIKO/credits.
|
|
4
4
|
homepage: https://platform.heyamiko.com
|
|
5
5
|
metadata: {"openclaw":{"emoji":"🤖","requires":{"bins":["node"]}}}
|
|
6
6
|
---
|
|
@@ -43,6 +43,8 @@ Call your shell tool (your runtime calls it `bash`, `shell`, `run`, or similar)
|
|
|
43
43
|
| "mark all my chats as read" | shell → `amiko chat mark-read --all --yes` (after the owner confirms — it clears every badge and senders see read ticks) |
|
|
44
44
|
| "any unread mentions or replies to me?" | shell → `amiko chat list --mentions` (chats whose unread messages @mention the owner or reply to them) |
|
|
45
45
|
| "what chat lists do I have? what's in my Work list?" | shell → `amiko chat lists` |
|
|
46
|
+
| "call Sophie 'Soph' from now on" | shell → `amiko friends nickname set Sophie "Soph" --yes` (after the owner confirms) |
|
|
47
|
+
| "what nicknames have I given my friends?" | shell → `amiko friends nickname` |
|
|
46
48
|
| "add the team group to my Work list" | shell → `amiko chat lists add "Work" --conversation "<group>" --yes` |
|
|
47
49
|
| "what can amiko do?" | shell → `amiko --help` |
|
|
48
50
|
| "what's my MiniMax / ElevenLabs voice id?" | shell → `amiko info` |
|
|
@@ -63,7 +65,7 @@ Discover everything via `amiko --help`. Groups: `markets` (paid MPP services), `
|
|
|
63
65
|
|
|
64
66
|
## Critical Rules
|
|
65
67
|
|
|
66
|
-
1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI refuses to run `markets *`, `create *`, `card mint`, `credits topup`, `wallets swap send`, `wallets bridge send`, `wallets transfer` unless `--yes` is passed. The refusal prints the cost and the re-run command. **Quote the cost, get explicit approval, THEN append `--yes`.** The same `--yes` gate also covers **free outward social actions** — `chat send`, `chat pin` (the whole chat sees a pinned-message announcement), `chat group create`, `chat group add` (real people see them, so get the owner's explicit approval for the action itself) — and **destructive ops** like `chat unpin` (removes the pin for everyone), `chat mark-read --all` (irreversibly marks every conversation read — senders see read ticks), `chat group remove/leave/rename/promote/mention-all`, `chat lists create/rename/add/remove/delete` (private to the owner, but they reshape the owner's chat UI), `twin update --public`, `drive delete`, `drive share` / `drive folder share` (exposes the file — or the folder's ENTIRE subtree — to anyone with the link), `friends remove`, `friends reports request`, `avatar update`, `voice reset`, `review reject`. **For these free actions never mention cost, never say "the cost is 0", and never call it a paid operation** — ask for plain confirmation of the action itself (e.g. "Send "hi" to Mars — go ahead?").
|
|
68
|
+
1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI refuses to run `markets *`, `create *`, `card mint`, `credits topup`, `wallets swap send`, `wallets bridge send`, `wallets transfer` unless `--yes` is passed. The refusal prints the cost and the re-run command. **Quote the cost, get explicit approval, THEN append `--yes`.** The same `--yes` gate also covers **free outward social actions** — `chat send`, `chat pin` (the whole chat sees a pinned-message announcement), `chat group create`, `chat group add` (real people see them, so get the owner's explicit approval for the action itself) — and **destructive ops** like `chat unpin` (removes the pin for everyone), `chat mark-read --all` (irreversibly marks every conversation read — senders see read ticks), `chat group remove/leave/rename/promote/mention-all`, `chat lists create/rename/add/remove/delete` (private to the owner, but they reshape the owner's chat UI), `friends nickname set/remove` (private to the owner, but it changes how that friend is displayed everywhere in the owner's apps), `twin update --public`, `drive delete`, `drive share` / `drive folder share` (exposes the file — or the folder's ENTIRE subtree — to anyone with the link), `friends remove`, `friends reports request`, `avatar update`, `voice reset`, `review reject`. **For these free actions never mention cost, never say "the cost is 0", and never call it a paid operation** — ask for plain confirmation of the action itself (e.g. "Send "hi" to Mars — go ahead?").
|
|
67
69
|
2. **After every paid command, report the remaining balance.** The CLI prints a `Balance: N credits` line — include that figure in your reply.
|
|
68
70
|
3. **Never retry a failed command.** Report and stop. Every paid call costs tokens even on failure.
|
|
69
71
|
4. **Auth is automatic.** Never suggest `amiko login` / `amiko connect`. Run from the agent's workspace folder; if anything looks off, `amiko accounts` shows the resolved `userId` / `twinId`.
|
|
@@ -200,6 +202,15 @@ The drive is **shared between the user and the agent** — anything either side
|
|
|
200
202
|
|
|
201
203
|
## Friends & relationships
|
|
202
204
|
|
|
205
|
+
### Private nicknames — `amiko friends nickname`
|
|
206
|
+
|
|
207
|
+
The owner's private pet names for friends. **Only the owner ever sees a nickname** — it becomes that friend's display name across the owner's feed, chats, and profile views; the friend is never notified and never sees it. Nicknames exist only for **accepted, person-to-person** friends (not twins, not pending requests).
|
|
208
|
+
|
|
209
|
+
- `amiko friends nickname` (or `... nickname list`) — every nickname the owner has set. Ungated read; `--json` for ids.
|
|
210
|
+
- `amiko friends nickname set <friend> "<nickname>"` — set or change (max 50 characters). `<friend>` takes a name, `@handle`, user id, or friendship id — and also matches the **current nickname** ("rename Soph to Sophy" works). Ambiguity errors listing candidates; ask the owner, don't guess.
|
|
211
|
+
- `amiko friends nickname remove <friend>` — clear it (the friend's real name shows again). Removing when none is set is a harmless no-op.
|
|
212
|
+
- `set`/`remove` are `--yes`-gated. They're private — confirm the action itself; never mention cost or "other people will see this."
|
|
213
|
+
|
|
203
214
|
### "How is my relationship with X?" playbook
|
|
204
215
|
|
|
205
216
|
1. **Resolve to a user id**: `amiko friends list --json` first; if not a friend, `amiko users search "<name>" --json`. If ambiguous, ask.
|