Skip to content

feat(conversation): add conversation list command#35

Merged
lmjabreu merged 4 commits into
mainfrom
lmjabreu/infallible-wing-85542b
Jun 18, 2026
Merged

feat(conversation): add conversation list command#35
lmjabreu merged 4 commits into
mainfrom
lmjabreu/infallible-wing-85542b

Conversation

@lmjabreu

Copy link
Copy Markdown
Contributor

Overview

Adds tdc conversation list so you can find a DM or group conversation by who's in it, its title, or whether it's a 1:1 or a group. There was no way to list conversations from the CLI before: conversation unread shows only unread, and conversation with <user> finds only your 1:1 with one person.

It reuses the existing plumbing rather than adding new infrastructure: the shared fetch (getAllConversationsgetConversationsByState) and renderer (listConversationsWithUserrenderConversationList) were generalised, and conversation with now calls the renamed helpers with no behaviour change. Flag shapes follow the family: a value-enum flag per mutually-exclusive dimension (--kind, --state), like channel list --state and search --type, with active-by-default lists and archived as opt-in.

DM content search stays out of scope; it already works via tdc search --type messages / --conversation / --to and just needs better docs (separate change).

Usage

tdc conversation list                       # active DMs and groups, newest first
tdc conversation list --kind group          # only groups (3+ people)
tdc conversation list --participant "Jane"  # conversations including these users
tdc conversation list --name "release"      # title substring match
tdc conversation list --state archived --json

Flags: --workspace, --participant, --name, --kind <group|direct>, --state <active|all|archived> (default active), --snippet, --limit, --json / --ndjson / --full.

Test plan

  • type-check, lint:check, check:skill-sync, and test (815 pass, 13 new) all green.
  • Live read-only smoke test: kind / participant / name / state / limit filters return the expected rows (including a 117-person group); an unmatched --name prints the empty message; an ambiguous --participant surfaces a clean AMBIGUOUS_USER error.

For the maintainer

getConversations isn't server-paginated (same as getThreads / getChannels), so this is one fetch plus client-side --limit. Any server-side cap on conversation count for very large workspaces? Not a blocker; with --include-groups already relies on the same single-call behaviour.

lmjabreu and others added 3 commits June 18, 2026 00:34
`tdc thread reply/create --notify` already accepts custom group IDs
(partitioned via resolveNotifyIds → groups), but the help text only
mentioned "user IDs". Since Comms group IDs are non-numeric base58
strings that look nothing like user IDs, the capability was effectively
undiscoverable from --help. Mention groups in both option descriptions
and add a group-notify example to the reply command's existing example
block (alongside --close / --file).

No behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per AGENTS.md, src/lib/skills/content.ts must track command-description and
example changes. The reply/create --notify examples said "users" only; make
them mention groups and add a group-notify example (base58 group ID next to a
numeric user ID). Regenerated skills/comms-cli/SKILL.md via `sync:skill`;
check:skill-sync passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
List DMs and group conversations filtered by participant, name, or kind.
Reuses the existing plumbing: getAllConversations becomes
getConversationsByState(state), and listConversationsWithUser becomes the
shared renderConversationList. Defaults to active conversations; --state
opts into archived. SKILL_CONTENT and README updated, SKILL.md regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lmjabreu lmjabreu requested a review from scottlovegrove June 18, 2026 09:27

@doistbot doistbot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds tdc conversation list with kind, participant, name, state, and limit filters, nicely reusing the existing conversation fetch and render plumbing.

Few things worth tightening:

  • Participant resolution misses removed users: --participant resolves against the active workspace roster only, so --state archived --participant "Former User" fails even when that archived conversation exists. Resolve participants with includeRemoved: true so archived DMs/groups are findable.
  • Avoidable full-workspace user read in JSON mode: The list path always builds a workspace-wide user-name map via renderConversationList, even for --json/--ndjson without --full where those names are filtered out. Short-circuit machine-readable output before name expansion.
  • Concurrency / fail-fast: getConversationsByState and resolveUserRefs are independent and could run concurrently with Promise.all, or resolveUserRefs could be moved first to fail fast on an ambiguous or invalid participant before fetching conversations.
  • Type safety and defaults in shared helpers: kind and state on ConversationListOptions are typed as plain string rather than exported unions, and getConversationsByState() defaults state to 'all' (the most expensive option) — making state required and passing 'all' explicitly from with.ts would avoid a future footgun.

I also included a few optional follow-up notes in the details below.

Optional follow-up notes (2)
  • [P3] src/commands/conversation/helpers.ts:25: kind and state are typed as plain strings here even though the command constrains them to a fixed set of values. That weakens the handler contract compared with nearby patterns like archiveFilter?: ArchiveFilter and forces list.ts to re-narrow them, where bad non-Commander inputs currently fall back to undefined/active instead of failing. Export shared unions for these fields and use them on ConversationListOptions so callers can't pass arbitrary strings.
  • [P3] src/commands/conversation/helpers.ts:80: Giving getConversationsByState() a default of 'all' hides the most expensive behavior behind an omitted argument. Both current callers already know which state they want (list wants active, with --include-groups wants all), so making state required and passing 'all' explicitly from with.ts would keep that choice at the call site and avoid an easy future footgun where a new caller accidentally fetches archived conversations too.

Share FeedbackReview Logs

Comment thread src/commands/conversation/list.ts Outdated
Comment thread src/commands/conversation/list.ts Outdated
Comment thread src/commands/conversation/list.ts
@scottlovegrove scottlovegrove changed the base branch from main to docs/notify-groups June 18, 2026 09:51

@scottlovegrove scottlovegrove left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs doistbot's feedback addressing before merging, but all good from my side.

Three doistbot P2s on the conversation list command:
- Resolve --participant against the full roster (includeRemoved) so a
  participant who has left the workspace still matches; the renderer already
  shows removed participants, and archived DMs often include them.
- Resolve participants and fetch conversations concurrently (Promise.all)
  rather than sequentially — they're independent once the workspace is known.
- Skip the workspace-wide user-map fetch for --json/--ndjson without --full,
  where participantNames are filtered back out anyway (also speeds up
  conversation with machine output, which shares the renderer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lmjabreu lmjabreu deleted the branch main June 18, 2026 11:06
Base automatically changed from docs/notify-groups to main June 18, 2026 11:06
@lmjabreu lmjabreu closed this Jun 18, 2026
@lmjabreu lmjabreu reopened this Jun 18, 2026
@lmjabreu lmjabreu merged commit 46a000e into main Jun 18, 2026
9 checks passed
@lmjabreu lmjabreu deleted the lmjabreu/infallible-wing-85542b branch June 18, 2026 11:09
doist-release-bot Bot added a commit that referenced this pull request Jun 18, 2026
## [1.7.0](v1.6.5...v1.7.0) (2026-06-18)

### Features

* **conversation:** add conversation list command ([#35](#35)) ([46a000e](46a000e))
@doist-release-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants