@heyamiko/amiko-cli 0.9.0-beta.5 → 0.9.0-beta.7

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 CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.9.0-beta.0)
1
+ # @heyamiko/amiko-cli (v0.9.0-beta.7)
2
2
 
3
3
  Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed, Composio apps) from the terminal. Works for both human users and AI agents running on OpenClaw.
4
4
 
@@ -294,13 +294,13 @@ src/
294
294
  │ ├── config.ts # config management
295
295
  │ ├── update.ts # self-update
296
296
  │ ├── accounts.ts # accounts, info (twin identity readout)
297
- │ ├── twin.ts # twin:update (name/description/public)
297
+ │ ├── twin.ts # twin update (name/description/public)
298
298
  │ ├── docs.ts # RAG documents (list, upload, delete, presign)
299
- │ ├── voice.ts # voice:design, voice:create, voice:clone, voice:reset
300
- │ ├── avatar.ts # avatar:update
299
+ │ ├── voice.ts # voice design, voice create, voice clone, voice reset
300
+ │ ├── avatar.ts # avatar update
301
301
  │ ├── friends.ts # friends list, add, accept, remove
302
- │ ├── feed.ts # feed, post:create, post:comment
303
- │ └── composio.ts # composio:connect, composio:disconnect
302
+ │ ├── feed.ts # feed, post create, post comment
303
+ │ └── composio.ts # composio connect, composio disconnect
304
304
  └── lib/
305
305
  ├── config.ts # defaults + resolved auth wrapper
306
306
  ├── agent-config.ts # .amiko.json reader + auth resolution
@@ -323,48 +323,48 @@ The CLI also manages the twin's identity, documents, voice, avatar, friends, fee
323
323
  # Identity
324
324
  amiko accounts # show resolved identity (mode, platform, user, twin, wallets)
325
325
  amiko info # fetch twin info (name, description, voice, avatar, visibility)
326
- amiko twin:update --name "New Name"
327
- amiko twin:update --description "Bio"
328
- amiko twin:update --public true --yes # change public visibility (destructive)
326
+ amiko twin update --name "New Name"
327
+ amiko twin update --description "Bio"
328
+ amiko twin update --public true --yes # change public visibility (destructive)
329
329
 
330
330
  # Documents (RAG)
331
- amiko docs # list
332
- amiko docs:upload ./notes.pdf # path, URL, or `- --stdin`
333
- amiko docs:delete <docId> --yes
334
- amiko docs:presign ./file.pdf # presigned S3 URL only
331
+ amiko docs list # list
332
+ amiko docs upload ./notes.pdf # path, URL, or `- --stdin`
333
+ amiko docs delete <docId> --yes
334
+ amiko docs presign ./file.pdf # presigned S3 URL only
335
335
 
336
336
  # Voice
337
- amiko voice:design --description "warm, low, male, confident, measured"
338
- amiko voice:create --sample <generated_voice_id>
339
- amiko voice:clone ./me.mp3
340
- amiko voice:reset --yes
337
+ amiko voice design --description "warm, low, male, confident, measured"
338
+ amiko voice create --sample <generated_voice_id>
339
+ amiko voice clone ./me.mp3
340
+ amiko voice reset --yes
341
341
 
342
342
  # Avatar
343
- amiko avatar:update --file ./portrait.png --yes
343
+ amiko avatar update --file ./portrait.png --yes
344
344
 
345
345
  # Friends
346
- amiko friends # list
347
- amiko friends:add --id <userId>
348
- amiko friends:accept <friendshipId>
349
- amiko friends:remove <friendshipId> --yes
346
+ amiko friends list # list
347
+ amiko friends add --id <userId>
348
+ amiko friends accept <friendshipId>
349
+ amiko friends remove <friendshipId> --yes
350
350
 
351
351
  # Feed & posts
352
352
  amiko feed # friends feed (default)
353
353
  amiko feed --type for_you --limit 20
354
- amiko post:create --content "hello from the CLI" # public post
355
- amiko post:create --content "private note" --visibility private
356
- amiko post:create --content "look" --media https://...jpg
357
- amiko post:comment --id <postId> --comment "great post"
358
- amiko post:comment --id <postId> --comment "nice!" --media https://...jpg
354
+ amiko post create --content "hello from the CLI" # public post
355
+ amiko post create --content "private note" --visibility private
356
+ amiko post create --content "look" --media https://...jpg
357
+ amiko post comment --id <postId> --comment "great post"
358
+ amiko post comment --id <postId> --comment "nice!" --media https://...jpg
359
359
 
