@md2do/core 0.2.0 โ†’ 0.2.2

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +515 -0
  3. package/package.json +12 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @md2do/core
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#12](https://github.com/TeamNickHart/md2do/pull/12) [`f07ba4c`](https://github.com/TeamNickHart/md2do/commit/f07ba4c4f6f46b43129e1ebd2b8ea67800e21dee) Thanks [@nickhart](https://github.com/nickhart)! - Enhance npm package discoverability with comprehensive READMEs and improved keywords
8
+ - Add conversion-focused README for @md2do/cli with Quick Start and common use cases
9
+ - Add developer-focused README for @md2do/core with complete API reference
10
+ - Add npm badges (version, downloads, license) to all package READMEs
11
+ - Expand keywords across all packages for better npm search discoverability
12
+ - Update root README tagline and add npm badges
13
+
3
14
  ## 0.2.0
4
15
 
5
16
  ### Minor Changes
package/README.md ADDED
@@ -0,0 +1,515 @@
1
+ # @md2do/core
2
+
3
+ [![npm version](https://badge.fury.io/js/%40md2do%2Fcore.svg)](https://www.npmjs.com/package/@md2do/core)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@md2do/core.svg)](https://www.npmjs.com/package/@md2do/core)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Core library for md2do - parsing, filtering, scanning, and writing markdown tasks.
8
+
9
+ ## Overview
10
+
11
+ `@md2do/core` provides the fundamental building blocks for working with TODO items in markdown files. This is a library package designed for developers who want to build their own task management tools or integrate md2do functionality into their applications.
12
+
13
+ **For end users:** Use [@md2do/cli](https://www.npmjs.com/package/@md2do/cli) instead - it provides a ready-to-use command-line interface.
14
+
15
+ **For developers:** This package gives you programmatic access to all core functionality.
16
+
17
+ ## Features
18
+
19
+ - ๐Ÿ“ **Parser** - Extract TODO items from markdown with rich metadata (assignees, priorities, tags, due dates)
20
+ - ๐Ÿ” **Scanner** - Recursively scan directories for markdown files with glob patterns
21
+ - ๐ŸŽฏ **Filters** - Filter tasks by any metadata field with type-safe functions
22
+ - ๐Ÿ“Š **Sorting** - Sort tasks by priority, due date, assignee, file, etc.
23
+ - โœ๏ธ **Writer** - Atomically update markdown files while preserving formatting
24
+ - ๐Ÿ”ง **Utilities** - Date parsing, ID generation, and helper functions
25
+ - โœ… **Type Safe** - Full TypeScript support with strict mode
26
+ - ๐Ÿงช **Well Tested** - 200+ tests with comprehensive coverage
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @md2do/core
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```typescript
37
+ import { scanMarkdownFile, filters, sorting } from '@md2do/core';
38
+
39
+ // Scan a markdown file
40
+ const result = scanMarkdownFile('tasks.md', 'tasks.md');
41
+
42
+ console.log(`Found ${result.tasks.length} tasks`);
43
+
44
+ // Filter urgent tasks assigned to nick
45
+ const urgentNickTasks = result.tasks
46
+ .filter(filters.byAssignee('nick'))
47
+ .filter(filters.byPriority('urgent'));
48
+
49
+ // Sort by due date
50
+ const sorted = sorting.sortTasks(urgentNickTasks, 'due');
51
+
52
+ console.log(sorted);
53
+ ```
54
+
55
+ ## API Reference
56
+
57
+ ### Parser
58
+
59
+ Parse markdown content to extract tasks.
60
+
61
+ #### `parseMarkdown(content, filePath?)`
62
+
63
+ Parse markdown content and extract all tasks.
64
+
65
+ ```typescript
66
+ import { parseMarkdown } from '@md2do/core';
67
+
68
+ const content = `
69
+ - [ ] Fix bug @nick !!! #backend (2026-01-25)
70
+ - [x] Write docs @jane !! #docs
71
+ `;
72
+
73
+ const tasks = parseMarkdown(content, 'tasks.md');
74
+ // [
75
+ // {
76
+ // id: 'abc123',
77
+ // text: 'Fix bug @nick !!! #backend (2026-01-25)',
78
+ // completed: false,
79
+ // file: 'tasks.md',
80
+ // line: 2,
81
+ // assignee: 'nick',
82
+ // priority: 'urgent',
83
+ // tags: ['backend'],
84
+ // dueDate: Date('2026-01-25'),
85
+ // ...
86
+ // },
87
+ // ...
88
+ // ]
89
+ ```
90
+
91
+ #### `parseTaskLine(line, lineNumber, filePath?)`
92
+
93
+ Parse a single markdown task line.
94
+
95
+ ```typescript
96
+ import { parseTaskLine } from '@md2do/core';
97
+
98
+ const task = parseTaskLine(
99
+ '- [ ] Fix bug @nick !!! #backend (2026-01-25)',
100
+ 5,
101
+ 'tasks.md',
102
+ );
103
+ ```
104
+
105
+ #### Pattern Exports
106
+
107
+ ```typescript
108
+ import {
109
+ TASK_PATTERN,
110
+ ASSIGNEE_PATTERN,
111
+ PRIORITY_PATTERN,
112
+ TAG_PATTERN,
113
+ DUE_DATE_PATTERN,
114
+ TODOIST_ID_PATTERN,
115
+ } from '@md2do/core';
116
+ ```
117
+
118
+ ### Scanner
119
+
120
+ Scan directories for markdown files and extract tasks.
121
+
122
+ #### `scanMarkdownFile(filePath, rootPath?)`
123
+
124
+ Scan a single markdown file.
125
+
126
+ ```typescript
127
+ import { scanMarkdownFile } from '@md2do/core';
128
+
129
+ const result = scanMarkdownFile('./notes/tasks.md', './notes');
130
+ // {
131
+ // tasks: [...],
132
+ // metadata: {
133
+ // filesScanned: 1,
134
+ // totalTasks: 15,
135
+ // completed: 3,
136
+ // incomplete: 12
137
+ // }
138
+ // }
139
+ ```
140
+
141
+ #### `scanMarkdownFiles(options)`
142
+
143
+ Scan multiple markdown files with glob patterns.
144
+
145
+ ```typescript
146
+ import { scanMarkdownFiles } from '@md2do/core';
147
+
148
+ const result = await scanMarkdownFiles({
149
+ path: './docs',
150
+ pattern: '**/*.md',
151
+ exclude: ['node_modules/**', 'dist/**'],
152
+ });
153
+
154
+ console.log(
155
+ `Found ${result.metadata.totalTasks} tasks in ${result.metadata.filesScanned} files`,
156
+ );
157
+ ```
158
+
159
+ **Options:**
160
+
161
+ ```typescript
162
+ {
163
+ path?: string; // Root path to scan (default: '.')
164
+ pattern?: string; // Glob pattern (default: '**/*.md')
165
+ exclude?: string[]; // Patterns to exclude
166
+ }
167
+ ```
168
+
169
+ ### Filters
170
+
171
+ Type-safe filter functions for tasks.
172
+
173
+ ```typescript
174
+ import { filters } from '@md2do/core';
175
+
176
+ // All filters return (task: Task) => boolean
177
+
178
+ // By assignee
179
+ const nickTasks = tasks.filter(filters.byAssignee('nick'));
180
+
181
+ // By priority
182
+ const urgentTasks = tasks.filter(filters.byPriority('urgent'));
183
+
184
+ // By tag
185
+ const backendTasks = tasks.filter(filters.byTag('backend'));
186
+
187
+ // By project
188
+ const acmeTasks = tasks.filter(filters.byProject('acme-app'));
189
+
190
+ // By person (from 1-1s)
191
+ const janeTasks = tasks.filter(filters.byPerson('jane'));
192
+
193
+ // By completion status
194
+ const incomplete = tasks.filter(filters.incomplete());
195
+ const completed = tasks.filter(filters.completed());
196
+
197
+ // By due date
198
+ const overdue = tasks.filter(filters.overdue());
199
+ const dueToday = tasks.filter(filters.dueToday());
200
+ const dueThisWeek = tasks.filter(filters.dueThisWeek());
201
+ const dueWithin = tasks.filter(filters.dueWithinDays(7));
202
+
203
+ // Combine filters
204
+ const urgentBackendTasks = tasks
205
+ .filter(filters.byAssignee('nick'))
206
+ .filter(filters.byPriority('urgent'))
207
+ .filter(filters.byTag('backend'))
208
+ .filter(filters.incomplete());
209
+ ```
210
+
211
+ ### Sorting
212
+
213
+ Sort tasks by various fields.
214
+
215
+ ```typescript
216
+ import { sorting } from '@md2do/core';
217
+
218
+ // Sort by priority (urgent โ†’ high โ†’ normal โ†’ low)
219
+ const byPriority = sorting.sortTasks(tasks, 'due');
220
+
221
+ // Sort by due date (earliest first)
222
+ const byDue = sorting.sortTasks(tasks, 'due');
223
+
224
+ // Sort by creation date
225
+ const byCreated = sorting.sortTasks(tasks, 'created');
226
+
227
+ // Sort by file path
228
+ const byFile = sorting.sortTasks(tasks, 'file');
229
+
230
+ // Sort by project
231
+ const byProject = sorting.sortTasks(tasks, 'project');
232
+
233
+ // Sort by assignee (alphabetical)
234
+ const byAssignee = sorting.sortTasks(tasks, 'assignee');
235
+
236
+ // Reverse order
237
+ const reversed = sorting.sortTasks(tasks, 'priority', true);
238
+ ```
239
+
240
+ **Sort fields:**
241
+
242
+ - `'due'` - Due date (earliest first)
243
+ - `'priority'` - Priority (urgent โ†’ high โ†’ normal โ†’ low)
244
+ - `'created'` - Creation date (oldest first)
245
+ - `'file'` - File path (alphabetical)
246
+ - `'project'` - Project name (alphabetical)
247
+ - `'assignee'` - Assignee (alphabetical)
248
+
249
+ ### Writer
250
+
251
+ Atomically update markdown files.
252
+
253
+ #### `updateTask(options)`
254
+
255
+ Update a task in a markdown file.
256
+
257
+ ```typescript
258
+ import { updateTask } from '@md2do/core';
259
+
260
+ await updateTask({
261
+ file: 'tasks.md',
262
+ line: 5,
263
+ updates: {
264
+ completed: true,
265
+ text: 'Fix bug @nick !!! #backend (2026-01-25) [todoist:123456]',
266
+ },
267
+ });
268
+ ```
269
+
270
+ **Options:**
271
+
272
+ ```typescript
273
+ {
274
+ file: string; // File path
275
+ line: number; // Line number (1-indexed)
276
+ updates: {
277
+ completed?: boolean; // Toggle completion
278
+ text?: string; // Replace entire task text
279
+ };
280
+ }
281
+ ```
282
+
283
+ **Features:**
284
+
285
+ - Atomic writes (uses temp file + rename)
286
+ - Preserves file formatting
287
+ - Updates timestamps for completed tasks
288
+ - Safe error handling
289
+
290
+ ### Types
291
+
292
+ All types are exported for TypeScript users.
293
+
294
+ ```typescript
295
+ import type {
296
+ Task,
297
+ Priority,
298
+ ScanResult,
299
+ ScanOptions,
300
+ UpdateTaskOptions,
301
+ TaskUpdate,
302
+ } from '@md2do/core';
303
+
304
+ interface Task {
305
+ id: string;
306
+ text: string;
307
+ completed: boolean;
308
+ file: string;
309
+ line: number;
310
+ assignee?: string;
311
+ priority?: Priority;
312
+ tags: string[];
313
+ dueDate?: Date;
314
+ createdDate?: Date;
315
+ completedDate?: Date;
316
+ todoistId?: string;
317
+ project?: string;
318
+ person?: string;
319
+ heading?: string;
320
+ }
321
+
322
+ type Priority = 'urgent' | 'high' | 'normal' | 'low';
323
+ ```
324
+
325
+ ### Utilities
326
+
327
+ #### Date Utilities
328
+
329
+ ```typescript
330
+ import {
331
+ parseDate,
332
+ formatDate,
333
+ isOverdue,
334
+ isDueToday,
335
+ isDueThisWeek,
336
+ isDueWithinDays,
337
+ } from '@md2do/core';
338
+
339
+ // Parse dates
340
+ const date = parseDate('2026-01-25'); // Date object
341
+ const relative = parseDate('tomorrow'); // Relative dates
342
+ const natural = parseDate('next friday'); // Natural language
343
+
344
+ // Format dates
345
+ const formatted = formatDate(new Date()); // '2026-01-21'
346
+
347
+ // Check due dates
348
+ isOverdue(task); // boolean
349
+ isDueToday(task); // boolean
350
+ isDueThisWeek(task); // boolean
351
+ isDueWithinDays(task, 7); // boolean
352
+ ```
353
+
354
+ #### ID Generation
355
+
356
+ ```typescript
357
+ import { generateId } from '@md2do/core';
358
+
359
+ const id = generateId('tasks.md', 5); // Generate deterministic ID
360
+ // 'f7a3b2c1'
361
+ ```
362
+
363
+ ## Usage Examples
364
+
365
+ ### Build a Custom CLI
366
+
367
+ ```typescript
368
+ import { scanMarkdownFiles, filters, sorting } from '@md2do/core';
369
+
370
+ async function listTasks(options: {
371
+ assignee?: string;
372
+ priority?: string;
373
+ tag?: string;
374
+ }) {
375
+ // Scan all markdown files
376
+ const result = await scanMarkdownFiles({
377
+ path: process.cwd(),
378
+ pattern: '**/*.md',
379
+ exclude: ['node_modules/**'],
380
+ });
381
+
382
+ let filtered = result.tasks;
383
+
384
+ // Apply filters
385
+ if (options.assignee) {
386
+ filtered = filtered.filter(filters.byAssignee(options.assignee));
387
+ }
388
+ if (options.priority) {
389
+ filtered = filtered.filter(filters.byPriority(options.priority as any));
390
+ }
391
+ if (options.tag) {
392
+ filtered = filtered.filter(filters.byTag(options.tag));
393
+ }
394
+
395
+ // Sort by priority
396
+ const sorted = sorting.sortTasks(filtered, 'priority');
397
+
398
+ // Display
399
+ console.log(`Found ${sorted.length} tasks`);
400
+ sorted.forEach((task) => {
401
+ console.log(`- ${task.text}`);
402
+ });
403
+ }
404
+
405
+ listTasks({ assignee: 'nick', priority: 'urgent' });
406
+ ```
407
+
408
+ ### Generate Statistics
409
+
410
+ ```typescript
411
+ import { scanMarkdownFiles } from '@md2do/core';
412
+
413
+ async function getStats() {
414
+ const result = await scanMarkdownFiles({ path: '.' });
415
+
416
+ // Group by assignee
417
+ const byAssignee = new Map<string, number>();
418
+ result.tasks.forEach((task) => {
419
+ if (task.assignee) {
420
+ const count = byAssignee.get(task.assignee) || 0;
421
+ byAssignee.set(task.assignee, count + 1);
422
+ }
423
+ });
424
+
425
+ console.log('Tasks by assignee:');
426
+ byAssignee.forEach((count, assignee) => {
427
+ console.log(` ${assignee}: ${count}`);
428
+ });
429
+ }
430
+ ```
431
+
432
+ ### Sync Task Completion
433
+
434
+ ```typescript
435
+ import { scanMarkdownFile, updateTask } from '@md2do/core';
436
+
437
+ async function markComplete(filePath: string, taskId: string) {
438
+ const result = scanMarkdownFile(filePath, filePath);
439
+ const task = result.tasks.find((t) => t.id === taskId);
440
+
441
+ if (!task) {
442
+ throw new Error(`Task ${taskId} not found`);
443
+ }
444
+
445
+ await updateTask({
446
+ file: task.file,
447
+ line: task.line,
448
+ updates: { completed: true },
449
+ });
450
+
451
+ console.log(`โœ“ Marked task as complete: ${task.text}`);
452
+ }
453
+ ```
454
+
455
+ ### Watch for Changes
456
+
457
+ ```typescript
458
+ import { watch } from 'fs';
459
+ import { scanMarkdownFile } from '@md2do/core';
460
+
461
+ function watchFile(filePath: string, onChange: (tasks: Task[]) => void) {
462
+ watch(filePath, async () => {
463
+ const result = scanMarkdownFile(filePath, filePath);
464
+ onChange(result.tasks);
465
+ });
466
+ }
467
+
468
+ watchFile('./tasks.md', (tasks) => {
469
+ console.log(`File updated: ${tasks.length} tasks found`);
470
+ });
471
+ ```
472
+
473
+ ## Testing
474
+
475
+ Run tests:
476
+
477
+ ```bash
478
+ # Run all tests
479
+ pnpm test
480
+
481
+ # Run tests in watch mode
482
+ pnpm test
483
+
484
+ # Run specific test suite
485
+ pnpm test parser
486
+
487
+ # Run with coverage
488
+ pnpm test:coverage
489
+ ```
490
+
491
+ **Test coverage:**
492
+
493
+ - Parser: 70 tests
494
+ - Scanner: 43 tests
495
+ - Filters: 41 tests
496
+ - Sorting: 26 tests
497
+ - Writer: 15 tests
498
+ - Utilities: 45 tests
499
+
500
+ ## Related Packages
501
+
502
+ - **[@md2do/cli](../cli)** - Command-line interface (uses this package)
503
+ - **[@md2do/config](../config)** - Configuration management
504
+ - **[@md2do/todoist](../todoist)** - Todoist API integration (uses this package)
505
+ - **[@md2do/mcp](../mcp)** - MCP server for AI integration (uses this package)
506
+
507
+ ## Documentation
508
+
509
+ - [Main Documentation](https://md2do.com)
510
+ - [GitHub Repository](https://github.com/TeamNickHart/md2do)
511
+ - [Contributing Guide](https://github.com/TeamNickHart/md2do/blob/main/docs/development/contributing.md)
512
+
513
+ ## License
514
+
515
+ MIT ยฉ [Nick Hart](https://github.com/TeamNickHart)
package/package.json CHANGED
@@ -1,12 +1,22 @@
1
1
  {
2
2
  "name": "@md2do/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Core parsing, filtering, scanning, and file writing for md2do",
5
5
  "keywords": [
6
6
  "markdown",
7
7
  "todo",
8
8
  "parser",
9
- "task-management"
9
+ "task-management",
10
+ "task",
11
+ "task-parser",
12
+ "markdown-parser",
13
+ "todo-list",
14
+ "task-tracker",
15
+ "filtering",
16
+ "sorting",
17
+ "scanner",
18
+ "library",
19
+ "typescript"
10
20
  ],
11
21
  "homepage": "https://md2do.com",
12
22
  "bugs": "https://github.com/TeamNickHart/md2do/issues",