@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 +28 -0
- package/LICENSE +21 -0
- package/README.md +597 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/client.ts.html +694 -0
- package/coverage/coverage-final.json +4 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +146 -0
- package/coverage/index.ts.html +118 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/client.ts.html +694 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/index.ts.html +118 -0
- package/coverage/lcov-report/mapper.ts.html +760 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +523 -0
- package/coverage/mapper.ts.html +760 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/index.d.mts +121 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +318 -0
- package/dist/index.mjs +285 -0
- package/package.json +50 -0
- package/src/client.ts +203 -0
- package/src/index.ts +11 -0
- package/src/mapper.ts +225 -0
- package/tests/mapper.test.ts +283 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +25 -0
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
|