Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
],
"dependencies": {
"@doist/cli-core": "1.7.2",
"@doist/comms-sdk": "3.8.1",
"@doist/comms-sdk": "3.9.1",
"@pnpm/tabtab": "0.5.4",
"chalk": "5.6.2",
"commander": "14.0.3",
Expand Down
4 changes: 3 additions & 1 deletion skills/comms-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ tdc users --include-removed # Include users removed from the workspace
tdc users --ids-only # Output only user IDs
tdc status # Show your status (default login is enough)
tdc status --json # Show it as { status } JSON (null when unset; --full for the whole user)
tdc status set "Deep work" --emoji 🎯 --for 2h --pause-notifications # Set it (replaces any current status; needs --full-access)
tdc status set "Deep work" --emoji 🎯 --for 2h --pause-notifications # Set supported fields (needs --full-access)
tdc status set "On holiday" --pause-activity # Pause Todoist Karma and streaks while it is active (independent of --pause-notifications)
tdc status set "On holiday" --until 2026-10-20T09:00 # End at a date (YYYY-MM-DD, YYYY-MM-DDTHH:MM or a full ISO 8601 timestamp; local time unless it has Z or an offset)
tdc status set "Away" --dry-run # Preview without setting
Expand Down Expand Up @@ -323,6 +323,8 @@ All group *writes* (`groups create`, `rename`, `delete`, `add-user`, `remove-use

`tdc status set` and `tdc status clear` need the `user:write` scope, which only `tdc auth login --full-access` grants. `tdc status` (show) works on a default login.

`tdc status set` replaces the fields this CLI supports: omitted emoji, expiration and pause flags clear those values, while other fields and effects are preserved.

Only pass `--pause-notifications` when the user asks for it. It holds back Comms notifications only for now: each app has to read the status and hold its own notifications back, and Todoist does not yet. Held notifications are not sent later.

Only pass `--pause-activity` when the user asks for it. It pauses their Todoist Karma and streaks while the status is active. It is independent of `--pause-notifications`: it does not hold back notifications, and pausing notifications does not protect Karma.
Expand Down
3 changes: 2 additions & 1 deletion src/commands/status/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Examples:

status
.command('set <message>')
.description('Set your status, replacing any current one')
.description('Set your status, replacing its message, emoji, expiration, and pause effects')
.option('--emoji <emoji>', 'Emoji to show next to the message')
.option(
'--until <datetime>',
Expand All @@ -49,6 +49,7 @@ Setting or clearing it needs the user:write scope, so log in with tdc auth login
its own notifications back, and Todoist does not yet. Held notifications are not sent later.
--pause-activity protects your Todoist Karma and streaks. It does not hold back notifications,
and --pause-notifications does not protect Karma, so pass both if you want both.
Omitted emoji, expiration, and pause flags clear those values. Other fields and effects are preserved.

Examples:
tdc status set "On holiday" --emoji 🏝️ --until 2026-10-20T09:00
Expand Down
24 changes: 10 additions & 14 deletions src/commands/status/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,21 @@ export async function setStatus(message: string, options: SetStatusOptions): Pro

const args: SetStatusArgs = {
message: text,
...(emoji ? { emoji } : {}),
...(activeUntil ? { activeUntil } : {}),
...(options.pauseNotifications || options.pauseActivity
? {
effects: {
...(options.pauseNotifications ? { pauseNotifications: {} } : {}),
...(options.pauseActivity ? { pauseActivity: {} } : {}),
},
}
: {}),
emoji: emoji ?? null,
Comment thread
enricenrich marked this conversation as resolved.
Resolved
activeUntil: activeUntil ?? null,
effects: {
pauseNotifications: options.pauseNotifications ? {} : null,
pauseActivity: options.pauseActivity ? {} : null,
},
}

if (options.dryRun) {
printDryRun('set status', {
Message: text,
Emoji: emoji,
Until: activeUntil ? formatDateTime(activeUntil) : undefined,
'Pause notifications': options.pauseNotifications ? 'yes' : undefined,
'Pause activity': options.pauseActivity ? 'yes' : undefined,
Emoji: emoji ?? 'cleared',
Until: activeUntil ? formatDateTime(activeUntil) : 'cleared',
'Pause notifications': options.pauseNotifications ? 'yes' : 'cleared',
'Pause activity': options.pauseActivity ? 'yes' : 'cleared',
})
return
}
Expand Down
58 changes: 42 additions & 16 deletions src/commands/status/status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,15 @@ describe('tdc status', () => {
})

describe('set', () => {
it('sends the message alone when nothing else is given', async () => {
it('explicitly removes omitted optional values and known effects', async () => {
captureConsole('log')
await run('set', 'Away')
expect(users.setStatus).toHaveBeenCalledWith({ message: 'Away' })
expect(users.setStatus).toHaveBeenCalledWith({
message: 'Away',
emoji: null,
activeUntil: null,
effects: { pauseNotifications: null, pauseActivity: null },
})
})

it('sends emoji, an --until date and the pause effect', async () => {
Expand All @@ -152,39 +157,49 @@ describe('tdc status', () => {
message: 'Holiday',
emoji: '🏝️',
activeUntil: new Date('2026-10-20T09:00:00Z'),
effects: { pauseNotifications: {} },
effects: { pauseNotifications: {}, pauseActivity: null },
})
})

it('sends only the activity effect for --pause-activity', async () => {
captureConsole('log')
await run('set', 'Holiday', '--pause-activity')
expect(users.setStatus).toHaveBeenCalledWith({
emoji: null,
activeUntil: null,
message: 'Holiday',
effects: { pauseActivity: {} },
effects: { pauseNotifications: null, pauseActivity: {} },
})
})

it('sends both effects when both flags are given', async () => {
captureConsole('log')
await run('set', 'Holiday', '--pause-notifications', '--pause-activity')
expect(users.setStatus).toHaveBeenCalledWith({
emoji: null,
activeUntil: null,
message: 'Holiday',
effects: { pauseNotifications: {}, pauseActivity: {} },
})
})

it('lists the activity pause in the --dry-run preview only when asked for', async () => {
const spy = captureConsole('log')
await run('set', 'Holiday', '--pause-activity', '--dry-run')
expect(logged(spy)).toContain('Pause activity')
expect(users.setStatus).not.toHaveBeenCalled()

spy.mockClear()
await run('set', 'Holiday', '--dry-run')
expect(logged(spy)).toContain('Would set status')
expect(logged(spy)).not.toContain('Pause activity')
})
it.each([
{ flags: [], pauseActivity: 'cleared' },
{ flags: ['--pause-activity'], pauseActivity: 'yes' },
])(
'previews cleared values and Pause activity: $pauseActivity on --dry-run',
async ({ flags, pauseActivity }) => {
const spy = captureConsole('log')
await run('set', 'Holiday', ...flags, '--dry-run')
expect(users.setStatus).not.toHaveBeenCalled()
const out = logged(spy)
expect(out).toContain('Message: Holiday')
expect(out).toContain('Emoji: cleared')
expect(out).toContain('Until: cleared')
expect(out).toContain('Pause notifications: cleared')
expect(out).toContain(`Pause activity: ${pauseActivity}`)
},
)

it('prints the activity pause in the text output', async () => {
users.setStatus.mockResolvedValue({
Expand All @@ -204,6 +219,8 @@ describe('tdc status', () => {
captureConsole('log')
await run('set', 'Focus', '--for', '1h30m')
expect(users.setStatus).toHaveBeenCalledWith({
emoji: null,
effects: { pauseNotifications: null, pauseActivity: null },
message: 'Focus',
activeUntil: new Date('2026-10-14T13:30:00.000Z'),
})
Expand Down Expand Up @@ -240,6 +257,8 @@ describe('tdc status', () => {
captureConsole('log')
await run('set', 'Holiday', '--until', '2026-10-20')
expect(users.setStatus).toHaveBeenCalledWith({
emoji: null,
effects: { pauseNotifications: null, pauseActivity: null },
message: 'Holiday',
activeUntil: new Date(2026, 9, 20),
})
Expand Down Expand Up @@ -357,7 +376,12 @@ describe('tdc status set input guards', () => {
it('sends the trimmed message', async () => {
captureConsole('log')
await run('set', ' Away ')
expect(users.setStatus).toHaveBeenCalledWith({ message: 'Away' })
expect(users.setStatus).toHaveBeenCalledWith({
emoji: null,
activeUntil: null,
effects: { pauseNotifications: null, pauseActivity: null },
message: 'Away',
})
})

it.each([80, 41])(
Expand All @@ -373,6 +397,8 @@ describe('tdc status set input guards', () => {
captureConsole('log')
await run('set', 'ok', '--until', '2026-10-20T09:00+02:00')
expect(users.setStatus).toHaveBeenCalledWith({
emoji: null,
effects: { pauseNotifications: null, pauseActivity: null },
message: 'ok',
activeUntil: new Date('2026-10-20T07:00:00Z'),
})
Expand Down
4 changes: 3 additions & 1 deletion src/lib/skills/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ tdc users --include-removed # Include users removed from the workspace
tdc users --ids-only # Output only user IDs
tdc status # Show your status (default login is enough)
tdc status --json # Show it as { status } JSON (null when unset; --full for the whole user)
tdc status set "Deep work" --emoji 🎯 --for 2h --pause-notifications # Set it (replaces any current status; needs --full-access)
tdc status set "Deep work" --emoji 🎯 --for 2h --pause-notifications # Set supported fields (needs --full-access)
tdc status set "On holiday" --pause-activity # Pause Todoist Karma and streaks while it is active (independent of --pause-notifications)
tdc status set "On holiday" --until 2026-10-20T09:00 # End at a date (YYYY-MM-DD, YYYY-MM-DDTHH:MM or a full ISO 8601 timestamp; local time unless it has Z or an offset)
tdc status set "Away" --dry-run # Preview without setting
Expand Down Expand Up @@ -327,6 +327,8 @@ All group *writes* (\`groups create\`, \`rename\`, \`delete\`, \`add-user\`, \`r

\`tdc status set\` and \`tdc status clear\` need the \`user:write\` scope, which only \`tdc auth login --full-access\` grants. \`tdc status\` (show) works on a default login.

\`tdc status set\` replaces the fields this CLI supports: omitted emoji, expiration and pause flags clear those values, while other fields and effects are preserved.

Only pass \`--pause-notifications\` when the user asks for it. It holds back Comms notifications only for now: each app has to read the status and hold its own notifications back, and Todoist does not yet. Held notifications are not sent later.

Only pass \`--pause-activity\` when the user asks for it. It pauses their Todoist Karma and streaks while the status is active. It is independent of \`--pause-notifications\`: it does not hold back notifications, and pausing notifications does not protect Karma.
Expand Down
Loading