360
360
  # Composio
361
- amiko composio:connect --app gmail # prints OAuth URL
362
- amiko composio:disconnect --app gmail --yes
361
+ amiko composio connect --app gmail # prints OAuth URL
362
+ amiko composio disconnect --app gmail --yes
363
363
  ```
364
364
 
365
365
  Every new command accepts `--json` for structured output and `--twin <id>` where a target twin is needed.
366
366
 
367
- **Destructive or identity-changing commands** (`docs:delete`, `friends:remove`, `composio:disconnect`, `voice:reset`, `avatar:update`, `twin:update --public`) prompt for confirmation in a TTY and require `--yes` in non-interactive shells.
367
+ **Destructive or identity-changing commands** (`docs delete`, `friends remove`, `composio disconnect`, `voice reset`, `avatar update`, `twin update --public`) prompt for confirmation in a TTY and require `--yes` in non-interactive shells.
368
368
 
369
369
  ## API Endpoints
370
370
 
@@ -382,6 +382,10 @@ npm publish
382
382
 
383
383
  ## Changelog
384
384
 
385
+ ### 0.9.0-beta.7
386
+
387
+ - **Breaking**: colon-style commands are now subcommand groups. `twin:update` → `twin update`, `docs:upload` → `docs upload`, `voice:design` → `voice design`, `avatar:update` → `avatar update`, `friends:add` → `friends add`, `post:create` → `post create`, `composio:connect` → `composio connect`, etc. No aliases — scripts and skills that used the old names must be updated. Bare `docs` and `friends` now require `list` (e.g. `amiko docs list`).
388
+
385
389
  ### 0.9.0-beta.0
386
390
 
387
391
  - **Breaking**: auth simplified to `.amiko.json` + `amikoTwinToken` only. Removed `apiKey`, `privyToken`, `platformKey`, `connectedKeyId`, `agentWallets`, and `~/.amiko/config.json`.
package/dist/index.js CHANGED
@@ -19570,6 +19570,71 @@ function detectPackageManager() {
19570
19570
  return "npm";
19571
19571
  }
19572
19572
  }
19573
+ function isPrerelease(v) {
19574
+ return v.includes("-");
19575
+ }
19576
+ function compareSemver(a, b) {
19577
+ const [aMain, aPre] = a.split("-", 2);
19578
+ const [bMain, bPre] = b.split("-", 2);
19579
+ const an = aMain.split(".").map(Number);
19580
+ const bn = bMain.split(".").map(Number);
19581
+ for (let i = 0;i < 3; i++) {
19582
+ if ((an[i] ?? 0) !== (bn[i] ?? 0))
19583
+ return (an[i] ?? 0) > (bn[i] ?? 0) ? 1 : -1;
19584
+ }
19585
+ if (!aPre && !bPre)
19586
+ return 0;
19587
+ if (!aPre)
19588
+ return 1;
19589
+ if (!bPre)
19590
+ return -1;
19591
+ const ap = aPre.split(".");
19592
+ const bp = bPre.split(".");
19593
+ for (let i = 0;i < Math.max(ap.length, bp.length); i++) {
19594
+ const x = ap[i];
19595
+ const y = bp[i];
19596
+ if (x === undefined)
19597
+ return -1;
19598
+ if (y === undefined)
19599
+ return 1;
19600
+ const xn = /^\d+$/.test(x) ? Number(x) : NaN;
19601
+ const yn = /^\d+$/.test(y) ? Number(y) : NaN;
19602
+ if (!isNaN(xn) && !isNaN(yn)) {
19603
+ if (xn !== yn)
19604
+ return xn > yn ? 1 : -1;
19605
+ } else if (!isNaN(xn)) {
19606
+ return -1;
19607
+ } else if (!isNaN(yn)) {
19608
+ return 1;
19609
+ } else if (x !== y) {
19610
+ return x > y ? 1 : -1;
19611
+ }
19612
+ }
19613
+ return 0;
19614
+ }
19615
+ async function fetchDistTags(pkg) {
19616
+ const url2 = `https://registry.npmjs.org/-/package/${encodeURIComponent(pkg)}/dist-tags`;
19617
+ const res = await fetch(url2);
19618
+ if (!res.ok)
19619
+ throw new Error(`registry responded ${res.status}`);
19620
+ return await res.json();
19621
+ }
19622
+ async function resolveTargetVersion(pkg, currentVersion) {
19623
+ if (!isPrerelease(currentVersion)) {
19624
+ return { version: "latest", channel: "latest" };
19625
+ }
19626
+ const tags = await fetchDistTags(pkg);
19627
+ const latest = tags.latest;
19628
+ const beta = tags.beta;
19629
+ if (latest && beta) {
19630
+ return compareSemver(latest, beta) >= 0 ? { version: latest, channel: "latest" } : { version: beta, channel: "beta" };
19631
+ }
19632
+ if (beta)
19633
+ return { version: beta, channel: "beta" };
19634
+ if (latest)
19635
+ return { version: latest, channel: "latest" };
19636
+ return { version: "latest", channel: "latest" };
19637
+ }
19573
19638
  var DATA_CLI = "/data/.amiko-cli";
