feat(channel): add create, delete, archive, unarchive subcommands#246
feat(channel): add create, delete, archive, unarchive subcommands#246lmjabreu wants to merge 2 commits into
Conversation
Wraps the SDK's createChannel, deleteChannel, archiveChannel and unarchiveChannel endpoints so channel lifecycle no longer requires the Twist web UI. Includes a clear FORBIDDEN error when the API rejects deletion (typically restricted to workspace admins), plus unit tests and skill-content updates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
doistbot
left a comment
There was a problem hiding this comment.
This PR successfully bridges a crucial functionality gap by introducing subcommands for creating, deleting, archiving, and unarchiving channels. These additions will significantly improve the CLI's utility for workspace administration and end-to-end testing. There are a few opportunities for refinement, specifically around extracting shared logic for delete confirmations and archive toggling, broadening the channel resolution bounds to include unjoined or archived channels, and addressing minor gaps in test coverage, error handling, and agent documentation.
| options: DeleteChannelOptions, | ||
| ): Promise<void> { | ||
| const workspaceId = await getCurrentWorkspaceId() | ||
| const channel = await resolveChannelRef(ref, workspaceId) |
There was a problem hiding this comment.
[P2] resolveChannelRef() only does name matching against getChannels({ workspaceId }), which this codebase treats as the joined-channel set. That means tw channel delete "Foo" can't resolve a public channel the admin hasn't joined, and archived channels can't be deleted by name at all. The same lookup pattern is used in archive/unarchive. For these management commands, use a broader lookup (public + archived) or tighten the accepted-ref docs/examples.
There was a problem hiding this comment.
Agreed, but this is the existing behaviour of resolveChannelRef from before this PR — it bites every command that uses it, not just these. I've drafted a focused follow-up that widens the name-resolution to also cover public and archived channels (and removes the temporary pass id: for archived channels signpost from the unarchive help). Keeping this PR scoped to the create/delete/archive surface so the fix can land as one self-contained change. Leaving this thread open until that PR lands.
- Add FORBIDDEN to the ErrorCode union. - Use SDK CreateChannelArgs type for the createChannel wrapper. - Use TwistRequestError instanceof for the 403 check in delete. - Reorder delete guards: short-circuit --json without --yes before hitting workspace/channel lookups, and gate --dry-run before --yes. - Add --workspace flag to delete/archive/unarchive for parity with other channel subcommands. - Extract setArchiveState helper to share archive/unarchive flow, and skip the API call when the channel is already in the target state. - Update skill content to mention --workspace and the no-op archive behaviour, regenerate SKILL.md. - Tests: fix unrestored console.log spies in create tests, add --json --full create test, add --yes --dry-run delete test, add already-archived no-op test, and rework the 403 test to throw a real TwistRequestError. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
tw channel create,tw channel delete,tw channel archive,tw channel unarchive— wraps the four SDK endpoints that were already exposed but not surfaced in the CLI. Discovered as a gap while landing channel-membership commands in #244; end-to-end channel testing previously required the web UI.createsupports--workspace,--description,--private(default public),--dry-run,--json,--full.deleterequires--yesto mutate, with structuredMISSING_YES_FLAGCliError in--jsonmode, and a clearFORBIDDENCliError when the Twist API returns 403 (channel deletion is typically restricted to workspace admins).archive/unarchivemirror the same shape with--dry-runand--json.SKILL.mdregenerated.Test plan
npm run type-checknpm run lintnpm test— 667/667 passing (17 new tests across the four commands)--state archived→ unarchive → re-archiveFORBIDDENerror message ondelete --yes(Twist returns 403 for non-admins, as expected)delete— blocked by workspace admin policy; unit-tested only🤖 Generated with Claude Code