@heyamiko/amiko-cli 0.14.0-beta.19 → 0.14.0-beta.20
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 +8 -1
- package/dist/index.js +90 -5
- package/package.json +1 -1
- package/skills/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -509,7 +509,7 @@ src/
|
|
|
509
509
|
│ ├── avatar.ts # avatar update
|
|
510
510
|
│ ├── friends.ts # friends list, requests, add, accept, remove, matches, reports
|
|
511
511
|
│ ├── users.ts # users search, profile
|
|
512
|
-
│ └── feed.ts # feed, post create, post comment
|
|
512
|
+
│ └── feed.ts # feed, post create/drafts/publish, post comment
|
|
513
513
|
└── lib/
|
|
514
514
|
├── config.ts # defaults + resolved auth wrapper
|
|
515
515
|
├── agent-config.ts # .amiko.json reader + auth resolution
|
|
@@ -570,6 +570,9 @@ amiko feed --type for_you --limit 20
|
|
|
570
570
|
amiko post create --content "hello from the CLI" # public post — prints the canonical share URL (post_url in --json); share that verbatim, never hand-build one from the id
|
|
571
571
|
amiko post create --content "private note" --visibility private
|
|
572
572
|
amiko post create --content "look" --media https://...jpg
|
|
573
|
+
amiko post create --content "wip idea" --draft # save a draft — no share URL until published
|
|
574
|
+
amiko post drafts # list draft posts (--limit/--offset/--json)
|
|
575
|
+
amiko post publish <postId> # publish a draft (fresh timestamp, prints the share URL)
|
|
573
576
|
amiko post comment --id <postId> --comment "great post"
|
|
574
577
|
amiko post comment --id <postId> --comment "nice!" --media https://...jpg
|
|
575
578
|
```
|
|
@@ -594,6 +597,10 @@ npm publish
|
|
|
594
597
|
|
|
595
598
|
## Changelog
|
|
596
599
|
|
|
600
|
+
### 0.14.0-beta.20
|
|
601
|
+
|
|
602
|
+
- **Draft posts: `amiko post create --draft`, `amiko post drafts`, `amiko post publish <postId>`.** `--draft` saves via `POST /api/posts { status: "draft" }` — no side effects fire and no share URL is printed (drafts 404 by id until published; success copy says "Draft saved" with a publish hint, and `post_url` is omitted from `--json`). `drafts` lists the owner's draft posts (`GET /api/posts?status=draft`, offset pagination via `--limit`/`--offset`; owner-wide across all the owner's twins). `publish` PATCHes `{ status: "published" }`, stamps a fresh timestamp, fires mention/hashtag notifications, and prints the canonical share URL; a 404 points at `amiko post drafts` since drafts can't be fetched by id. Requires the amiko-web draft-posts API (`feat/draft-posts`).
|
|
603
|
+
|
|
597
604
|
### 0.14.0-beta.19
|
|
598
605
|
|
|
599
606
|
- **Pinned messages: `amiko chat pin <messageId>`, `amiko chat unpin <messageId>`, `amiko chat pinned <target>`.** Pins are conversation-wide: `pin` posts an "X pinned a message" announcement to the whole chat (POST `/api/messages/{id}/pin`), so it sits behind the outward-action `--yes` gate with a content preview; `unpin` (DELETE, silent but removes shared state for everyone) sits behind the destructive gate. Groups are admin-gated server-side — a 403 names the rule and the `chat group info` check; DMs let either side pin. 409 at the 20-pin cap and 400 for unpinnable (system/in-flight) messages get status-specific copy. `pinned <target>` is an ungated read using the same target resolution as `chat read` (conversation id, user id, `@handle`, or name; never creates a DM), listing pins oldest-first with sender, message id, and pinner name; `--raw` on all three. Requires the companion amiko-web `feat/chat-pinned-messages` deploy.
|
package/dist/index.js
CHANGED
|
@@ -30739,35 +30739,92 @@ function registerFeedCommand(program2) {
|
|
|
30739
30739
|
});
|
|
30740
30740
|
}
|
|
30741
30741
|
function registerPostCommand(program2) {
|
|
30742
|
-
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 <pathOrUrl...>", "Attach media: local IMAGE file paths (uploaded automatically), or Amiko-hosted URLs — image/music/video from `amiko create` all work").option("--json", "Output as JSON").action(async (opts) => {
|
|
30742
|
+
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 <pathOrUrl...>", "Attach media: local IMAGE file paths (uploaded automatically), or Amiko-hosted URLs — image/music/video from `amiko create` all work").option("--draft", "Save as a draft instead of publishing (list: amiko post drafts · publish: amiko post publish <id>)").option("--json", "Output as JSON").action(async (opts) => {
|
|
30743
30743
|
const auth = resolveAuth();
|
|
30744
30744
|
const visibility = opts.visibility === "private" ? "private" : "public";
|
|
30745
|
-
const
|
|
30745
|
+
const workingText = opts.draft ? "Saving draft..." : "Publishing post...";
|
|
30746
|
+
const spinner = opts.json ? null : ora(workingText).start();
|
|
30746
30747
|
try {
|
|
30747
30748
|
const body = {
|
|
30748
30749
|
content: opts.content,
|
|
30749
30750
|
visibility
|
|
30750
30751
|
};
|
|
30752
|
+
if (opts.draft)
|
|
30753
|
+
body.status = "draft";
|
|
30751
30754
|
if (opts.media?.length) {
|
|
30752
30755
|
if (spinner)
|
|
30753
30756
|
spinner.text = "Uploading media...";
|
|
30754
30757
|
body.media_urls = await resolveMediaUrls(auth, opts.media);
|
|
30755
30758
|
if (spinner)
|
|
30756
|
-
spinner.text =
|
|
30759
|
+
spinner.text = workingText;
|
|
30757
30760
|
}
|
|
30758
30761
|
const data = await amikoWebFetch(auth, "/api/posts", { method: "POST", body });
|
|
30759
30762
|
spinner?.stop();
|
|
30760
|
-
const postUrl = data.post?.id ? `https://platform.heyamiko.com/post/${data.post.id}` : null;
|
|
30763
|
+
const postUrl = !opts.draft && data.post?.id ? `https://platform.heyamiko.com/post/${data.post.id}` : null;
|
|
30761
30764
|
renderOutput(postUrl ? { ...data, post_url: postUrl } : data, (d) => {
|
|
30762
|
-
console.log(success("Post published"));
|
|
30765
|
+
console.log(success(opts.draft ? "Draft saved" : "Post published"));
|
|
30763
30766
|
const post = d.post;
|
|
30764
30767
|
if (post) {
|
|
30765
30768
|
if (post.id)
|
|
30766
30769
|
console.log(label("Post ID", String(post.id)));
|
|
30767
30770
|
if (postUrl)
|
|
30768
30771
|
console.log(label("URL", postUrl));
|
|
30772
|
+
if (opts.draft)
|
|
30773
|
+
console.log(label("Status", "draft"));
|
|
30769
30774
|
console.log(label("Visibility", visibility));
|
|
30775
|
+
if (opts.draft && post.id) {
|
|
30776
|
+
console.log(dim(`No public URL until published. List: amiko post drafts · Publish: amiko post publish ${post.id}`));
|
|
30777
|
+
}
|
|
30778
|
+
}
|
|
30779
|
+
}, { json: opts.json });
|
|
30780
|
+
} catch (e5) {
|
|
30781
|
+
spinner?.stop();
|
|
30782
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
30783
|
+
process.exit(1);
|
|
30784
|
+
}
|
|
30785
|
+
});
|
|
30786
|
+
program2.command("drafts").description("List your draft posts (newest first)").option("--limit <n>", "Max results (1-100, default 20)", "20").option("--offset <n>", "Pagination offset", "0").option("--json", "Output as JSON").action(async (opts) => {
|
|
30787
|
+
const limit = Number(opts.limit ?? "20");
|
|
30788
|
+
const offset = Number(opts.offset ?? "0");
|
|
30789
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
30790
|
+
console.error(error("--limit must be an integer between 1 and 100"));
|
|
30791
|
+
process.exit(1);
|
|
30792
|
+
}
|
|
30793
|
+
if (!Number.isInteger(offset) || offset < 0) {
|
|
30794
|
+
console.error(error("--offset must be a non-negative integer"));
|
|
30795
|
+
process.exit(1);
|
|
30796
|
+
}
|
|
30797
|
+
const auth = resolveAuth();
|
|
30798
|
+
const spinner = opts.json ? null : ora("Loading drafts...").start();
|
|
30799
|
+
try {
|
|
30800
|
+
const data = await amikoWebFetch(auth, "/api/posts", {
|
|
30801
|
+
query: {
|
|
30802
|
+
status: "draft",
|
|
30803
|
+
limit,
|
|
30804
|
+
offset
|
|
30805
|
+
}
|
|
30806
|
+
});
|
|
30807
|
+
spinner?.stop();
|
|
30808
|
+
const posts = data.posts ?? [];
|
|
30809
|
+
renderOutput(data, () => {
|
|
30810
|
+
if (posts.length === 0) {
|
|
30811
|
+
console.log(dim('No drafts. Save one with: amiko post create --content "..." --draft'));
|
|
30812
|
+
return;
|
|
30770
30813
|
}
|
|
30814
|
+
console.log(heading(`Drafts — ${posts.length}`));
|
|
30815
|
+
for (const p of posts) {
|
|
30816
|
+
console.log("");
|
|
30817
|
+
console.log(label(p.twin?.name ?? "you", (p.updated_at ?? p.created_at)?.slice(0, 16) ?? ""));
|
|
30818
|
+
console.log(p.content.length > 120 ? `${p.content.slice(0, 120)}…` : p.content);
|
|
30819
|
+
const mediaCount = p.media_urls?.length ?? 0;
|
|
30820
|
+
console.log(dim(`${p.visibility ?? "public"}${mediaCount ? ` · ${mediaCount} media` : ""}`));
|
|
30821
|
+
console.log(dim(`id: ${p.id}`));
|
|
30822
|
+
}
|
|
30823
|
+
console.log("");
|
|
30824
|
+
if (data.hasMore) {
|
|
30825
|
+
console.log(dim(`More available — pass --offset ${offset + posts.length}`));
|
|
30826
|
+
}
|
|
30827
|
+
console.log(dim("Publish: amiko post publish <id>"));
|
|
30771
30828
|
}, { json: opts.json });
|
|
30772
30829
|
} catch (e5) {
|
|
30773
30830
|
spinner?.stop();
|
|
@@ -30775,6 +30832,34 @@ function registerPostCommand(program2) {
|
|
|
30775
30832
|
process.exit(1);
|
|
30776
30833
|
}
|
|
30777
30834
|
});
|
|
30835
|
+
program2.command("publish <postId>").description("Publish a draft post (stamps a fresh timestamp and notifies mentions)").option("--json", "Output as JSON").action(async (postId, opts) => {
|
|
30836
|
+
const auth = resolveAuth();
|
|
30837
|
+
const spinner = opts.json ? null : ora("Publishing draft...").start();
|
|
30838
|
+
try {
|
|
30839
|
+
const data = await amikoWebFetch(auth, `/api/posts/${encodeURIComponent(postId)}`, {
|
|
30840
|
+
method: "PATCH",
|
|
30841
|
+
body: { status: "published" }
|
|
30842
|
+
});
|
|
30843
|
+
spinner?.stop();
|
|
30844
|
+
const postUrl = data.post?.id ? `https://platform.heyamiko.com/post/${data.post.id}` : null;
|
|
30845
|
+
renderOutput(postUrl ? { ...data, post_url: postUrl } : data, (d) => {
|
|
30846
|
+
console.log(success("Post published"));
|
|
30847
|
+
const post = d.post;
|
|
30848
|
+
if (post?.id)
|
|
30849
|
+
console.log(label("Post ID", String(post.id)));
|
|
30850
|
+
if (postUrl)
|
|
30851
|
+
console.log(label("URL", postUrl));
|
|
30852
|
+
}, { json: opts.json });
|
|
30853
|
+
} catch (e5) {
|
|
30854
|
+
spinner?.stop();
|
|
30855
|
+
if (e5.status === 404) {
|
|
30856
|
+
console.error(error(`Post ${postId} not found (or not yours). Find draft ids via: amiko post drafts`));
|
|
30857
|
+
} else {
|
|
30858
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
30859
|
+
}
|
|
30860
|
+
process.exit(1);
|
|
30861
|
+
}
|
|
30862
|
+
});
|
|
30778
30863
|
program2.command("comments").description("List comments on a post").requiredOption("--id <postId>", "Target post id").option("--limit <n>", "Max results (default 20, max 100)").option("--cursor <id>", "Pagination cursor").option("--replies", "Include nested replies (default: top-level only)").option("--json", "Output as JSON").action(async (opts) => {
|
|
30779
30864
|
const auth = resolveAuth();
|
|
30780
30865
|
const spinner = opts.json ? null : ora("Loading comments...").start();
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -31,6 +31,8 @@ Call your shell tool (your runtime calls it `bash`, `shell`, `run`, or similar)
|
|
|
31
31
|
| "download the file with id X" | shell → `amiko drive download X` |
|
|
32
32
|
| "find files about Q1 revenue" | shell → `amiko drive search "Q1 revenue"` |
|
|
33
33
|
| "what comments are on my post?" | shell → `amiko post comments --id <postId>` |
|
|
34
|
+
| "save this as a post draft, don't publish yet" | shell → `amiko post create --content "…" --draft` |
|
|
35
|
+
| "publish that draft" | shell → `amiko post drafts` (copy the id) → `amiko post publish <postId>` |
|
|
34
36
|
| "search memory for X" | shell → `amiko memory search "X"` |
|
|
35
37
|
| "share the group's invite link" (asked by an admin) | shell → `amiko chat group info "<group>"` (confirm they're admin) → `amiko chat group invite "<group>"` |
|
|
36
38
|
| "react to the latest message from Ava about the team with a heart" | shell → `amiko chat read "Ava"` (find that message, copy its `id`) → `amiko chat react <messageId> ❤️ --yes` |
|
|
@@ -211,6 +213,8 @@ Workflow: start with `friends matches --limit 20 --json`; narrow with `--dimensi
|
|
|
211
213
|
|
|
212
214
|
**Sharing a post link: copy the printed URL verbatim.** `amiko post create` prints the post's canonical `URL` (`https://platform.heyamiko.com/post/<id>`; also `post_url` in `--json`). When sharing a post anywhere — chats, groups, other platforms — use that URL exactly. **Never compose a post URL yourself from the id**: guessed domains (e.g. `amiko.ai`) are not Amiko and send readers to a parked page.
|
|
213
215
|
|
|
216
|
+
**Draft posts vs the review queue — two different "drafts."** `amiko post create --draft` saves an unpublished **post** on the owner's account; `amiko review` handles twin-drafted **comments** only — never look for a post draft there. Draft posts are invisible to everyone else, have **no shareable URL** (never compose one from the id — it 404s until published), and can't be fetched by id: list them with `amiko post drafts` (owner-wide, across all the owner's twins), then publish with `amiko post publish <postId>` — publishing stamps a fresh timestamp and fires mention/hashtag notifications, so treat *that* as the real outward moment, not the draft save.
|
|
217
|
+
|
|
214
218
|
**Comments authored by a twin always go through review.** Any comment created with `amiko post comment ... --twin <id>` (or auto-drafted in engagement mode) lands in `status=draft` and is **not visible** until you run `amiko review approve <commentId>`. Comments without `--twin` (the owner posting) publish immediately. Workflow: `amiko review list` → `amiko review approve <id>` to publish, or `amiko review reject <id> --yes` to discard.
|
|
215
219
|
|
|
216
220
|
## Notifications
|