type Priority = 'urgent' | 'high' | 'normal' | 'low'; interface Task { id: string; text: string; completed: boolean; file: string; line: number; project?: string; person?: string; assignee?: string; dueDate?: Date; priority?: Priority; tags: string[]; sources?: Record; completedDate?: Date; } interface ParsingContext { project?: string; person?: string; workdayStartTime?: string; workdayEndTime?: string; defaultDueTime?: 'start' | 'end'; } interface TaskFilterCriteria { assignee?: string | string[]; completed?: boolean; overdue?: boolean; dueDate?: { before?: Date; after?: Date; exact?: Date; }; priority?: Priority | Priority[]; project?: string | string[]; person?: string | string[]; tags?: string | string[]; hasTag?: boolean; path?: string; } interface ScanResult { tasks: Task[]; warnings: Warning[]; metadata: { filesScanned: number; totalTasks: number; parseErrors: number; }; } type WarningSeverity = 'info' | 'warning' | 'error'; type WarningCode = 'unsupported-bullet' | 'malformed-checkbox' | 'missing-space-after' | 'missing-space-before' | 'relative-date-no-context' | 'missing-due-date' | 'missing-completed-date' | 'duplicate-source-id' | 'file-read-error'; interface Warning { file: string; line: number; column?: number; severity: WarningSeverity; source: 'md2do'; ruleId: WarningCode; message: string; text?: string; url?: string; /** @deprecated Use message instead */ reason?: string; } interface SourceTask { externalId: string; text: string; completed: boolean; priority?: 'urgent' | 'high' | 'normal' | 'low'; dueDate?: string; tags?: string[]; assignee?: string; metadata?: Record; } interface FetchOptions { since?: Date; limit?: number; filter?: Record; } interface SourceProvider { readonly slug: string; readonly name: string; fetchTasks(options?: FetchOptions): Promise; completeTask?(externalId: string): Promise; reopenTask?(externalId: string): Promise; } interface IngestRecord { source: string; externalId: string; text: string; completed: boolean; priority?: string; dueDate?: string; tags?: string[]; assignee?: string; metadata?: Record; } /** * Extract assignee from task text * * @param text - Task text * @returns Username without @ symbol, or undefined * * @example * extractAssignee("@nick Review PR") // => "nick" * extractAssignee("Review PR") // => undefined */ declare function extractAssignee(text: string): string | undefined; /** * Extract priority level from task text * * Priority is determined by exclamation marks: * - !!! = urgent * - !! = high * - ! = normal * - (none) = low * * @param text - Task text * @returns Priority level or undefined if no priority markers */ declare function extractPriority(text: string): Priority | undefined; /** * Extract all tags from task text * * @param text - Task text * @returns Array of tag names (without # symbol) * * @example * extractTags("Review PR #backend #urgent") // => ["backend", "urgent"] * extractTags("No tags here") // => [] */ declare function extractTags(text: string): string[]; /** * Extract Todoist ID from task text * * @param text - Task text * @returns Todoist ID string or undefined * * @example * extractTodoistId("Task {todoist:123456}") // => "123456" * extractTodoistId("Task [todoist:123456]") // => "123456" (legacy) * @deprecated Use extractSources() instead */ declare function extractTodoistId(text: string): string | undefined; /** * Extract all source links from task text as a slug → id map * * Matches {slug:value} tokens, skipping reserved slugs (e.g. "completed"). * Also handles legacy [todoist:NNN] bracket syntax. * * @param text - Task text * @returns Record of slug → externalId, or undefined if none found * * @example * extractSources("Task {todoist:123} {teams:msg-456}") // => { todoist: "123", teams: "msg-456" } * extractSources("Task [todoist:123]") // => { todoist: "123" } (legacy) * extractSources("Task {completed:2026-01-18}") // => undefined (reserved) */ declare function extractSources(text: string): Record | undefined; /** * Format sources record as space-separated {slug:id} tokens * * @param sources - Record of slug → externalId * @returns Space-separated string of {slug:id} tokens * * @example * formatSources({ todoist: "123", teams: "msg-456" }) // => "{todoist:123} {teams:msg-456}" */ declare function formatSources(sources: Record): string; /** * Extract completion date from task text * * @param text - Task text * @returns Parsed Date or undefined * * @example * extractCompletedDate("{completed:2026-01-18}") // => Date(2026-01-18) * extractCompletedDate("[completed: 2026-01-18]") // => Date(2026-01-18) (legacy) */ declare function extractCompletedDate(text: string): Date | undefined; /** * Extract due date from task text * * Handles absolute dates (#due/2026-01-25 or legacy [due: 2026-01-25]). * Legacy relative dates ([due: tomorrow]) always produce a warning. * * @param text - Task text * @param context - Parsing context (for workday config) * @returns Object with parsed date and optional warning */ declare function extractDueDate(text: string, context: ParsingContext): { date: Date | undefined; warning?: Warning; }; /** * Clean task text by removing metadata markers * * Removes: * - Assignees (@username) * - Due dates ([due: ...]) * - Priority markers (!, !!, !!!) * - Tags (#tag) * - Todoist IDs ([todoist:...]) * - Completion dates ([completed:...]) * * @param text - Raw task text * @returns Cleaned text for display * * @example * cleanTaskText("@nick Review PR !! #backend [due: 2026-01-25]") * // => "Review PR" */ declare function cleanTaskText(text: string): string; /** * Parse a single line as a task * * This is the main parser entry point. It: * 1. Checks if the line is a task * 2. Extracts completion status * 3. Parses all metadata (assignee, priority, dates, tags) * 4. Cleans the text * 5. Generates a stable ID * 6. Applies context (project, person) * * @param line - Line of text to parse * @param lineNumber - Line number in file (1-indexed) * @param file - Relative file path * @param context - Parsing context * @returns Parsed Task object or null if line is not a task, plus any warnings */ declare function parseTask(line: string, lineNumber: number, file: string, context: ParsingContext): { task: Task | null; warnings: Warning[]; }; /** * Regular expression patterns for parsing markdown task syntax * * All patterns are documented with examples and test cases. */ /** * Matches GitHub Flavored Markdown task checkbox syntax * * Examples: * "- [ ] Task" → match, incomplete * "- [x] Task" → match, complete * " - [X] Task" → match, complete (case-insensitive) * "* [ ] Task" → no match (only dash lists supported) * * Groups: * [1] - Leading whitespace (indentation) * [2] - Checkbox state: space (incomplete) or x/X (complete) */ declare const TASK_CHECKBOX: RegExp; /** * Matches assignee mentions (@username) * * Examples: * "@nick" → "nick" * "@jane-doe" → "jane-doe" * "@alex_chen" → no match (underscores not supported) * * Groups: * [1] - Username (alphanumeric and hyphens only) */ declare const ASSIGNEE: RegExp; /** * Matches urgent priority marker (triple exclamation) * * Examples: * "Task !!!" → match * "Task !!" → no match */ declare const PRIORITY_URGENT: RegExp; /** * Matches high priority marker (double exclamation) * * Examples: * "Task !!" → match * "Task !!!" → no match (would match urgent first) */ declare const PRIORITY_HIGH: RegExp; /** * Matches normal priority marker (single exclamation, not part of !! or !!!) * * Examples: * "Task !" → match * "Task !!" → no match * "Task !!!" → no match * * Uses negative lookbehind and lookahead to ensure single ! */ declare const PRIORITY_NORMAL: RegExp; /** * Matches due date in new syntax (#due/YYYY-MM-DD) or legacy bracket syntax * * New syntax examples: * "#due/2026-01-25" → group 1: "2026-01-25" * * Legacy syntax examples: * "[due: 2026-01-25]" → group 2: "2026-01-25", group 3: optional time * "[due: 2026-01-25 17:00]" → group 2: "2026-01-25", group 3: "17:00" * "[due:2026-01-25]" → group 2: "2026-01-25" * * Groups: * [1] - Date from new #due/ syntax * [2] - Date from legacy [due:] syntax * [3] - Optional time from legacy syntax */ declare const DUE_DATE_ABSOLUTE: RegExp; /** * Matches relative due date keywords * * Examples: * "[due: tomorrow]" → "tomorrow" * "[due: next week]" → "next week" * "[due: today]" → "today" * "[due: next month]" → "next month" * * Groups: * [1] - Relative date keyword */ declare const DUE_DATE_RELATIVE: RegExp; /** * Matches short date format [due: M/D] or [due: M/D/YY] * * Examples: * "[due: 1/25]" → "1/25" * "[due: 1/25/26]" → "1/25/26" * "[due: 12/31/2026]" → "12/31/2026" * * Groups: * [1] - Date string in M/D or M/D/YY or M/D/YYYY format */ declare const DUE_DATE_SHORT: RegExp; /** * Matches hashtags for categorization, excluding #due/ prefix * * Examples: * "#backend" → "backend" * "#urgent-fix" → "urgent-fix" * "# heading" → no match (space after #) * "#due/2026-01-25" → no match (due date, not a tag) * * Groups: * [1] - Tag name (alphanumeric and hyphens only) * * Note: Use with .match() or .matchAll() to get all tags */ declare const TAG: RegExp; /** * Matches Todoist ID in new syntax ({todoist:NNN}) or legacy bracket syntax * * New syntax examples: * "{todoist:123456789}" → group 1: "123456789" * * Legacy syntax examples: * "[todoist:123456789]" → group 2: "123456789" * "[todoist: 987654321]" → group 2: "987654321" * * Groups: * [1] - Todoist ID from new {todoist:} syntax * [2] - Todoist ID from legacy [todoist:] syntax */ declare const TODOIST_ID: RegExp; /** * Matches any {slug:value} brace token (global) * * Examples: * "{todoist:123}" → slug: "todoist", value: "123" * "{teams:msg-456}" → slug: "teams", value: "msg-456" * "{completed:2026-01-18}" → slug: "completed", value: "2026-01-18" * * Groups: * [1] - Slug (starts with letter, alphanumeric) * [2] - Value (anything up to closing brace) */ declare const BRACE_TOKEN: RegExp; /** * Matches {slug:value} brace tokens that are NOT reserved slugs (global) * Reserved slugs: completed * * Examples: * "{todoist:123}" → match * "{teams:msg-456}" → match * "{completed:2026-01-18}" → no match (reserved) */ declare const BRACE_TOKEN_NON_RESERVED: RegExp; /** * Legacy bracket-only form for Todoist ID * * Examples: * "[todoist:123456]" → group 1: "123456" * "[todoist: 987654321]" → group 1: "987654321" */ declare const TODOIST_ID_LEGACY: RegExp; /** * Reserved slug names that cannot be used as source identifiers */ declare const RESERVED_SLUGS: Set; /** * Matches completion date in new syntax ({completed:YYYY-MM-DD}) or legacy bracket syntax * * New syntax examples: * "{completed:2026-01-18}" → group 1: "2026-01-18" * * Legacy syntax examples: * "[completed: 2026-01-18]" → group 2: "2026-01-18" * "[completed:2026-01-18]" → group 2: "2026-01-18" * * Groups: * [1] - Date from new {completed:} syntax * [2] - Date from legacy [completed:] syntax */ declare const COMPLETED_DATE: RegExp; /** * Combined patterns object for easy import */ declare const PATTERNS: { readonly TASK_CHECKBOX: RegExp; readonly ASSIGNEE: RegExp; readonly PRIORITY_URGENT: RegExp; readonly PRIORITY_HIGH: RegExp; readonly PRIORITY_NORMAL: RegExp; readonly DUE_DATE_ABSOLUTE: RegExp; readonly DUE_DATE_RELATIVE: RegExp; readonly DUE_DATE_SHORT: RegExp; readonly TAG: RegExp; readonly TODOIST_ID: RegExp; readonly TODOIST_ID_LEGACY: RegExp; readonly COMPLETED_DATE: RegExp; readonly BRACE_TOKEN: RegExp; readonly BRACE_TOKEN_NON_RESERVED: RegExp; readonly RESERVED_SLUGS: Set; }; /** * Extract project name from file path * * Looks for "projects/" directory in path and returns the * immediate subdirectory name. * * @param filePath - Relative file path * @returns Project name or undefined * * @example * extractProjectFromPath('projects/acme-app/notes.md') // => 'acme-app' * extractProjectFromPath('1-1s/jane.md') // => undefined */ declare function extractProjectFromPath(filePath: string): string | undefined; /** * Extract person name from file path * * Looks for "1-1s/" directory in path and extracts the filename * (without extension) as the person identifier. * * @param filePath - Relative file path * @returns Person identifier or undefined * * @example * extractPersonFromFilename('1-1s/jane-doe.md') // => 'jane-doe' * extractPersonFromFilename('projects/acme/notes.md') // => undefined */ declare function extractPersonFromFilename(filePath: string): string | undefined; /** * MarkdownScanner scans markdown file content and extracts tasks * * Key features: * - Context tracking: Maintains project and person context * - Pure function: No I/O, just string processing * - Warning collection: Reports issues like relative dates without context * * @example * const scanner = new MarkdownScanner(); * const result = scanner.scanFile('projects/acme-app/notes.md', fileContent); * console.log(result.tasks); // Array of Task objects * console.log(result.warnings); // Array of Warning objects */ declare class MarkdownScanner { /** * Scan a single markdown file's content * * Processes the file line-by-line, tracking context and extracting tasks. * * @param filePath - Relative file path (for context extraction) * @param content - File content as string * @param options - Optional scanner options including workday config * @returns Object containing tasks and warnings */ scanFile(filePath: string, content: string, options?: { workdayStartTime?: string; workdayEndTime?: string; defaultDueTime?: 'start' | 'end'; }): { tasks: Task[]; warnings: Warning[]; }; /** * Scan multiple files * * Note: This method expects file contents to be provided by the caller. * File I/O should be handled in the CLI package using fast-glob. * * @param files - Array of {path, content} objects * @returns Combined scan results from all files */ scanFiles(files: Array<{ path: string; content: string; }>): { tasks: Task[]; warnings: Warning[]; }; } interface UpdateTaskOptions { /** * Path to the markdown file */ file: string; /** * Line number (1-indexed) of the task to update */ line: number; /** * Updates to apply to the task */ updates: { /** * Mark task as completed or incomplete */ completed?: boolean; /** * Update the task text */ text?: string; /** * Replace the entire line */ replaceLine?: string; }; } interface WriteTaskResult { /** * Whether the task was successfully updated */ success: boolean; /** * The updated task (after changes) */ task?: Task; /** * Error message if update failed */ error?: string; } /** * Update a task in a markdown file * This performs an atomic write using a temporary file */ declare function updateTask(options: UpdateTaskOptions): Promise; /** * Batch update multiple tasks in a file * More efficient than calling updateTask multiple times */ declare function updateTasks(file: string, updates: Array<{ line: number; updates: UpdateTaskOptions['updates']; }>): Promise<{ success: boolean; updatedCount: number; errors: Array<{ line: number; error: string; }>; }>; /** * Add a task to a markdown file */ declare function addTask(file: string, text: string, options?: { /** * Line number to insert at (1-indexed) * If not specified, appends to end of file */ line?: number; /** * Whether the task is completed */ completed?: boolean; }): Promise; /** * Filter predicate function type * Returns true if task should be included in results */ type TaskFilter = (task: Task) => boolean; /** * Filter tasks by assignee * * @param assignee - Username to filter by (without @ symbol) * @returns Filter predicate * * @example * tasks.filter(byAssignee('nick')) */ declare function byAssignee$1(assignee: string): TaskFilter; /** * Filter tasks by completion status * * @param completed - true for completed, false for incomplete * @returns Filter predicate * * @example * tasks.filter(byCompleted(false)) // Get incomplete tasks */ declare function byCompleted(completed: boolean): TaskFilter; /** * Filter tasks by priority level * * @param priority - Priority level to filter by * @returns Filter predicate * * @example * tasks.filter(byPriority('urgent')) */ declare function byPriority$1(priority: Priority): TaskFilter; /** * Filter tasks by project * * @param project - Project name to filter by * @returns Filter predicate * * @example * tasks.filter(byProject('acme-app')) */ declare function byProject$1(project: string): TaskFilter; /** * Filter tasks by person (from 1-1 context) * * @param person - Person identifier to filter by * @returns Filter predicate * * @example * tasks.filter(byPerson('jane-doe')) */ declare function byPerson$1(person: string): TaskFilter; /** * Filter tasks by tag * * @param tag - Tag to filter by (without # symbol) * @returns Filter predicate * * @example * tasks.filter(byTag('urgent')) */ declare function byTag(tag: string): TaskFilter; /** * Filter tasks by file path * * @param path - File path or directory to filter by * @param options - Filter options * @returns Filter predicate * * @example * // Match exact file * tasks.filter(byPath('projects/acme-app/sprint.md')) * * // Match directory (recursive) * tasks.filter(byPath('projects/acme-app')) * * // Match directory (non-recursive) * tasks.filter(byPath('projects/acme-app', { recursive: false })) */ declare function byPath(path: string, options?: { recursive?: boolean; }): TaskFilter; /** * Filter tasks that are overdue * A task is overdue if it has a due date in the past * * @param referenceDate - Date to compare against (defaults to now) * @returns Filter predicate * * @example * tasks.filter(isOverdue()) */ declare function isOverdue(referenceDate?: Date): TaskFilter; /** * Filter tasks due today * * @param referenceDate - Date to compare against (defaults to now) * @returns Filter predicate * * @example * tasks.filter(isDueToday()) */ declare function isDueToday(referenceDate?: Date): TaskFilter; /** * Filter tasks due this week * * @param referenceDate - Date to compare against (defaults to now) * @returns Filter predicate * * @example * tasks.filter(isDueThisWeek()) */ declare function isDueThisWeek(referenceDate?: Date): TaskFilter; /** * Filter tasks due within a specific number of days * * @param days - Number of days from reference date * @param referenceDate - Date to compare against (defaults to now) * @returns Filter predicate * * @example * tasks.filter(isDueWithinDays(7)) // Due in next 7 days */ declare function isDueWithinDays(days: number, referenceDate?: Date): TaskFilter; /** * Filter tasks that have a due date set * * @returns Filter predicate * * @example * tasks.filter(hasDueDate()) */ declare function hasDueDate(): TaskFilter; /** * Filter tasks that have no due date set * * @returns Filter predicate * * @example * tasks.filter(hasNoDueDate()) */ declare function hasNoDueDate(): TaskFilter; /** * Combine multiple filters with AND logic * * @param filters - Array of filter predicates * @returns Combined filter predicate * * @example * tasks.filter(combineFilters([ * byAssignee('nick'), * byPriority('urgent'), * isOverdue() * ])) */ declare function combineFilters(filters: TaskFilter[]): TaskFilter; /** * Combine multiple filters with OR logic * * @param filters - Array of filter predicates * @returns Combined filter predicate * * @example * tasks.filter(combineFiltersOr([ * isOverdue(), * isDueToday() * ])) */ declare function combineFiltersOr(filters: TaskFilter[]): TaskFilter; /** * Negate a filter * * @param filter - Filter predicate to negate * @returns Negated filter predicate * * @example * tasks.filter(not(byCompleted(true))) // Get incomplete tasks */ declare function not(filter: TaskFilter): TaskFilter; type index$1_TaskFilter = TaskFilter; declare const index$1_byCompleted: typeof byCompleted; declare const index$1_byPath: typeof byPath; declare const index$1_byTag: typeof byTag; declare const index$1_combineFilters: typeof combineFilters; declare const index$1_combineFiltersOr: typeof combineFiltersOr; declare const index$1_hasDueDate: typeof hasDueDate; declare const index$1_hasNoDueDate: typeof hasNoDueDate; declare const index$1_isDueThisWeek: typeof isDueThisWeek; declare const index$1_isDueToday: typeof isDueToday; declare const index$1_isDueWithinDays: typeof isDueWithinDays; declare const index$1_isOverdue: typeof isOverdue; declare const index$1_not: typeof not; declare namespace index$1 { export { type index$1_TaskFilter as TaskFilter, byAssignee$1 as byAssignee, index$1_byCompleted as byCompleted, index$1_byPath as byPath, byPerson$1 as byPerson, byPriority$1 as byPriority, byProject$1 as byProject, index$1_byTag as byTag, index$1_combineFilters as combineFilters, index$1_combineFiltersOr as combineFiltersOr, index$1_hasDueDate as hasDueDate, index$1_hasNoDueDate as hasNoDueDate, index$1_isDueThisWeek as isDueThisWeek, index$1_isDueToday as isDueToday, index$1_isDueWithinDays as isDueWithinDays, index$1_isOverdue as isOverdue, index$1_not as not }; } /** * Comparator function type for sorting tasks * Returns negative if a < b, positive if a > b, 0 if equal */ type TaskComparator = (a: Task, b: Task) => number; /** * Sort tasks by due date (earliest first) * Tasks without due dates come last * * @returns Comparator function * * @example * tasks.sort(byDueDate()) */ declare function byDueDate(): TaskComparator; /** * Sort tasks by priority (urgent > high > normal > low) * * @returns Comparator function * * @example * tasks.sort(byPriority()) */ declare function byPriority(): TaskComparator; /** * Sort tasks by file path (alphabetically) * * @returns Comparator function * * @example * tasks.sort(byFile()) */ declare function byFile(): TaskComparator; /** * Sort tasks by project (alphabetically) * Tasks without projects come last * * @returns Comparator function * * @example * tasks.sort(byProject()) */ declare function byProject(): TaskComparator; /** * Sort tasks by person (alphabetically) * Tasks without person context come last * * @returns Comparator function * * @example * tasks.sort(byPerson()) */ declare function byPerson(): TaskComparator; /** * Sort tasks by assignee (alphabetically) * Tasks without assignees come last * * @returns Comparator function * * @example * tasks.sort(byAssignee()) */ declare function byAssignee(): TaskComparator; /** * Sort tasks by completion status (incomplete first) * * @returns Comparator function * * @example * tasks.sort(byCompletionStatus()) */ declare function byCompletionStatus(): TaskComparator; /** * Combine multiple comparators * Applies comparators in order until a non-zero result is found * * @param comparators - Array of comparator functions * @returns Combined comparator function * * @example * // Sort by priority, then by due date * tasks.sort(combineComparators([byPriority(), byDueDate()])) */ declare function combineComparators(comparators: TaskComparator[]): TaskComparator; /** * Reverse a comparator * * @param comparator - Comparator to reverse * @returns Reversed comparator * * @example * // Sort by due date descending (latest first) * tasks.sort(reverse(byDueDate())) */ declare function reverse(comparator: TaskComparator): TaskComparator; type index_TaskComparator = TaskComparator; declare const index_byAssignee: typeof byAssignee; declare const index_byCompletionStatus: typeof byCompletionStatus; declare const index_byDueDate: typeof byDueDate; declare const index_byFile: typeof byFile; declare const index_byPerson: typeof byPerson; declare const index_byPriority: typeof byPriority; declare const index_byProject: typeof byProject; declare const index_combineComparators: typeof combineComparators; declare const index_reverse: typeof reverse; declare namespace index { export { type index_TaskComparator as TaskComparator, index_byAssignee as byAssignee, index_byCompletionStatus as byCompletionStatus, index_byDueDate as byDueDate, index_byFile as byFile, index_byPerson as byPerson, index_byPriority as byPriority, index_byProject as byProject, index_combineComparators as combineComparators, index_reverse as reverse }; } interface WarningFilterConfig { enabled?: boolean | undefined; rules?: Record | undefined; } /** * Filter warnings based on configuration rules * * This function applies warning configuration rules to filter out disabled warnings * and optionally all warnings if globally disabled. * * @param warnings - Array of warnings to filter * @param config - Warning configuration with enabled flag and rule overrides * @returns Filtered array of warnings * * @example * ```typescript * const config = { * enabled: true, * rules: { * 'missing-due-date': 'off', * 'duplicate-source-id': 'error', * }, * }; * * const filtered = filterWarnings(allWarnings, config); * // Returns warnings except those with ruleId 'missing-due-date' * ``` */ declare function filterWarnings(warnings: Warning[], config?: WarningFilterConfig): Warning[]; /** * Group warnings by severity * * Useful for displaying warnings in order of importance or * treating errors differently from warnings. * * @param warnings - Array of warnings to group * @returns Object with warnings grouped by severity level * * @example * ```typescript * const grouped = groupWarningsBySeverity(warnings); * console.log(`${grouped.error.length} errors`); * console.log(`${grouped.warning.length} warnings`); * console.log(`${grouped.info.length} info messages`); * ``` */ declare function groupWarningsBySeverity(warnings: Warning[]): { error: Warning[]; warning: Warning[]; info: Warning[]; }; interface MigrationResult { content: string; changes: MigrationChange[]; warnings: MigrationWarning[]; } interface MigrationChange { line: number; original: string; migrated: string; rule: string; } interface MigrationWarning { line: number; text: string; message: string; } /** * Migrate content from legacy bracket syntax to new tag/brace syntax. * * Rules: * [due: YYYY-MM-DD] → #due/YYYY-MM-DD * [due: YYYY-MM-DD H:MM] → #due/YYYY-MM-DD (time dropped with warning) * [due: M/D/YY] → #due/YYYY-MM-DD (converted to ISO) * [due: M/D] → dropped with warning (no year) * [due: tomorrow] → dropped with warning * [completed: YYYY-MM-DD] → {completed:YYYY-MM-DD} * [todoist: NNN] → {todoist:NNN} */ declare function migrateContent(content: string): MigrationResult; /** * Parse JSONL content into IngestRecord array * * @param content - JSONL string (one JSON object per line) * @returns Array of validated IngestRecord objects * @throws Error with line number on malformed JSON or missing required fields */ declare function parseJsonl(content: string): IngestRecord[]; /** * Convert a single IngestRecord to a markdown task line * * Format: - [ ] text @assignee PRIORITY #tags #due/DATE {source:externalId} {completed:DATE} * * @param record - The ingest record to convert * @param today - Today's date string (YYYY-MM-DD) used for completed date; defaults to current date * @returns Markdown task line string */ declare function ingestRecordToLine(record: IngestRecord, today?: string): string; /** * Generate a markdown document from an array of IngestRecord objects * * Groups incomplete tasks first, then a "## Completed" section. * H1 title defaults to title-cased source slug from first record. * * @param records - Array of ingest records * @param title - Optional override for the H1 title * @param today - Today's date string (YYYY-MM-DD) for completed dates * @returns Full markdown document string */ declare function ingestRecords(records: IngestRecord[], title?: string, today?: string): string; /** * Parse a time string in HH:MM or H:MM format * * @param timeStr - Time string to parse (e.g., "17:00", "9:00") * @returns Object with hours and minutes, or null if invalid * * @example * parseTime("17:00") // => { hours: 17, minutes: 0 } * parseTime("9:30") // => { hours: 9, minutes: 30 } * parseTime("25:00") // => null (invalid hour) */ declare function parseTime(timeStr: string): { hours: number; minutes: number; } | null; /** * Parse an absolute date string in various formats, with optional time * * Supported formats: * - ISO: 2026-01-25 * - US short: 1/25/26 * - US full: 1/25/2026 * * @param dateStr - Date string to parse * @param timeStr - Optional time string in HH:MM or H:MM format * @returns Parsed Date object or null if invalid * * @example * parseAbsoluteDate("2026-01-25") // => Date at midnight * parseAbsoluteDate("2026-01-25", "17:00") // => Date at 5 PM */ declare function parseAbsoluteDate(dateStr: string, timeStr?: string): Date | null; /** * Resolve a relative date keyword against a base date * * Supported keywords: * - today * - tomorrow * - next week (next Monday) * - next month * * @param relative - Relative date keyword * @param baseDate - Reference date to calculate from * @returns Resolved Date object or null if keyword unknown */ declare function resolveRelativeDate(relative: string, baseDate: Date): Date | null; /** * Generate a stable, unique ID for a task * * The ID is based on a hash of the file path, line number, and task text. * This ensures: * - Same task = same ID (stable across scans) * - Task moves or changes = new ID * - Different tasks = different IDs (even if text similar) * * @param file - Relative file path * @param line - Line number in file * @param text - Clean task text (without metadata) * @returns 8-character hex ID * * @example * generateTaskId('notes.md', 42, 'Review PR') * // => 'a3f2d8b1' */ declare function generateTaskId(file: string, line: number, text: string): string; export { ASSIGNEE, BRACE_TOKEN, BRACE_TOKEN_NON_RESERVED, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, type FetchOptions, type IngestRecord, MarkdownScanner, type MigrationChange, type MigrationResult, type MigrationWarning, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, RESERVED_SLUGS, type ScanResult, type SourceProvider, type SourceTask, TAG, TASK_CHECKBOX, TODOIST_ID, TODOIST_ID_LEGACY, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractSources, extractTags, extractTodoistId, filterWarnings, index$1 as filters, formatSources, generateTaskId, groupWarningsBySeverity, ingestRecordToLine, ingestRecords, migrateContent, parseAbsoluteDate, parseJsonl, parseTask, parseTime, resolveRelativeDate, index as sorting, updateTask, updateTasks };