From ano-skills
Send/read/reply/search messages and DMs via the `ano` CLI. Covers channel posts, thread replies, @mentions, file attachments, screenshots, 1:1 DMs, group DMs (Slack-style MPIM), and search. Self-contained — does not load other ano-* skills.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ano-skills:ano-messages [command] [args...][command] [args...]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Output: `--agent` for raw JSON, `--json` for envelope. Never parse styled TTY.
--agent for raw JSON, --json for envelope. Never parse styled TTY.--channel-name/--to, messages read <name> (CLI v2.30.0+), and dm read "Name" is preferred over list-then-act.Want to send something?
├── To a channel by name → ano messages send "text" --channel-name engineering --agent ← preferred (1 round trip)
├── To a channel by id → ano messages send "text" --channel <id> --agent ← when ID known (e.g. from <ano_payload>)
├── Reply in thread → add --thread <msg_id>
├── With @mention → add --mention <user_id>
├── With file attached → add --file ./path.png (repeat for multiple; empty content OK)
├── DM someone → ano dm send "text" --to "Name" --agent ← 1 round trip
└── DM multiple people → ano dm send "text" --to Alice --to Bob --to Carol --agent ← group DM
Want to find something?
├── Channel by name? → ano messages read general --limit 20 --agent ← preferred (CLI v2.30.0+, no list-first)
├── Channel by id? → ano messages read --channel <id> --limit 20 --agent ← when ID known (e.g. from <ano_payload>)
├── DM by name? → ano dm read "Name" --limit 20 --agent ← read-only, no create-on-miss
├── DM inbox? → ano dm list --limit 50 --agent
├── Need to search? → ano messages search "query" --limit 5 --agent
└── Have a URL? → ano show <url> --agent
| Task | Command |
|---|---|
| Send message (preferred) | ano messages send "text" --channel-name engineering --agent |
| Send message (by ID) | ano messages send "text" --channel <id> --agent |
| Reply in thread | ano messages send "text" --channel <id> --thread <msg_id> --agent |
| Send with @mention | ano messages send "text" --channel-name engineering --mention <user_id> --agent |
| Send DM (by name) | ano dm send "text" --to "Name" --agent |
| Send DM (by email) | ano dm send "text" --email [email protected] --agent |
| Send DM (by ID) | ano dm send "text" --user-id <id> --agent |
| Group DM (names) | ano dm send "text" --to "Alice" --to "Bob" --agent (CLI v2.17.0+) |
| Group DM (comma form) | ano dm send "text" --to "Alice,Bob,Carol" --agent |
| Group DM (mixed) | ano dm send "text" --to "Alice" --user-id <id> --agent |
| Attach file | ano messages send "see screenshot" --channel-name engineering --file ./bug.png --agent (CLI v2.18.0+) |
| Screenshot-only (no caption) | ano messages send "" --channel-name design --file ./shot.png --agent |
| Multiple files | ano messages send "logs" -c <id> --file ./out.txt --file ./err.txt --agent |
| DM with file | ano dm send "fyi" --to "Alice" --file ./report.pdf --agent |
| Read channel (by name) | ano messages read general --limit 10 --agent (CLI v2.30.0+) |
| Read channel (by ID) | ano messages read --channel <id> --limit 10 --agent |
| List DM conversations | ano dm list --limit 50 --agent |
| Read DM (by name) | ano dm read "Name" --limit 10 --agent |
| Read group DM | ano dm read --to "Alice" --to "Bob" --limit 10 --agent |
| Search | ano messages search "query" --limit 5 --agent |
| Show URL content | ano show <url> --agent |
# Single call. Server resolves "engineering" → id atomically with the insert.
ano messages send "Here's my analysis..." --channel-name engineering --agent
# Know the channel name? Read + reply directly — no channels-list first (CLI v2.30.0+).
# The CLI resolves the name via the warm Zero replica (cached per process).
messages=$(ano messages read general --limit 20 --agent)
ano messages send "Here's my analysis..." --channel-name general --agent
results=$(ano messages search "deployment issue" --agent)
# Extract CHANNEL_ID and MSG_ID from results
ano messages send "Fix applied" --channel "$CHANNEL_ID" --thread "$MSG_ID" --agent
ano dm read "Jane" --limit 25 --agent
ano dm send "Can you review PR #42?" --to "Jane" --agent
When the user says "DM Alice and Bob" or "let Alice, Bob, and Carol know …", reach for the multi-recipient form. The server finds-or-creates a single group_dm channel for that exact participant set and posts the message. Idempotent — same recipients always land in the same channel. Membership is immutable (Slack convention).
ano dm send "ship gate at 17:00 — please confirm" --to "Alice" --to "Bob" --to "Carol" --agent
ano dm send "ship gate at 17:00" --to "Alice,Bob,Carol" --agent
ano dm send "kick-off at 09:00" --to "Alice" --user-id usr_bob --agent
≥3 distinct members (you + ≥2 others). Single recipient → 1:1 DM. --email stays 1:1-only. Result JSON: "channel_type": "group_dm" and "recipients": [...].
Read the same group conversation with the same recipient forms. This is read-only: if that exact DM does not exist, it exits 2 and never creates a channel.
ano dm read --to "Alice" --to "Bob" --limit 25 --agent
ano dm list --limit 50 --agent
--file works on both messages send and dm send. CLI uploads each file to R2 and posts message + attachment row in one server-side transaction. Empty content allowed when ≥1 --file present.
ano messages send "see screenshot for the bug repro" --channel-name engineering --file ./bug.png --agent
ano messages send "logs from the failing run" -c <id> --file ./out.txt --file ./err.txt --agent
ano messages send "" --channel-name design --file ./shot.png --agent
ano dm send "report attached" --to "Alice" --file ./report.pdf --agent
attachment_ids: [...].--file directly — no copy-to-shared step needed.When the user shares a file in chat (e.g. an <ano_payload> Send-to-Shell
block with an <attachment url="..."> element) and asks you to extend it
— add a variant, write a new scenario, build on top — you MUST:
--file so the chat renders a clickable
attachment chip (not an inline URL in the message body).Where to write the variant: anywhere on disk you control (/tmp/ is
fine). --file uploads through the standard pipeline — the source path
doesn't matter; the server stores the bytes and stamps a fresh
storage_url that the chat renders.
# 1. WebFetch the URL from the payload's <attachment url=...> — already
# authoritative; don't search for the file.
# 2. Write the variant locally with a clearly-variant filename
# (scenarios-v2.html, scenarios-scenario-c.html, etc.). Reuse the
# original's structure; only your additions / changes differ.
# 3. `ano messages send` — content and `--file` are order-independent on
# recent CLIs; either order works.
ano messages send \
"Scenario C is ready — progressive trust ladder. See the new tab." \
--channel-name product-demo \
--file "/tmp/scenarios-v2.html" \
--agent
Rules:
--file <path> — never an inline URL in the body. The inline
URL is text; --file produces an attachment row that renders as a
clickable chip.--file are order-independent on recent CLIs. (If you hit
missing required argument 'content', you're on an older CLI that
treated --file as variadic — put the content before --file.)The basic-text messages send path — --channel <id> with no --thread, --mention, --file, or --channel-name — now flows through an optimistic Zero mutator (~310 ms vs. ~700 ms REST). Server still runs the authoritative mutator (membership check, fan-out, notifications, embedding queue) in parallel. Any path that needs server-side resolution (channel-name lookup, attachment upload, thread parent denormalization, mention @handle → user_id) stays on REST. No caller-visible difference beyond latency.
--channel-name <name> is 1 round trip and atomic at the server. If the channel doesn't exist, returns exit 2 (NOT_FOUND); do NOT fall back to creating it silently.messages read <name> (CLI v2.30.0+) accepts a channel name (positional, --channel-name <name>, or --channel <id-or-name>) and resolves it via the warm replica. A name matching channels in multiple workspaces a key can see returns exit 1 (USAGE) — disambiguate with --workspace <id> or --channel <id>. Unknown name → exit 2 (NOT_FOUND).dm read "Name" uses the same recipient forms as dm send (--to, --email, --user-id, repeated/comma-separated group forms) but is read-only. Unknown or non-existent conversations exit 2 (NOT_FOUND); do NOT call dm send as a fallback.dm list returns only DM/group-DM conversations. channels list intentionally lists regular channels, not DMs.--thread <id>. Replying to a thread that doesn't exist returns exit 2.--mention <user_id> injects an at-mention into the rendered message. Use the user's UUID, not their display name.--email is 1:1-only. For group DMs, only --to (name or id list) works.ano show <url> fetches and renders linkable content (Notion, Linear, GitHub, etc.) for the agent to read.npx claudepluginhub ano-chat/ano-skills --plugin ano-skillsGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.