calendar-mcp 1.0.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.
@@ -0,0 +1,46 @@
1
+ import { readCalendarFile } from '../utils/ical.js';
2
+ import { parseNaturalDate } from '../utils/dates.js';
3
+ const DEFAULT_LIMIT = 50;
4
+ /**
5
+ * List calendar events with optional filtering
6
+ * @param params - Filter parameters
7
+ * @returns List of events with count and hasMore indicator
8
+ */
9
+ export async function listEvents(params = {}) {
10
+ // Read all events
11
+ let events = await readCalendarFile();
12
+ // Filter by start date
13
+ if (params.startDate) {
14
+ const startFilter = parseNaturalDate(params.startDate);
15
+ events = events.filter(event => new Date(event.start) >= startFilter);
16
+ }
17
+ // Filter by end date
18
+ if (params.endDate) {
19
+ const endFilter = parseNaturalDate(params.endDate);
20
+ events = events.filter(event => new Date(event.start) <= endFilter);
21
+ }
22
+ // Sort by start date
23
+ const sortOrder = params.sortOrder ?? 'asc';
24
+ events.sort((a, b) => {
25
+ const dateA = new Date(a.start).getTime();
26
+ const dateB = new Date(b.start).getTime();
27
+ return sortOrder === 'asc' ? dateA - dateB : dateB - dateA;
28
+ });
29
+ // Calculate total count before limiting
30
+ const totalCount = events.length;
31
+ // Apply limit
32
+ const limit = params.limit ?? DEFAULT_LIMIT;
33
+ const hasMore = events.length > limit;
34
+ events = events.slice(0, limit);
35
+ return {
36
+ events,
37
+ count: events.length,
38
+ hasMore,
39
+ };
40
+ }
41
+ /**
42
+ * Get all events without filtering (internal use)
43
+ */
44
+ export async function getAllEvents() {
45
+ return readCalendarFile();
46
+ }
@@ -0,0 +1,7 @@
1
+ import type { SearchEventsParams, CalendarEvent } from '../types.js';
2
+ /**
3
+ * Search for events matching criteria
4
+ * @param params - Search parameters
5
+ * @returns List of matching events
6
+ */
7
+ export declare function searchEvents(params?: SearchEventsParams): Promise<CalendarEvent[]>;
@@ -0,0 +1,43 @@
1
+ import { readCalendarFile } from '../utils/ical.js';
2
+ import { parseNaturalDate } from '../utils/dates.js';
3
+ const DEFAULT_LIMIT = 20;
4
+ /**
5
+ * Search for events matching criteria
6
+ * @param params - Search parameters
7
+ * @returns List of matching events
8
+ */
9
+ export async function searchEvents(params = {}) {
10
+ // Read all events
11
+ let events = await readCalendarFile();
12
+ // Text search (case-insensitive)
13
+ if (params.query) {
14
+ const query = params.query.toLowerCase();
15
+ events = events.filter(event => {
16
+ const titleMatch = event.title.toLowerCase().includes(query);
17
+ const descriptionMatch = event.description?.toLowerCase().includes(query) ?? false;
18
+ const locationMatch = event.location?.toLowerCase().includes(query) ?? false;
19
+ return titleMatch || descriptionMatch || locationMatch;
20
+ });
21
+ }
22
+ // Filter by start date
23
+ if (params.startDate) {
24
+ const startFilter = parseNaturalDate(params.startDate);
25
+ events = events.filter(event => new Date(event.start) >= startFilter);
26
+ }
27
+ // Filter by end date
28
+ if (params.endDate) {
29
+ const endFilter = parseNaturalDate(params.endDate);
30
+ events = events.filter(event => new Date(event.start) <= endFilter);
31
+ }
32
+ // Filter by location (case-insensitive contains)
33
+ if (params.location) {
34
+ const locationQuery = params.location.toLowerCase();
35
+ events = events.filter(event => event.location?.toLowerCase().includes(locationQuery) ?? false);
36
+ }
37
+ // Sort by start date (most recent first for search results)
38
+ events.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
39
+ // Apply limit
40
+ const limit = params.limit ?? DEFAULT_LIMIT;
41
+ events = events.slice(0, limit);
42
+ return events;
43
+ }
@@ -0,0 +1,7 @@
1
+ import type { UpdateEventParams } from '../types.js';
2
+ /**
3
+ * Update an existing calendar event
4
+ * @param params - Update parameters (id required, other fields optional)
5
+ * @returns Success message
6
+ */
7
+ export declare function updateEvent(params: UpdateEventParams): Promise<string>;
@@ -0,0 +1,64 @@
1
+ import { readCalendarFile, writeCalendarFile } from '../utils/ical.js';
2
+ import { parseNaturalDate, toISO, formatForDisplay } from '../utils/dates.js';
3
+ import { commitAndPush } from '../utils/git.js';
4
+ /**
5
+ * Update an existing calendar event
6
+ * @param params - Update parameters (id required, other fields optional)
7
+ * @returns Success message
8
+ */
9
+ export async function updateEvent(params) {
10
+ // Validate required field
11
+ if (!params.id) {
12
+ throw new Error('Event ID is required');
13
+ }
14
+ // Read existing events
15
+ const events = await readCalendarFile();
16
+ // Find the event to update
17
+ const eventIndex = events.findIndex(e => e.id === params.id);
18
+ if (eventIndex === -1) {
19
+ throw new Error(`Event not found: ${params.id}`);
20
+ }
21
+ const event = events[eventIndex];
22
+ const originalTitle = event.title;
23
+ // Update fields if provided
24
+ if (params.title !== undefined) {
25
+ if (params.title.trim() === '') {
26
+ throw new Error('Event title cannot be empty');
27
+ }
28
+ event.title = params.title.trim();
29
+ }
30
+ if (params.start !== undefined) {
31
+ const startDate = parseNaturalDate(params.start);
32
+ event.start = toISO(startDate);
33
+ }
34
+ if (params.end !== undefined) {
35
+ const endDate = parseNaturalDate(params.end);
36
+ event.end = toISO(endDate);
37
+ }
38
+ if (params.location !== undefined) {
39
+ event.location = params.location.trim() || undefined;
40
+ }
41
+ if (params.description !== undefined) {
42
+ event.description = params.description.trim() || undefined;
43
+ }
44
+ // Validate end is after start if both are set
45
+ if (event.end) {
46
+ const startDate = new Date(event.start);
47
+ const endDate = new Date(event.end);
48
+ if (endDate <= startDate) {
49
+ throw new Error('End date/time must be after start date/time');
50
+ }
51
+ }
52
+ // Update lastModified
53
+ event.lastModified = toISO(new Date());
54
+ // Update in array
55
+ events[eventIndex] = event;
56
+ // Write back to file
57
+ await writeCalendarFile(events);
58
+ // Commit changes
59
+ await commitAndPush(`Update event: ${event.title}`);
60
+ // Return success message
61
+ const startDate = new Date(event.start);
62
+ const displayDate = formatForDisplay(startDate, event.allDay);
63
+ return `Updated "${originalTitle}" - now "${event.title}" on ${displayDate}`;
64
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Calendar event representation (internal format)
3
+ */
4
+ export interface CalendarEvent {
5
+ /** Unique identifier (UID from iCal, without domain suffix) */
6
+ id: string;
7
+ /** Event title/summary */
8
+ title: string;
9
+ /** Start date/time in ISO 8601 format */
10
+ start: string;
11
+ /** End date/time in ISO 8601 format (optional) */
12
+ end?: string;
13
+ /** Event location (optional) */
14
+ location?: string;
15
+ /** Event description (optional) */
16
+ description?: string;
17
+ /** Whether this is an all-day event */
18
+ allDay: boolean;
19
+ /** Event status: TENTATIVE, CONFIRMED, or CANCELLED */
20
+ status: EventStatus;
21
+ /** Creation timestamp in ISO 8601 format */
22
+ created?: string;
23
+ /** Last modification timestamp in ISO 8601 format */
24
+ lastModified?: string;
25
+ }
26
+ export type EventStatus = 'TENTATIVE' | 'CONFIRMED' | 'CANCELLED';
27
+ /**
28
+ * iCalendar VEVENT component fields
29
+ */
30
+ export interface ICalEvent {
31
+ uid: string;
32
+ dtstamp: Date;
33
+ dtstart: Date;
34
+ dtend?: Date;
35
+ summary: string;
36
+ description?: string;
37
+ location?: string;
38
+ status?: EventStatus;
39
+ created?: Date;
40
+ lastModified?: Date;
41
+ allDay: boolean;
42
+ }
43
+ /**
44
+ * Parsed iCalendar file structure
45
+ */
46
+ export interface ICalendar {
47
+ prodId: string;
48
+ version: string;
49
+ calName?: string;
50
+ timezone?: string;
51
+ events: ICalEvent[];
52
+ }
53
+ export interface CreateEventParams {
54
+ /** Event title/summary (required) */
55
+ title: string;
56
+ /** Start date/time - ISO 8601 or natural language (required) */
57
+ start: string;
58
+ /** End date/time - ISO 8601 or natural language (optional) */
59
+ end?: string;
60
+ /** Event location (optional) */
61
+ location?: string;
62
+ /** Event description (optional) */
63
+ description?: string;
64
+ /** Whether this is an all-day event (optional, default: false) */
65
+ allDay?: boolean;
66
+ }
67
+ export interface ListEventsParams {
68
+ /** Filter events starting after this date (optional) */
69
+ startDate?: string;
70
+ /** Filter events ending before this date (optional) */
71
+ endDate?: string;
72
+ /** Maximum number of events to return (default: 50) */
73
+ limit?: number;
74
+ /** Sort order by start date (default: 'asc') */
75
+ sortOrder?: 'asc' | 'desc';
76
+ }
77
+ export interface UpdateEventParams {
78
+ /** Event ID to update (required) */
79
+ id: string;
80
+ /** New title (optional) */
81
+ title?: string;
82
+ /** New start date/time (optional) */
83
+ start?: string;
84
+ /** New end date/time (optional) */
85
+ end?: string;
86
+ /** New location (optional) */
87
+ location?: string;
88
+ /** New description (optional) */
89
+ description?: string;
90
+ }
91
+ export interface DeleteEventParams {
92
+ /** Event ID to delete (required) */
93
+ id: string;
94
+ }
95
+ export interface FindFreeTimeParams {
96
+ /** Required duration in minutes */
97
+ duration: number;
98
+ /** Start of search range - ISO 8601 or natural language */
99
+ startDate: string;
100
+ /** End of search range - ISO 8601 or natural language */
101
+ endDate: string;
102
+ /** Only search during working hours 9am-5pm (default: true) */
103
+ workingHoursOnly?: boolean;
104
+ /** Maximum number of slots to return (default: 5) */
105
+ maxResults?: number;
106
+ }
107
+ export interface SearchEventsParams {
108
+ /** Search text - searches title, description, location */
109
+ query?: string;
110
+ /** Filter events after this date (optional) */
111
+ startDate?: string;
112
+ /** Filter events before this date (optional) */
113
+ endDate?: string;
114
+ /** Filter by location (optional) */
115
+ location?: string;
116
+ /** Maximum results (default: 20) */
117
+ limit?: number;
118
+ }
119
+ export interface ListEventsResult {
120
+ events: CalendarEvent[];
121
+ count: number;
122
+ hasMore: boolean;
123
+ }
124
+ export interface FreeSlot {
125
+ /** Start of free slot in ISO 8601 format */
126
+ start: string;
127
+ /** End of free slot in ISO 8601 format */
128
+ end: string;
129
+ /** Duration in minutes */
130
+ durationMinutes: number;
131
+ }
132
+ export interface FindFreeTimeResult {
133
+ freeSlots: FreeSlot[];
134
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Parse a date string that can be either ISO 8601 or natural language
3
+ * @param input - Date string (e.g., "2025-01-25T14:00:00Z" or "tomorrow at 2pm")
4
+ * @param referenceDate - Reference date for natural language parsing (defaults to now)
5
+ * @returns Parsed Date object
6
+ * @throws Error if the date cannot be parsed
7
+ */
8
+ export declare function parseNaturalDate(input: string, referenceDate?: Date): Date;
9
+ /**
10
+ * Format a Date object to iCalendar DATE-TIME format (UTC)
11
+ * Format: YYYYMMDDTHHMMSSZ
12
+ */
13
+ export declare function formatICalDateTime(date: Date): string;
14
+ /**
15
+ * Format a Date object to iCalendar DATE format (for all-day events)
16
+ * Format: YYYYMMDD
17
+ */
18
+ export declare function formatICalDate(date: Date): string;
19
+ /**
20
+ * Parse an iCalendar DATE-TIME or DATE string to a Date object
21
+ * Supports: YYYYMMDDTHHMMSSZ (UTC) or YYYYMMDD (date only)
22
+ */
23
+ export declare function parseICalDate(icalDate: string): {
24
+ date: Date;
25
+ allDay: boolean;
26
+ };
27
+ /**
28
+ * Add a duration in minutes to a date
29
+ */
30
+ export declare function addMinutes(date: Date, minutes: number): Date;
31
+ /**
32
+ * Add hours to a date
33
+ */
34
+ export declare function addHours(date: Date, hours: number): Date;
35
+ /**
36
+ * Add days to a date
37
+ */
38
+ export declare function addDays(date: Date, days: number): Date;
39
+ /**
40
+ * Format a Date to ISO 8601 string
41
+ */
42
+ export declare function toISO(date: Date): string;
43
+ /**
44
+ * Get the start of a day (midnight UTC)
45
+ */
46
+ export declare function startOfDay(date: Date): Date;
47
+ /**
48
+ * Get the end of a day (23:59:59.999 UTC)
49
+ */
50
+ export declare function endOfDay(date: Date): Date;
51
+ /**
52
+ * Check if two time ranges overlap
53
+ */
54
+ export declare function rangesOverlap(start1: Date, end1: Date, start2: Date, end2: Date): boolean;
55
+ /**
56
+ * Format a date for human-readable display
57
+ */
58
+ export declare function formatForDisplay(date: Date, allDay: boolean): string;
@@ -0,0 +1,145 @@
1
+ import * as chrono from 'chrono-node';
2
+ /**
3
+ * Parse a date string that can be either ISO 8601 or natural language
4
+ * @param input - Date string (e.g., "2025-01-25T14:00:00Z" or "tomorrow at 2pm")
5
+ * @param referenceDate - Reference date for natural language parsing (defaults to now)
6
+ * @returns Parsed Date object
7
+ * @throws Error if the date cannot be parsed
8
+ */
9
+ export function parseNaturalDate(input, referenceDate) {
10
+ // First try parsing as ISO 8601
11
+ const isoDate = new Date(input);
12
+ if (!isNaN(isoDate.getTime()) && input.includes('-')) {
13
+ return isoDate;
14
+ }
15
+ // Try natural language parsing with chrono
16
+ const ref = referenceDate ?? new Date();
17
+ const results = chrono.parse(input, ref, { forwardDate: true });
18
+ if (results.length === 0) {
19
+ throw new Error(`Could not parse date: "${input}"`);
20
+ }
21
+ const parsed = results[0].start.date();
22
+ return parsed;
23
+ }
24
+ /**
25
+ * Format a Date object to iCalendar DATE-TIME format (UTC)
26
+ * Format: YYYYMMDDTHHMMSSZ
27
+ */
28
+ export function formatICalDateTime(date) {
29
+ const year = date.getUTCFullYear();
30
+ const month = String(date.getUTCMonth() + 1).padStart(2, '0');
31
+ const day = String(date.getUTCDate()).padStart(2, '0');
32
+ const hours = String(date.getUTCHours()).padStart(2, '0');
33
+ const minutes = String(date.getUTCMinutes()).padStart(2, '0');
34
+ const seconds = String(date.getUTCSeconds()).padStart(2, '0');
35
+ return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
36
+ }
37
+ /**
38
+ * Format a Date object to iCalendar DATE format (for all-day events)
39
+ * Format: YYYYMMDD
40
+ */
41
+ export function formatICalDate(date) {
42
+ const year = date.getUTCFullYear();
43
+ const month = String(date.getUTCMonth() + 1).padStart(2, '0');
44
+ const day = String(date.getUTCDate()).padStart(2, '0');
45
+ return `${year}${month}${day}`;
46
+ }
47
+ /**
48
+ * Parse an iCalendar DATE-TIME or DATE string to a Date object
49
+ * Supports: YYYYMMDDTHHMMSSZ (UTC) or YYYYMMDD (date only)
50
+ */
51
+ export function parseICalDate(icalDate) {
52
+ // Remove any VALUE=DATE: prefix
53
+ const cleanDate = icalDate.replace(/^VALUE=DATE:?/, '');
54
+ // All-day format: YYYYMMDD
55
+ if (cleanDate.length === 8 && !cleanDate.includes('T')) {
56
+ const year = parseInt(cleanDate.slice(0, 4), 10);
57
+ const month = parseInt(cleanDate.slice(4, 6), 10) - 1;
58
+ const day = parseInt(cleanDate.slice(6, 8), 10);
59
+ return { date: new Date(Date.UTC(year, month, day)), allDay: true };
60
+ }
61
+ // DateTime format: YYYYMMDDTHHMMSSZ or YYYYMMDDTHHMMSS
62
+ if (cleanDate.includes('T')) {
63
+ const datePart = cleanDate.slice(0, 8);
64
+ const timePart = cleanDate.slice(9, 15);
65
+ const isUtc = cleanDate.endsWith('Z');
66
+ const year = parseInt(datePart.slice(0, 4), 10);
67
+ const month = parseInt(datePart.slice(4, 6), 10) - 1;
68
+ const day = parseInt(datePart.slice(6, 8), 10);
69
+ const hours = parseInt(timePart.slice(0, 2), 10);
70
+ const minutes = parseInt(timePart.slice(2, 4), 10);
71
+ const seconds = parseInt(timePart.slice(4, 6), 10);
72
+ if (isUtc) {
73
+ return { date: new Date(Date.UTC(year, month, day, hours, minutes, seconds)), allDay: false };
74
+ }
75
+ else {
76
+ return { date: new Date(year, month, day, hours, minutes, seconds), allDay: false };
77
+ }
78
+ }
79
+ throw new Error(`Invalid iCal date format: "${icalDate}"`);
80
+ }
81
+ /**
82
+ * Add a duration in minutes to a date
83
+ */
84
+ export function addMinutes(date, minutes) {
85
+ return new Date(date.getTime() + minutes * 60 * 1000);
86
+ }
87
+ /**
88
+ * Add hours to a date
89
+ */
90
+ export function addHours(date, hours) {
91
+ return addMinutes(date, hours * 60);
92
+ }
93
+ /**
94
+ * Add days to a date
95
+ */
96
+ export function addDays(date, days) {
97
+ return addMinutes(date, days * 24 * 60);
98
+ }
99
+ /**
100
+ * Format a Date to ISO 8601 string
101
+ */
102
+ export function toISO(date) {
103
+ return date.toISOString();
104
+ }
105
+ /**
106
+ * Get the start of a day (midnight UTC)
107
+ */
108
+ export function startOfDay(date) {
109
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0));
110
+ }
111
+ /**
112
+ * Get the end of a day (23:59:59.999 UTC)
113
+ */
114
+ export function endOfDay(date) {
115
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 23, 59, 59, 999));
116
+ }
117
+ /**
118
+ * Check if two time ranges overlap
119
+ */
120
+ export function rangesOverlap(start1, end1, start2, end2) {
121
+ return start1 < end2 && end1 > start2;
122
+ }
123
+ /**
124
+ * Format a date for human-readable display
125
+ */
126
+ export function formatForDisplay(date, allDay) {
127
+ if (allDay) {
128
+ return date.toLocaleDateString('en-US', {
129
+ weekday: 'long',
130
+ year: 'numeric',
131
+ month: 'long',
132
+ day: 'numeric',
133
+ timeZone: 'UTC'
134
+ });
135
+ }
136
+ return date.toLocaleString('en-US', {
137
+ weekday: 'long',
138
+ year: 'numeric',
139
+ month: 'long',
140
+ day: 'numeric',
141
+ hour: 'numeric',
142
+ minute: '2-digit',
143
+ timeZoneName: 'short'
144
+ });
145
+ }
@@ -0,0 +1,35 @@
1
+ import { SimpleGit } from 'simple-git';
2
+ /**
3
+ * Get the SimpleGit instance for the vault
4
+ * Uses singleton pattern to reuse the same instance
5
+ */
6
+ export declare function getGit(): SimpleGit;
7
+ /**
8
+ * Reset the git instance (useful for testing)
9
+ */
10
+ export declare function resetGit(): void;
11
+ /**
12
+ * Commit changes and push to remote
13
+ * @param message - Commit message
14
+ * @returns Success status and optional error message
15
+ */
16
+ export declare function commitAndPush(message: string): Promise<{
17
+ success: boolean;
18
+ error?: string;
19
+ }>;
20
+ /**
21
+ * Get the current git status
22
+ */
23
+ export declare function getStatus(): Promise<{
24
+ isRepo: boolean;
25
+ hasChanges: boolean;
26
+ ahead: number;
27
+ behind: number;
28
+ }>;
29
+ /**
30
+ * Pull latest changes from remote
31
+ */
32
+ export declare function pull(): Promise<{
33
+ success: boolean;
34
+ error?: string;
35
+ }>;
@@ -0,0 +1,88 @@
1
+ import { simpleGit } from 'simple-git';
2
+ import { getVaultPath } from './paths.js';
3
+ let gitInstance = null;
4
+ /**
5
+ * Get the SimpleGit instance for the vault
6
+ * Uses singleton pattern to reuse the same instance
7
+ */
8
+ export function getGit() {
9
+ if (!gitInstance) {
10
+ gitInstance = simpleGit(getVaultPath());
11
+ }
12
+ return gitInstance;
13
+ }
14
+ /**
15
+ * Reset the git instance (useful for testing)
16
+ */
17
+ export function resetGit() {
18
+ gitInstance = null;
19
+ }
20
+ /**
21
+ * Commit changes and push to remote
22
+ * @param message - Commit message
23
+ * @returns Success status and optional error message
24
+ */
25
+ export async function commitAndPush(message) {
26
+ try {
27
+ const git = getGit();
28
+ // Check if there are changes to commit
29
+ const status = await git.status();
30
+ if (status.files.length === 0) {
31
+ return { success: true }; // Nothing to commit
32
+ }
33
+ // Add all changes in the Calendar folder
34
+ await git.add('Calendar/*');
35
+ // Commit with message
36
+ await git.commit(message);
37
+ // Try to push (may fail if no remote or offline)
38
+ try {
39
+ await git.push();
40
+ }
41
+ catch (pushError) {
42
+ // Push failed, but commit succeeded - that's okay for offline use
43
+ console.error('Push failed (may be offline):', pushError);
44
+ }
45
+ return { success: true };
46
+ }
47
+ catch (error) {
48
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
49
+ return { success: false, error: errorMessage };
50
+ }
51
+ }
52
+ /**
53
+ * Get the current git status
54
+ */
55
+ export async function getStatus() {
56
+ try {
57
+ const git = getGit();
58
+ const status = await git.status();
59
+ return {
60
+ isRepo: true,
61
+ hasChanges: status.files.length > 0,
62
+ ahead: status.ahead,
63
+ behind: status.behind,
64
+ };
65
+ }
66
+ catch {
67
+ return {
68
+ isRepo: false,
69
+ hasChanges: false,
70
+ ahead: 0,
71
+ behind: 0,
72
+ };
73
+ }
74
+ }
75
+ /**
76
+ * Pull latest changes from remote
77
+ */
78
+ export async function pull() {
79
+ try {
80
+ const git = getGit();
81
+ await git.pull();
82
+ return { success: true };
83
+ }
84
+ catch (error) {
85
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
86
+ return { success: false, error: errorMessage };
87
+ }
88
+ }
@@ -0,0 +1,47 @@
1
+ import type { CalendarEvent } from '../types.js';
2
+ /**
3
+ * Generate a unique event ID
4
+ */
5
+ export declare function generateUID(): string;
6
+ /**
7
+ * Extract the ID part from a UID (without domain)
8
+ */
9
+ export declare function extractId(uid: string): string;
10
+ /**
11
+ * Create a full UID from an ID
12
+ */
13
+ export declare function createUID(id: string): string;
14
+ /**
15
+ * Escape special characters for iCal text fields
16
+ */
17
+ export declare function escapeICalText(text: string): string;
18
+ /**
19
+ * Unescape special characters from iCal text fields
20
+ */
21
+ export declare function unescapeICalText(text: string): string;
22
+ /**
23
+ * Fold long lines according to iCal spec (max 75 chars per line)
24
+ */
25
+ export declare function foldLine(line: string): string;
26
+ /**
27
+ * Unfold lines that were folded according to iCal spec
28
+ */
29
+ export declare function unfoldLines(content: string): string;
30
+ /**
31
+ * Parse iCalendar content to events
32
+ */
33
+ export declare function parseICalendar(content: string): CalendarEvent[];
34
+ /**
35
+ * Generate iCalendar content from events
36
+ */
37
+ export declare function generateICalendar(events: CalendarEvent[]): string;
38
+ /**
39
+ * Read the calendar file and return parsed events
40
+ * Returns empty array if file doesn't exist
41
+ */
42
+ export declare function readCalendarFile(): Promise<CalendarEvent[]>;
43
+ /**
44
+ * Write events to the calendar file
45
+ * Creates the Calendar directory if it doesn't exist
46
+ */
47
+ export declare function writeCalendarFile(events: CalendarEvent[]): Promise<void>;