@md2do/todoist 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # @md2do/todoist
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#9](https://github.com/TeamNickHart/md2do/pull/9) [`8a2550b`](https://github.com/TeamNickHart/md2do/commit/8a2550bd23ce247bab3129e45b09a46b3c17d3c1) Thanks [@nickhart](https://github.com/nickhart)! - Initial release of md2do - a powerful CLI tool for managing TODO tasks in markdown files.
8
+
9
+ **Features:**
10
+ - ๐Ÿ“ Markdown-native task parsing with rich metadata (assignees, priorities, due dates, tags)
11
+ - ๐Ÿ” Powerful filtering and sorting capabilities
12
+ - ๐Ÿ“Š Rich statistics and aggregation
13
+ - ๐Ÿ”„ Todoist integration with bidirectional sync
14
+ - ๐Ÿค– MCP (Model Context Protocol) server for AI integration
15
+ - โš™๏ธ Hierarchical configuration system
16
+ - ๐ŸŽจ Beautiful CLI output with multiple formats (pretty, table, JSON)
17
+
18
+ **Packages:**
19
+ - `@md2do/cli` - Main CLI interface (install this one!)
20
+ - `@md2do/core` - Core parsing, filtering, and file operations
21
+ - `@md2do/config` - Configuration management
22
+ - `@md2do/todoist` - Todoist API integration
23
+ - `@md2do/mcp` - MCP server for AI assistants
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [[`8a2550b`](https://github.com/TeamNickHart/md2do/commit/8a2550bd23ce247bab3129e45b09a46b3c17d3c1)]:
28
+ - @md2do/core@0.2.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TeamNickHart
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,597 @@
1
+ # @md2do/todoist
2
+
3
+ Todoist API integration for md2do, providing bidirectional task synchronization between markdown files and Todoist.
4
+
5
+ ## Features
6
+
7
+ - ๐Ÿ”„ **Bidirectional Task Mapping** - Convert between md2do and Todoist task formats
8
+ - ๐ŸŽฏ **Priority Conversion** - Map semantic priorities (urgent/high/normal/low) to Todoist priorities (4/3/2/1)
9
+ - ๐Ÿ“… **Date Handling** - UTC-aware date parsing and formatting
10
+ - ๐Ÿท๏ธ **Label Management** - Automatic label creation and mapping
11
+ - ๐Ÿ”Œ **Official SDK** - Built on `@doist/todoist-api-typescript`
12
+ - โœ… **Type Safe** - Full TypeScript support with strict mode
13
+ - ๐Ÿงช **Well Tested** - 31 tests with comprehensive coverage
14
+
15
+ ## Getting Your Todoist API Token
16
+
17
+ Before using this package, you'll need a Todoist API token:
18
+
19
+ 1. **Log in to Todoist** at [https://app.todoist.com](https://app.todoist.com)
20
+ 2. **Go to Settings** โ†’ **Integrations**
21
+ - Direct link: [Todoist Integrations Settings](https://app.todoist.com/app/settings/integrations/developer)
22
+ 3. **Copy your API token** from the "API token" section
23
+ 4. **Keep it secure!** This token provides full access to your Todoist account
24
+
25
+ ## Installation
26
+
27
+ ### For CLI Usage (Recommended)
28
+
29
+ If you're using md2do CLI, the Todoist integration is already included:
30
+
31
+ ```bash
32
+ # Install md2do globally
33
+ npm install -g @md2do/cli
34
+
35
+ # Or use locally
36
+ npx @md2do/cli todoist list
37
+ ```
38
+
39
+ ### For Programmatic Usage
40
+
41
+ ```bash
42
+ # Install with peer dependencies
43
+ pnpm add @md2do/todoist @md2do/core
44
+
45
+ # Or using npm
46
+ npm install @md2do/todoist @md2do/core
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ ### For CLI Users
52
+
53
+ Configure your API token using one of these methods:
54
+
55
+ **Option 1: Environment Variable** (recommended for security)
56
+
57
+ ```bash
58
+ export TODOIST_API_TOKEN="your-api-token-here"
59
+ ```
60
+
61
+ **Option 2: Global Config File** (~/.md2do.json)
62
+
63
+ ```json
64
+ {
65
+ "todoist": {
66
+ "apiToken": "your-api-token-here",
67
+ "defaultProject": "Inbox"
68
+ }
69
+ }
70
+ ```
71
+
72
+ **Option 3: Project Config File** (./.md2do.json)
73
+
74
+ ```json
75
+ {
76
+ "todoist": {
77
+ "apiToken": "your-project-token-here",
78
+ "defaultProject": "Work"
79
+ }
80
+ }
81
+ ```
82
+
83
+ For complete configuration options, see the [Configuration Guide](https://md2do.com/todoist) (coming soon).
84
+
85
+ ### For Programmatic Usage
86
+
87
+ Pass the API token directly to the client:
88
+
89
+ ```typescript
90
+ import { TodoistClient } from '@md2do/todoist';
91
+
92
+ // Initialize client with API token
93
+ const client = new TodoistClient({
94
+ apiToken: process.env.TODOIST_API_TOKEN!,
95
+ });
96
+
97
+ // Get all tasks
98
+ const tasks = await client.getTasks();
99
+
100
+ // Get tasks for a specific project
101
+ const projectTasks = await client.getTasks({ projectId: '123456' });
102
+
103
+ // Create a new task
104
+ const newTask = await client.createTask({
105
+ content: 'Review pull request',
106
+ priority: 3,
107
+ labels: ['work', 'urgent'],
108
+ due_date: '2026-01-25',
109
+ });
110
+
111
+ // Complete a task
112
+ await client.completeTask(newTask.id);
113
+ ```
114
+
115
+ ## API Reference
116
+
117
+ ### TodoistClient
118
+
119
+ Wrapper around the Todoist API with error handling and convenience methods.
120
+
121
+ #### Constructor
122
+
123
+ ```typescript
124
+ new TodoistClient(config: TodoistClientConfig)
125
+ ```
126
+
127
+ **Config:**
128
+
129
+ - `apiToken: string` - Your Todoist API token
130
+
131
+ #### Methods
132
+
133
+ ##### `getTasks(options?)`
134
+
135
+ Get all active tasks, optionally filtered by project or label.
136
+
137
+ ```typescript
138
+ const tasks = await client.getTasks({
139
+ projectId: '123456', // optional
140
+ labelId: '789', // optional
141
+ });
142
+ ```
143
+
144
+ ##### `getTask(taskId)`
145
+
146
+ Get a specific task by ID.
147
+
148
+ ```typescript
149
+ const task = await client.getTask('123456');
150
+ ```
151
+
152
+ ##### `createTask(params)`
153
+
154
+ Create a new task.
155
+
156
+ ```typescript
157
+ const task = await client.createTask({
158
+ content: 'Task description',
159
+ priority: 3, // 1-4 (1=low, 4=urgent)
160
+ labels: ['work'], // optional
161
+ due_date: '2026-01-25', // optional, YYYY-MM-DD
162
+ project_id: '123456', // optional
163
+ });
164
+ ```
165
+
166
+ ##### `updateTask(taskId, params)`
167
+
168
+ Update an existing task.
169
+
170
+ ```typescript
171
+ await client.updateTask('123456', {
172
+ content: 'Updated description',
173
+ priority: 4,
174
+ });
175
+ ```
176
+
177
+ ##### `completeTask(taskId)`
178
+
179
+ Mark a task as completed.
180
+
181
+ ```typescript
182
+ await client.completeTask('123456');
183
+ ```
184
+
185
+ ##### `reopenTask(taskId)`
186
+
187
+ Reopen a completed task.
188
+
189
+ ```typescript
190
+ await client.reopenTask('123456');
191
+ ```
192
+
193
+ ##### `deleteTask(taskId)`
194
+
195
+ Delete a task permanently.
196
+
197
+ ```typescript
198
+ await client.deleteTask('123456');
199
+ ```
200
+
201
+ ##### `getProjects()`
202
+
203
+ Get all projects.
204
+
205
+ ```typescript
206
+ const projects = await client.getProjects();
207
+ ```
208
+
209
+ ##### `getProject(projectId)`
210
+
211
+ Get a specific project by ID.
212
+
213
+ ```typescript
214
+ const project = await client.getProject('123456');
215
+ ```
216
+
217
+ ##### `findProjectByName(name)`
218
+
219
+ Find a project by name (case-insensitive).
220
+
221
+ ```typescript
222
+ const project = await client.findProjectByName('Work');
223
+ ```
224
+
225
+ ##### `getLabels()`
226
+
227
+ Get all labels.
228
+
229
+ ```typescript
230
+ const labels = await client.getLabels();
231
+ ```
232
+
233
+ ##### `ensureLabel(name)`
234
+
235
+ Create a label if it doesn't exist, otherwise return the existing label.
236
+
237
+ ```typescript
238
+ const label = await client.ensureLabel('urgent');
239
+ ```
240
+
241
+ ##### `test()`
242
+
243
+ Test the API connection.
244
+
245
+ ```typescript
246
+ const isConnected = await client.test();
247
+ ```
248
+
249
+ ### Task Mapping
250
+
251
+ Convert between md2do and Todoist task formats.
252
+
253
+ #### `md2doToTodoist(task, projectId?)`
254
+
255
+ Convert an md2do task to Todoist task creation parameters.
256
+
257
+ ```typescript
258
+ import { md2doToTodoist } from '@md2do/todoist';
259
+ import type { Task } from '@md2do/core';
260
+
261
+ const mdTask: Task = {
262
+ id: 'abc123',
263
+ text: 'Fix bug',
264
+ completed: false,
265
+ file: 'bugs.md',
266
+ line: 5,
267
+ tags: ['backend', 'urgent'],
268
+ assignee: 'nick',
269
+ priority: 'urgent',
270
+ dueDate: new Date('2026-01-25'),
271
+ };
272
+
273
+ const todoistParams = md2doToTodoist(mdTask, 'project-123');
274
+ // {
275
+ // content: 'Fix bug',
276
+ // priority: 4,
277
+ // labels: ['backend', 'urgent'],
278
+ // due_date: '2026-01-25',
279
+ // project_id: 'project-123',
280
+ // }
281
+ ```
282
+
283
+ #### `todoistToMd2do(todoistTask, assignee?)`
284
+
285
+ Convert a Todoist task to md2do task update data.
286
+
287
+ ```typescript
288
+ import { todoistToMd2do } from '@md2do/todoist';
289
+
290
+ const update = todoistToMd2do(todoistTask, 'nick');
291
+ // {
292
+ // text: 'Fix bug @nick !!! #backend #urgent (2026-01-25) [todoist:123456]',
293
+ // completed: false,
294
+ // todoistId: '123456',
295
+ // priority: 'urgent',
296
+ // tags: ['backend', 'urgent'],
297
+ // due: Date('2026-01-25'),
298
+ // }
299
+ ```
300
+
301
+ ### Priority Mapping
302
+
303
+ #### `md2doToTodoistPriority(priority?)`
304
+
305
+ Convert md2do priority to Todoist priority.
306
+
307
+ ```typescript
308
+ import { md2doToTodoistPriority } from '@md2do/todoist';
309
+
310
+ md2doToTodoistPriority('urgent'); // 4
311
+ md2doToTodoistPriority('high'); // 3
312
+ md2doToTodoistPriority('normal'); // 2
313
+ md2doToTodoistPriority('low'); // 1
314
+ md2doToTodoistPriority(undefined); // 1
315
+ ```
316
+
317
+ #### `todoistToMd2doPriority(priority)`
318
+
319
+ Convert Todoist priority to md2do priority.
320
+
321
+ ```typescript
322
+ import { todoistToMd2doPriority } from '@md2do/todoist';
323
+
324
+ todoistToMd2doPriority(4); // 'urgent'
325
+ todoistToMd2doPriority(3); // 'high'
326
+ todoistToMd2doPriority(2); // 'normal'
327
+ todoistToMd2doPriority(1); // 'low'
328
+ ```
329
+
330
+ ### Text Formatting
331
+
332
+ #### `extractTaskContent(text)`
333
+
334
+ Extract clean task content by removing all metadata.
335
+
336
+ ```typescript
337
+ import { extractTaskContent } from '@md2do/todoist';
338
+
339
+ const text = 'Fix bug @nick !!! #backend (2026-01-25) [todoist:123456]';
340
+ extractTaskContent(text); // 'Fix bug'
341
+ ```
342
+
343
+ #### `formatTaskContent(content, options)`
344
+
345
+ Format task content with metadata.
346
+
347
+ ```typescript
348
+ import { formatTaskContent } from '@md2do/todoist';
349
+
350
+ formatTaskContent('Fix bug', {
351
+ assignee: 'nick',
352
+ priority: 'urgent',
353
+ tags: ['backend'],
354
+ due: new Date('2026-01-25'),
355
+ todoistId: '123456',
356
+ });
357
+ // 'Fix bug @nick !!! #backend (2026-01-25) [todoist:123456]'
358
+ ```
359
+
360
+ ## CLI Usage Examples
361
+
362
+ Once configured, use the md2do CLI commands:
363
+
364
+ ### List Todoist Tasks
365
+
366
+ ```bash
367
+ # List all tasks from default project
368
+ md2do todoist list
369
+
370
+ # List from specific project
371
+ md2do todoist list --project Work
372
+
373
+ # Limit results
374
+ md2do todoist list --limit 10
375
+
376
+ # JSON output
377
+ md2do todoist list --format json
378
+ ```
379
+
380
+ ### Create Tasks
381
+
382
+ ```bash
383
+ # Simple task
384
+ md2do todoist add "Review pull request"
385
+
386
+ # With priority and labels
387
+ md2do todoist add "Fix bug" --priority urgent --labels bug,backend
388
+
389
+ # With due date
390
+ md2do todoist add "Meeting prep" --due tomorrow --project Work
391
+ ```
392
+
393
+ ### Import Markdown Tasks
394
+
395
+ ```bash
396
+ # Import a task from markdown to Todoist
397
+ md2do todoist import tasks.md:15
398
+
399
+ # Specify project
400
+ md2do todoist import notes.md:42 --project Personal
401
+ ```
402
+
403
+ ### Sync with Todoist
404
+
405
+ ```bash
406
+ # Sync all tasks (dry run first)
407
+ md2do todoist sync --dry-run
408
+
409
+ # Actually sync
410
+ md2do todoist sync
411
+
412
+ # Sync specific directory
413
+ md2do todoist sync --path ./work-notes
414
+
415
+ # Pull only (update markdown from Todoist)
416
+ md2do todoist sync --direction pull
417
+ ```
418
+
419
+ ## Programmatic Usage with Configuration
420
+
421
+ Use with `@md2do/config` for automatic token management:
422
+
423
+ ```typescript
424
+ import { loadConfig } from '@md2do/config';
425
+ import { TodoistClient } from '@md2do/todoist';
426
+
427
+ // Load config from environment, project, or global config
428
+ const config = await loadConfig();
429
+
430
+ if (!config.todoist?.apiToken) {
431
+ throw new Error('Todoist API token not configured');
432
+ }
433
+
434
+ // Create client with configured token
435
+ const client = new TodoistClient({
436
+ apiToken: config.todoist.apiToken,
437
+ });
438
+
439
+ // Find default project if configured
440
+ let projectId: string | undefined;
441
+ if (config.todoist.defaultProject) {
442
+ const project = await client.findProjectByName(config.todoist.defaultProject);
443
+ projectId = project?.id;
444
+ }
445
+
446
+ // Use client...
447
+ ```
448
+
449
+ ## Error Handling
450
+
451
+ All client methods throw descriptive errors:
452
+
453
+ ```typescript
454
+ try {
455
+ const task = await client.getTask('invalid-id');
456
+ } catch (error) {
457
+ console.error(error.message);
458
+ // 'Failed to get task invalid-id: Task not found'
459
+ }
460
+ ```
461
+
462
+ ## Date Handling
463
+
464
+ All dates are handled in UTC to avoid timezone issues:
465
+
466
+ ```typescript
467
+ import { md2doToTodoist } from '@md2do/todoist';
468
+
469
+ const task = {
470
+ // ... other fields
471
+ dueDate: new Date('2026-01-25T00:00:00.000Z'), // Always use UTC
472
+ };
473
+
474
+ const params = md2doToTodoist(task);
475
+ params.due_date; // '2026-01-25'
476
+ ```
477
+
478
+ ## TypeScript Types
479
+
480
+ All types are fully typed and exported:
481
+
482
+ ```typescript
483
+ import type {
484
+ TodoistClientConfig,
485
+ TodoistTaskParams,
486
+ Md2doTaskUpdate,
487
+ } from '@md2do/todoist';
488
+
489
+ import type { Task as TodoistTask } from '@doist/todoist-api-typescript';
490
+ ```
491
+
492
+ ## Testing
493
+
494
+ Run tests:
495
+
496
+ ```bash
497
+ # Run tests
498
+ pnpm test
499
+
500
+ # Run tests in watch mode
501
+ pnpm test
502
+
503
+ # Run tests with UI
504
+ pnpm test:ui
505
+
506
+ # Run tests once
507
+ pnpm test:run
508
+ ```
509
+
510
+ ## Examples
511
+
512
+ ### Sync Tasks from Todoist
513
+
514
+ ```typescript
515
+ import { TodoistClient, todoistToMd2do } from '@md2do/todoist';
516
+ import { updateTask } from '@md2do/core';
517
+
518
+ const client = new TodoistClient({ apiToken: process.env.TODOIST_API_TOKEN! });
519
+
520
+ // Get tasks from Todoist
521
+ const todoistTasks = await client.getTasks({ projectId: '123456' });
522
+
523
+ // Convert and update markdown files
524
+ for (const todoistTask of todoistTasks) {
525
+ const update = todoistToMd2do(todoistTask);
526
+
527
+ // Find corresponding markdown task and update
528
+ await updateTask({
529
+ file: 'tasks.md',
530
+ line: 5, // line number from scanning
531
+ updates: {
532
+ completed: update.completed,
533
+ text: update.text,
534
+ },
535
+ });
536
+ }
537
+ ```
538
+
539
+ ### Push Tasks to Todoist
540
+
541
+ ```typescript
542
+ import { TodoistClient, md2doToTodoist } from '@md2do/todoist';
543
+ import { scanMarkdownFile } from '@md2do/core';
544
+
545
+ const client = new TodoistClient({ apiToken: process.env.TODOIST_API_TOKEN! });
546
+
547
+ // Scan markdown file
548
+ const { tasks } = scanMarkdownFile('tasks.md', 'tasks.md');
549
+
550
+ // Push tasks without Todoist IDs
551
+ for (const task of tasks) {
552
+ if (!task.todoistId && !task.completed) {
553
+ const params = md2doToTodoist(task, 'project-123');
554
+ const created = await client.createTask(params);
555
+
556
+ // Update markdown with Todoist ID
557
+ await updateTask({
558
+ file: task.file,
559
+ line: task.line,
560
+ updates: {
561
+ text: `${task.text} [todoist:${created.id}]`,
562
+ },
563
+ });
564
+ }
565
+ }
566
+ ```
567
+
568
+ ### Ensure Labels Exist
569
+
570
+ ```typescript
571
+ import { TodoistClient } from '@md2do/todoist';
572
+
573
+ const client = new TodoistClient({ apiToken: process.env.TODOIST_API_TOKEN! });
574
+
575
+ // Ensure all required labels exist
576
+ const requiredLabels = ['urgent', 'backend', 'frontend', 'bug', 'feature'];
577
+
578
+ for (const labelName of requiredLabels) {
579
+ await client.ensureLabel(labelName);
580
+ }
581
+ ```
582
+
583
+ ## Related Packages
584
+
585
+ - **[@md2do/core](../core)** - Core parsing, filtering, and scanning
586
+ - **[@md2do/config](../config)** - Configuration management
587
+ - **[@md2do/cli](../cli)** - Command-line interface
588
+
589
+ ## Documentation
590
+
591
+ - [Todoist Setup Guide](../../docs/todoist-setup.md) - Complete configuration guide
592
+ - [Implementation Plan](../../docs/todoist-implementation-plan.md) - Technical roadmap
593
+ - [Todoist API Docs](https://developer.todoist.com/rest/v2/) - Official API documentation
594
+
595
+ ## License
596
+
597
+ MIT