Skip to content

feat(channel): add delete, archive, unarchive subcommands#8

Merged
scottlovegrove merged 2 commits into
mainfrom
feat/channel-lifecycle
May 27, 2026
Merged

feat(channel): add delete, archive, unarchive subcommands#8
scottlovegrove merged 2 commits into
mainfrom
feat/channel-lifecycle

Conversation

@scottlovegrove

Copy link
Copy Markdown
Collaborator

Summary

Ports the remaining channel-lifecycle commands from Doist/twist-cli#246. create already existed in comms-cli, so this adds the missing three:

tdc channel delete <ch-ref> --yes        # permanently delete (usually admin-only)
tdc channel archive <ch-ref>             # archive (no-op if already archived)
tdc channel unarchive id:<id>            # unarchive

All three support --workspace, --dry-run and --json.

  • delete requires --yes to mutate; short-circuits a structured MISSING_YES_FLAG CliError in --json mode before any lookup, and translates a Comms 403 into a clear FORBIDDEN CliError (channel deletion is typically restricted to workspace admins).
  • archive / unarchive share a setArchiveState helper and skip the API call when the channel is already in the target state. Name-ref resolution only finds active channels, so unarchive documents passing id:/numeric refs.

Adaptations from the twist-cli source

comms-cli diverges, so this is not a 1:1 copy:

  • String channel IDs — comms channel id is a base58 string; the SDK's deleteChannel/archiveChannel/unarchiveChannel take id: string.
  • Direct client callsgetCommsClient().channels.X(...) (matching sibling create.ts/update.ts), not api.ts wrappers. Only spinner-message entries were added to api.ts.
  • CommsRequestError (from @doist/comms-sdk) for the 403 check, instead of twist's TwistRequestError.
  • tdc naming throughout.

No scope changes needed — channels:write/channels:remove already landed with the membership PR (#7). The read-only write-gate applies automatically via the safe-by-default isMutatingMethod.

Files

Layer File
Delete src/commands/channel/delete.ts (new)
Archive/Unarchive src/commands/channel/archive.ts (new)
Spinners src/lib/api.ts
Error code src/lib/errors.ts (FORBIDDEN)
Registration src/commands/channel/index.ts
Skill docs src/lib/skills/content.ts + regenerated skills/comms-cli/SKILL.md
Tests src/commands/channel/channel.test.ts (+13)

Test plan

  • npm run type-check
  • npm run lint
  • npm test — 668/668 passing (13 new across delete/archive/unarchive)
  • npm run sync:skill (SKILL.md regenerated)
  • --help smoke for all three commands
  • Live end-to-end mutation against a real workspace (create → archive → list --state archived → unarchive → delete)

🤖 Generated with Claude Code

Ports the remaining channel-lifecycle commands from twist-cli #246
(create already existed). Wraps the SDK's deleteChannel, archiveChannel
and unarchiveChannel endpoints so channel lifecycle no longer requires
the web UI.

- delete requires --yes to mutate, short-circuits MISSING_YES_FLAG in
  --json mode before any lookup, and translates a 403 into a clear
  FORBIDDEN CliError (deletion is typically admin-only).
- archive/unarchive share a setArchiveState helper and skip the API
  call when the channel is already in the target state.
- All three support --workspace, --dry-run and --json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@scottlovegrove scottlovegrove self-assigned this May 27, 2026
@doistbot doistbot requested a review from henningmu May 27, 2026 16:23

@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.

Thanks scottlovegrove for your contribution 🤗. The implementation for these new lifecycle commands is excellent, particularly the shared archive state mutation and clean error mapping.

Few things worth tightening:

  • Bypass workspace resolution for direct channel refs (id: or URLs) using getDirectChannelId(), matching how channel update handles cross-workspace channels.
  • Add test coverage for the --workspace flag across these new subcommands to verify the correct workspace ID is passed to the resolver.

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

Optional follow-up note (1)
  • [P3] src/commands/channel/delete.ts:19: This does the full workspace/channel resolution before the plain !options.yes early return below. On a name ref, resolveChannelRef() loads the channel list for the workspace, so tdc channel delete <name> without --yes still pays for multiple API calls just to print Use --yes to confirm. Consider moving the non---yes/non---dry-run return above the lookup and using the raw ref in that message so the no-op path stays local.

Share FeedbackReview Logs

Comment thread src/commands/channel/delete.ts Outdated
Comment thread src/commands/channel/channel.test.ts
Addresses doistbot feedback on PR #8.

- Add a shared resolveChannelByRef helper: direct refs (id:/URL) are
  fetched by ID via getChannel (workspace-agnostic), so deleting or
  (un)archiving a channel in another workspace no longer fails with
  CHANNEL_NOT_FOUND. Name refs still resolve a workspace. Mirrors how
  `channel update` already special-cases direct refs.
- delete: move the non-yes/non-dry-run early return above the channel
  lookup and reference the raw ref, so the confirmation prompt no longer
  pays for API calls.
- Tests: table-driven --workspace coverage (asserts the passed workspace
  ID reaches resolveChannelRef) and direct-ref bypass coverage (asserts
  getChannel is used and no workspace is resolved) across all three
  subcommands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@scottlovegrove scottlovegrove added the 👀 Show PR PR must be reviewed before or after merging label May 27, 2026
@scottlovegrove scottlovegrove merged commit 41acfe6 into main May 27, 2026
5 checks passed
@scottlovegrove scottlovegrove deleted the feat/channel-lifecycle branch May 27, 2026 16:45
doist-release-bot Bot added a commit that referenced this pull request May 27, 2026
## [1.2.0](v1.1.0...v1.2.0) (2026-05-27)

### Features

* **channel:** add delete, archive, unarchive subcommands ([#8](#8)) ([41acfe6](41acfe6)), closes [#246](https://github.com/Doist/comms-cli/issues/246)
@doist-release-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@henningmu henningmu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Labels

released 👀 Show PR PR must be reviewed before or after merging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants