From ano-skills
Channel + workspace administration via the `ano` CLI — list channels, list workspaces + members, archive a channel, add/remove channel + workspace members, send invites. Also covers the unread-triage "what did I miss?" surface via `ano channels list --unread`. Self-contained. Does NOT cover messaging (see ano-messages) or coworker creation (see ano-coworkers).
How this skill is triggered — by the user, by Claude, or both
Slash command
/ano-skills:ano-channels [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.ano channels list --agent and ano users list --agent are the lookups.Listing or inspecting?
├── Which channels exist? → ano channels list --agent
├── Who's in the workspace? → ano users list --agent
└── Multiple workspaces? → ano workspaces list --agent
Managing a channel?
├── Archive (irreversible-ish) → ano channels archive <channel-id> --agent
├── Add a teammate → ano channels member-add <channel-id> <user-id> --agent
├── Remove a teammate → ano channels member-remove <channel-id> <user-id> --agent
└── Need the user-id? → ano users list --agent (by display name or email)
Managing workspace membership?
├── Add a member (or rejoin) → ano workspaces member-add <workspace-id> <user-id> --agent
├── Remove a member (soft-delete) → ano workspaces member-remove <workspace-id> <user-id> --agent
├── Need the user-id? → ano user_get_by_email "[email protected]" --agent
└── List members → ano users list --agent
Inviting someone NOT yet in the workspace?
├── By email → ano invite [email protected] --expires-hours 72
└── Open invite (no email) → ano invite --expires-hours 72
| Task | Command |
|---|---|
| List channels | ano channels list --agent |
| Unread channels (triage) | ano channels list --unread --agent |
| Create channel | ano channels create <name> [--private] [--topic ...] [--members u1,u2] |
| List users | ano users list --agent |
| List workspaces | ano workspaces list --agent |
| Set active workspace | ano workspaces use <workspace-id> |
| Archive channel | ano channels archive <channel-id> --agent |
| Add channel member | ano channels member-add <channel-id> <user-id> --agent |
| Remove channel member | ano channels member-remove <channel-id> <user-id> --agent |
| Add workspace member | ano workspaces member-add <workspace-id> <user-id> --agent |
| Remove workspace member | ano workspaces member-remove <workspace-id> <user-id> --agent |
| Invite teammate by email | ano invite <email> [--expires-hours N] |
| Open invite (no email) | ano invite [--expires-hours N] |
When the user asks an open question like "what did I miss?" / "anything new?" / "catch me up", do NOT ask which channel — discover it.
Prefer the pre-baked /unread slash command if your runtime can
invoke one programmatically. The command (at commands/unread.md
in this plugin) bundles the unread-discovery + per-channel-history
fetch into one shot. On Claude Code specifically, slash commands are
invoked via the Skill tool — when the user types /<name> the
runtime maps it to Skill(skill: "<name>", ...), and you can call
the same tool yourself for natural-language triggers using the exact
slug from your available-skills system reminder (typically
ano-skills:unread, or unread if unprefixed). Other runtimes may
expose slash commands differently, or not at all — fall back to the
bash recipe below in that case.
Manual fallback (whenever you can't reach the slash command — different runtime, slug not listed, or no slash-command infrastructure at all):
# Single bash one-liner — same recipe `/unread` bakes in. The `JUMP:`
# prefix line before each message gives you the pre-built deep-link URL;
# wrap it in Markdown `[Jump to message](...)` when you emit the summary.
# The base comes from `ano web-url` — the deployed web app for the current
# environment (app.ano.dev / app-staging.ano.dev / localhost:1420 in dev).
# It's a normal web URL: clicked in the Ano in-app shell it opens the
# channel in-app; clicked in any other terminal it opens the web app.
BASE=$(ano web-url 2>/dev/null || echo "http://localhost:1420")
ids=$(ano channels list --unread --agent | jq -r '.id')
if [ -z "$ids" ]; then
echo "(no unread channels)"
else
for ch in $ids; do
echo "=== CHANNEL $ch ==="
ano messages read --channel "$ch" --limit 100 --agent | \
jq -r --arg ch "$ch" --arg base "$BASE" '"JUMP: \($base)/?channel=\($ch)&message=\(.id)\n" + (. | tostring)'
done
fi
If the unread list is empty, say "you're caught up" and stop.
Rules for the summary itself (apply to both paths):
--unread are the ONLY channels you summarise.
Don't iterate other channels "for completeness" — they have nothing
new for this user by definition.[Jump to message](<web-url>/?channel=<channel_id>&message=<message_id>)
(base from ano web-url; the JUMP: line has the full URL pre-built).
Pick by content: a wrap-up message ("wrapping up", "to recap",
"summary of options", scenario lists, embedded prototype URLs,
explicit asks for sign-off) wins; otherwise the most recent. Do not
phrase as "see message " — emit the Markdown link. Without it
the summary is dead-end text.channels=$(ano channels list --agent)
# Extract CHANNEL_ID for "old-project"
ano channels archive "$CHANNEL_ID" --agent
# Archived channels disappear from sidebars; messages are preserved.
users=$(ano users list --agent)
# Find USER_ID by display name or email
ano channels member-add "$CHANNEL_ID" "$USER_ID" --agent
# Idempotent: already-a-member returns 200, doesn't error.
# Rejects: archived channel (400), DM/space target (400), non-workspace member (404).
ano invite [email protected] --expires-hours 72
# Returns { token, invite_url, expires_at } — share invite_url with Alice.
# Re-running with the same email revokes the previous token first.
ano channels archive and ano channels member-remove now flow through optimistic Zero mutators when the daemon's Zero replica is connected — the local replica reflects the change immediately and the server's authoritative mutator confirms in parallel (~145 ms archive, ~346 ms member-remove vs. ~700 ms REST). Auth failures still throw synchronously. Falls back to REST automatically if Zero is unavailable; behavior is identical from the caller's perspective.
member-add is idempotent. Rejects archived channels, non-channel types (DMs, spaces), and non-workspace users with 400/404 — no opaque 500s.member-remove is a soft-delete (removed_at tombstone). Re-addable later; history preserved. Immediately after channels create, the local channel_members row may not have replicated yet — the CLI transparently falls back to REST for that brief window.member-add is idempotent — rejoins removed members (clears removed_at), promotes collaborator → member, no-op if already a full member. Auto-joins public channels in the same transaction.member-remove is a soft-delete. Forbidden against the workspace's primary_owner — transfer ownership through the desktop UI first.removed_by). Use the desktop UI.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.