@heyamiko/amiko-cli 0.9.0-beta.6 → 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
@@ -20448,7 +20448,7 @@ function parseBool(raw, flag) {
20448
20448
  throw new Error(`${flag} must be true or false, got "${raw}"`);
20449
20449
  }
20450
20450
  function registerTwinCommand(program2) {
20451
- 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) => {
20452
20452
  if (opts.name === undefined && opts.description === undefined && opts.public === undefined) {
20453
20453
  console.error(error("Pass at least one of --name, --description, --public"));
20454
20454
  process.exit(1);
@@ -20483,7 +20483,7 @@ function registerTwinCommand(program2) {
20483
20483
  action: `Set twin visibility to ${next ? "PUBLIC" : "PRIVATE"}`,
20484
20484
  detail: next ? "Anyone on amiko.com will be able to find and chat with this twin." : "The twin will no longer be discoverable.",
20485
20485
  yes: opts.yes,
20486
- commandExample: `amiko twin:update --public ${next}`
20486
+ commandExample: `amiko twin update --public ${next}`
20487
20487
  });
20488
20488
  }
20489
20489
  const spinner = opts.json ? null : ora("Updating twin...").start();
@@ -20631,7 +20631,7 @@ function fmtBytes(n) {
20631
20631
  return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`;
20632
20632
  }
20633
20633
  function registerDocsCommand(program2) {
20634
- 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) => {
20635
20635
  const { twinId, auth } = twinFromFlag(opts);
20636
20636
  const spinner = opts.json ? null : ora("Loading documents...").start();
20637
20637
  try {
@@ -20667,7 +20667,7 @@ function registerDocsCommand(program2) {
20667
20667
  process.exit(1);
20668
20668
  }
20669
20669
  });
20670
- 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) => {
20671
20671
  const { twinId, auth } = twinFromFlag(opts);
20672
20672
  const spinner = opts.json ? null : ora("Reading input...").start();
20673
20673
  try {
@@ -20718,13 +20718,13 @@ function registerDocsCommand(program2) {
20718
20718
  process.exit(1);
20719
20719
  }
20720
20720
  });
20721
- 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) => {
20722
20722
  const { twinId, auth } = twinFromFlag(opts);
20723
20723
  await confirmDestructive({
20724
20724
  action: `Delete document ${docId}`,
20725
20725
  detail: "This will remove the document and its embeddings from the twin's RAG.",
20726
20726
  yes: opts.yes,
20727
- commandExample: `amiko docs:delete ${docId}`
20727
+ commandExample: `amiko docs delete ${docId}`
20728
20728
  });
20729
20729
  const spinner = opts.json ? null : ora("Deleting document...").start();
20730
20730
  try {
@@ -20737,7 +20737,7 @@ function registerDocsCommand(program2) {
20737
20737
  process.exit(1);
20738
20738
  }
20739
20739
  });
20740
- 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) => {
20741
20741
  const { twinId, auth } = twinFromFlag(opts);
20742
20742
  const filename = opts.name ?? (source === "-" ? "stdin" : source.split("/").pop() ?? source);
20743
20743
  try {
@@ -20765,7 +20765,7 @@ function twinFromFlag2(opts) {
20765
20765
  }
20766
20766
  }
20767
20767
  function registerVoiceCommand(program2) {
20768
- 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) => {
20769
20769
  if (opts.description.trim().length < 20) {
20770
20770
  console.error(error("--description must be at least 20 characters"));
20771
20771
  process.exit(1);
@@ -20785,7 +20785,7 @@ function registerVoiceCommand(program2) {
20785
20785
  console.log(label(`#${i + 1} id`, p.generated_voice_id ?? "?"));
20786
20786
  }
20787
20787
  console.log("");
20788
- console.log(dim("Pick one with: amiko voice:create --sample <id>"));
20788
+ console.log(dim("Pick one with: amiko voice create --sample <id>"));
20789
20789
  }, { json: opts.json });
20790
20790
  } catch (e) {
20791
20791
  spinner?.stop();
@@ -20793,7 +20793,7 @@ function registerVoiceCommand(program2) {
20793
20793
  process.exit(1);
20794
20794
  }
20795
20795
  });
20796
- 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) => {
20797
20797
  const { twinId, auth } = twinFromFlag2(opts);
20798
20798
  const spinner = opts.json ? null : ora("Creating voice...").start();
20799
20799
  try {
@@ -20819,7 +20819,7 @@ function registerVoiceCommand(program2) {
20819
20819
  process.exit(1);
20820
20820
  }
20821
20821
  });
20822
- 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) => {
20823
20823
  const { twinId, auth } = twinFromFlag2(opts);
20824
20824
  const spinner = opts.json ? null : ora("Reading audio...").start();
20825
20825
  try {
@@ -20847,13 +20847,13 @@ function registerVoiceCommand(program2) {
20847
20847
  process.exit(1);
20848
20848
  }
20849
20849
  });
20850
- 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) => {
20851
20851
  const { twinId, auth } = twinFromFlag2(opts);
20852
20852
  await confirmDestructive({
20853
20853
  action: "Reset voice",
20854
20854
  detail: "This removes the custom voice. You can redesign or clone a new one later.",
20855
20855
  yes: opts.yes,
20856
- commandExample: "amiko voice:reset"
20856
+ commandExample: "amiko voice reset"
20857
20857
  });
20858
20858
  const spinner = opts.json ? null : ora("Resetting voice...").start();
20859
20859
  try {
@@ -20870,7 +20870,7 @@ function registerVoiceCommand(program2) {
20870
20870
 
20871
20871
  // src/commands/avatar.ts
20872
20872
  function registerAvatarCommand(program2) {
20873
- 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) => {
20874
20874
  const config2 = loadConfig();
20875
20875
  const auth = resolveAuth();
20876
20876
  let twinId;
@@ -20887,7 +20887,7 @@ function registerAvatarCommand(program2) {
20887
20887
  action: "Replace twin avatar",
20888
20888
  detail: "The twin's public avatar will change immediately.",
20889
20889
  yes: opts.yes,
20890
- commandExample: `amiko avatar:update --file ${opts.file}`
20890
+ commandExample: `amiko avatar update --file ${opts.file}`
20891
20891
  });
20892
20892
  const spinner = opts.json ? null : ora("Reading image...").start();
20893
20893
  try {
@@ -20929,7 +20929,7 @@ function registerAvatarCommand(program2) {
20929
20929
 
20930
20930
  // src/commands/friends.ts
20931
20931
  function registerFriendsCommand(program2) {
20932
- 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) => {
20933
20933
  const auth = resolveAuth();
20934
20934
  const spinner = opts.json ? null : ora("Loading friends...").start();
20935
20935
  try {
@@ -20960,7 +20960,7 @@ function registerFriendsCommand(program2) {
20960
20960
  process.exit(1);
20961
20961
  }
20962
20962
  });
20963
- 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) => {
20964
20964
  const auth = resolveAuth();
20965
20965
  const spinner = opts.json ? null : ora("Sending friend request...").start();
20966
20966
  try {
@@ -20980,7 +20980,7 @@ function registerFriendsCommand(program2) {
20980
20980
  process.exit(1);
20981
20981
  }
20982
20982
  });
20983
- 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) => {
20984
20984
  const auth = resolveAuth();
20985
20985
  const spinner = opts.json ? null : ora("Accepting...").start();
20986
20986
  try {
@@ -20993,11 +20993,11 @@ function registerFriendsCommand(program2) {
20993
20993
  process.exit(1);
20994
20994
  }
20995
20995
  });
20996
- 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) => {
20997
20997
  await confirmDestructive({
20998
20998
  action: `Remove friend ${friendshipId}`,
20999
20999
  yes: opts.yes,
21000
- commandExample: `amiko friends:remove ${friendshipId}`
21000
+ commandExample: `amiko friends remove ${friendshipId}`
21001
21001
  });
21002
21002
  const auth = resolveAuth();
21003
21003
  const spinner = opts.json ? null : ora("Removing...").start();
@@ -21056,7 +21056,9 @@ function registerFeedCommand(program2) {
21056
21056
  process.exit(1);
21057
21057
  }
21058
21058
  });
21059
- 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) => {
21060
21062
  const auth = resolveAuth();
21061
21063
  const visibility = opts.visibility === "private" ? "private" : "public";
21062
21064
  const spinner = opts.json ? null : ora("Publishing post...").start();
@@ -21084,7 +21086,7 @@ function registerFeedCommand(program2) {
21084
21086
  process.exit(1);
21085
21087
  }
21086
21088
  });
21087
- 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) => {
21088
21090
  const config2 = loadConfig();
21089
21091
  const auth = resolveAuth();
21090
21092
  let twinId;
@@ -21136,7 +21138,7 @@ function twinFromFlag3(opts) {
21136
21138
  }
21137
21139
  }
21138
21140
  function registerComposioCommand(program2) {
21139
- 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) => {
21140
21142
  const { twinId, auth } = twinFromFlag3(opts);
21141
21143
  const spinner = opts.json ? null : ora(`Connecting ${opts.app}...`).start();
21142
21144
  try {
@@ -21172,13 +21174,13 @@ function registerComposioCommand(program2) {
21172
21174
  process.exit(1);
21173
21175
  }
21174
21176
  });
21175
- 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) => {
21176
21178
  const { twinId, auth } = twinFromFlag3(opts);
21177
21179
  await confirmDestructive({
21178
21180
  action: `Disconnect ${opts.app}`,
21179
21181
  detail: "The twin will lose access to the app's tools until reconnected.",
21180
21182
  yes: opts.yes,
21181
- commandExample: `amiko composio:disconnect --app ${opts.app}`
21183
+ commandExample: `amiko composio disconnect --app ${opts.app}`
21182
21184
  });
21183
21185
  const spinner = opts.json ? null : ora("Finding connection...").start();
21184
21186
  try {
@@ -21304,6 +21306,13 @@ var program2 = new Command;
21304
21306
  program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace services").version(PKG_VERSION);
21305
21307
  var market = program2.command("market").description("MPP marketplace — discover and call paid AMIKO services");
21306
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");
21307
21316
  registerCreditsCommand(program2);
21308
21317
  registerSearchCommand(market);
21309
21318
  registerImageCommand(market);
@@ -21317,11 +21326,12 @@ registerServicesCommand(market);
21317
21326
  registerDiscoveryCommand(market);
21318
21327
  registerUpdateCommand(program2);
21319
21328
  registerAccountsCommand(program2);
21320
- registerTwinCommand(program2);
21321
- registerDocsCommand(program2);
21322
- registerVoiceCommand(program2);
21323
- registerAvatarCommand(program2);
21324
- registerFriendsCommand(program2);
21329
+ registerTwinCommand(twin);
21330
+ registerDocsCommand(docs);
21331
+ registerVoiceCommand(voice);
21332
+ registerAvatarCommand(avatar);
21333
+ registerFriendsCommand(friends);
21325
21334
  registerFeedCommand(program2);
21326
- registerComposioCommand(program2);
21335
+ registerPostCommand(post);
21336
+ registerComposioCommand(composio);
21327
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.6",
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