Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions skills/comms-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,13 @@ tdc channel threads <ref> --since 2026-01-01 # Filter by last-updated date (ISO)
tdc channel threads <ref> --limit 20 # Max threads per page (default: 50)
tdc channel threads <ref> --limit 20 --cursor <cursor-from-prev> # Paginate
tdc channel threads <ref> --json # { results, nextCursor } with isUnread + url
tdc channel members <channel-ref> # List a channel's members + groups fully in the channel
tdc channel members <ref> --json # JSON with id, name, workspaceId, members
tdc channel members add <ref> alice group:Design # Add users and/or expand group:<ref> members
tdc channel members add <ref> a@d.com id:789 --json # Add refs, output result as JSON
tdc channel members remove <ref> alice group:Frontend # Remove users and/or group members
tdc channel members set <ref> group:Squad --apply # Replace membership with the resolved set
tdc channel members set <ref> alice bob # Dry-run by default; refuses to remove you (--include-self to override)
tdc groups # List workspace groups
tdc groups --search "frontend" # Filter groups by name (case-insensitive)
tdc groups --json # JSON output
Expand All @@ -251,6 +258,8 @@ If a channel is not found in `tdc channels`, widen with broader listings such as

`tdc channel threads` returns every thread in the channel; pagination filters (`--limit`, `--cursor`, `--since`, `--until`, `--unread`) are applied client-side after fetch. `--archive-filter` is applied server-side. Results are sorted newest-first by last activity. In `--json` / `--ndjson`, the response includes a `nextCursor` string (opaque) you can pass via `--cursor` to fetch the next page; NDJSON emits the cursor as a final `{ "_meta": true, "nextCursor": "..." }` line.

For `tdc channel members add/remove/set`, refs accept user identifiers (`id:N`, email, name) or `group:<ref>`, which expands to the group's current members. Group expansion is one-shot — it is not a persistent link, so users added to the group later will not auto-join the channel. `set` replaces membership with the resolved set and is dry-run by default (pass `--apply` to mutate); it refuses to remove the acting user unless `--include-self` is passed.

## Reactions

```bash
Expand Down
9 changes: 9 additions & 0 deletions src/commands/channel/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { type ChannelMutationOptions, mutateChannelMembership } from './membership-helpers.js'

export async function addChannelMembers(
channelRef: string,
refs: string[],
options: ChannelMutationOptions,
): Promise<void> {
return mutateChannelMembership(channelRef, refs, 'add', options)
}
96 changes: 95 additions & 1 deletion src/commands/channel/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { Command, Option } from 'commander'
import { withCaseInsensitiveChoices } from '../../lib/completion.js'
import { addChannelMembers } from './add.js'
import { createChannel } from './create.js'
import { listChannels } from './list.js'
import { listChannelMembers } from './members.js'
import { removeChannelMembers } from './remove.js'
import { setChannelMembers } from './set.js'
import { showChannelThreads } from './threads.js'
import { updateChannel } from './update.js'

export function registerChannelCommand(program: Command): void {
const channel = program
.command('channel')
.alias('channels')
.description('Channel operations (list, create, update, threads)')
.description('Channel operations (list, create, update, threads, members)')

channel
.command('list [workspace-ref]', { isDefault: true })
Expand Down Expand Up @@ -131,4 +135,94 @@ Notes:
and --unread are applied client-side; --archive-filter is applied server-side.`,
)
.action(showChannelThreads)

const members = channel
.command('members')
.description('Channel membership operations (list, add, remove, set)')

members
.command('list <channel-ref>', { isDefault: true })
.description("List a channel's members and groups fully present in the channel")
.option('--json', 'Output as JSON')
.option('--ndjson', 'Output as newline-delimited JSON')
.option('--full', 'Include all fields in JSON output')
.addHelpText(
'after',
`
Examples:
tdc channel members 12345
tdc channel members "general" --json
tdc channel members add 12345 alice group:Design
tdc channel members remove 12345 alice
tdc channel members set 12345 group:Squad --apply

Notes:
"Groups fully in channel" lists groups whose entire current membership is
already in the channel — a hint, not a persistent link.`,
)
.action(listChannelMembers)

members
.command('add <channel-ref> [refs...]')
.description('Add users and/or groups to a channel')
.option('--dry-run', 'Show what would change without changing')
.option('--json', 'Output result as JSON')
.option('--full', 'Include the full updated channel in JSON output')
.addHelpText(
'after',
`
Examples:
tdc channel members add 12345 alice@doist.com bob@doist.com
tdc channel members add "general" group:Frontend
tdc channel members add 12345 alice group:Design id:789 --json

Notes:
Refs accept user identifiers (id:N, email, name) or "group:<ref>" to expand
a group to its current members. Group expansion is one-shot — users added
later to the group will not auto-join the channel.`,
)
.action(addChannelMembers)

members
.command('remove <channel-ref> [refs...]')
.description('Remove users and/or groups from a channel')
.option('--dry-run', 'Show what would change without changing')
.option('--json', 'Output result as JSON')
.option('--full', 'Include the full updated channel in JSON output')
.addHelpText(
'after',
`
Examples:
tdc channel members remove 12345 alice@doist.com
tdc channel members remove "general" group:Frontend

Notes:
Refs accept user identifiers (id:N, email, name) or "group:<ref>" to expand
a group to its current members.`,
)
.action(removeChannelMembers)

members
.command('set <channel-ref> [refs...]')
.description('Replace channel membership with the resolved set of refs')
.option('--apply', 'Actually mutate (otherwise dry-run)')
.option('--include-self', 'Allow set to remove the acting user')
.option('--dry-run', 'Force dry-run (default behaviour)')
.option('--json', 'Output result as JSON')
.option('--full', 'Include the full updated channel in JSON output')
.addHelpText(
'after',
`
Examples:
tdc channel members set 12345 group:Frontend group:Design
tdc channel members set "general" alice bob carol --apply
tdc channel members set 12345 group:Squad --apply --include-self

Notes:
Dry-run by default. Pass --apply to mutate.
Refuses to remove the acting user unless --include-self is also passed.
Group expansion is one-shot — users added later to a referenced group will
not auto-join the channel.`,
)
.action(setChannelMembers)
}
Loading
Loading