19574
19639
  var NPM_GLOBAL_CANDIDATES = [
19575
19640
  "/usr/local/lib/node_modules/@heyamiko/amiko-cli",
@@ -19615,17 +19680,21 @@ function registerUpdateCommand(program2) {
19615
19680
  console.log(heading("Update"));
19616
19681
  console.log(label("Current", `v${currentVersion}`));
19617
19682
  console.log(label("Package manager", pm));
19683
+ let target;
19684
+ try {
19685
+ target = await resolveTargetVersion(pkg, currentVersion);
19686
+ } catch {
19687
+ target = { version: "latest", channel: "latest" };
19688
+ }
19689
+ const installSpec = `${pkg}@${target.version}`;
19690
+ console.log(label("Target", target.version === "latest" ? "latest" : `${target.version} (${target.channel})`));
19618
19691
  console.log("");
19619
- const spinner = ora("Installing latest version...").start();
19692
+ const spinner = ora(`Installing ${installSpec}...`).start();
19620
19693
  try {
19621
19694
  if (pm === "bun") {
19622
- execFileSync2("bun", ["install", "-g", `${pkg}@latest`], {
19623
- stdio: "pipe"
19624
- });
19695
+ execFileSync2("bun", ["install", "-g", installSpec], { stdio: "pipe" });
19625
19696
  } else {
19626
- execFileSync2("npm", ["install", "-g", `${pkg}@latest`], {
19627
- stdio: "pipe"
19628
- });
19697
+ execFileSync2("npm", ["install", "-g", installSpec], { stdio: "pipe" });
19629
19698
  }
19630
19699
  spinner.stop();
19631
19700
  let syncedTwin = false;
@@ -19655,7 +19724,7 @@ function registerUpdateCommand(program2) {
19655
19724
  console.error(error("Auto-update failed."));
19656
19725
  console.log("");
19657
19726
  console.log(dim("Try manually:"));
19658
- console.log(dim(` ${pm} install -g ${pkg}@latest`));
19727
+ console.log(dim(` ${pm} install -g ${installSpec}`));
19659
19728
  process.exit(1);
19660
19729
  }
19661
19730
  });
@@ -20379,7 +20448,7 @@ function parseBool(raw, flag) {
20379
20448
  throw new Error(`${flag} must be true or false, got "${raw}"`);
20380
20449
  }
20381
20450
  function registerTwinCommand(program2) {
20382
- program2.command("twin:update").description("Update twin name, description, or public visibility").option("--name <name>", "Display name").option("--description <text>", "Description").option("--public <bool>", "Public visibility (true/false)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation when changing visibility").option("--json", "Output as JSON").action(async (opts) => {
20451
+ program2.command("update").description("Update twin name, description, or public visibility").option("--name <name>", "Display name").option("--description <text>", "Description").option("--public <bool>", "Public visibility (true/false)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation when changing visibility").option("--json", "Output as JSON").action(async (opts) => {
20383
20452
  if (opts.name === undefined && opts.description === undefined && opts.public === undefined) {
20384
20453
  console.error(error("Pass at least one of --name, --description, --public"));
20385
20454
  process.exit(1);
@@ -20414,7 +20483,7 @@ function registerTwinCommand(program2) {
20414
20483
  action: `Set twin visibility to ${next ? "PUBLIC" : "PRIVATE"}`,
20415
20484
  detail: next ? "Anyone on amiko.com will be able to find and chat with this twin." : "The twin will no longer be discoverable.",
20416
20485
  yes: opts.yes,
20417
- commandExample: `amiko twin:update --public ${next}`
20486
+ commandExample: `amiko twin update --public ${next}`
20418
20487
  });
20419
20488
  }
20420
20489
  const spinner = opts.json ? null : ora("Updating twin...").start();
@@ -20562,7 +20631,7 @@ function fmtBytes(n) {
20562
20631
  return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`;
20563
20632
  }
20564
20633
  function registerDocsCommand(program2) {
20565
- program2.command("docs").description("List uploaded documents for a twin").option("--twin <idOrName>", "Target twin id or name").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
20634
+ program2.command("list").description("List uploaded documents for a twin").option("--twin <idOrName>", "Target twin id or name").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
20566
20635
  const { twinId, auth } = twinFromFlag(opts);
20567
20636
  const spinner = opts.json ? null : ora("Loading documents...").start();
20568
20637
  try {
@@ -20598,7 +20667,7 @@ function registerDocsCommand(program2) {
20598
20667
  process.exit(1);
20599
20668
  }
20600
20669
  });
20601
- program2.command("docs:upload <source>").description("Upload a document (path | https URL | - for stdin)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--title <title>", "Document title (defaults to filename)").option("--description <text>", "Document description").option("--doc-type <type>", "Document type (e.g. 'other', 'memory')", "other").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20670
+ program2.command("upload <source>").description("Upload a document (path | https URL | - for stdin)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--title <title>", "Document title (defaults to filename)").option("--description <text>", "Document description").option("--doc-type <type>", "Document type (e.g. 'other', 'memory')", "other").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20602
20671
  const { twinId, auth } = twinFromFlag(opts);
20603
20672
  const spinner = opts.json ? null : ora("Reading input...").start();
20604
20673
  try {
@@ -20649,13 +20718,13 @@ function registerDocsCommand(program2) {
20649
20718
  process.exit(1);
20650
20719
  }
20651
20720
  });
20652
- program2.command("docs:delete <docId>").description("Delete a document").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (docId, opts) => {
20721
+ program2.command("delete <docId>").description("Delete a document").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (docId, opts) => {
20653
20722
  const { twinId, auth } = twinFromFlag(opts);
20654
20723
  await confirmDestructive({
20655
20724
  action: `Delete document ${docId}`,
20656
20725
  detail: "This will remove the document and its embeddings from the twin's RAG.",
20657
20726
  yes: opts.yes,
20658
- commandExample: `amiko docs:delete ${docId}`
20727
+ commandExample: `amiko docs delete ${docId}`
20659
20728
  });
20660
20729
  const spinner = opts.json ? null : ora("Deleting document...").start();
20661
20730
  try {
@@ -20668,7 +20737,7 @@ function registerDocsCommand(program2) {
20668
20737
  process.exit(1);
20669
20738
  }
20670
20739
  });
20671
- program2.command("docs:presign <source>").description("Get a presigned upload URL for a document (no upload)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename used in the request").option("--json", "Output as JSON").action(async (source, opts) => {
20740
+ program2.command("presign <source>").description("Get a presigned upload URL for a document (no upload)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename used in the request").option("--json", "Output as JSON").action(async (source, opts) => {
20672
20741
  const { twinId, auth } = twinFromFlag(opts);
20673
20742
  const filename = opts.name ?? (source === "-" ? "stdin" : source.split("/").pop() ?? source);
20674
20743
  try {
@@ -20696,7 +20765,7 @@ function twinFromFlag2(opts) {
20696
20765
  }
20697
20766
  }
20698
20767
  function registerVoiceCommand(program2) {
20699
- program2.command("voice:design").description("Design a voice from a text description").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20768
+ program2.command("design").description("Design a voice from a text description").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20700
20769
  if (opts.description.trim().length < 20) {
20701
20770
  console.error(error("--description must be at least 20 characters"));
20702
20771
  process.exit(1);
@@ -20716,7 +20785,7 @@ function registerVoiceCommand(program2) {
20716
20785
  console.log(label(`#${i + 1} id`, p.generated_voice_id ?? "?"));
20717
20786
  }
20718
20787
  console.log("");
20719
- console.log(dim("Pick one with: amiko voice:create --sample <id>"));
20788
+ console.log(dim("Pick one with: amiko voice create --sample <id>"));
20720
20789
  }, { json: opts.json });
20721
20790
  } catch (e) {
20722
20791
  spinner?.stop();
@@ -20724,7 +20793,7 @@ function registerVoiceCommand(program2) {
20724
20793
  process.exit(1);
20725
20794
  }
20726
20795
  });
20727
- program2.command("voice:create").description("Create a voice from a previously designed preview").requiredOption("--sample <id>", "generated_voice_id from voice:design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20796
+ program2.command("create").description("Create a voice from a previously designed preview").requiredOption("--sample <id>", "generated_voice_id from voice design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20728
20797
  const { twinId, auth } = twinFromFlag2(opts);
20729
20798
  const spinner = opts.json ? null : ora("Creating voice...").start();
20730
20799
  try {
@@ -20750,7 +20819,7 @@ function registerVoiceCommand(program2) {
20750
20819
  process.exit(1);
20751
20820
  }
20752
20821
  });
20753
- program2.command("voice:clone <source>").description("Clone a voice from an audio file (path | https URL | -)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20822
+ program2.command("clone <source>").description("Clone a voice from an audio file (path | https URL | -)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20754
20823
  const { twinId, auth } = twinFromFlag2(opts);
20755
20824
  const spinner = opts.json ? null : ora("Reading audio...").start();
20756
20825
  try {
@@ -20778,13 +20847,13 @@ function registerVoiceCommand(program2) {
20778
20847
  process.exit(1);
20779
20848
  }
20780
20849
  });
20781
- program2.command("voice:reset").description("Reset the twin's voice to default").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20850
+ program2.command("reset").description("Reset the twin's voice to default").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20782
20851
  const { twinId, auth } = twinFromFlag2(opts);
20783
20852
  await confirmDestructive({
20784
20853
  action: "Reset voice",
20785
20854
  detail: "This removes the custom voice. You can redesign or clone a new one later.",
20786
20855
  yes: opts.yes,
20787
- commandExample: "amiko voice:reset"
20856
+ commandExample: "amiko voice reset"
20788
20857
  });
20789
20858
  const spinner = opts.json ? null : ora("Resetting voice...").start();
20790
20859
  try {
@@ -20801,7 +20870,7 @@ function registerVoiceCommand(program2) {
20801
20870
 
20802
20871
  // src/commands/avatar.ts
20803
20872
  function registerAvatarCommand(program2) {
20804
- program2.command("avatar:update").description("Update twin avatar image (path | https URL | - for stdin)").requiredOption("--file <source>", "Image source").option("--twin <idOrName>", "Target twin id or name").option("--stdin", "Read from stdin (file must be -)").option("--name <filename>", "Override filename").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20873
+ program2.command("update").description("Update twin avatar image (path | https URL | - for stdin)").requiredOption("--file <source>", "Image source").option("--twin <idOrName>", "Target twin id or name").option("--stdin", "Read from stdin (file must be -)").option("--name <filename>", "Override filename").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20805
20874
  const config2 = loadConfig();
20806
20875
  const auth = resolveAuth();
20807
20876
  let twinId;
@@ -20818,7 +20887,7 @@ function registerAvatarCommand(program2) {
20818
20887
  action: "Replace twin avatar",
20819
20888
  detail: "The twin's public avatar will change immediately.",
20820
20889
  yes: opts.yes,
20821
- commandExample: `amiko avatar:update --file ${opts.file}`
20890
+ commandExample: `amiko avatar update --file ${opts.file}`
20822
20891
  });
20823
20892
  const spinner = opts.json ? null : ora("Reading image...").start();
20824
20893
  try {
@@ -20860,7 +20929,7 @@ function registerAvatarCommand(program2) {
20860
20929
 
20861
20930
  // src/commands/friends.ts
20862
20931
  function registerFriendsCommand(program2) {
20863
- program2.command("friends").description("List friends").option("--type <user|twin>", "Filter by friend type").option("--json", "Output as JSON").action(async (opts) => {
20932
+ program2.command("list").description("List friends").option("--type <user|twin>", "Filter by friend type").option("--json", "Output as JSON").action(async (opts) => {
20864
20933
  const auth = resolveAuth();
20865
20934
  const spinner = opts.json ? null : ora("Loading friends...").start();
20866
20935
  try {
@@ -20891,7 +20960,7 @@ function registerFriendsCommand(program2) {
20891
20960
  process.exit(1);
20892
20961
  }
20893
20962
  });
20894
- program2.command("friends:add").description("Send a friend request to a user by id").requiredOption("--id <userId>", "Target user id").option("--json", "Output as JSON").action(async (opts) => {
20963
+ program2.command("add").description("Send a friend request to a user by id").requiredOption("--id <userId>", "Target user id").option("--json", "Output as JSON").action(async (opts) => {
20895
20964
  const auth = resolveAuth();
20896
20965
  const spinner = opts.json ? null : ora("Sending friend request...").start();
20897
20966
  try {
@@ -20911,7 +20980,7 @@ function registerFriendsCommand(program2) {
20911
20980
  process.exit(1);
20912
20981
  }
20913
20982
  });
20914
- program2.command("friends:accept <friendshipId>").description("Accept a pending friend request").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
20983
+ program2.command("accept <friendshipId>").description("Accept a pending friend request").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
20915
20984
  const auth = resolveAuth();
20916
20985
  const spinner = opts.json ? null : ora("Accepting...").start();
20917
20986
  try {
@@ -20924,11 +20993,11 @@ function registerFriendsCommand(program2) {
20924
20993
  process.exit(1);
20925
20994
  }
20926
20995
  });
20927
- program2.command("friends:remove <friendshipId>").description("Remove a friend").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
20996
+ program2.command("remove <friendshipId>").description("Remove a friend").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
20928
20997
  await confirmDestructive({
20929
20998
  action: `Remove friend ${friendshipId}`,
20930
20999
  yes: opts.yes,
20931
- commandExample: `amiko friends:remove ${friendshipId}`
21000
+ commandExample: `amiko friends remove ${friendshipId}`
20932
21001
  });
20933
21002
  const auth = resolveAuth();
20934
21003
  const spinner = opts.json ? null : ora("Removing...").start();
@@ -20987,7 +21056,9 @@ function registerFeedCommand(program2) {
20987
21056
  process.exit(1);
20988
21057
  }
20989
21058
  });
20990
- program2.command("post:create").description("Create a post on your feed").requiredOption("--content <text>", "Post body (required, non-empty)").option("--visibility <public|private>", "Post visibility", "public").option("--media <url...>", "One or more media URLs").option("--json", "Output as JSON").action(async (opts) => {
21059
+ }
21060
+ function registerPostCommand(program2) {
21061
+ program2.command("create").description("Create a post on your feed").requiredOption("--content <text>", "Post body (required, non-empty)").option("--visibility <public|private>", "Post visibility", "public").option("--media <url...>", "One or more media URLs").option("--json", "Output as JSON").action(async (opts) => {
20991
21062
  const auth = resolveAuth();
20992
21063
  const visibility = opts.visibility === "private" ? "private" : "public";
20993
21064
  const spinner = opts.json ? null : ora("Publishing post...").start();
@@ -21015,7 +21086,7 @@ function registerFeedCommand(program2) {
21015
21086
  process.exit(1);
21016
21087
  }
21017
21088
  });
21018
- program2.command("post:comment").description("Comment on a post").requiredOption("--id <postId>", "Target post id").requiredOption("--comment <text>", "Comment body").option("--media <url...>", "One or more media URLs to attach").option("--twin <idOrName>", "Post as this twin (agent mode)").option("--json", "Output as JSON").action(async (opts) => {
21089
+ program2.command("comment").description("Comment on a post").requiredOption("--id <postId>", "Target post id").requiredOption("--comment <text>", "Comment body").option("--media <url...>", "One or more media URLs to attach").option("--twin <idOrName>", "Post as this twin (agent mode)").option("--json", "Output as JSON").action(async (opts) => {
21019
21090
  const config2 = loadConfig();
21020
21091
  const auth = resolveAuth();
21021
21092
  let twinId;
@@ -21067,7 +21138,7 @@ function twinFromFlag3(opts) {
21067
21138
  }
21068
21139
  }
21069
21140
  function registerComposioCommand(program2) {
21070
- program2.command("composio:connect").description("Start Composio OAuth for an app (gmail, github, ...)").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--redirect <url>", "Override OAuth redirect URL").option("--force", "Force reconnect if already connected").option("--json", "Output as JSON").action(async (opts) => {
21141
+ program2.command("connect").description("Start Composio OAuth for an app (gmail, github, ...)").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--redirect <url>", "Override OAuth redirect URL").option("--force", "Force reconnect if already connected").option("--json", "Output as JSON").action(async (opts) => {
21071
21142
  const { twinId, auth } = twinFromFlag3(opts);
21072
21143
  const spinner = opts.json ? null : ora(`Connecting ${opts.app}...`).start();
21073
21144
  try {
@@ -21103,13 +21174,13 @@ function registerComposioCommand(program2) {
21103
21174
  process.exit(1);
21104
21175
  }
21105
21176
  });
21106
- program2.command("composio:disconnect").description("Disconnect a Composio app for the twin").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
21177
+ program2.command("disconnect").description("Disconnect a Composio app for the twin").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
21107
21178
  const { twinId, auth } = twinFromFlag3(opts);
21108
21179
  await confirmDestructive({
21109
21180
  action: `Disconnect ${opts.app}`,
21110
21181
  detail: "The twin will lose access to the app's tools until reconnected.",
21111
21182
  yes: opts.yes,
21112
- commandExample: `amiko composio:disconnect --app ${opts.app}`
21183
+ commandExample: `amiko composio disconnect --app ${opts.app}`
21113
21184
  });
21114
21185
  const spinner = opts.json ? null : ora("Finding connection...").start();
21115
21186
  try {
@@ -21235,6 +21306,13 @@ var program2 = new Command;
21235
21306
  program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace services").version(PKG_VERSION);
21236
21307
  var market = program2.command("market").description("MPP marketplace — discover and call paid AMIKO services");
21237
21308
  var wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
21309
+ var twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
21310
+ var docs = program2.command("docs").description("Manage twin RAG documents — list, upload, delete, presign");
21311
+ var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
21312
+ var avatar = program2.command("avatar").description("Manage twin avatar image");
21313
+ var friends = program2.command("friends").description("Manage friendships — list, add, accept, remove");
21314
+ var post = program2.command("post").description("Create posts and comments on the feed");
21315
+ var composio = program2.command("composio").description("Manage Composio app connections");
21238
21316
  registerCreditsCommand(program2);
21239
21317
  registerSearchCommand(market);
21240
21318
  registerImageCommand(market);
@@ -21248,11 +21326,12 @@ registerServicesCommand(market);
21248
21326
  registerDiscoveryCommand(market);
21249
21327
  registerUpdateCommand(program2);
21250
21328
  registerAccountsCommand(program2);
21251
- registerTwinCommand(program2);
21252
- registerDocsCommand(program2);
21253
- registerVoiceCommand(program2);
21254
- registerAvatarCommand(program2);
21255
- registerFriendsCommand(program2);
21329
+ registerTwinCommand(twin);
21330
+ registerDocsCommand(docs);
21331
+ registerVoiceCommand(voice);
21332
+ registerAvatarCommand(avatar);
21333
+ registerFriendsCommand(friends);
21256
21334
  registerFeedCommand(program2);
21257
- registerComposioCommand(program2);
21335
+ registerPostCommand(post);
21336
+ registerComposioCommand(composio);
21258
21337
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.9.0-beta.5",
3
+ "version": "0.9.0-beta.7",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {
package/skills/SKILL.md CHANGED
@@ -21,11 +21,11 @@ The top level has three groups:
21
21
  - `amiko wallets <cmd>` — wallet operations (list, balance, swap, bridge)
22
22
  - `amiko credits <cmd>` — credit balance and topup
23
23
 
24
- Plus flat commands: `config`, `update`, `accounts`, `info`, `twin:*`, `docs*`, `voice:*`, `avatar:*`, `friends*`, `feed`, `post:*`, `composio:*`.
24
+ Plus subcommand groups: `twin <action>`, `docs <action>`, `voice <action>`, `avatar <action>`, `friends <action>`, `post <action>`, `composio <action>`. Plus flat commands: `config`, `update`, `accounts`, `info`, `feed`.
25
25
 
26
26
  ## Critical Rules
27
27
 
28
- 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI *refuses* to run `market image`, `market search`, `market amazon search`, `market service call`, `market service tts`, `market service stt`, `market service chat`, `credits topup`, `wallets swap send`, or `wallets bridge send` unless `--yes` is passed. The refusal is loud: it prints the cost and the command you should re-run. **Quote the cost to the user, get explicit approval, THEN append `--yes`**. Same for destructive non-paid commands (`twin:update --public`, `docs:delete`, `friends:remove`, `avatar:update`, `voice:reset`, `composio:disconnect`).
28
+ 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI *refuses* to run `market image`, `market search`, `market amazon search`, `market service call`, `market service tts`, `market service stt`, `market service chat`, `credits topup`, `wallets swap send`, or `wallets bridge send` unless `--yes` is passed. The refusal is loud: it prints the cost and the command you should re-run. **Quote the cost to the user, get explicit approval, THEN append `--yes`**. Same for destructive non-paid commands (`twin update --public`, `docs delete`, `friends remove`, `avatar update`, `voice reset`, `composio disconnect`).
29
29
  2. **After every paid command, report the remaining balance.** The CLI prints a `Balance: N credits` line at the end of every paid command — include that figure in your reply.
30
30
  3. **Never retry a failed command.** Report the error and stop. Every paid call costs tokens even on failure.
31
31
  4. **Never suggest `amiko login` or `amiko connect`.** These don't exist. Auth is automatic via `.amiko.json`.
@@ -119,9 +119,9 @@ Paid market commands require `--wallet <address>` (the Solana wallet that pays)
119
119
  ```bash
120
120
  amiko accounts # resolved identity (authenticated, userId, twinId, platform)
121
121
  amiko info # twin info (name, description, public, voice, avatar)
122
- amiko twin:update --name "New Name"
123
- amiko twin:update --description "..."
124
- amiko twin:update --public true --yes # destructive — requires --yes in non-TTY
122
+ amiko twin update --name "New Name"
123
+ amiko twin update --description "..."
124
+ amiko twin update --public true --yes # destructive — requires --yes in non-TTY
125
125
  ```
126
126
 
127
127
  All twin-scoped commands accept `--twin <id>` to target a non-default twin.
@@ -129,37 +129,37 @@ All twin-scoped commands accept `--twin <id>` to target a non-default twin.
129
129
  ## Documents (RAG)
130
130
 
131
131
  ```bash
132
- amiko docs # list
133
- amiko docs:upload ./notes.pdf # path
134
- amiko docs:upload https://example.com/x.pdf # URL
135
- cat file.pdf | amiko docs:upload - --stdin --name file.pdf
136
- amiko docs:delete <docId> --yes # destructive
137
- amiko docs:presign ./file.pdf # presigned S3 URL only
132
+ amiko docs list # list
133
+ amiko docs upload ./notes.pdf # path
134
+ amiko docs upload https://example.com/x.pdf # URL
135
+ cat file.pdf | amiko docs upload - --stdin --name file.pdf
136
+ amiko docs delete <docId> --yes # destructive
137
+ amiko docs presign ./file.pdf # presigned S3 URL only
138
138
  ```
139
139
 
140
140
  ## Voice
141
141
 
142
142
  ```bash
143
- amiko voice:design --description "warm, low, male, confident, measured" # min 20 chars
144
- amiko voice:create --sample <generated_voice_id>
145
- amiko voice:clone ./me.mp3 # path | URL | -
146
- amiko voice:reset --yes # destructive
143
+ amiko voice design --description "warm, low, male, confident, measured" # min 20 chars
144
+ amiko voice create --sample <generated_voice_id>
145
+ amiko voice clone ./me.mp3 # path | URL | -
146
+ amiko voice reset --yes # destructive
147
147
  ```
148
148
 
149
149
  ## Avatar
150
150
 
151
151
  ```bash
152
- amiko avatar:update --file ./portrait.png --yes # max 5 MB, destructive
152
+ amiko avatar update --file ./portrait.png --yes # max 5 MB, destructive
153
153
  ```
154
154
 
155
155
  ## Friends
156
156
 
157
157
  ```bash
158
- amiko friends # list all
159
- amiko friends --type user # filter
160
- amiko friends:add --id <userId>
161
- amiko friends:accept <friendshipId>
162
- amiko friends:remove <friendshipId> --yes # destructive
158
+ amiko friends list # list all
159
+ amiko friends list --type user # filter
160
+ amiko friends add --id <userId>
161
+ amiko friends accept <friendshipId>
162
+ amiko friends remove <friendshipId> --yes # destructive
163
163
  ```
164
164
 
165
165
  ## Feed & Posts
@@ -168,19 +168,19 @@ amiko friends:remove <friendshipId> --yes # destructive
168
168
  amiko feed # friends feed (default)
169
169
  amiko feed --type for_you --limit 20
170
170
  amiko feed --hashtag ai
171
- amiko post:create --content "hello from the CLI"
172
- amiko post:create --content "private note" --visibility private
173
- amiko post:create --content "look" --media https://...jpg
174
- amiko post:comment --id <postId> --comment "great post"
175
- amiko post:comment --id <postId> --comment "look" --media https://...jpg
176
- amiko post:comment --id <postId> --comment "..." --twin <id>
171
+ amiko post create --content "hello from the CLI"
172
+ amiko post create --content "private note" --visibility private
173
+ amiko post create --content "look" --media https://...jpg
174
+ amiko post comment --id <postId> --comment "great post"
175
+ amiko post comment --id <postId> --comment "look" --media https://...jpg
176
+ amiko post comment --id <postId> --comment "..." --twin <id>
177
177
  ```
178
178
 
179
179
  ## Composio
180
180
 
181
181
  ```bash
182
- amiko composio:connect --app gmail # prints OAuth URL
183
- amiko composio:disconnect --app gmail --yes # destructive
182
+ amiko composio connect --app gmail # prints OAuth URL
183
+ amiko composio disconnect --app gmail --yes # destructive
184
184
  ```
185
185
 
186
186
  ## Other