feat(thread): add reversible inbox-state commands#69
Conversation
0c70fc3 to
02b29b4
Compare
02b29b4 to
ebe9108
Compare
|
@doistbot /review |
ebe9108 to
10eeaa4
Compare
10eeaa4 to
534abc4
Compare
|
@doistbot /review |
doistbot
left a comment
There was a problem hiding this comment.
This PR introduces valuable commands for managing thread inbox states and read toggles, bringing them into a unified mutation flow with dry-run and JSON support. These additions significantly improve CLI scriptability and provide a safer path for reversing accidental inbox actions. A few minor adjustments have been noted around simplifying redundant variadic argument checks, ensuring JSON bulk mutations without confirmation correctly trigger an error rather than returning a successful preview, and adjusting the sequence of no-op checks so dry-runs accurately reflect unchanged items.
src/commands/thread/index.ts
Outdated
| ) | ||
| .action(markThreadDone) | ||
| .action((refs, options) => { | ||
| if ((!refs || refs.length === 0) && !options.fromFile) { |
There was a problem hiding this comment.
[P3] Commander variadic arguments ([thread-refs...]) are always passed as an array, defaulting to [] if none are provided. The falsy checks and nullish coalescing are redundant here and in the other new commands.
You can simplify this condition to if (refs.length === 0 && !options.fromFile) and call the action directly with refs instead of refs ?? [].
| ) | ||
| } | ||
|
|
||
| const needsConfirmation = rawRefs.length > 1 && !options.yes && !options.dryRun |
There was a problem hiding this comment.
[P1] In --json mode this turns a bulk mutation without --yes into a successful preview payload with exit code 0, even though nothing was changed. That is easy for automation to misread as success, and it diverges from thread delete, which throws MISSING_YES_FLAG in JSON mode. Consider treating options.json && needsConfirmation as an error (or at least a non-zero exit) instead of a successful preview.
src/commands/thread/state-helpers.ts
Outdated
| } | ||
| } | ||
|
|
||
| if (options.dryRun) { |
There was a problem hiding this comment.
[P2] The no-op check happens after the confirmation/dry-run branches, so already-open/read/unread threads get reported as preview (Would ... / Dry run: would ...) instead of unchanged. That makes the preview output inaccurate and changes the JSON status for idempotent inputs. Moving the mutationPlan.length === 0 branch above these preview returns would keep no-op items idempotent in dry-run and bulk-preview mode too.
8722e53 to
9bc402a
Compare
9bc402a to
82e9b4a
Compare
| import { assertChannelIsPublic } from '../../lib/public-channels.js' | ||
| import { resolveThreadId } from '../../lib/refs.js' | ||
|
|
||
| export type ThreadStateAction = 'done' | 'read' | 'reopen' | 'restore' | 'unread' |
There was a problem hiding this comment.
Do we not have something for reversing marking a thread as "done"?
|
|
||
| \`--notify\` automatically resolves IDs: group IDs are routed to the \`groups\` API field, user IDs to \`recipients\`. No special syntax needed. | ||
|
|
||
| Migration note: \`tw thread done\` is reversible via \`tw thread reopen\`. |
There was a problem hiding this comment.
Is that right? I thought reopen was just for threads that had been explicitly closed? Calling reopen on a non-closed thread will move it to done?
There was a problem hiding this comment.
I forgot about closing threads. I'll rethink this a bit then.
# Conflicts: # README.md # src/commands/thread/index.ts
Summary
This PR makes thread inbox state reversible and scriptable from the CLI.
It adds explicit commands to reopen threads and toggle read state, and moves
tw thread doneonto the same mutation flow so accidental inbox actions can be undone safely.What Changed
tw thread reopen [thread-refs...]tw thread mark-read [thread-refs...]tw thread mark-unread [thread-refs...]tw thread restore [thread-refs...] --unreadtw thread doneonto the same shared batch/state mutation pathBehavior
--from-file--dry-run--jsonwith per-itembefore/aftersnapshots:idtitleisArchivedinInboxisUnreadclosedlastUpdated--yesunless--dry-runDocs And Tests
README.mdskills/twist-cli/SKILL.mdrestore --unread, and mixed success/failureVerification
npx vitest run src/__tests__/thread.test.tsNotes
Repo-wide
npm test/npm run buildare currently blocked by unrelated pre-existing issues in:src/commands/away/set.tssrc/commands/thread/reply.tssrc/lib/secure-store.ts