@md2do/core 0.3.0 → 0.5.1
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 +38 -0
- package/README.md +6 -6
- package/dist/index.d.mts +40 -10
- package/dist/index.d.ts +40 -10
- package/dist/index.js +59 -11
- package/dist/index.mjs +61 -12
- package/package.json +1 -1
- package/src/parser/index.ts +28 -5
- package/src/parser/patterns.ts +8 -5
- package/src/scanner/index.ts +17 -0
- package/src/types/index.ts +4 -0
- package/src/utils/dates.ts +58 -10
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -10
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -206
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -206
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/filters/index.html +0 -116
- package/coverage/lcov-report/src/filters/index.ts.html +0 -1156
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov-report/src/index.ts.html +0 -157
- package/coverage/lcov-report/src/parser/index.html +0 -131
- package/coverage/lcov-report/src/parser/index.ts.html +0 -1102
- package/coverage/lcov-report/src/parser/patterns.ts.html +0 -661
- package/coverage/lcov-report/src/scanner/index.html +0 -116
- package/coverage/lcov-report/src/scanner/index.ts.html +0 -658
- package/coverage/lcov-report/src/sorting/index.html +0 -116
- package/coverage/lcov-report/src/sorting/index.ts.html +0 -736
- package/coverage/lcov-report/src/utils/dates.ts.html +0 -499
- package/coverage/lcov-report/src/utils/id.ts.html +0 -169
- package/coverage/lcov-report/src/utils/index.html +0 -131
- package/coverage/lcov-report/src/writer/index.html +0 -116
- package/coverage/lcov-report/src/writer/index.ts.html +0 -1093
- package/coverage/lcov.info +0 -2294
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/filters/index.html +0 -116
- package/coverage/src/filters/index.ts.html +0 -1156
- package/coverage/src/index.html +0 -116
- package/coverage/src/index.ts.html +0 -157
- package/coverage/src/parser/index.html +0 -131
- package/coverage/src/parser/index.ts.html +0 -1102
- package/coverage/src/parser/patterns.ts.html +0 -661
- package/coverage/src/scanner/index.html +0 -116
- package/coverage/src/scanner/index.ts.html +0 -658
- package/coverage/src/sorting/index.html +0 -116
- package/coverage/src/sorting/index.ts.html +0 -736
- package/coverage/src/utils/dates.ts.html +0 -499
- package/coverage/src/utils/id.ts.html +0 -169
- package/coverage/src/utils/index.html +0 -131
- package/coverage/src/writer/index.html +0 -116
- package/coverage/src/writer/index.ts.html +0 -1093
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @md2do/core
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#47](https://github.com/TeamNickHart/md2do/pull/47) [`ce65eaf`](https://github.com/TeamNickHart/md2do/commit/ce65eaf588c702254fb564f748a5841241cc5c97) Thanks [@nickhart](https://github.com/nickhart)! - Documentation improvements and standardization
|
|
8
|
+
- Standardized bracket syntax across all docs (use space after colon: `[due: ...]`)
|
|
9
|
+
- Marked semantic/relative dates as experimental with clear warnings
|
|
10
|
+
- Clarified Todoist sync is one-way (pull only), not bidirectional
|
|
11
|
+
- Documented context extraction limitation (must run from repo root)
|
|
12
|
+
- Updated code examples in READMEs to match best practices
|
|
13
|
+
- Removed unhelpful parentheses syntax warning
|
|
14
|
+
|
|
15
|
+
## 0.4.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 8e0f80a: Add time support to due dates with workday configuration
|
|
20
|
+
|
|
21
|
+
**New Features:**
|
|
22
|
+
- Support optional time component in due dates: `[due: 2026-02-06 17:00]`
|
|
23
|
+
- Parse both 24-hour format times (H:MM and HH:MM)
|
|
24
|
+
- Added `parseTime()` utility for time validation
|
|
25
|
+
- New workday config schema with `startTime`, `endTime`, and `defaultDueTime`
|
|
26
|
+
- When no time specified in due date, applies default from config (defaults to 17:00 end of workday)
|
|
27
|
+
- Prevents "due 8 hours ago" issues for dates without explicit times
|
|
28
|
+
|
|
29
|
+
**Configuration:**
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"workday": {
|
|
34
|
+
"startTime": "08:00",
|
|
35
|
+
"endTime": "17:00",
|
|
36
|
+
"defaultDueTime": "end"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
3
41
|
## 0.3.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -66,15 +66,15 @@ Parse markdown content and extract all tasks.
|
|
|
66
66
|
import { parseMarkdown } from '@md2do/core';
|
|
67
67
|
|
|
68
68
|
const content = `
|
|
69
|
-
- [ ] Fix bug @nick !!! #backend
|
|
70
|
-
- [x] Write docs @jane !! #docs
|
|
69
|
+
- [ ] Fix bug @nick !!! #backend [due: 2026-01-25]
|
|
70
|
+
- [x] Write docs @jane !! #docs [completed: 2026-01-20]
|
|
71
71
|
`;
|
|
72
72
|
|
|
73
73
|
const tasks = parseMarkdown(content, 'tasks.md');
|
|
74
74
|
// [
|
|
75
75
|
// {
|
|
76
76
|
// id: 'abc123',
|
|
77
|
-
// text: 'Fix bug
|
|
77
|
+
// text: 'Fix bug',
|
|
78
78
|
// completed: false,
|
|
79
79
|
// file: 'tasks.md',
|
|
80
80
|
// line: 2,
|
|
@@ -96,7 +96,7 @@ Parse a single markdown task line.
|
|
|
96
96
|
import { parseTaskLine } from '@md2do/core';
|
|
97
97
|
|
|
98
98
|
const task = parseTaskLine(
|
|
99
|
-
'- [ ] Fix bug @nick !!! #backend
|
|
99
|
+
'- [ ] Fix bug @nick !!! #backend [due: 2026-01-25]',
|
|
100
100
|
5,
|
|
101
101
|
'tasks.md',
|
|
102
102
|
);
|
|
@@ -262,7 +262,7 @@ await updateTask({
|
|
|
262
262
|
line: 5,
|
|
263
263
|
updates: {
|
|
264
264
|
completed: true,
|
|
265
|
-
text: 'Fix bug @nick !!! #backend
|
|
265
|
+
text: 'Fix bug @nick !!! #backend [due: 2026-01-25] [todoist: 123456] [completed: 2026-01-26]',
|
|
266
266
|
},
|
|
267
267
|
});
|
|
268
268
|
```
|
|
@@ -501,7 +501,7 @@ pnpm test:coverage
|
|
|
501
501
|
|
|
502
502
|
- **[@md2do/cli](../cli)** - Command-line interface (uses this package)
|
|
503
503
|
- **[@md2do/config](../config)** - Configuration management
|
|
504
|
-
- **[@md2do/todoist](../todoist)** - Todoist API integration (uses this package)
|
|
504
|
+
- **[@md2do/todoist](../todoist)** - [Todoist](https://www.todoist.com) API integration (uses this package)
|
|
505
505
|
- **[@md2do/mcp](../mcp)** - MCP server for AI integration (uses this package)
|
|
506
506
|
|
|
507
507
|
## Documentation
|
package/dist/index.d.mts
CHANGED
|
@@ -21,6 +21,9 @@ interface ParsingContext {
|
|
|
21
21
|
person?: string;
|
|
22
22
|
currentDate?: Date;
|
|
23
23
|
currentHeading?: string;
|
|
24
|
+
workdayStartTime?: string;
|
|
25
|
+
workdayEndTime?: string;
|
|
26
|
+
defaultDueTime?: 'start' | 'end';
|
|
24
27
|
}
|
|
25
28
|
interface TaskFilterCriteria {
|
|
26
29
|
assignee?: string | string[];
|
|
@@ -122,10 +125,10 @@ declare function extractCompletedDate(text: string): Date | undefined;
|
|
|
122
125
|
* Extract due date from task text with context awareness
|
|
123
126
|
*
|
|
124
127
|
* Handles both absolute dates ([due: 2026-01-25]) and relative dates
|
|
125
|
-
* ([due: tomorrow]) which require context.
|
|
128
|
+
* ([due: tomorrow]) which require context. Supports optional time ([due: 2026-01-25 17:00]).
|
|
126
129
|
*
|
|
127
130
|
* @param text - Task text
|
|
128
|
-
* @param context - Parsing context (for relative dates)
|
|
131
|
+
* @param context - Parsing context (for relative dates and workday config)
|
|
129
132
|
* @returns Object with parsed date and optional warning
|
|
130
133
|
*/
|
|
131
134
|
declare function extractDueDate(text: string, context: ParsingContext): {
|
|
@@ -232,15 +235,17 @@ declare const PRIORITY_HIGH: RegExp;
|
|
|
232
235
|
*/
|
|
233
236
|
declare const PRIORITY_NORMAL: RegExp;
|
|
234
237
|
/**
|
|
235
|
-
* Matches absolute due date in ISO format [due: YYYY-MM-DD]
|
|
238
|
+
* Matches absolute due date in ISO format [due: YYYY-MM-DD] with optional time [due: YYYY-MM-DD HH:MM]
|
|
236
239
|
*
|
|
237
240
|
* Examples:
|
|
238
|
-
* "[due: 2026-01-25]" → "2026-01-25"
|
|
239
|
-
* "[due:2026-01-25]" → "2026-01-25"
|
|
240
|
-
* "[due:
|
|
241
|
+
* "[due: 2026-01-25]" → "2026-01-25", undefined
|
|
242
|
+
* "[due: 2026-01-25 17:00]" → "2026-01-25", "17:00"
|
|
243
|
+
* "[due: 2026-01-25 9:00]" → "2026-01-25", "9:00"
|
|
244
|
+
* "[due:2026-01-25]" → "2026-01-25", undefined (spaces optional)
|
|
241
245
|
*
|
|
242
246
|
* Groups:
|
|
243
247
|
* [1] - Date string in YYYY-MM-DD format
|
|
248
|
+
* [2] - Optional time string in H:MM or HH:MM format (24-hour)
|
|
244
249
|
*/
|
|
245
250
|
declare const DUE_DATE_ABSOLUTE: RegExp;
|
|
246
251
|
/**
|
|
@@ -400,9 +405,14 @@ declare class MarkdownScanner {
|
|
|
400
405
|
*
|
|
401
406
|
* @param filePath - Relative file path (for context extraction)
|
|
402
407
|
* @param content - File content as string
|
|
408
|
+
* @param options - Optional scanner options including workday config
|
|
403
409
|
* @returns Object containing tasks and warnings
|
|
404
410
|
*/
|
|
405
|
-
scanFile(filePath: string, content: string
|
|
411
|
+
scanFile(filePath: string, content: string, options?: {
|
|
412
|
+
workdayStartTime?: string;
|
|
413
|
+
workdayEndTime?: string;
|
|
414
|
+
defaultDueTime?: 'start' | 'end';
|
|
415
|
+
}): {
|
|
406
416
|
tasks: Task[];
|
|
407
417
|
warnings: Warning[];
|
|
408
418
|
};
|
|
@@ -874,7 +884,22 @@ declare function groupWarningsBySeverity(warnings: Warning[]): {
|
|
|
874
884
|
};
|
|
875
885
|
|
|
876
886
|
/**
|
|
877
|
-
* Parse
|
|
887
|
+
* Parse a time string in HH:MM or H:MM format
|
|
888
|
+
*
|
|
889
|
+
* @param timeStr - Time string to parse (e.g., "17:00", "9:00")
|
|
890
|
+
* @returns Object with hours and minutes, or null if invalid
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* parseTime("17:00") // => { hours: 17, minutes: 0 }
|
|
894
|
+
* parseTime("9:30") // => { hours: 9, minutes: 30 }
|
|
895
|
+
* parseTime("25:00") // => null (invalid hour)
|
|
896
|
+
*/
|
|
897
|
+
declare function parseTime(timeStr: string): {
|
|
898
|
+
hours: number;
|
|
899
|
+
minutes: number;
|
|
900
|
+
} | null;
|
|
901
|
+
/**
|
|
902
|
+
* Parse an absolute date string in various formats, with optional time
|
|
878
903
|
*
|
|
879
904
|
* Supported formats:
|
|
880
905
|
* - ISO: 2026-01-25
|
|
@@ -882,9 +907,14 @@ declare function groupWarningsBySeverity(warnings: Warning[]): {
|
|
|
882
907
|
* - US full: 1/25/2026
|
|
883
908
|
*
|
|
884
909
|
* @param dateStr - Date string to parse
|
|
910
|
+
* @param timeStr - Optional time string in HH:MM or H:MM format
|
|
885
911
|
* @returns Parsed Date object or null if invalid
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* parseAbsoluteDate("2026-01-25") // => Date at midnight
|
|
915
|
+
* parseAbsoluteDate("2026-01-25", "17:00") // => Date at 5 PM
|
|
886
916
|
*/
|
|
887
|
-
declare function parseAbsoluteDate(dateStr: string): Date | null;
|
|
917
|
+
declare function parseAbsoluteDate(dateStr: string, timeStr?: string): Date | null;
|
|
888
918
|
/**
|
|
889
919
|
* Resolve a relative date keyword against a base date
|
|
890
920
|
*
|
|
@@ -937,4 +967,4 @@ declare function extractDateFromHeading(line: string): Date | null;
|
|
|
937
967
|
*/
|
|
938
968
|
declare function generateTaskId(file: string, line: number, text: string): string;
|
|
939
969
|
|
|
940
|
-
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, filterWarnings, index$1 as filters, generateTaskId, groupWarningsBySeverity, parseAbsoluteDate, parseTask, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
|
970
|
+
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, filterWarnings, index$1 as filters, generateTaskId, groupWarningsBySeverity, parseAbsoluteDate, parseTask, parseTime, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ interface ParsingContext {
|
|
|
21
21
|
person?: string;
|
|
22
22
|
currentDate?: Date;
|
|
23
23
|
currentHeading?: string;
|
|
24
|
+
workdayStartTime?: string;
|
|
25
|
+
workdayEndTime?: string;
|
|
26
|
+
defaultDueTime?: 'start' | 'end';
|
|
24
27
|
}
|
|
25
28
|
interface TaskFilterCriteria {
|
|
26
29
|
assignee?: string | string[];
|
|
@@ -122,10 +125,10 @@ declare function extractCompletedDate(text: string): Date | undefined;
|
|
|
122
125
|
* Extract due date from task text with context awareness
|
|
123
126
|
*
|
|
124
127
|
* Handles both absolute dates ([due: 2026-01-25]) and relative dates
|
|
125
|
-
* ([due: tomorrow]) which require context.
|
|
128
|
+
* ([due: tomorrow]) which require context. Supports optional time ([due: 2026-01-25 17:00]).
|
|
126
129
|
*
|
|
127
130
|
* @param text - Task text
|
|
128
|
-
* @param context - Parsing context (for relative dates)
|
|
131
|
+
* @param context - Parsing context (for relative dates and workday config)
|
|
129
132
|
* @returns Object with parsed date and optional warning
|
|
130
133
|
*/
|
|
131
134
|
declare function extractDueDate(text: string, context: ParsingContext): {
|
|
@@ -232,15 +235,17 @@ declare const PRIORITY_HIGH: RegExp;
|
|
|
232
235
|
*/
|
|
233
236
|
declare const PRIORITY_NORMAL: RegExp;
|
|
234
237
|
/**
|
|
235
|
-
* Matches absolute due date in ISO format [due: YYYY-MM-DD]
|
|
238
|
+
* Matches absolute due date in ISO format [due: YYYY-MM-DD] with optional time [due: YYYY-MM-DD HH:MM]
|
|
236
239
|
*
|
|
237
240
|
* Examples:
|
|
238
|
-
* "[due: 2026-01-25]" → "2026-01-25"
|
|
239
|
-
* "[due:2026-01-25]" → "2026-01-25"
|
|
240
|
-
* "[due:
|
|
241
|
+
* "[due: 2026-01-25]" → "2026-01-25", undefined
|
|
242
|
+
* "[due: 2026-01-25 17:00]" → "2026-01-25", "17:00"
|
|
243
|
+
* "[due: 2026-01-25 9:00]" → "2026-01-25", "9:00"
|
|
244
|
+
* "[due:2026-01-25]" → "2026-01-25", undefined (spaces optional)
|
|
241
245
|
*
|
|
242
246
|
* Groups:
|
|
243
247
|
* [1] - Date string in YYYY-MM-DD format
|
|
248
|
+
* [2] - Optional time string in H:MM or HH:MM format (24-hour)
|
|
244
249
|
*/
|
|
245
250
|
declare const DUE_DATE_ABSOLUTE: RegExp;
|
|
246
251
|
/**
|
|
@@ -400,9 +405,14 @@ declare class MarkdownScanner {
|
|
|
400
405
|
*
|
|
401
406
|
* @param filePath - Relative file path (for context extraction)
|
|
402
407
|
* @param content - File content as string
|
|
408
|
+
* @param options - Optional scanner options including workday config
|
|
403
409
|
* @returns Object containing tasks and warnings
|
|
404
410
|
*/
|
|
405
|
-
scanFile(filePath: string, content: string
|
|
411
|
+
scanFile(filePath: string, content: string, options?: {
|
|
412
|
+
workdayStartTime?: string;
|
|
413
|
+
workdayEndTime?: string;
|
|
414
|
+
defaultDueTime?: 'start' | 'end';
|
|
415
|
+
}): {
|
|
406
416
|
tasks: Task[];
|
|
407
417
|
warnings: Warning[];
|
|
408
418
|
};
|
|
@@ -874,7 +884,22 @@ declare function groupWarningsBySeverity(warnings: Warning[]): {
|
|
|
874
884
|
};
|
|
875
885
|
|
|
876
886
|
/**
|
|
877
|
-
* Parse
|
|
887
|
+
* Parse a time string in HH:MM or H:MM format
|
|
888
|
+
*
|
|
889
|
+
* @param timeStr - Time string to parse (e.g., "17:00", "9:00")
|
|
890
|
+
* @returns Object with hours and minutes, or null if invalid
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* parseTime("17:00") // => { hours: 17, minutes: 0 }
|
|
894
|
+
* parseTime("9:30") // => { hours: 9, minutes: 30 }
|
|
895
|
+
* parseTime("25:00") // => null (invalid hour)
|
|
896
|
+
*/
|
|
897
|
+
declare function parseTime(timeStr: string): {
|
|
898
|
+
hours: number;
|
|
899
|
+
minutes: number;
|
|
900
|
+
} | null;
|
|
901
|
+
/**
|
|
902
|
+
* Parse an absolute date string in various formats, with optional time
|
|
878
903
|
*
|
|
879
904
|
* Supported formats:
|
|
880
905
|
* - ISO: 2026-01-25
|
|
@@ -882,9 +907,14 @@ declare function groupWarningsBySeverity(warnings: Warning[]): {
|
|
|
882
907
|
* - US full: 1/25/2026
|
|
883
908
|
*
|
|
884
909
|
* @param dateStr - Date string to parse
|
|
910
|
+
* @param timeStr - Optional time string in HH:MM or H:MM format
|
|
885
911
|
* @returns Parsed Date object or null if invalid
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* parseAbsoluteDate("2026-01-25") // => Date at midnight
|
|
915
|
+
* parseAbsoluteDate("2026-01-25", "17:00") // => Date at 5 PM
|
|
886
916
|
*/
|
|
887
|
-
declare function parseAbsoluteDate(dateStr: string): Date | null;
|
|
917
|
+
declare function parseAbsoluteDate(dateStr: string, timeStr?: string): Date | null;
|
|
888
918
|
/**
|
|
889
919
|
* Resolve a relative date keyword against a base date
|
|
890
920
|
*
|
|
@@ -937,4 +967,4 @@ declare function extractDateFromHeading(line: string): Date | null;
|
|
|
937
967
|
*/
|
|
938
968
|
declare function generateTaskId(file: string, line: number, text: string): string;
|
|
939
969
|
|
|
940
|
-
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, filterWarnings, index$1 as filters, generateTaskId, groupWarningsBySeverity, parseAbsoluteDate, parseTask, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
|
970
|
+
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, filterWarnings, index$1 as filters, generateTaskId, groupWarningsBySeverity, parseAbsoluteDate, parseTask, parseTime, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
package/dist/index.js
CHANGED
|
@@ -63,6 +63,7 @@ __export(index_exports, {
|
|
|
63
63
|
groupWarningsBySeverity: () => groupWarningsBySeverity,
|
|
64
64
|
parseAbsoluteDate: () => parseAbsoluteDate,
|
|
65
65
|
parseTask: () => parseTask,
|
|
66
|
+
parseTime: () => parseTime,
|
|
66
67
|
resolveRelativeDate: () => resolveRelativeDate,
|
|
67
68
|
sorting: () => sorting_exports,
|
|
68
69
|
updateTask: () => updateTask,
|
|
@@ -76,7 +77,7 @@ var ASSIGNEE = /@([\w-]+)/;
|
|
|
76
77
|
var PRIORITY_URGENT = /!!!/;
|
|
77
78
|
var PRIORITY_HIGH = /!!/;
|
|
78
79
|
var PRIORITY_NORMAL = /(?<!!)!(?!!)/;
|
|
79
|
-
var DUE_DATE_ABSOLUTE = /\[due:\s*(\d{4}-\d{2}-\d{2})\s*\]/i;
|
|
80
|
+
var DUE_DATE_ABSOLUTE = /\[due:\s*(\d{4}-\d{2}-\d{2})(?:\s+(\d{1,2}:\d{2}))?\s*\]/i;
|
|
80
81
|
var DUE_DATE_RELATIVE = /\[due:\s*(tomorrow|today|next\s+week|next\s+month)\]/i;
|
|
81
82
|
var DUE_DATE_SHORT = /\[due:\s*(\d{1,2}\/\d{1,2}(?:\/\d{2,4})?)\]/i;
|
|
82
83
|
var TAG = /#([\w-]+)/g;
|
|
@@ -104,15 +105,34 @@ var PATTERNS = {
|
|
|
104
105
|
|
|
105
106
|
// src/utils/dates.ts
|
|
106
107
|
var import_date_fns = require("date-fns");
|
|
107
|
-
function
|
|
108
|
+
function parseTime(timeStr) {
|
|
109
|
+
const match = timeStr.match(/^(\d{1,2}):(\d{2})$/);
|
|
110
|
+
if (!match || !match[1] || !match[2]) return null;
|
|
111
|
+
const hours = parseInt(match[1], 10);
|
|
112
|
+
const minutes = parseInt(match[2], 10);
|
|
113
|
+
if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return { hours, minutes };
|
|
117
|
+
}
|
|
118
|
+
function parseAbsoluteDate(dateStr, timeStr) {
|
|
108
119
|
const referenceDate = /* @__PURE__ */ new Date();
|
|
109
120
|
let date = (0, import_date_fns.parse)(dateStr, "yyyy-MM-dd", referenceDate);
|
|
110
|
-
if ((0, import_date_fns.isValid)(date))
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
if (!(0, import_date_fns.isValid)(date)) {
|
|
122
|
+
date = (0, import_date_fns.parse)(dateStr, "M/d/yy", referenceDate);
|
|
123
|
+
}
|
|
124
|
+
if (!(0, import_date_fns.isValid)(date)) {
|
|
125
|
+
date = (0, import_date_fns.parse)(dateStr, "M/d/yyyy", referenceDate);
|
|
126
|
+
}
|
|
127
|
+
if (!(0, import_date_fns.isValid)(date)) return null;
|
|
128
|
+
if (timeStr) {
|
|
129
|
+
const time = parseTime(timeStr);
|
|
130
|
+
if (time) {
|
|
131
|
+
date = (0, import_date_fns.setHours)(date, time.hours);
|
|
132
|
+
date = (0, import_date_fns.setMinutes)(date, time.minutes);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return date;
|
|
116
136
|
}
|
|
117
137
|
function resolveRelativeDate(relative, baseDate) {
|
|
118
138
|
const normalized = relative.toLowerCase().trim();
|
|
@@ -204,12 +224,29 @@ function extractCompletedDate(text) {
|
|
|
204
224
|
function extractDueDate(text, context) {
|
|
205
225
|
const absoluteMatch = text.match(PATTERNS.DUE_DATE_ABSOLUTE);
|
|
206
226
|
if (absoluteMatch?.[1]) {
|
|
207
|
-
const
|
|
227
|
+
const dateStr = absoluteMatch[1];
|
|
228
|
+
let timeStr = absoluteMatch[2];
|
|
229
|
+
if (!timeStr && context.defaultDueTime) {
|
|
230
|
+
if (context.defaultDueTime === "start" && context.workdayStartTime) {
|
|
231
|
+
timeStr = context.workdayStartTime;
|
|
232
|
+
} else if (context.defaultDueTime === "end" && context.workdayEndTime) {
|
|
233
|
+
timeStr = context.workdayEndTime;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const date = parseAbsoluteDate(dateStr, timeStr);
|
|
208
237
|
return { date: date ?? void 0 };
|
|
209
238
|
}
|
|
210
239
|
const shortMatch = text.match(PATTERNS.DUE_DATE_SHORT);
|
|
211
240
|
if (shortMatch?.[1]) {
|
|
212
|
-
|
|
241
|
+
let timeStr;
|
|
242
|
+
if (context.defaultDueTime) {
|
|
243
|
+
if (context.defaultDueTime === "start" && context.workdayStartTime) {
|
|
244
|
+
timeStr = context.workdayStartTime;
|
|
245
|
+
} else if (context.defaultDueTime === "end" && context.workdayEndTime) {
|
|
246
|
+
timeStr = context.workdayEndTime;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const date = parseAbsoluteDate(shortMatch[1], timeStr);
|
|
213
250
|
return { date: date ?? void 0 };
|
|
214
251
|
}
|
|
215
252
|
const relativeMatch = text.match(PATTERNS.DUE_DATE_RELATIVE);
|
|
@@ -376,9 +413,10 @@ var MarkdownScanner = class {
|
|
|
376
413
|
*
|
|
377
414
|
* @param filePath - Relative file path (for context extraction)
|
|
378
415
|
* @param content - File content as string
|
|
416
|
+
* @param options - Optional scanner options including workday config
|
|
379
417
|
* @returns Object containing tasks and warnings
|
|
380
418
|
*/
|
|
381
|
-
scanFile(filePath, content) {
|
|
419
|
+
scanFile(filePath, content, options) {
|
|
382
420
|
const tasks = [];
|
|
383
421
|
const warnings = [];
|
|
384
422
|
const todoistIds = /* @__PURE__ */ new Map();
|
|
@@ -387,6 +425,15 @@ var MarkdownScanner = class {
|
|
|
387
425
|
if (project !== void 0) context.project = project;
|
|
388
426
|
const person = extractPersonFromFilename(filePath);
|
|
389
427
|
if (person !== void 0) context.person = person;
|
|
428
|
+
if (options?.workdayStartTime) {
|
|
429
|
+
context.workdayStartTime = options.workdayStartTime;
|
|
430
|
+
}
|
|
431
|
+
if (options?.workdayEndTime) {
|
|
432
|
+
context.workdayEndTime = options.workdayEndTime;
|
|
433
|
+
}
|
|
434
|
+
if (options?.defaultDueTime) {
|
|
435
|
+
context.defaultDueTime = options.defaultDueTime;
|
|
436
|
+
}
|
|
390
437
|
const lines = content.split("\n");
|
|
391
438
|
for (let i = 0; i < lines.length; i++) {
|
|
392
439
|
const line = lines[i];
|
|
@@ -966,6 +1013,7 @@ function groupWarningsBySeverity(warnings) {
|
|
|
966
1013
|
groupWarningsBySeverity,
|
|
967
1014
|
parseAbsoluteDate,
|
|
968
1015
|
parseTask,
|
|
1016
|
+
parseTime,
|
|
969
1017
|
resolveRelativeDate,
|
|
970
1018
|
sorting,
|
|
971
1019
|
updateTask,
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ var ASSIGNEE = /@([\w-]+)/;
|
|
|
10
10
|
var PRIORITY_URGENT = /!!!/;
|
|
11
11
|
var PRIORITY_HIGH = /!!/;
|
|
12
12
|
var PRIORITY_NORMAL = /(?<!!)!(?!!)/;
|
|
13
|
-
var DUE_DATE_ABSOLUTE = /\[due:\s*(\d{4}-\d{2}-\d{2})\s*\]/i;
|
|
13
|
+
var DUE_DATE_ABSOLUTE = /\[due:\s*(\d{4}-\d{2}-\d{2})(?:\s+(\d{1,2}:\d{2}))?\s*\]/i;
|
|
14
14
|
var DUE_DATE_RELATIVE = /\[due:\s*(tomorrow|today|next\s+week|next\s+month)\]/i;
|
|
15
15
|
var DUE_DATE_SHORT = /\[due:\s*(\d{1,2}\/\d{1,2}(?:\/\d{2,4})?)\]/i;
|
|
16
16
|
var TAG = /#([\w-]+)/g;
|
|
@@ -43,17 +43,38 @@ import {
|
|
|
43
43
|
addDays,
|
|
44
44
|
addWeeks,
|
|
45
45
|
addMonths,
|
|
46
|
-
startOfWeek
|
|
46
|
+
startOfWeek,
|
|
47
|
+
setHours,
|
|
48
|
+
setMinutes
|
|
47
49
|
} from "date-fns";
|
|
48
|
-
function
|
|
50
|
+
function parseTime(timeStr) {
|
|
51
|
+
const match = timeStr.match(/^(\d{1,2}):(\d{2})$/);
|
|
52
|
+
if (!match || !match[1] || !match[2]) return null;
|
|
53
|
+
const hours = parseInt(match[1], 10);
|
|
54
|
+
const minutes = parseInt(match[2], 10);
|
|
55
|
+
if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return { hours, minutes };
|
|
59
|
+
}
|
|
60
|
+
function parseAbsoluteDate(dateStr, timeStr) {
|
|
49
61
|
const referenceDate = /* @__PURE__ */ new Date();
|
|
50
62
|
let date = parse(dateStr, "yyyy-MM-dd", referenceDate);
|
|
51
|
-
if (isValid(date))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
if (!isValid(date)) {
|
|
64
|
+
date = parse(dateStr, "M/d/yy", referenceDate);
|
|
65
|
+
}
|
|
66
|
+
if (!isValid(date)) {
|
|
67
|
+
date = parse(dateStr, "M/d/yyyy", referenceDate);
|
|
68
|
+
}
|
|
69
|
+
if (!isValid(date)) return null;
|
|
70
|
+
if (timeStr) {
|
|
71
|
+
const time = parseTime(timeStr);
|
|
72
|
+
if (time) {
|
|
73
|
+
date = setHours(date, time.hours);
|
|
74
|
+
date = setMinutes(date, time.minutes);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return date;
|
|
57
78
|
}
|
|
58
79
|
function resolveRelativeDate(relative, baseDate) {
|
|
59
80
|
const normalized = relative.toLowerCase().trim();
|
|
@@ -145,12 +166,29 @@ function extractCompletedDate(text) {
|
|
|
145
166
|
function extractDueDate(text, context) {
|
|
146
167
|
const absoluteMatch = text.match(PATTERNS.DUE_DATE_ABSOLUTE);
|
|
147
168
|
if (absoluteMatch?.[1]) {
|
|
148
|
-
const
|
|
169
|
+
const dateStr = absoluteMatch[1];
|
|
170
|
+
let timeStr = absoluteMatch[2];
|
|
171
|
+
if (!timeStr && context.defaultDueTime) {
|
|
172
|
+
if (context.defaultDueTime === "start" && context.workdayStartTime) {
|
|
173
|
+
timeStr = context.workdayStartTime;
|
|
174
|
+
} else if (context.defaultDueTime === "end" && context.workdayEndTime) {
|
|
175
|
+
timeStr = context.workdayEndTime;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const date = parseAbsoluteDate(dateStr, timeStr);
|
|
149
179
|
return { date: date ?? void 0 };
|
|
150
180
|
}
|
|
151
181
|
const shortMatch = text.match(PATTERNS.DUE_DATE_SHORT);
|
|
152
182
|
if (shortMatch?.[1]) {
|
|
153
|
-
|
|
183
|
+
let timeStr;
|
|
184
|
+
if (context.defaultDueTime) {
|
|
185
|
+
if (context.defaultDueTime === "start" && context.workdayStartTime) {
|
|
186
|
+
timeStr = context.workdayStartTime;
|
|
187
|
+
} else if (context.defaultDueTime === "end" && context.workdayEndTime) {
|
|
188
|
+
timeStr = context.workdayEndTime;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const date = parseAbsoluteDate(shortMatch[1], timeStr);
|
|
154
192
|
return { date: date ?? void 0 };
|
|
155
193
|
}
|
|
156
194
|
const relativeMatch = text.match(PATTERNS.DUE_DATE_RELATIVE);
|
|
@@ -317,9 +355,10 @@ var MarkdownScanner = class {
|
|
|
317
355
|
*
|
|
318
356
|
* @param filePath - Relative file path (for context extraction)
|
|
319
357
|
* @param content - File content as string
|
|
358
|
+
* @param options - Optional scanner options including workday config
|
|
320
359
|
* @returns Object containing tasks and warnings
|
|
321
360
|
*/
|
|
322
|
-
scanFile(filePath, content) {
|
|
361
|
+
scanFile(filePath, content, options) {
|
|
323
362
|
const tasks = [];
|
|
324
363
|
const warnings = [];
|
|
325
364
|
const todoistIds = /* @__PURE__ */ new Map();
|
|
@@ -328,6 +367,15 @@ var MarkdownScanner = class {
|
|
|
328
367
|
if (project !== void 0) context.project = project;
|
|
329
368
|
const person = extractPersonFromFilename(filePath);
|
|
330
369
|
if (person !== void 0) context.person = person;
|
|
370
|
+
if (options?.workdayStartTime) {
|
|
371
|
+
context.workdayStartTime = options.workdayStartTime;
|
|
372
|
+
}
|
|
373
|
+
if (options?.workdayEndTime) {
|
|
374
|
+
context.workdayEndTime = options.workdayEndTime;
|
|
375
|
+
}
|
|
376
|
+
if (options?.defaultDueTime) {
|
|
377
|
+
context.defaultDueTime = options.defaultDueTime;
|
|
378
|
+
}
|
|
331
379
|
const lines = content.split("\n");
|
|
332
380
|
for (let i = 0; i < lines.length; i++) {
|
|
333
381
|
const line = lines[i];
|
|
@@ -906,6 +954,7 @@ export {
|
|
|
906
954
|
groupWarningsBySeverity,
|
|
907
955
|
parseAbsoluteDate,
|
|
908
956
|
parseTask,
|
|
957
|
+
parseTime,
|
|
909
958
|
resolveRelativeDate,
|
|
910
959
|
sorting_exports as sorting,
|
|
911
960
|
updateTask,
|
package/package.json
CHANGED
package/src/parser/index.ts
CHANGED
|
@@ -94,27 +94,50 @@ export function extractCompletedDate(text: string): Date | undefined {
|
|
|
94
94
|
* Extract due date from task text with context awareness
|
|
95
95
|
*
|
|
96
96
|
* Handles both absolute dates ([due: 2026-01-25]) and relative dates
|
|
97
|
-
* ([due: tomorrow]) which require context.
|
|
97
|
+
* ([due: tomorrow]) which require context. Supports optional time ([due: 2026-01-25 17:00]).
|
|
98
98
|
*
|
|
99
99
|
* @param text - Task text
|
|
100
|
-
* @param context - Parsing context (for relative dates)
|
|
100
|
+
* @param context - Parsing context (for relative dates and workday config)
|
|
101
101
|
* @returns Object with parsed date and optional warning
|
|
102
102
|
*/
|
|
103
103
|
export function extractDueDate(
|
|
104
104
|
text: string,
|
|
105
105
|
context: ParsingContext,
|
|
106
106
|
): { date: Date | undefined; warning?: Warning } {
|
|
107
|
-
// Try absolute date first
|
|
107
|
+
// Try absolute date first (with optional time)
|
|
108
108
|
const absoluteMatch = text.match(PATTERNS.DUE_DATE_ABSOLUTE);
|
|
109
109
|
if (absoluteMatch?.[1]) {
|
|
110
|
-
const
|
|
110
|
+
const dateStr = absoluteMatch[1];
|
|
111
|
+
let timeStr = absoluteMatch[2]; // May be undefined
|
|
112
|
+
|
|
113
|
+
// If no time specified, apply default from workday config
|
|
114
|
+
if (!timeStr && context.defaultDueTime) {
|
|
115
|
+
if (context.defaultDueTime === 'start' && context.workdayStartTime) {
|
|
116
|
+
timeStr = context.workdayStartTime;
|
|
117
|
+
} else if (context.defaultDueTime === 'end' && context.workdayEndTime) {
|
|
118
|
+
timeStr = context.workdayEndTime;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const date = parseAbsoluteDate(dateStr, timeStr);
|
|
111
123
|
return { date: date ?? undefined };
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
// Try short format (M/D or M/D/YY)
|
|
115
127
|
const shortMatch = text.match(PATTERNS.DUE_DATE_SHORT);
|
|
116
128
|
if (shortMatch?.[1]) {
|
|
117
|
-
|
|
129
|
+
let timeStr: string | undefined;
|
|
130
|
+
|
|
131
|
+
// Apply default time from workday config
|
|
132
|
+
if (context.defaultDueTime) {
|
|
133
|
+
if (context.defaultDueTime === 'start' && context.workdayStartTime) {
|
|
134
|
+
timeStr = context.workdayStartTime;
|
|
135
|
+
} else if (context.defaultDueTime === 'end' && context.workdayEndTime) {
|
|
136
|
+
timeStr = context.workdayEndTime;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const date = parseAbsoluteDate(shortMatch[1], timeStr);
|
|
118
141
|
return { date: date ?? undefined };
|
|
119
142
|
}
|
|
120
143
|
|
package/src/parser/patterns.ts
CHANGED
|
@@ -63,17 +63,20 @@ export const PRIORITY_HIGH = /!!/;
|
|
|
63
63
|
export const PRIORITY_NORMAL = /(?<!!)!(?!!)/;
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Matches absolute due date in ISO format [due: YYYY-MM-DD]
|
|
66
|
+
* Matches absolute due date in ISO format [due: YYYY-MM-DD] with optional time [due: YYYY-MM-DD HH:MM]
|
|
67
67
|
*
|
|
68
68
|
* Examples:
|
|
69
|
-
* "[due: 2026-01-25]" → "2026-01-25"
|
|
70
|
-
* "[due:2026-01-25]" → "2026-01-25"
|
|
71
|
-
* "[due:
|
|
69
|
+
* "[due: 2026-01-25]" → "2026-01-25", undefined
|
|
70
|
+
* "[due: 2026-01-25 17:00]" → "2026-01-25", "17:00"
|
|
71
|
+
* "[due: 2026-01-25 9:00]" → "2026-01-25", "9:00"
|
|
72
|
+
* "[due:2026-01-25]" → "2026-01-25", undefined (spaces optional)
|
|
72
73
|
*
|
|
73
74
|
* Groups:
|
|
74
75
|
* [1] - Date string in YYYY-MM-DD format
|
|
76
|
+
* [2] - Optional time string in H:MM or HH:MM format (24-hour)
|
|
75
77
|
*/
|
|
76
|
-
export const DUE_DATE_ABSOLUTE =
|
|
78
|
+
export const DUE_DATE_ABSOLUTE =
|
|
79
|
+
/\[due:\s*(\d{4}-\d{2}-\d{2})(?:\s+(\d{1,2}:\d{2}))?\s*\]/i;
|
|
77
80
|
|
|
78
81
|
/**
|
|
79
82
|
* Matches relative due date keywords
|