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
Copy link
Copy Markdown
Member

@doistbot doistbot left a comment

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 📦🚀

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.

2 participants