Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ This is a TypeScript CLI (`tdc`) for Comms messaging, built with Commander.js.
**Lib** (`src/lib/`):

- `api.ts` - Singleton CommsApi client from `@doist/comms-sdk`, workspace/user caching
- `refs.ts` - Reference parsing: accepts IDs (`id:123` or bare `123`), Comms URLs, or fuzzy names for workspaces/users
- `refs.ts` - Reference parsing: accepts IDs (`id:<id>` or bare IDs when unambiguous), Comms URLs, or fuzzy names for workspaces/users/channels/groups
- `output.ts` - JSON/NDJSON formatting with essential field filtering per entity type
- `config.ts` - Persists config to `~/.config/comms-cli/config.json`
- `auth.ts` - Token loading/saving/clearing (env var or config file)
- `markdown.ts` - Terminal markdown rendering via `marked` + `marked-terminal`
- `completion.ts` - Commander tree-walker + completion helpers for shell tab completion

**Reference system**: The CLI accepts flexible references throughout - numeric IDs, `id:` prefixed IDs, full Comms URLs (parsed via `parseCommsUrl`), or fuzzy name matching for workspaces/users.
**Reference system**: The CLI accepts flexible references throughout - `id:` prefixed IDs, bare IDs when unambiguous, full Comms URLs (parsed via `parseCommsUrl`), or fuzzy name matching for workspaces/users/channels/groups. Workspace and user IDs are numeric; most Comms entity IDs (threads, channels, comments, messages, conversations, groups) are opaque strings.

## Key Patterns

Expand Down
7 changes: 4 additions & 3 deletions CODEBASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,10 @@ don't duplicate it here.

## Ref resolution

All in `src/lib/refs.ts`. A ref is one of: a bare numeric id (`123`), an
`id:`-prefixed id, a full Comms URL (`parseCommsUrl` → `classifyCommsUrl` routes
`tdc view <url>`), or a fuzzy name (workspaces/users/channels/groups). Async
All in `src/lib/refs.ts`. A ref is one of: an `id:`-prefixed id, a bare id when
unambiguous, a full Comms URL (`parseCommsUrl` → `classifyCommsUrl` routes
`tdc view <url>`), or a fuzzy name (workspaces/users/channels/groups). Workspace
and user IDs are numeric; most Comms entity IDs are opaque strings. Async
resolvers return the resolved id or entity and throw `CliError` (e.g.
`AMBIGUOUS_*`, `*_NOT_FOUND`) on miss. `looksLikeRawId()` decides when a string
is tried as an id vs a name.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ tdc inbox --unread # unread threads only
tdc mentions # content mentioning you
tdc mentions --since 2026-04-01 --all --json
tdc thread view <ref> # view thread with comments
tdc thread view <ref> --comment 123 # view a specific comment
tdc thread view <ref> --comment id:CbM8n2Kp4Qx6Rz9Lm3Va # view a specific comment
tdc thread reply <ref> # reply to a thread
tdc thread rename <ref> "New title" # rename a thread
tdc thread update <ref> "New body" # edit a thread's body (first post)
Expand All @@ -145,7 +145,7 @@ tdc groups add-user <ref> alice@doist.com bob@doist.com
tdc groups remove-user <ref> id:123,id:456
```

References accept IDs (`123` or `id:123`), Comms URLs, or fuzzy names (for workspaces/users).
References accept IDs (`id:<id>`; bare IDs when unambiguous), Comms URLs, or fuzzy names (for workspaces/users/channels/groups). Workspace and user IDs are numeric; threads, channels, comments, messages, conversations, and groups use opaque string IDs.

Run `tdc --help` or `tdc <command> --help` for more options.

Expand Down
32 changes: 16 additions & 16 deletions docs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Arguments:

Content input priority:

1. Stdin (if piped: `echo "text" | tdc thread reply id:123`)
1. Stdin (if piped: `echo "text" | tdc thread reply id:CbT8n2Kp4Qx6Rz9Lm3Va`)
2. Argument (if provided)
3. Opens `$EDITOR` (if neither stdin nor argument)

Expand Down Expand Up @@ -388,8 +388,8 @@ Options:

Commands support these reference formats:

- `id:123456` - Direct ID lookup
- `123456` - Bare ID (when unambiguous context)
- `id:CbT8n2Kp4Qx6Rz9Lm3Va` - Direct ID lookup
- `CbT8n2Kp4Qx6Rz9Lm3Va` - Bare ID (when unambiguous context)
- Full Comms URLs - Parsed to extract IDs
- `"Workspace Name"` - Name matching for workspaces only (case-insensitive)

Expand Down Expand Up @@ -492,40 +492,40 @@ tdc inbox
tdc inbox --unread

# View a thread
tdc thread view id:123456
tdc thread view https://comms.todoist.com/a/12345/ch/67890/t/123456
tdc thread view id:CbT8n2Kp4Qx6Rz9Lm3Va
tdc thread view https://comms.todoist.com/a/12345/ch/CbC8n2Kp4Qx6Rz9Lm3Va/t/CbT8n2Kp4Qx6Rz9Lm3Va

# Reply to a thread
tdc thread reply id:123456 "Great idea!"
echo "Multiline\nreply" | tdc thread reply id:123456
tdc thread reply id:123456 # opens $EDITOR
tdc thread reply id:CbT8n2Kp4Qx6Rz9Lm3Va "Great idea!"
echo "Multiline\nreply" | tdc thread reply id:CbT8n2Kp4Qx6Rz9Lm3Va
tdc thread reply id:CbT8n2Kp4Qx6Rz9Lm3Va # opens $EDITOR

# Mark thread as done
tdc thread done id:123456 --yes
tdc thread done id:CbT8n2Kp4Qx6Rz9Lm3Va --yes

# List unread conversations
tdc conversation unread

# View and reply to a conversation
tdc conversation view id:456789
tdc conversation reply id:456789 "Thanks!"
tdc conversation view id:CbV8n2Kp4Qx6Rz9Lm3Va
tdc conversation reply id:CbV8n2Kp4Qx6Rz9Lm3Va "Thanks!"

# Search
tdc search "quarterly report"
tdc search "bug fix" --author id:123 --since 2024-01-01

# React to content
tdc react thread id:123456 +1
tdc react comment id:789 👍
tdc unreact message id:456 heart
tdc react thread id:CbT8n2Kp4Qx6Rz9Lm3Va +1
tdc react comment id:CbM8n2Kp4Qx6Rz9Lm3Va 👍
tdc unreact message id:CbS8n2Kp4Qx6Rz9Lm3Va heart

# List channels and users
tdc channels
tdc users --search "john"

# Dry run before mutating
tdc thread reply id:123 "test" --dry-run
tdc thread done id:123 --dry-run
tdc thread reply id:CbT8n2Kp4Qx6Rz9Lm3Va "test" --dry-run
tdc thread done id:CbT8n2Kp4Qx6Rz9Lm3Va --dry-run

# JSON output for scripting
tdc inbox --json
Expand Down
32 changes: 16 additions & 16 deletions skills/comms-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ tdc thread view <ref> --raw # Show raw markdown
tdc thread create <channel-ref> "Title" "content" # Create a new thread
tdc thread create <channel-ref> "Title" "content" --json # Create and return as JSON
tdc thread create <channel-ref> "Title" "content" --json --full # Include all thread fields
tdc thread create <channel-ref> "Title" "content" --notify 123,456 # Notify specific users and/or groups by ID
tdc thread create <channel-ref> "Title" "content" --notify 123,Cbzzm11ZeYZoJYD4a6rti # Notify user and/or group IDs
tdc thread create <channel-ref> "Title" "content" --unarchive # Land thread in author's Inbox (overrides default Comms auto-archive)
tdc thread create <channel-ref> "Title" "content" --no-unarchive # Force archive even when userSettings.unarchiveNewThreads=true
tdc thread create <channel-ref> "Title" "content" --dry-run # Preview without posting
Expand All @@ -112,7 +112,7 @@ tdc thread done <ref> --yes # Archive thread (mark done)
tdc thread done <ref> --yes --json # Archive and return status as JSON
tdc thread mark-read <ref> # Mark a thread read
tdc thread mark-read <ref> <ref> --yes # Mark multiple threads read
printf "123\n456\n" | tdc thread mark-read --dry-run # Preview bulk mark-read from stdin
printf "id:CbT8n2Kp4Qx6Rz9Lm3Va\nid:CbT9m4Qr7Vz2Nx8Lp5Sa\n" | tdc thread mark-read --dry-run # Preview bulk mark-read from stdin
tdc thread mute <ref> # Mute thread for 60 minutes (default)
tdc thread mute <ref> --minutes 480 # Mute for custom duration
tdc thread mute <ref> --json # Mute and return { id, mutedUntil } as JSON
Expand Down Expand Up @@ -248,8 +248,8 @@ tdc channel update <ref> --description "Team discussions" --json --full # Update
tdc channel delete <channel-ref> --yes # Permanently delete a channel (requires --yes; usually admin-only)
tdc channel delete <ref> --dry-run # Preview deletion
tdc channel archive <channel-ref> # Archive a channel (no-op if already archived)
tdc channel unarchive id:<id> # Unarchive a channel (pass id:/numeric ref for archived channels)
tdc channel threads <channel-ref> # List threads in a channel (fuzzy name, id:, numeric ID, or URL)
tdc channel unarchive id:<id> # Unarchive a channel (pass id: for archived channels)
tdc channel threads <channel-ref> # List threads in a channel (fuzzy name, id:, or URL)
tdc channel threads "general" --unread # Only unread threads
tdc channel threads <ref> --archive-filter all # Include archived threads (active|archived|all)
tdc channel threads <ref> --since 2026-01-01 # Filter by last-updated date (ISO)
Expand Down Expand Up @@ -410,9 +410,9 @@ Run without --dry-run to execute.
## Reference System

Commands accept flexible references:
- **Numeric IDs**: `123` or `id:123`
- **IDs**: `id:<id>` (or a bare ID when unambiguous). Workspace/user IDs are numeric; most Comms entity IDs are opaque strings.
- **Comms URLs**: Full `https://comms.todoist.com/...` URLs (parsed automatically)
- **Fuzzy names**: For workspaces/users - `"My Workspace"` or partial matches
- **Fuzzy names**: For workspaces/users/channels/groups - `"My Workspace"` or partial matches

## Piping Input

Expand All @@ -429,26 +429,26 @@ If no content argument is provided and no stdin is piped, the CLI opens `$EDITOR
`tdc thread mark-read` also accepts thread refs from stdin, one per line:

```bash
printf "123\n456\n" | tdc thread mark-read --yes
printf "id:CbT8n2Kp4Qx6Rz9Lm3Va\nid:CbT9m4Qr7Vz2Nx8Lp5Sa\n" | tdc thread mark-read --yes
```

## Common Workflows

**View by URL (auto-routes to the right command):**
```bash
tdc view https://comms.todoist.com/1585/ch/100/t/200 # View thread
tdc view https://comms.todoist.com/a/1585/ch/100/t/200 # View thread
tdc view https://comms.todoist.com/a/1585/ch/100/t/200/c/300 # View comment
tdc view https://comms.todoist.com/a/1585/msg/400 # View conversation
tdc view https://comms.todoist.com/a/1585/msg/400/m/500 --json # View message as JSON
tdc view https://comms.todoist.com/1585/ch/CbC8n2Kp4Qx6Rz9Lm3Va/t/CbT8n2Kp4Qx6Rz9Lm3Va # View thread
tdc view https://comms.todoist.com/a/1585/ch/CbC8n2Kp4Qx6Rz9Lm3Va/t/CbT8n2Kp4Qx6Rz9Lm3Va # View thread
tdc view https://comms.todoist.com/a/1585/ch/CbC8n2Kp4Qx6Rz9Lm3Va/t/CbT8n2Kp4Qx6Rz9Lm3Va/c/CbM8n2Kp4Qx6Rz9Lm3Va # View comment
tdc view https://comms.todoist.com/a/1585/msg/CbV8n2Kp4Qx6Rz9Lm3Va # View conversation
tdc view https://comms.todoist.com/a/1585/msg/CbV8n2Kp4Qx6Rz9Lm3Va/m/CbS8n2Kp4Qx6Rz9Lm3Va --json # View message as JSON
```

**Check inbox and respond:**
```bash
tdc inbox --unread --json
tdc thread view <id> --unread
tdc thread reply <id> "Thanks, I'll look into this."
tdc thread done <id> --yes
tdc thread view <thread-ref> --unread
tdc thread reply <thread-ref> "Thanks, I'll look into this."
tdc thread done <thread-ref> --yes
```

**Search and review:**
Expand All @@ -464,5 +464,5 @@ tdc conversation unread --json
tdc conversation list --kind group --json # find a group DM by participants/name
tdc conversation view <conversation-id>
tdc conversation with "Alice Example"
tdc conversation reply <id> "Got it, thanks!"
tdc conversation reply <conversation-ref> "Got it, thanks!"
```
30 changes: 15 additions & 15 deletions src/commands/channel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ Examples:
tdc channel unarchive id:abc123 --json

Notes:
Name-ref resolution only finds active channels — pass id: or numeric ID for archived channels.`,
Name-ref resolution only finds active channels — pass an id: ref for archived channels.`,
)
.action(unarchiveChannel)

Expand Down Expand Up @@ -182,12 +182,12 @@ Notes:
'after',
`
Examples:
tdc channel threads 12345
tdc channel threads id:CbC8n2Kp4Qx6Rz9Lm3Va
tdc channel threads "general"
tdc channel threads id:12345 --unread
tdc channel threads 12345 --archive-filter all --since 2026-01-01
tdc channel threads 12345 --limit 20 --json
tdc channel threads 12345 --limit 20 --cursor <cursor-from-previous>
tdc channel threads id:CbC8n2Kp4Qx6Rz9Lm3Va --unread
tdc channel threads id:CbC8n2Kp4Qx6Rz9Lm3Va --archive-filter all --since 2026-01-01
tdc channel threads id:CbC8n2Kp4Qx6Rz9Lm3Va --limit 20 --json
tdc channel threads id:CbC8n2Kp4Qx6Rz9Lm3Va --limit 20 --cursor <cursor-from-previous>

Notes:
Sorted newest-first by last activity. --limit, --cursor, --since, --until,
Expand All @@ -209,11 +209,11 @@ Notes:
'after',
`
Examples:
tdc channel members 12345
tdc channel members id:CbC8n2Kp4Qx6Rz9Lm3Va
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
tdc channel members add id:CbC8n2Kp4Qx6Rz9Lm3Va alice group:Design
tdc channel members remove id:CbC8n2Kp4Qx6Rz9Lm3Va alice
tdc channel members set id:CbC8n2Kp4Qx6Rz9Lm3Va group:Squad --apply

Notes:
"Groups fully in channel" lists groups whose entire current membership is
Expand All @@ -231,9 +231,9 @@ Notes:
'after',
`
Examples:
tdc channel members add 12345 alice@doist.com bob@doist.com
tdc channel members add id:CbC8n2Kp4Qx6Rz9Lm3Va alice@doist.com bob@doist.com
tdc channel members add "general" group:Frontend
tdc channel members add 12345 alice group:Design id:789 --json
tdc channel members add id:CbC8n2Kp4Qx6Rz9Lm3Va alice group:Design id:789 --json

Notes:
Refs accept user identifiers (id:N, email, name) or "group:<ref>" to expand
Expand All @@ -252,7 +252,7 @@ Notes:
'after',
`
Examples:
tdc channel members remove 12345 alice@doist.com
tdc channel members remove id:CbC8n2Kp4Qx6Rz9Lm3Va alice@doist.com
tdc channel members remove "general" group:Frontend

Notes:
Expand All @@ -273,9 +273,9 @@ Notes:
'after',
`
Examples:
tdc channel members set 12345 group:Frontend group:Design
tdc channel members set id:CbC8n2Kp4Qx6Rz9Lm3Va group:Frontend group:Design
tdc channel members set "general" alice bob carol --apply
tdc channel members set 12345 group:Squad --apply --include-self
tdc channel members set id:CbC8n2Kp4Qx6Rz9Lm3Va group:Squad --apply --include-self

Notes:
Dry-run by default. Pass --apply to mutate.
Expand Down
14 changes: 7 additions & 7 deletions src/commands/comment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export function registerCommentCommand(program: Command): void {
'after',
`
Examples:
tdc comment 12345
tdc comment view 12345 --json`,
tdc comment id:CbM8n2Kp4Qx6Rz9Lm3Va
tdc comment view id:CbM8n2Kp4Qx6Rz9Lm3Va --json`,
)
.action((ref, options) => {
if (!ref) {
Expand All @@ -40,9 +40,9 @@ Examples:
'after',
`
Examples:
tdc comment update 12345 "Updated text"
echo "New content" | tdc comment update 12345
tdc comment update 12345 "Fixed" --json`,
tdc comment update id:CbM8n2Kp4Qx6Rz9Lm3Va "Updated text"
echo "New content" | tdc comment update id:CbM8n2Kp4Qx6Rz9Lm3Va
tdc comment update id:CbM8n2Kp4Qx6Rz9Lm3Va "Fixed" --json`,
)
.action(updateComment)

Expand All @@ -56,8 +56,8 @@ Examples:
'after',
`
Examples:
tdc comment delete 12345 --yes
tdc comment delete 12345 --dry-run`,
tdc comment delete id:CbM8n2Kp4Qx6Rz9Lm3Va --yes
tdc comment delete id:CbM8n2Kp4Qx6Rz9Lm3Va --dry-run`,
)
.action(deleteComment)
}
24 changes: 12 additions & 12 deletions src/commands/conversation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ Notes:
'after',
`
Examples:
tdc conversation 12345
tdc conversation view 12345 --limit 20
tdc conversation view 12345 --since 2025-01-01 --json`,
tdc conversation id:CbV8n2Kp4Qx6Rz9Lm3Va
tdc conversation view id:CbV8n2Kp4Qx6Rz9Lm3Va --limit 20
tdc conversation view id:CbV8n2Kp4Qx6Rz9Lm3Va --since 2025-01-01 --json`,
)
.action((ref, options) => {
if (!ref) {
Expand Down Expand Up @@ -136,10 +136,10 @@ Examples:
'after',
`
Examples:
tdc conversation reply 12345 "Hello!"
echo "Message body" | tdc conversation reply 12345
tdc conversation reply 12345 "Update" --json
tdc conversation reply 12345 "See attached" --file ./photo.jpg`,
tdc conversation reply id:CbV8n2Kp4Qx6Rz9Lm3Va "Hello!"
echo "Message body" | tdc conversation reply id:CbV8n2Kp4Qx6Rz9Lm3Va
tdc conversation reply id:CbV8n2Kp4Qx6Rz9Lm3Va "Update" --json
tdc conversation reply id:CbV8n2Kp4Qx6Rz9Lm3Va "See attached" --file ./photo.jpg`,
)
.action(replyToConversation)

Expand All @@ -153,8 +153,8 @@ Examples:
'after',
`
Examples:
tdc conversation done 12345 --yes
tdc conversation done 12345 --dry-run`,
tdc conversation done id:CbV8n2Kp4Qx6Rz9Lm3Va --yes
tdc conversation done id:CbV8n2Kp4Qx6Rz9Lm3Va --dry-run`,
)
.action(markConversationDone)

Expand All @@ -169,8 +169,8 @@ Examples:
'after',
`
Examples:
tdc conversation mute 12345
tdc conversation mute 12345 --minutes 480`,
tdc conversation mute id:CbV8n2Kp4Qx6Rz9Lm3Va
tdc conversation mute id:CbV8n2Kp4Qx6Rz9Lm3Va --minutes 480`,
)
.action(muteConversation)

Expand All @@ -184,7 +184,7 @@ Examples:
'after',
`
Examples:
tdc conversation unmute 12345`,
tdc conversation unmute id:CbV8n2Kp4Qx6Rz9Lm3Va`,
)
.action(unmuteConversation)
}
Loading
Loading