main
1// index.ts
2import { DynamicBorder } from "@earendil-works/pi-coding-agent";
3import { execSync } from "node:child_process";
4import { homedir } from "node:os";
5import { join } from "node:path";
6
7// node_modules/chrono-node/dist/esm/types.js
8var Meridiem;
9(function(Meridiem2) {
10 Meridiem2[Meridiem2["AM"] = 0] = "AM";
11 Meridiem2[Meridiem2["PM"] = 1] = "PM";
12})(Meridiem || (Meridiem = {}));
13var Weekday;
14(function(Weekday2) {
15 Weekday2[Weekday2["SUNDAY"] = 0] = "SUNDAY";
16 Weekday2[Weekday2["MONDAY"] = 1] = "MONDAY";
17 Weekday2[Weekday2["TUESDAY"] = 2] = "TUESDAY";
18 Weekday2[Weekday2["WEDNESDAY"] = 3] = "WEDNESDAY";
19 Weekday2[Weekday2["THURSDAY"] = 4] = "THURSDAY";
20 Weekday2[Weekday2["FRIDAY"] = 5] = "FRIDAY";
21 Weekday2[Weekday2["SATURDAY"] = 6] = "SATURDAY";
22})(Weekday || (Weekday = {}));
23var Month;
24(function(Month2) {
25 Month2[Month2["JANUARY"] = 1] = "JANUARY";
26 Month2[Month2["FEBRUARY"] = 2] = "FEBRUARY";
27 Month2[Month2["MARCH"] = 3] = "MARCH";
28 Month2[Month2["APRIL"] = 4] = "APRIL";
29 Month2[Month2["MAY"] = 5] = "MAY";
30 Month2[Month2["JUNE"] = 6] = "JUNE";
31 Month2[Month2["JULY"] = 7] = "JULY";
32 Month2[Month2["AUGUST"] = 8] = "AUGUST";
33 Month2[Month2["SEPTEMBER"] = 9] = "SEPTEMBER";
34 Month2[Month2["OCTOBER"] = 10] = "OCTOBER";
35 Month2[Month2["NOVEMBER"] = 11] = "NOVEMBER";
36 Month2[Month2["DECEMBER"] = 12] = "DECEMBER";
37})(Month || (Month = {}));
38
39// node_modules/chrono-node/dist/esm/utils/dates.js
40function assignSimilarDate(component, target) {
41 component.assign("day", target.getDate());
42 component.assign("month", target.getMonth() + 1);
43 component.assign("year", target.getFullYear());
44}
45function assignSimilarTime(component, target) {
46 component.assign("hour", target.getHours());
47 component.assign("minute", target.getMinutes());
48 component.assign("second", target.getSeconds());
49 component.assign("millisecond", target.getMilliseconds());
50 component.assign("meridiem", target.getHours() < 12 ? Meridiem.AM : Meridiem.PM);
51}
52function implySimilarDate(component, target) {
53 component.imply("day", target.getDate());
54 component.imply("month", target.getMonth() + 1);
55 component.imply("year", target.getFullYear());
56}
57function implySimilarTime(component, target) {
58 component.imply("hour", target.getHours());
59 component.imply("minute", target.getMinutes());
60 component.imply("second", target.getSeconds());
61 component.imply("millisecond", target.getMilliseconds());
62 component.imply("meridiem", target.getHours() < 12 ? Meridiem.AM : Meridiem.PM);
63}
64
65// node_modules/chrono-node/dist/esm/timezone.js
66var TIMEZONE_ABBR_MAP = {
67 ACDT: 630,
68 ACST: 570,
69 ADT: -180,
70 AEDT: 660,
71 AEST: 600,
72 AFT: 270,
73 AKDT: -480,
74 AKST: -540,
75 ALMT: 360,
76 AMST: -180,
77 AMT: -240,
78 ANAST: 720,
79 ANAT: 720,
80 AQTT: 300,
81 ART: -180,
82 AST: -240,
83 AWDT: 540,
84 AWST: 480,
85 AZOST: 0,
86 AZOT: -60,
87 AZST: 300,
88 AZT: 240,
89 BNT: 480,
90 BOT: -240,
91 BRST: -120,
92 BRT: -180,
93 BST: 60,
94 BTT: 360,
95 CAST: 480,
96 CAT: 120,
97 CCT: 390,
98 CDT: -300,
99 CEST: 120,
100 CET: {
101 timezoneOffsetDuringDst: 2 * 60,
102 timezoneOffsetNonDst: 60,
103 dstStart: (year) => getLastWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2),
104 dstEnd: (year) => getLastWeekdayOfMonth(year, Month.OCTOBER, Weekday.SUNDAY, 3)
105 },
106 CHADT: 825,
107 CHAST: 765,
108 CKT: -600,
109 CLST: -180,
110 CLT: -240,
111 COT: -300,
112 CST: -360,
113 CT: {
114 timezoneOffsetDuringDst: -5 * 60,
115 timezoneOffsetNonDst: -6 * 60,
116 dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
117 dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
118 },
119 CVT: -60,
120 CXT: 420,
121 ChST: 600,
122 DAVT: 420,
123 EASST: -300,
124 EAST: -360,
125 EAT: 180,
126 ECT: -300,
127 EDT: -240,
128 EEST: 180,
129 EET: 120,
130 EGST: 0,
131 EGT: -60,
132 EST: -300,
133 ET: {
134 timezoneOffsetDuringDst: -4 * 60,
135 timezoneOffsetNonDst: -5 * 60,
136 dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
137 dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
138 },
139 FJST: 780,
140 FJT: 720,
141 FKST: -180,
142 FKT: -240,
143 FNT: -120,
144 GALT: -360,
145 GAMT: -540,
146 GET: 240,
147 GFT: -180,
148 GILT: 720,
149 GMT: 0,
150 GST: 240,
151 GYT: -240,
152 HAA: -180,
153 HAC: -300,
154 HADT: -540,
155 HAE: -240,
156 HAP: -420,
157 HAR: -360,
158 HAST: -600,
159 HAT: -90,
160 HAY: -480,
161 HKT: 480,
162 HLV: -210,
163 HNA: -240,
164 HNC: -360,
165 HNE: -300,
166 HNP: -480,
167 HNR: -420,
168 HNT: -150,
169 HNY: -540,
170 HOVT: 420,
171 ICT: 420,
172 IDT: 180,
173 IOT: 360,
174 IRDT: 270,
175 IRKST: 540,
176 IRKT: 540,
177 IRST: 210,
178 IST: 330,
179 JST: 540,
180 KGT: 360,
181 KRAST: 480,
182 KRAT: 480,
183 KST: 540,
184 KUYT: 240,
185 LHDT: 660,
186 LHST: 630,
187 LINT: 840,
188 MAGST: 720,
189 MAGT: 720,
190 MART: -510,
191 MAWT: 300,
192 MDT: -360,
193 MESZ: 120,
194 MEZ: 60,
195 MHT: 720,
196 MMT: 390,
197 MSD: 240,
198 MSK: 180,
199 MST: -420,
200 MT: {
201 timezoneOffsetDuringDst: -6 * 60,
202 timezoneOffsetNonDst: -7 * 60,
203 dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
204 dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
205 },
206 MUT: 240,
207 MVT: 300,
208 MYT: 480,
209 NCT: 660,
210 NDT: -90,
211 NFT: 690,
212 NOVST: 420,
213 NOVT: 360,
214 NPT: 345,
215 NST: -150,
216 NUT: -660,
217 NZDT: 780,
218 NZST: 720,
219 OMSST: 420,
220 OMST: 420,
221 PDT: -420,
222 PET: -300,
223 PETST: 720,
224 PETT: 720,
225 PGT: 600,
226 PHOT: 780,
227 PHT: 480,
228 PKT: 300,
229 PMDT: -120,
230 PMST: -180,
231 PONT: 660,
232 PST: -480,
233 PT: {
234 timezoneOffsetDuringDst: -7 * 60,
235 timezoneOffsetNonDst: -8 * 60,
236 dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
237 dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
238 },
239 PWT: 540,
240 PYST: -180,
241 PYT: -240,
242 RET: 240,
243 SAMT: 240,
244 SAST: 120,
245 SBT: 660,
246 SCT: 240,
247 SGT: 480,
248 SRT: -180,
249 SST: -660,
250 TAHT: -600,
251 TFT: 300,
252 TJT: 300,
253 TKT: 780,
254 TLT: 540,
255 TMT: 300,
256 TVT: 720,
257 ULAT: 480,
258 UTC: 0,
259 UYST: -120,
260 UYT: -180,
261 UZT: 300,
262 VET: -210,
263 VLAST: 660,
264 VLAT: 660,
265 VUT: 660,
266 WAST: 120,
267 WAT: 60,
268 WEST: 60,
269 WESZ: 60,
270 WET: 0,
271 WEZ: 0,
272 WFT: 720,
273 WGST: -120,
274 WGT: -180,
275 WIB: 420,
276 WIT: 540,
277 WITA: 480,
278 WST: 780,
279 WT: 0,
280 YAKST: 600,
281 YAKT: 600,
282 YAPT: 600,
283 YEKST: 360,
284 YEKT: 360
285};
286function getNthWeekdayOfMonth(year, month, weekday, n, hour = 0) {
287 let dayOfMonth = 0;
288 let i = 0;
289 while (i < n) {
290 dayOfMonth++;
291 const date = new Date(year, month - 1, dayOfMonth);
292 if (date.getDay() === weekday)
293 i++;
294 }
295 return new Date(year, month - 1, dayOfMonth, hour);
296}
297function getLastWeekdayOfMonth(year, month, weekday, hour = 0) {
298 const oneIndexedWeekday = weekday === 0 ? 7 : weekday;
299 const date = new Date(year, month - 1 + 1, 1, 12);
300 const firstWeekdayNextMonth = date.getDay() === 0 ? 7 : date.getDay();
301 let dayDiff;
302 if (firstWeekdayNextMonth === oneIndexedWeekday)
303 dayDiff = 7;
304 else if (firstWeekdayNextMonth < oneIndexedWeekday)
305 dayDiff = 7 + firstWeekdayNextMonth - oneIndexedWeekday;
306 else
307 dayDiff = firstWeekdayNextMonth - oneIndexedWeekday;
308 date.setDate(date.getDate() - dayDiff);
309 return new Date(year, month - 1, date.getDate(), hour);
310}
311function toTimezoneOffset(timezoneInput, date, timezoneOverrides = {}) {
312 if (timezoneInput == null) {
313 return null;
314 }
315 if (typeof timezoneInput === "number") {
316 return timezoneInput;
317 }
318 const matchedTimezone = timezoneOverrides[timezoneInput] ?? TIMEZONE_ABBR_MAP[timezoneInput];
319 if (matchedTimezone == null) {
320 return null;
321 }
322 if (typeof matchedTimezone == "number") {
323 return matchedTimezone;
324 }
325 if (date == null) {
326 return null;
327 }
328 if (date > matchedTimezone.dstStart(date.getFullYear()) && !(date > matchedTimezone.dstEnd(date.getFullYear()))) {
329 return matchedTimezone.timezoneOffsetDuringDst;
330 }
331 return matchedTimezone.timezoneOffsetNonDst;
332}
333
334// node_modules/chrono-node/dist/esm/calculation/duration.js
335var EmptyDuration = {
336 day: 0,
337 second: 0,
338 millisecond: 0
339};
340function addDuration(ref, duration) {
341 let date = new Date(ref);
342 if (duration["y"]) {
343 duration["year"] = duration["y"];
344 delete duration["y"];
345 }
346 if (duration["mo"]) {
347 duration["month"] = duration["mo"];
348 delete duration["mo"];
349 }
350 if (duration["M"]) {
351 duration["month"] = duration["M"];
352 delete duration["M"];
353 }
354 if (duration["w"]) {
355 duration["week"] = duration["w"];
356 delete duration["w"];
357 }
358 if (duration["d"]) {
359 duration["day"] = duration["d"];
360 delete duration["d"];
361 }
362 if (duration["h"]) {
363 duration["hour"] = duration["h"];
364 delete duration["h"];
365 }
366 if (duration["m"]) {
367 duration["minute"] = duration["m"];
368 delete duration["m"];
369 }
370 if (duration["s"]) {
371 duration["second"] = duration["s"];
372 delete duration["s"];
373 }
374 if (duration["ms"]) {
375 duration["millisecond"] = duration["ms"];
376 delete duration["ms"];
377 }
378 if ("year" in duration) {
379 const floor = Math.floor(duration["year"]);
380 date.setFullYear(date.getFullYear() + floor);
381 const remainingFraction = duration["year"] - floor;
382 if (remainingFraction > 0) {
383 duration.month = duration?.month ?? 0;
384 duration.month += remainingFraction * 12;
385 }
386 }
387 if ("quarter" in duration) {
388 const floor = Math.floor(duration["quarter"]);
389 date.setMonth(date.getMonth() + floor * 3);
390 }
391 if ("month" in duration) {
392 const floor = Math.floor(duration["month"]);
393 date.setMonth(date.getMonth() + floor);
394 const remainingFraction = duration["month"] - floor;
395 if (remainingFraction > 0) {
396 duration.week = duration?.week ?? 0;
397 duration.week += remainingFraction * 4;
398 }
399 }
400 if ("week" in duration) {
401 const floor = Math.floor(duration["week"]);
402 date.setDate(date.getDate() + floor * 7);
403 const remainingFraction = duration["week"] - floor;
404 if (remainingFraction > 0) {
405 duration.day = duration?.day ?? 0;
406 duration.day += Math.round(remainingFraction * 7);
407 }
408 }
409 if ("day" in duration) {
410 const floor = Math.floor(duration["day"]);
411 date.setDate(date.getDate() + floor);
412 const remainingFraction = duration["day"] - floor;
413 if (remainingFraction > 0) {
414 duration.hour = duration?.hour ?? 0;
415 duration.hour += Math.round(remainingFraction * 24);
416 }
417 }
418 if ("hour" in duration) {
419 const floor = Math.floor(duration["hour"]);
420 date.setHours(date.getHours() + floor);
421 const remainingFraction = duration["hour"] - floor;
422 if (remainingFraction > 0) {
423 duration.minute = duration?.minute ?? 0;
424 duration.minute += Math.round(remainingFraction * 60);
425 }
426 }
427 if ("minute" in duration) {
428 const floor = Math.floor(duration["minute"]);
429 date.setMinutes(date.getMinutes() + floor);
430 const remainingFraction = duration["minute"] - floor;
431 if (remainingFraction > 0) {
432 duration.second = duration?.second ?? 0;
433 duration.second += Math.round(remainingFraction * 60);
434 }
435 }
436 if ("second" in duration) {
437 const floor = Math.floor(duration["second"]);
438 date.setSeconds(date.getSeconds() + floor);
439 const remainingFraction = duration["second"] - floor;
440 if (remainingFraction > 0) {
441 duration.millisecond = duration?.millisecond ?? 0;
442 duration.millisecond += Math.round(remainingFraction * 1000);
443 }
444 }
445 if ("millisecond" in duration) {
446 const floor = Math.floor(duration["millisecond"]);
447 date.setMilliseconds(date.getMilliseconds() + floor);
448 }
449 return date;
450}
451function reverseDuration(duration) {
452 const reversed = {};
453 for (const key in duration) {
454 reversed[key] = -duration[key];
455 }
456 return reversed;
457}
458
459// node_modules/chrono-node/dist/esm/results.js
460class ReferenceWithTimezone {
461 instant;
462 timezoneOffset;
463 constructor(instant, timezoneOffset) {
464 this.instant = instant ?? new Date;
465 this.timezoneOffset = timezoneOffset ?? null;
466 }
467 static fromDate(date) {
468 return new ReferenceWithTimezone(date);
469 }
470 static fromInput(input, timezoneOverrides) {
471 if (input instanceof Date) {
472 return ReferenceWithTimezone.fromDate(input);
473 }
474 const instant = input?.instant ?? new Date;
475 const timezoneOffset = toTimezoneOffset(input?.timezone, instant, timezoneOverrides);
476 return new ReferenceWithTimezone(instant, timezoneOffset);
477 }
478 getDateWithAdjustedTimezone() {
479 const date = new Date(this.instant);
480 if (this.timezoneOffset !== null) {
481 date.setMinutes(date.getMinutes() - this.getSystemTimezoneAdjustmentMinute(this.instant));
482 }
483 return date;
484 }
485 getSystemTimezoneAdjustmentMinute(date, overrideTimezoneOffset) {
486 if (!date) {
487 date = new Date;
488 }
489 const currentTimezoneOffset = -date.getTimezoneOffset();
490 const targetTimezoneOffset = overrideTimezoneOffset ?? this.timezoneOffset ?? currentTimezoneOffset;
491 return currentTimezoneOffset - targetTimezoneOffset;
492 }
493 getTimezoneOffset() {
494 return this.timezoneOffset ?? -this.instant.getTimezoneOffset();
495 }
496}
497
498class ParsingComponents {
499 knownValues;
500 impliedValues;
501 reference;
502 _tags = new Set;
503 constructor(reference, knownComponents) {
504 this.reference = reference;
505 this.knownValues = {};
506 this.impliedValues = {};
507 if (knownComponents) {
508 for (const key in knownComponents) {
509 this.knownValues[key] = knownComponents[key];
510 }
511 }
512 const date = reference.getDateWithAdjustedTimezone();
513 this.imply("day", date.getDate());
514 this.imply("month", date.getMonth() + 1);
515 this.imply("year", date.getFullYear());
516 this.imply("hour", 12);
517 this.imply("minute", 0);
518 this.imply("second", 0);
519 this.imply("millisecond", 0);
520 }
521 static createRelativeFromReference(reference, duration = EmptyDuration) {
522 let date = addDuration(reference.getDateWithAdjustedTimezone(), duration);
523 const components = new ParsingComponents(reference);
524 components.addTag("result/relativeDate");
525 if ("hour" in duration || "minute" in duration || "second" in duration || "millisecond" in duration) {
526 components.addTag("result/relativeDateAndTime");
527 assignSimilarTime(components, date);
528 assignSimilarDate(components, date);
529 components.assign("timezoneOffset", reference.getTimezoneOffset());
530 } else {
531 implySimilarTime(components, date);
532 components.imply("timezoneOffset", reference.getTimezoneOffset());
533 if ("day" in duration) {
534 components.assign("day", date.getDate());
535 components.assign("month", date.getMonth() + 1);
536 components.assign("year", date.getFullYear());
537 components.assign("weekday", date.getDay());
538 } else if ("week" in duration) {
539 components.assign("day", date.getDate());
540 components.assign("month", date.getMonth() + 1);
541 components.assign("year", date.getFullYear());
542 components.imply("weekday", date.getDay());
543 } else {
544 components.imply("day", date.getDate());
545 if ("month" in duration) {
546 components.assign("month", date.getMonth() + 1);
547 components.assign("year", date.getFullYear());
548 } else {
549 components.imply("month", date.getMonth() + 1);
550 if ("year" in duration) {
551 components.assign("year", date.getFullYear());
552 } else {
553 components.imply("year", date.getFullYear());
554 }
555 }
556 }
557 }
558 return components;
559 }
560 get(component) {
561 if (component in this.knownValues) {
562 return this.knownValues[component];
563 }
564 if (component in this.impliedValues) {
565 return this.impliedValues[component];
566 }
567 return null;
568 }
569 isCertain(component) {
570 return component in this.knownValues;
571 }
572 getCertainComponents() {
573 return Object.keys(this.knownValues);
574 }
575 imply(component, value) {
576 if (component in this.knownValues) {
577 return this;
578 }
579 this.impliedValues[component] = value;
580 return this;
581 }
582 assign(component, value) {
583 this.knownValues[component] = value;
584 delete this.impliedValues[component];
585 return this;
586 }
587 addDurationAsImplied(duration) {
588 const currentDate = this.dateWithoutTimezoneAdjustment();
589 const date = addDuration(currentDate, duration);
590 if ("day" in duration || "week" in duration || "month" in duration || "year" in duration) {
591 this.delete(["day", "weekday", "month", "year"]);
592 this.imply("day", date.getDate());
593 this.imply("weekday", date.getDay());
594 this.imply("month", date.getMonth() + 1);
595 this.imply("year", date.getFullYear());
596 }
597 if ("second" in duration || "minute" in duration || "hour" in duration) {
598 this.delete(["second", "minute", "hour"]);
599 this.imply("second", date.getSeconds());
600 this.imply("minute", date.getMinutes());
601 this.imply("hour", date.getHours());
602 }
603 return this;
604 }
605 delete(components) {
606 if (typeof components === "string") {
607 components = [components];
608 }
609 for (const component of components) {
610 delete this.knownValues[component];
611 delete this.impliedValues[component];
612 }
613 }
614 clone() {
615 const component = new ParsingComponents(this.reference);
616 component.knownValues = {};
617 component.impliedValues = {};
618 for (const key in this.knownValues) {
619 component.knownValues[key] = this.knownValues[key];
620 }
621 for (const key in this.impliedValues) {
622 component.impliedValues[key] = this.impliedValues[key];
623 }
624 return component;
625 }
626 isOnlyDate() {
627 return !this.isCertain("hour") && !this.isCertain("minute") && !this.isCertain("second");
628 }
629 isOnlyTime() {
630 return !this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month") && !this.isCertain("year");
631 }
632 isOnlyWeekdayComponent() {
633 return this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month");
634 }
635 isDateWithUnknownYear() {
636 return this.isCertain("month") && !this.isCertain("year");
637 }
638 isValidDate() {
639 const date = this.dateWithoutTimezoneAdjustment();
640 if (date.getFullYear() !== this.get("year"))
641 return false;
642 if (date.getMonth() !== this.get("month") - 1)
643 return false;
644 if (date.getDate() !== this.get("day"))
645 return false;
646 if (this.get("hour") != null && date.getHours() != this.get("hour"))
647 return false;
648 if (this.get("minute") != null && date.getMinutes() != this.get("minute"))
649 return false;
650 return true;
651 }
652 toString() {
653 return `[ParsingComponents {
654 tags: ${JSON.stringify(Array.from(this._tags).sort())},
655 knownValues: ${JSON.stringify(this.knownValues)},
656 impliedValues: ${JSON.stringify(this.impliedValues)}},
657 reference: ${JSON.stringify(this.reference)}]`;
658 }
659 date() {
660 const date = this.dateWithoutTimezoneAdjustment();
661 const timezoneAdjustment = this.reference.getSystemTimezoneAdjustmentMinute(date, this.get("timezoneOffset"));
662 return new Date(date.getTime() + timezoneAdjustment * 60000);
663 }
664 addTag(tag) {
665 this._tags.add(tag);
666 return this;
667 }
668 addTags(tags) {
669 for (const tag of tags) {
670 this._tags.add(tag);
671 }
672 return this;
673 }
674 tags() {
675 return new Set(this._tags);
676 }
677 dateWithoutTimezoneAdjustment() {
678 const date = new Date(this.get("year"), this.get("month") - 1, this.get("day"), this.get("hour"), this.get("minute"), this.get("second"), this.get("millisecond"));
679 date.setFullYear(this.get("year"));
680 return date;
681 }
682}
683
684class ParsingResult {
685 refDate;
686 index;
687 text;
688 reference;
689 start;
690 end;
691 constructor(reference, index, text, start, end) {
692 this.reference = reference;
693 this.refDate = reference.instant;
694 this.index = index;
695 this.text = text;
696 this.start = start || new ParsingComponents(reference);
697 this.end = end;
698 }
699 clone() {
700 const result = new ParsingResult(this.reference, this.index, this.text);
701 result.start = this.start ? this.start.clone() : null;
702 result.end = this.end ? this.end.clone() : null;
703 return result;
704 }
705 date() {
706 return this.start.date();
707 }
708 addTag(tag) {
709 this.start.addTag(tag);
710 if (this.end) {
711 this.end.addTag(tag);
712 }
713 return this;
714 }
715 addTags(tags) {
716 this.start.addTags(tags);
717 if (this.end) {
718 this.end.addTags(tags);
719 }
720 return this;
721 }
722 tags() {
723 const combinedTags = new Set(this.start.tags());
724 if (this.end) {
725 for (const tag of this.end.tags()) {
726 combinedTags.add(tag);
727 }
728 }
729 return combinedTags;
730 }
731 toString() {
732 const tags = Array.from(this.tags()).sort();
733 return `[ParsingResult {index: ${this.index}, text: '${this.text}', tags: ${JSON.stringify(tags)} ...}]`;
734 }
735}
736
737// node_modules/chrono-node/dist/esm/utils/pattern.js
738function repeatedTimeunitPattern(prefix, singleTimeunitPattern, connectorPattern = "\\s{0,5},?\\s{0,5}") {
739 const singleTimeunitPatternNoCapture = singleTimeunitPattern.replace(/\((?!\?)/g, "(?:");
740 return `${prefix}${singleTimeunitPatternNoCapture}(?:${connectorPattern}${singleTimeunitPatternNoCapture}){0,10}`;
741}
742function extractTerms(dictionary) {
743 let keys;
744 if (dictionary instanceof Array) {
745 keys = [...dictionary];
746 } else if (dictionary instanceof Map) {
747 keys = Array.from(dictionary.keys());
748 } else {
749 keys = Object.keys(dictionary);
750 }
751 return keys;
752}
753function matchAnyPattern(dictionary) {
754 const joinedTerms = extractTerms(dictionary).sort((a, b) => b.length - a.length).join("|").replace(/\./g, "\\.");
755 return `(?:${joinedTerms})`;
756}
757
758// node_modules/chrono-node/dist/esm/calculation/years.js
759function findMostLikelyADYear(yearNumber) {
760 if (yearNumber < 100) {
761 if (yearNumber > 50) {
762 yearNumber = yearNumber + 1900;
763 } else {
764 yearNumber = yearNumber + 2000;
765 }
766 }
767 return yearNumber;
768}
769function findYearClosestToRef(refDate, day, month) {
770 let date = new Date(refDate);
771 date.setMonth(month - 1);
772 date.setDate(day);
773 const nextYear = addDuration(date, { year: 1 });
774 const lastYear = addDuration(date, { year: -1 });
775 if (Math.abs(nextYear.getTime() - refDate.getTime()) < Math.abs(date.getTime() - refDate.getTime())) {
776 date = nextYear;
777 } else if (Math.abs(lastYear.getTime() - refDate.getTime()) < Math.abs(date.getTime() - refDate.getTime())) {
778 date = lastYear;
779 }
780 return date.getFullYear();
781}
782
783// node_modules/chrono-node/dist/esm/locales/en/constants.js
784var WEEKDAY_DICTIONARY = {
785 sunday: 0,
786 sun: 0,
787 "sun.": 0,
788 monday: 1,
789 mon: 1,
790 "mon.": 1,
791 tuesday: 2,
792 tue: 2,
793 "tue.": 2,
794 wednesday: 3,
795 wed: 3,
796 "wed.": 3,
797 thursday: 4,
798 thurs: 4,
799 "thurs.": 4,
800 thur: 4,
801 "thur.": 4,
802 thu: 4,
803 "thu.": 4,
804 friday: 5,
805 fri: 5,
806 "fri.": 5,
807 saturday: 6,
808 sat: 6,
809 "sat.": 6
810};
811var FULL_MONTH_NAME_DICTIONARY = {
812 january: 1,
813 february: 2,
814 march: 3,
815 april: 4,
816 may: 5,
817 june: 6,
818 july: 7,
819 august: 8,
820 september: 9,
821 october: 10,
822 november: 11,
823 december: 12
824};
825var MONTH_DICTIONARY = {
826 ...FULL_MONTH_NAME_DICTIONARY,
827 jan: 1,
828 "jan.": 1,
829 feb: 2,
830 "feb.": 2,
831 mar: 3,
832 "mar.": 3,
833 apr: 4,
834 "apr.": 4,
835 jun: 6,
836 "jun.": 6,
837 jul: 7,
838 "jul.": 7,
839 aug: 8,
840 "aug.": 8,
841 sep: 9,
842 "sep.": 9,
843 sept: 9,
844 "sept.": 9,
845 oct: 10,
846 "oct.": 10,
847 nov: 11,
848 "nov.": 11,
849 dec: 12,
850 "dec.": 12
851};
852var INTEGER_WORD_DICTIONARY = {
853 one: 1,
854 two: 2,
855 three: 3,
856 four: 4,
857 five: 5,
858 six: 6,
859 seven: 7,
860 eight: 8,
861 nine: 9,
862 ten: 10,
863 eleven: 11,
864 twelve: 12
865};
866var ORDINAL_WORD_DICTIONARY = {
867 first: 1,
868 second: 2,
869 third: 3,
870 fourth: 4,
871 fifth: 5,
872 sixth: 6,
873 seventh: 7,
874 eighth: 8,
875 ninth: 9,
876 tenth: 10,
877 eleventh: 11,
878 twelfth: 12,
879 thirteenth: 13,
880 fourteenth: 14,
881 fifteenth: 15,
882 sixteenth: 16,
883 seventeenth: 17,
884 eighteenth: 18,
885 nineteenth: 19,
886 twentieth: 20,
887 "twenty first": 21,
888 "twenty-first": 21,
889 "twenty second": 22,
890 "twenty-second": 22,
891 "twenty third": 23,
892 "twenty-third": 23,
893 "twenty fourth": 24,
894 "twenty-fourth": 24,
895 "twenty fifth": 25,
896 "twenty-fifth": 25,
897 "twenty sixth": 26,
898 "twenty-sixth": 26,
899 "twenty seventh": 27,
900 "twenty-seventh": 27,
901 "twenty eighth": 28,
902 "twenty-eighth": 28,
903 "twenty ninth": 29,
904 "twenty-ninth": 29,
905 thirtieth: 30,
906 "thirty first": 31,
907 "thirty-first": 31
908};
909var TIME_UNIT_DICTIONARY_NO_ABBR = {
910 second: "second",
911 seconds: "second",
912 minute: "minute",
913 minutes: "minute",
914 hour: "hour",
915 hours: "hour",
916 day: "day",
917 days: "day",
918 week: "week",
919 weeks: "week",
920 month: "month",
921 months: "month",
922 quarter: "quarter",
923 quarters: "quarter",
924 year: "year",
925 years: "year"
926};
927var TIME_UNIT_DICTIONARY = {
928 s: "second",
929 sec: "second",
930 second: "second",
931 seconds: "second",
932 m: "minute",
933 min: "minute",
934 mins: "minute",
935 minute: "minute",
936 minutes: "minute",
937 h: "hour",
938 hr: "hour",
939 hrs: "hour",
940 hour: "hour",
941 hours: "hour",
942 d: "day",
943 day: "day",
944 days: "day",
945 w: "week",
946 week: "week",
947 weeks: "week",
948 mo: "month",
949 mon: "month",
950 mos: "month",
951 month: "month",
952 months: "month",
953 qtr: "quarter",
954 quarter: "quarter",
955 quarters: "quarter",
956 y: "year",
957 yr: "year",
958 year: "year",
959 years: "year",
960 ...TIME_UNIT_DICTIONARY_NO_ABBR
961};
962var NUMBER_PATTERN = `(?:${matchAnyPattern(INTEGER_WORD_DICTIONARY)}|[0-9]+|[0-9]+\\.[0-9]+|half(?:\\s{0,2}an?)?|an?\\b(?:\\s{0,2}few)?|few|several|the|a?\\s{0,2}couple\\s{0,2}(?:of)?)`;
963function parseNumberPattern(match) {
964 const num = match.toLowerCase();
965 if (INTEGER_WORD_DICTIONARY[num] !== undefined) {
966 return INTEGER_WORD_DICTIONARY[num];
967 } else if (num === "a" || num === "an" || num == "the") {
968 return 1;
969 } else if (num.match(/few/)) {
970 return 3;
971 } else if (num.match(/half/)) {
972 return 0.5;
973 } else if (num.match(/couple/)) {
974 return 2;
975 } else if (num.match(/several/)) {
976 return 7;
977 }
978 return parseFloat(num);
979}
980var ORDINAL_NUMBER_PATTERN = `(?:${matchAnyPattern(ORDINAL_WORD_DICTIONARY)}|[0-9]{1,2}(?:st|nd|rd|th)?)`;
981function parseOrdinalNumberPattern(match) {
982 let num = match.toLowerCase();
983 if (ORDINAL_WORD_DICTIONARY[num] !== undefined) {
984 return ORDINAL_WORD_DICTIONARY[num];
985 }
986 num = num.replace(/(?:st|nd|rd|th)$/i, "");
987 return parseInt(num);
988}
989var YEAR_PATTERN = `(?:[1-9][0-9]{0,3}\\s{0,2}(?:BE|AD|BC|BCE|CE)|[1-9][0-9]{3}|[5-9][0-9]|2[0-5])`;
990function parseYear(match) {
991 if (/BE/i.test(match)) {
992 match = match.replace(/BE/i, "");
993 return parseInt(match) - 543;
994 }
995 if (/BCE?/i.test(match)) {
996 match = match.replace(/BCE?/i, "");
997 return -parseInt(match);
998 }
999 if (/(AD|CE)/i.test(match)) {
1000 match = match.replace(/(AD|CE)/i, "");
1001 return parseInt(match);
1002 }
1003 const rawYearNumber = parseInt(match);
1004 return findMostLikelyADYear(rawYearNumber);
1005}
1006var SINGLE_TIME_UNIT_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(TIME_UNIT_DICTIONARY)})`;
1007var SINGLE_TIME_UNIT_REGEX = new RegExp(SINGLE_TIME_UNIT_PATTERN, "i");
1008var SINGLE_TIME_UNIT_NO_ABBR_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(TIME_UNIT_DICTIONARY_NO_ABBR)})`;
1009var TIME_UNIT_CONNECTOR_PATTERN = `\\s{0,5},?(?:\\s*and)?\\s{0,5}`;
1010var TIME_UNITS_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s{0,3})?`, SINGLE_TIME_UNIT_PATTERN, TIME_UNIT_CONNECTOR_PATTERN);
1011var TIME_UNITS_NO_ABBR_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s{0,3})?`, SINGLE_TIME_UNIT_NO_ABBR_PATTERN, TIME_UNIT_CONNECTOR_PATTERN);
1012function parseDuration(timeunitText) {
1013 const fragments = {};
1014 let remainingText = timeunitText;
1015 let match = SINGLE_TIME_UNIT_REGEX.exec(remainingText);
1016 while (match) {
1017 collectDateTimeFragment(fragments, match);
1018 remainingText = remainingText.substring(match[0].length).trim();
1019 match = SINGLE_TIME_UNIT_REGEX.exec(remainingText);
1020 }
1021 if (Object.keys(fragments).length == 0) {
1022 return null;
1023 }
1024 return fragments;
1025}
1026function collectDateTimeFragment(fragments, match) {
1027 if (match[0].match(/^[a-zA-Z]+$/)) {
1028 return;
1029 }
1030 const num = parseNumberPattern(match[1]);
1031 const unit = TIME_UNIT_DICTIONARY[match[2].toLowerCase()];
1032 fragments[unit] = num;
1033}
1034
1035// node_modules/chrono-node/dist/esm/common/parsers/AbstractParserWithWordBoundary.js
1036class AbstractParserWithWordBoundaryChecking {
1037 innerPatternHasChange(context, currentInnerPattern) {
1038 return this.innerPattern(context) !== currentInnerPattern;
1039 }
1040 patternLeftBoundary() {
1041 return `(\\W|^)`;
1042 }
1043 cachedInnerPattern = null;
1044 cachedPattern = null;
1045 pattern(context) {
1046 if (this.cachedInnerPattern) {
1047 if (!this.innerPatternHasChange(context, this.cachedInnerPattern)) {
1048 return this.cachedPattern;
1049 }
1050 }
1051 this.cachedInnerPattern = this.innerPattern(context);
1052 this.cachedPattern = new RegExp(`${this.patternLeftBoundary()}${this.cachedInnerPattern.source}`, this.cachedInnerPattern.flags);
1053 return this.cachedPattern;
1054 }
1055 extract(context, match) {
1056 const header = match[1] ?? "";
1057 match.index = match.index + header.length;
1058 match[0] = match[0].substring(header.length);
1059 for (let i = 2;i < match.length; i++) {
1060 match[i - 1] = match[i];
1061 }
1062 return this.innerExtract(context, match);
1063 }
1064}
1065
1066// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.js
1067var PATTERN_WITH_OPTIONAL_PREFIX = new RegExp(`(?:(?:within|in|for)\\s*)?` + `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
1068var PATTERN_WITH_PREFIX = new RegExp(`(?:within|in|for)\\s*` + `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
1069var PATTERN_WITH_PREFIX_STRICT = new RegExp(`(?:within|in|for)\\s*` + `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
1070
1071class ENTimeUnitWithinFormatParser extends AbstractParserWithWordBoundaryChecking {
1072 strictMode;
1073 constructor(strictMode) {
1074 super();
1075 this.strictMode = strictMode;
1076 }
1077 innerPattern(context) {
1078 if (this.strictMode) {
1079 return PATTERN_WITH_PREFIX_STRICT;
1080 }
1081 return context.option.forwardDate ? PATTERN_WITH_OPTIONAL_PREFIX : PATTERN_WITH_PREFIX;
1082 }
1083 innerExtract(context, match) {
1084 if (match[0].match(/^for\s*the\s*\w+/)) {
1085 return null;
1086 }
1087 const timeUnits = parseDuration(match[1]);
1088 if (!timeUnits) {
1089 return null;
1090 }
1091 return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
1092 }
1093}
1094
1095// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameLittleEndianParser.js
1096var PATTERN = new RegExp(`(?:on\\s{0,3})?` + `(${ORDINAL_NUMBER_PATTERN})` + `(?:` + `\\s{0,3}(?:to|\\-|\\–|until|through|till)?\\s{0,3}` + `(${ORDINAL_NUMBER_PATTERN})` + ")?" + `(?:-|/|\\s{0,3}(?:of)?\\s{0,3})` + `(${matchAnyPattern(MONTH_DICTIONARY)})` + "(?:" + `(?:-|/|,?\\s{0,3})` + `(${YEAR_PATTERN}(?!\\w))` + ")?" + "(?=\\W|$)", "i");
1097var DATE_GROUP = 1;
1098var DATE_TO_GROUP = 2;
1099var MONTH_NAME_GROUP = 3;
1100var YEAR_GROUP = 4;
1101
1102class ENMonthNameLittleEndianParser extends AbstractParserWithWordBoundaryChecking {
1103 innerPattern() {
1104 return PATTERN;
1105 }
1106 innerExtract(context, match) {
1107 const result = context.createParsingResult(match.index, match[0]);
1108 const month = MONTH_DICTIONARY[match[MONTH_NAME_GROUP].toLowerCase()];
1109 const day = parseOrdinalNumberPattern(match[DATE_GROUP]);
1110 if (day > 31) {
1111 match.index = match.index + match[DATE_GROUP].length;
1112 return null;
1113 }
1114 result.start.assign("month", month);
1115 result.start.assign("day", day);
1116 if (match[YEAR_GROUP]) {
1117 const yearNumber = parseYear(match[YEAR_GROUP]);
1118 result.start.assign("year", yearNumber);
1119 } else {
1120 const year = findYearClosestToRef(context.refDate, day, month);
1121 result.start.imply("year", year);
1122 }
1123 if (match[DATE_TO_GROUP]) {
1124 const endDate = parseOrdinalNumberPattern(match[DATE_TO_GROUP]);
1125 result.end = result.start.clone();
1126 result.end.assign("day", endDate);
1127 }
1128 return result;
1129 }
1130}
1131
1132// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameMiddleEndianParser.js
1133var PATTERN2 = new RegExp(`(${matchAnyPattern(MONTH_DICTIONARY)})` + "(?:-|/|\\s*,?\\s*)" + `(${ORDINAL_NUMBER_PATTERN})(?!\\s*(?:am|pm))\\s*` + "(?:" + "(?:to|\\-)\\s*" + `(${ORDINAL_NUMBER_PATTERN})\\s*` + ")?" + "(?:" + `(?:-|/|\\s*,\\s*|\\s+)` + `(${YEAR_PATTERN})` + ")?" + "(?=\\W|$)(?!\\:\\d)", "i");
1134var MONTH_NAME_GROUP2 = 1;
1135var DATE_GROUP2 = 2;
1136var DATE_TO_GROUP2 = 3;
1137var YEAR_GROUP2 = 4;
1138
1139class ENMonthNameMiddleEndianParser extends AbstractParserWithWordBoundaryChecking {
1140 shouldSkipYearLikeDate;
1141 constructor(shouldSkipYearLikeDate) {
1142 super();
1143 this.shouldSkipYearLikeDate = shouldSkipYearLikeDate;
1144 }
1145 innerPattern() {
1146 return PATTERN2;
1147 }
1148 innerExtract(context, match) {
1149 const month = MONTH_DICTIONARY[match[MONTH_NAME_GROUP2].toLowerCase()];
1150 const day = parseOrdinalNumberPattern(match[DATE_GROUP2]);
1151 if (day > 31) {
1152 return null;
1153 }
1154 if (this.shouldSkipYearLikeDate) {
1155 if (!match[DATE_TO_GROUP2] && !match[YEAR_GROUP2] && match[DATE_GROUP2].match(/^2[0-5]$/)) {
1156 return null;
1157 }
1158 }
1159 const components = context.createParsingComponents({
1160 day,
1161 month
1162 }).addTag("parser/ENMonthNameMiddleEndianParser");
1163 if (match[YEAR_GROUP2]) {
1164 const year = parseYear(match[YEAR_GROUP2]);
1165 components.assign("year", year);
1166 } else {
1167 const year = findYearClosestToRef(context.refDate, day, month);
1168 components.imply("year", year);
1169 }
1170 if (!match[DATE_TO_GROUP2]) {
1171 return components;
1172 }
1173 const endDate = parseOrdinalNumberPattern(match[DATE_TO_GROUP2]);
1174 const result = context.createParsingResult(match.index, match[0]);
1175 result.start = components;
1176 result.end = components.clone();
1177 result.end.assign("day", endDate);
1178 return result;
1179 }
1180}
1181
1182// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameParser.js
1183var PATTERN3 = new RegExp(`((?:in)\\s*)?` + `(${matchAnyPattern(MONTH_DICTIONARY)})` + `\\s*` + `(?:` + `(?:,|-|of)?\\s*(${YEAR_PATTERN})?` + ")?" + "(?=[^\\s\\w]|\\s+[^0-9]|\\s+$|$)", "i");
1184var PREFIX_GROUP = 1;
1185var MONTH_NAME_GROUP3 = 2;
1186var YEAR_GROUP3 = 3;
1187
1188class ENMonthNameParser extends AbstractParserWithWordBoundaryChecking {
1189 innerPattern() {
1190 return PATTERN3;
1191 }
1192 innerExtract(context, match) {
1193 const monthName = match[MONTH_NAME_GROUP3].toLowerCase();
1194 if (match[0].length <= 3 && !FULL_MONTH_NAME_DICTIONARY[monthName]) {
1195 return null;
1196 }
1197 const result = context.createParsingResult(match.index + (match[PREFIX_GROUP] || "").length, match.index + match[0].length);
1198 result.start.imply("day", 1);
1199 result.start.addTag("parser/ENMonthNameParser");
1200 const month = MONTH_DICTIONARY[monthName];
1201 result.start.assign("month", month);
1202 if (match[YEAR_GROUP3]) {
1203 const year = parseYear(match[YEAR_GROUP3]);
1204 result.start.assign("year", year);
1205 } else {
1206 const year = findYearClosestToRef(context.refDate, 1, month);
1207 result.start.imply("year", year);
1208 }
1209 return result;
1210 }
1211}
1212
1213// node_modules/chrono-node/dist/esm/locales/en/parsers/ENYearMonthDayParser.js
1214var PATTERN4 = new RegExp(`([0-9]{4})[-\\.\\/\\s]` + `(?:(${matchAnyPattern(MONTH_DICTIONARY)})|([0-9]{1,2}))[-\\.\\/\\s]` + `([0-9]{1,2})` + "(?=\\W|$)", "i");
1215var YEAR_NUMBER_GROUP = 1;
1216var MONTH_NAME_GROUP4 = 2;
1217var MONTH_NUMBER_GROUP = 3;
1218var DATE_NUMBER_GROUP = 4;
1219
1220class ENYearMonthDayParser extends AbstractParserWithWordBoundaryChecking {
1221 strictMonthDateOrder;
1222 constructor(strictMonthDateOrder) {
1223 super();
1224 this.strictMonthDateOrder = strictMonthDateOrder;
1225 }
1226 innerPattern() {
1227 return PATTERN4;
1228 }
1229 innerExtract(context, match) {
1230 const year = parseInt(match[YEAR_NUMBER_GROUP]);
1231 let day = parseInt(match[DATE_NUMBER_GROUP]);
1232 let month = match[MONTH_NUMBER_GROUP] ? parseInt(match[MONTH_NUMBER_GROUP]) : MONTH_DICTIONARY[match[MONTH_NAME_GROUP4].toLowerCase()];
1233 if (month < 1 || month > 12) {
1234 if (this.strictMonthDateOrder) {
1235 return null;
1236 }
1237 if (day >= 1 && day <= 12) {
1238 [month, day] = [day, month];
1239 }
1240 }
1241 if (day < 1 || day > 31) {
1242 return null;
1243 }
1244 return {
1245 day,
1246 month,
1247 year
1248 };
1249 }
1250}
1251
1252// node_modules/chrono-node/dist/esm/locales/en/parsers/ENSlashMonthFormatParser.js
1253var PATTERN5 = new RegExp("([0-9]|0[1-9]|1[012])/([0-9]{4})" + "", "i");
1254var MONTH_GROUP = 1;
1255var YEAR_GROUP4 = 2;
1256
1257class ENSlashMonthFormatParser extends AbstractParserWithWordBoundaryChecking {
1258 innerPattern() {
1259 return PATTERN5;
1260 }
1261 innerExtract(context, match) {
1262 const year = parseInt(match[YEAR_GROUP4]);
1263 const month = parseInt(match[MONTH_GROUP]);
1264 return context.createParsingComponents().imply("day", 1).assign("month", month).assign("year", year);
1265 }
1266}
1267
1268// node_modules/chrono-node/dist/esm/common/parsers/AbstractTimeExpressionParser.js
1269function primaryTimePattern(leftBoundary, primaryPrefix, primarySuffix, flags) {
1270 return new RegExp(`${leftBoundary}` + `${primaryPrefix}` + `(\\d{1,4})` + `(?:` + `(?:\\.|:|:)` + `(\\d{1,2})` + `(?:` + `(?::|:)` + `(\\d{2})` + `(?:\\.(\\d{1,6}))?` + `)?` + `)?` + `(?:\\s*(a\\.m\\.|p\\.m\\.|am?|pm?))?` + `${primarySuffix}`, flags);
1271}
1272function followingTimePatten(followingPhase, followingSuffix) {
1273 return new RegExp(`^(${followingPhase})` + `(\\d{1,4})` + `(?:` + `(?:\\.|\\:|\\:)` + `(\\d{1,2})` + `(?:` + `(?:\\.|\\:|\\:)` + `(\\d{1,2})(?:\\.(\\d{1,6}))?` + `)?` + `)?` + `(?:\\s*(a\\.m\\.|p\\.m\\.|am?|pm?))?` + `${followingSuffix}`, "i");
1274}
1275var HOUR_GROUP = 2;
1276var MINUTE_GROUP = 3;
1277var SECOND_GROUP = 4;
1278var MILLI_SECOND_GROUP = 5;
1279var AM_PM_HOUR_GROUP = 6;
1280
1281class AbstractTimeExpressionParser {
1282 strictMode;
1283 constructor(strictMode = false) {
1284 this.strictMode = strictMode;
1285 }
1286 patternFlags() {
1287 return "i";
1288 }
1289 primaryPatternLeftBoundary() {
1290 return `(^|\\s|T|\\b)`;
1291 }
1292 primarySuffix() {
1293 return `(?!/)(?=\\W|$)`;
1294 }
1295 followingSuffix() {
1296 return `(?!/)(?=\\W|$)`;
1297 }
1298 pattern(context) {
1299 return this.getPrimaryTimePatternThroughCache();
1300 }
1301 extract(context, match) {
1302 const startComponents = this.extractPrimaryTimeComponents(context, match);
1303 if (!startComponents) {
1304 if (match[0].match(/^\d{4}/)) {
1305 match.index += 4;
1306 return null;
1307 }
1308 match.index += match[0].length;
1309 return null;
1310 }
1311 const index = match.index + match[1].length;
1312 const text = match[0].substring(match[1].length);
1313 const result = context.createParsingResult(index, text, startComponents);
1314 match.index += match[0].length;
1315 const remainingText = context.text.substring(match.index);
1316 const followingPattern = this.getFollowingTimePatternThroughCache();
1317 const followingMatch = followingPattern.exec(remainingText);
1318 if (text.match(/^\d{3,4}/) && followingMatch) {
1319 if (followingMatch[0].match(/^\s*([+-])\s*\d{2,4}$/)) {
1320 return null;
1321 }
1322 if (followingMatch[0].match(/^\s*([+-])\s*\d{2}\W\d{2}/)) {
1323 return null;
1324 }
1325 }
1326 if (!followingMatch || followingMatch[0].match(/^\s*([+-])\s*\d{3,4}$/)) {
1327 return this.checkAndReturnWithoutFollowingPattern(result);
1328 }
1329 result.end = this.extractFollowingTimeComponents(context, followingMatch, result);
1330 if (result.end) {
1331 result.text += followingMatch[0];
1332 }
1333 return this.checkAndReturnWithFollowingPattern(result);
1334 }
1335 extractPrimaryTimeComponents(context, match, strict = false) {
1336 const components = context.createParsingComponents();
1337 let minute = 0;
1338 let meridiem = null;
1339 let hour = parseInt(match[HOUR_GROUP]);
1340 if (hour > 100) {
1341 if (match[HOUR_GROUP].length == 4 && match[MINUTE_GROUP] == null && !match[AM_PM_HOUR_GROUP]) {
1342 return null;
1343 }
1344 if (this.strictMode || match[MINUTE_GROUP] != null) {
1345 return null;
1346 }
1347 minute = hour % 100;
1348 hour = Math.floor(hour / 100);
1349 }
1350 if (hour > 24) {
1351 return null;
1352 }
1353 if (match[MINUTE_GROUP] != null) {
1354 if (match[MINUTE_GROUP].length == 1 && !match[AM_PM_HOUR_GROUP]) {
1355 return null;
1356 }
1357 minute = parseInt(match[MINUTE_GROUP]);
1358 }
1359 if (minute >= 60) {
1360 return null;
1361 }
1362 if (hour > 12) {
1363 meridiem = Meridiem.PM;
1364 }
1365 if (match[AM_PM_HOUR_GROUP] != null) {
1366 if (hour > 12)
1367 return null;
1368 const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase();
1369 if (ampm == "a") {
1370 meridiem = Meridiem.AM;
1371 if (hour == 12) {
1372 hour = 0;
1373 }
1374 }
1375 if (ampm == "p") {
1376 meridiem = Meridiem.PM;
1377 if (hour != 12) {
1378 hour += 12;
1379 }
1380 }
1381 }
1382 components.assign("hour", hour);
1383 components.assign("minute", minute);
1384 if (meridiem !== null) {
1385 components.assign("meridiem", meridiem);
1386 } else {
1387 if (hour < 12) {
1388 components.imply("meridiem", Meridiem.AM);
1389 } else {
1390 components.imply("meridiem", Meridiem.PM);
1391 }
1392 }
1393 if (match[MILLI_SECOND_GROUP] != null) {
1394 const millisecond = parseInt(match[MILLI_SECOND_GROUP].substring(0, 3));
1395 if (millisecond >= 1000)
1396 return null;
1397 components.assign("millisecond", millisecond);
1398 }
1399 if (match[SECOND_GROUP] != null) {
1400 const second = parseInt(match[SECOND_GROUP]);
1401 if (second >= 60)
1402 return null;
1403 components.assign("second", second);
1404 }
1405 return components;
1406 }
1407 extractFollowingTimeComponents(context, match, result) {
1408 const components = context.createParsingComponents();
1409 if (match[MILLI_SECOND_GROUP] != null) {
1410 const millisecond = parseInt(match[MILLI_SECOND_GROUP].substring(0, 3));
1411 if (millisecond >= 1000)
1412 return null;
1413 components.assign("millisecond", millisecond);
1414 }
1415 if (match[SECOND_GROUP] != null) {
1416 const second = parseInt(match[SECOND_GROUP]);
1417 if (second >= 60)
1418 return null;
1419 components.assign("second", second);
1420 }
1421 let hour = parseInt(match[HOUR_GROUP]);
1422 let minute = 0;
1423 let meridiem = -1;
1424 if (match[MINUTE_GROUP] != null) {
1425 minute = parseInt(match[MINUTE_GROUP]);
1426 } else if (hour > 100) {
1427 minute = hour % 100;
1428 hour = Math.floor(hour / 100);
1429 }
1430 if (minute >= 60 || hour > 24) {
1431 return null;
1432 }
1433 if (hour >= 12) {
1434 meridiem = Meridiem.PM;
1435 }
1436 if (match[AM_PM_HOUR_GROUP] != null) {
1437 if (hour > 12) {
1438 return null;
1439 }
1440 const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase();
1441 if (ampm == "a") {
1442 meridiem = Meridiem.AM;
1443 if (hour == 12) {
1444 hour = 0;
1445 if (!components.isCertain("day")) {
1446 components.imply("day", components.get("day") + 1);
1447 }
1448 }
1449 }
1450 if (ampm == "p") {
1451 meridiem = Meridiem.PM;
1452 if (hour != 12)
1453 hour += 12;
1454 }
1455 if (!result.start.isCertain("meridiem")) {
1456 if (meridiem == Meridiem.AM) {
1457 result.start.imply("meridiem", Meridiem.AM);
1458 if (result.start.get("hour") == 12) {
1459 result.start.assign("hour", 0);
1460 }
1461 } else {
1462 result.start.imply("meridiem", Meridiem.PM);
1463 if (result.start.get("hour") != 12) {
1464 result.start.assign("hour", result.start.get("hour") + 12);
1465 }
1466 }
1467 }
1468 }
1469 components.assign("hour", hour);
1470 components.assign("minute", minute);
1471 if (meridiem >= 0) {
1472 components.assign("meridiem", meridiem);
1473 } else {
1474 const startAtPM = result.start.isCertain("meridiem") && result.start.get("hour") > 12;
1475 if (startAtPM) {
1476 if (result.start.get("hour") - 12 > hour) {
1477 components.imply("meridiem", Meridiem.AM);
1478 } else if (hour <= 12) {
1479 components.assign("hour", hour + 12);
1480 components.assign("meridiem", Meridiem.PM);
1481 }
1482 } else if (hour > 12) {
1483 components.imply("meridiem", Meridiem.PM);
1484 } else if (hour <= 12) {
1485 components.imply("meridiem", Meridiem.AM);
1486 }
1487 }
1488 if (components.date().getTime() < result.start.date().getTime()) {
1489 components.imply("day", components.get("day") + 1);
1490 }
1491 return components;
1492 }
1493 checkAndReturnWithoutFollowingPattern(result) {
1494 if (result.text.match(/^\d$/)) {
1495 return null;
1496 }
1497 if (result.text.match(/^\d\d\d+$/)) {
1498 return null;
1499 }
1500 if (result.text.match(/\d[apAP]$/)) {
1501 return null;
1502 }
1503 const endingWithNumbers = result.text.match(/[^\d:.](\d[\d.]+)$/);
1504 if (endingWithNumbers) {
1505 const endingNumbers = endingWithNumbers[1];
1506 if (this.strictMode) {
1507 return null;
1508 }
1509 if (endingNumbers.includes(".") && !endingNumbers.match(/\d(\.\d{2})+$/)) {
1510 return null;
1511 }
1512 const endingNumberVal = parseInt(endingNumbers);
1513 if (endingNumberVal > 24) {
1514 return null;
1515 }
1516 }
1517 return result;
1518 }
1519 checkAndReturnWithFollowingPattern(result) {
1520 if (result.text.match(/^\d+-\d+$/)) {
1521 return null;
1522 }
1523 const endingWithNumbers = result.text.match(/[^\d:.](\d[\d.]+)\s*-\s*(\d[\d.]+)$/);
1524 if (endingWithNumbers) {
1525 if (this.strictMode) {
1526 return null;
1527 }
1528 const startingNumbers = endingWithNumbers[1];
1529 const endingNumbers = endingWithNumbers[2];
1530 if (endingNumbers.includes(".") && !endingNumbers.match(/\d(\.\d{2})+$/)) {
1531 return null;
1532 }
1533 const endingNumberVal = parseInt(endingNumbers);
1534 const startingNumberVal = parseInt(startingNumbers);
1535 if (endingNumberVal > 24 || startingNumberVal > 24) {
1536 return null;
1537 }
1538 }
1539 return result;
1540 }
1541 cachedPrimaryPrefix = null;
1542 cachedPrimarySuffix = null;
1543 cachedPrimaryTimePattern = null;
1544 getPrimaryTimePatternThroughCache() {
1545 const primaryPrefix = this.primaryPrefix();
1546 const primarySuffix = this.primarySuffix();
1547 if (this.cachedPrimaryPrefix === primaryPrefix && this.cachedPrimarySuffix === primarySuffix) {
1548 return this.cachedPrimaryTimePattern;
1549 }
1550 this.cachedPrimaryTimePattern = primaryTimePattern(this.primaryPatternLeftBoundary(), primaryPrefix, primarySuffix, this.patternFlags());
1551 this.cachedPrimaryPrefix = primaryPrefix;
1552 this.cachedPrimarySuffix = primarySuffix;
1553 return this.cachedPrimaryTimePattern;
1554 }
1555 cachedFollowingPhase = null;
1556 cachedFollowingSuffix = null;
1557 cachedFollowingTimePatten = null;
1558 getFollowingTimePatternThroughCache() {
1559 const followingPhase = this.followingPhase();
1560 const followingSuffix = this.followingSuffix();
1561 if (this.cachedFollowingPhase === followingPhase && this.cachedFollowingSuffix === followingSuffix) {
1562 return this.cachedFollowingTimePatten;
1563 }
1564 this.cachedFollowingTimePatten = followingTimePatten(followingPhase, followingSuffix);
1565 this.cachedFollowingPhase = followingPhase;
1566 this.cachedFollowingSuffix = followingSuffix;
1567 return this.cachedFollowingTimePatten;
1568 }
1569}
1570
1571// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeExpressionParser.js
1572class ENTimeExpressionParser extends AbstractTimeExpressionParser {
1573 constructor(strictMode) {
1574 super(strictMode);
1575 }
1576 followingPhase() {
1577 return "\\s*(?:\\-|\\–|\\~|\\〜|to|until|through|till|\\?)\\s*";
1578 }
1579 primaryPrefix() {
1580 return "(?:(?:at|from)\\s*)??";
1581 }
1582 primarySuffix() {
1583 return "(?:\\s*(?:o\\W*clock|at\\s*night|in\\s*the\\s*(?:morning|afternoon)))?(?!/)(?=\\W|$)";
1584 }
1585 extractPrimaryTimeComponents(context, match) {
1586 const components = super.extractPrimaryTimeComponents(context, match);
1587 if (!components) {
1588 return components;
1589 }
1590 if (match[0].endsWith("night")) {
1591 const hour = components.get("hour");
1592 if (hour >= 6 && hour < 12) {
1593 components.assign("hour", components.get("hour") + 12);
1594 components.assign("meridiem", Meridiem.PM);
1595 } else if (hour < 6) {
1596 components.assign("meridiem", Meridiem.AM);
1597 }
1598 }
1599 if (match[0].endsWith("afternoon")) {
1600 components.assign("meridiem", Meridiem.PM);
1601 const hour = components.get("hour");
1602 if (hour >= 0 && hour <= 6) {
1603 components.assign("hour", components.get("hour") + 12);
1604 }
1605 }
1606 if (match[0].endsWith("morning")) {
1607 components.assign("meridiem", Meridiem.AM);
1608 const hour = components.get("hour");
1609 if (hour < 12) {
1610 components.assign("hour", components.get("hour"));
1611 }
1612 }
1613 return components.addTag("parser/ENTimeExpressionParser");
1614 }
1615 extractFollowingTimeComponents(context, match, result) {
1616 const followingComponents = super.extractFollowingTimeComponents(context, match, result);
1617 if (followingComponents) {
1618 followingComponents.addTag("parser/ENTimeExpressionParser");
1619 }
1620 return followingComponents;
1621 }
1622}
1623
1624// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitAgoFormatParser.js
1625var PATTERN6 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
1626var STRICT_PATTERN = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
1627
1628class ENTimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
1629 strictMode;
1630 constructor(strictMode) {
1631 super();
1632 this.strictMode = strictMode;
1633 }
1634 innerPattern() {
1635 return this.strictMode ? STRICT_PATTERN : PATTERN6;
1636 }
1637 innerExtract(context, match) {
1638 const duration = parseDuration(match[1]);
1639 if (!duration) {
1640 return null;
1641 }
1642 return ParsingComponents.createRelativeFromReference(context.reference, reverseDuration(duration));
1643 }
1644}
1645
1646// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitLaterFormatParser.js
1647var PATTERN7 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:later|after|from now|henceforth|forward|out)` + "(?=(?:\\W|$))", "i");
1648var STRICT_PATTERN2 = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(later|after|from now)(?=\\W|$)`, "i");
1649var GROUP_NUM_TIMEUNITS = 1;
1650
1651class ENTimeUnitLaterFormatParser extends AbstractParserWithWordBoundaryChecking {
1652 strictMode;
1653 constructor(strictMode) {
1654 super();
1655 this.strictMode = strictMode;
1656 }
1657 innerPattern() {
1658 return this.strictMode ? STRICT_PATTERN2 : PATTERN7;
1659 }
1660 innerExtract(context, match) {
1661 const timeUnits = parseDuration(match[GROUP_NUM_TIMEUNITS]);
1662 if (!timeUnits) {
1663 return null;
1664 }
1665 return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
1666 }
1667}
1668
1669// node_modules/chrono-node/dist/esm/common/abstractRefiners.js
1670class Filter {
1671 refine(context, results) {
1672 return results.filter((r) => this.isValid(context, r));
1673 }
1674}
1675
1676class MergingRefiner {
1677 refine(context, results) {
1678 if (results.length < 2) {
1679 return results;
1680 }
1681 const mergedResults = [];
1682 let curResult = results[0];
1683 let nextResult = null;
1684 for (let i = 1;i < results.length; i++) {
1685 nextResult = results[i];
1686 const textBetween = context.text.substring(curResult.index + curResult.text.length, nextResult.index);
1687 if (!this.shouldMergeResults(textBetween, curResult, nextResult, context)) {
1688 mergedResults.push(curResult);
1689 curResult = nextResult;
1690 } else {
1691 const left = curResult;
1692 const right = nextResult;
1693 const mergedResult = this.mergeResults(textBetween, left, right, context);
1694 context.debug(() => {
1695 console.log(`${this.constructor.name} merged ${left} and ${right} into ${mergedResult}`);
1696 });
1697 curResult = mergedResult;
1698 }
1699 }
1700 if (curResult != null) {
1701 mergedResults.push(curResult);
1702 }
1703 return mergedResults;
1704 }
1705}
1706
1707// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateRangeRefiner.js
1708class AbstractMergeDateRangeRefiner extends MergingRefiner {
1709 shouldMergeResults(textBetween, currentResult, nextResult) {
1710 return !currentResult.end && !nextResult.end && textBetween.match(this.patternBetween()) != null;
1711 }
1712 mergeResults(textBetween, fromResult, toResult) {
1713 if (!fromResult.start.isOnlyWeekdayComponent() && !toResult.start.isOnlyWeekdayComponent()) {
1714 toResult.start.getCertainComponents().forEach((key) => {
1715 if (!fromResult.start.isCertain(key)) {
1716 fromResult.start.imply(key, toResult.start.get(key));
1717 }
1718 });
1719 fromResult.start.getCertainComponents().forEach((key) => {
1720 if (!toResult.start.isCertain(key)) {
1721 toResult.start.imply(key, fromResult.start.get(key));
1722 }
1723 });
1724 }
1725 if (fromResult.start.date() > toResult.start.date()) {
1726 let fromDate = fromResult.start.date();
1727 let toDate = toResult.start.date();
1728 if (toResult.start.isOnlyWeekdayComponent() && addDuration(toDate, { day: 7 }) > fromDate) {
1729 toDate = addDuration(toDate, { day: 7 });
1730 toResult.start.imply("day", toDate.getDate());
1731 toResult.start.imply("month", toDate.getMonth() + 1);
1732 toResult.start.imply("year", toDate.getFullYear());
1733 } else if (fromResult.start.isOnlyWeekdayComponent() && addDuration(fromDate, { day: -7 }) < toDate) {
1734 fromDate = addDuration(fromDate, { day: -7 });
1735 fromResult.start.imply("day", fromDate.getDate());
1736 fromResult.start.imply("month", fromDate.getMonth() + 1);
1737 fromResult.start.imply("year", fromDate.getFullYear());
1738 } else if (toResult.start.isDateWithUnknownYear() && addDuration(toDate, { year: 1 }) > fromDate) {
1739 toDate = addDuration(toDate, { year: 1 });
1740 toResult.start.imply("year", toDate.getFullYear());
1741 } else if (fromResult.start.isDateWithUnknownYear() && addDuration(fromDate, { year: -1 }) < toDate) {
1742 fromDate = addDuration(fromDate, { year: -1 });
1743 fromResult.start.imply("year", fromDate.getFullYear());
1744 } else {
1745 [toResult, fromResult] = [fromResult, toResult];
1746 }
1747 }
1748 const result = fromResult.clone();
1749 result.start = fromResult.start;
1750 result.end = toResult.start;
1751 result.index = Math.min(fromResult.index, toResult.index);
1752 if (fromResult.index < toResult.index) {
1753 result.text = fromResult.text + textBetween + toResult.text;
1754 } else {
1755 result.text = toResult.text + textBetween + fromResult.text;
1756 }
1757 return result;
1758 }
1759}
1760
1761// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateRangeRefiner.js
1762class ENMergeDateRangeRefiner extends AbstractMergeDateRangeRefiner {
1763 patternBetween() {
1764 return /^\s*(to|-|–|until|through|till)\s*$/i;
1765 }
1766}
1767
1768// node_modules/chrono-node/dist/esm/calculation/mergingCalculation.js
1769function mergeDateTimeResult(dateResult, timeResult) {
1770 const result = dateResult.clone();
1771 const beginDate = dateResult.start;
1772 const beginTime = timeResult.start;
1773 result.start = mergeDateTimeComponent(beginDate, beginTime);
1774 if (dateResult.end != null || timeResult.end != null) {
1775 const endDate = dateResult.end == null ? dateResult.start : dateResult.end;
1776 const endTime = timeResult.end == null ? timeResult.start : timeResult.end;
1777 const endDateTime = mergeDateTimeComponent(endDate, endTime);
1778 if (dateResult.end == null && endDateTime.date().getTime() < result.start.date().getTime()) {
1779 const nextDay = new Date(endDateTime.date().getTime());
1780 nextDay.setDate(nextDay.getDate() + 1);
1781 if (endDateTime.isCertain("day")) {
1782 assignSimilarDate(endDateTime, nextDay);
1783 } else {
1784 implySimilarDate(endDateTime, nextDay);
1785 }
1786 }
1787 result.end = endDateTime;
1788 }
1789 return result;
1790}
1791function mergeDateTimeComponent(dateComponent, timeComponent) {
1792 const dateTimeComponent = dateComponent.clone();
1793 if (timeComponent.isCertain("hour")) {
1794 dateTimeComponent.assign("hour", timeComponent.get("hour"));
1795 dateTimeComponent.assign("minute", timeComponent.get("minute"));
1796 if (timeComponent.isCertain("second")) {
1797 dateTimeComponent.assign("second", timeComponent.get("second"));
1798 if (timeComponent.isCertain("millisecond")) {
1799 dateTimeComponent.assign("millisecond", timeComponent.get("millisecond"));
1800 } else {
1801 dateTimeComponent.imply("millisecond", timeComponent.get("millisecond"));
1802 }
1803 } else {
1804 dateTimeComponent.imply("second", timeComponent.get("second"));
1805 dateTimeComponent.imply("millisecond", timeComponent.get("millisecond"));
1806 }
1807 } else {
1808 dateTimeComponent.imply("hour", timeComponent.get("hour"));
1809 dateTimeComponent.imply("minute", timeComponent.get("minute"));
1810 dateTimeComponent.imply("second", timeComponent.get("second"));
1811 dateTimeComponent.imply("millisecond", timeComponent.get("millisecond"));
1812 }
1813 if (timeComponent.isCertain("timezoneOffset")) {
1814 dateTimeComponent.assign("timezoneOffset", timeComponent.get("timezoneOffset"));
1815 }
1816 const dateHasMeaningfulMeridiem = dateComponent.get("meridiem") != null && (dateComponent.isCertain("meridiem") || Array.from(dateComponent.tags()).some((t) => t.startsWith("casualReference/")));
1817 if (timeComponent.isCertain("meridiem")) {
1818 dateTimeComponent.assign("meridiem", timeComponent.get("meridiem"));
1819 } else if (timeComponent.get("meridiem") != null && !dateHasMeaningfulMeridiem) {
1820 dateTimeComponent.imply("meridiem", timeComponent.get("meridiem"));
1821 }
1822 if (dateTimeComponent.get("meridiem") == Meridiem.PM && dateTimeComponent.get("hour") < 12) {
1823 if (timeComponent.isCertain("hour")) {
1824 dateTimeComponent.assign("hour", dateTimeComponent.get("hour") + 12);
1825 } else {
1826 dateTimeComponent.imply("hour", dateTimeComponent.get("hour") + 12);
1827 }
1828 }
1829 dateTimeComponent.addTags(dateComponent.tags());
1830 dateTimeComponent.addTags(timeComponent.tags());
1831 return dateTimeComponent;
1832}
1833
1834// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateTimeRefiner.js
1835class AbstractMergeDateTimeRefiner extends MergingRefiner {
1836 shouldMergeResults(textBetween, currentResult, nextResult) {
1837 return (currentResult.start.isOnlyDate() && nextResult.start.isOnlyTime() || nextResult.start.isOnlyDate() && currentResult.start.isOnlyTime()) && textBetween.match(this.patternBetween()) != null;
1838 }
1839 mergeResults(textBetween, currentResult, nextResult) {
1840 const result = currentResult.start.isOnlyDate() ? mergeDateTimeResult(currentResult, nextResult) : mergeDateTimeResult(nextResult, currentResult);
1841 result.index = currentResult.index;
1842 result.text = currentResult.text + textBetween + nextResult.text;
1843 return result;
1844 }
1845}
1846
1847// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateTimeRefiner.js
1848class ENMergeDateTimeRefiner extends AbstractMergeDateTimeRefiner {
1849 patternBetween() {
1850 return new RegExp("^\\s*(T|at|after|before|on|of|,|-|\\.|∙|:)?\\s*$");
1851 }
1852}
1853
1854// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js
1855var TIMEZONE_NAME_PATTERN = new RegExp("^\\s*,?\\s*\\(?([A-Z]{2,4})\\)?(?=\\W|$)", "i");
1856
1857class ExtractTimezoneAbbrRefiner {
1858 timezoneOverrides;
1859 constructor(timezoneOverrides) {
1860 this.timezoneOverrides = timezoneOverrides;
1861 }
1862 refine(context, results) {
1863 const timezoneOverrides = context.option.timezones ?? {};
1864 results.forEach((result) => {
1865 const suffix = context.text.substring(result.index + result.text.length);
1866 const match = TIMEZONE_NAME_PATTERN.exec(suffix);
1867 if (!match) {
1868 return;
1869 }
1870 const timezoneAbbr = match[1].toUpperCase();
1871 const refDate = result.start.date() ?? result.refDate ?? new Date;
1872 const tzOverrides = { ...this.timezoneOverrides, ...timezoneOverrides };
1873 const extractedTimezoneOffset = toTimezoneOffset(timezoneAbbr, refDate, tzOverrides);
1874 if (extractedTimezoneOffset == null) {
1875 return;
1876 }
1877 context.debug(() => {
1878 console.log(`Extracting timezone: '${timezoneAbbr}' into: ${extractedTimezoneOffset} for: ${result.start}`);
1879 });
1880 const currentTimezoneOffset = result.start.get("timezoneOffset");
1881 if (currentTimezoneOffset !== null && extractedTimezoneOffset != currentTimezoneOffset) {
1882 if (result.start.isCertain("timezoneOffset")) {
1883 return;
1884 }
1885 if (timezoneAbbr != match[1]) {
1886 return;
1887 }
1888 }
1889 if (result.start.isOnlyDate()) {
1890 if (timezoneAbbr != match[1]) {
1891 return;
1892 }
1893 }
1894 result.text += match[0];
1895 if (!result.start.isCertain("timezoneOffset")) {
1896 result.start.assign("timezoneOffset", extractedTimezoneOffset);
1897 }
1898 if (result.end != null && !result.end.isCertain("timezoneOffset")) {
1899 result.end.assign("timezoneOffset", extractedTimezoneOffset);
1900 }
1901 });
1902 return results;
1903 }
1904}
1905
1906// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneOffsetRefiner.js
1907var TIMEZONE_OFFSET_PATTERN = new RegExp("^\\s*(?:\\(?(?:GMT|UTC)\\s?)?([+-])(\\d{1,2})(?::?(\\d{2}))?\\)?", "i");
1908var TIMEZONE_OFFSET_SIGN_GROUP = 1;
1909var TIMEZONE_OFFSET_HOUR_OFFSET_GROUP = 2;
1910var TIMEZONE_OFFSET_MINUTE_OFFSET_GROUP = 3;
1911
1912class ExtractTimezoneOffsetRefiner {
1913 refine(context, results) {
1914 results.forEach(function(result) {
1915 if (result.start.isCertain("timezoneOffset")) {
1916 return;
1917 }
1918 const suffix = context.text.substring(result.index + result.text.length);
1919 const match = TIMEZONE_OFFSET_PATTERN.exec(suffix);
1920 if (!match) {
1921 return;
1922 }
1923 context.debug(() => {
1924 console.log(`Extracting timezone: '${match[0]}' into : ${result}`);
1925 });
1926 const hourOffset = parseInt(match[TIMEZONE_OFFSET_HOUR_OFFSET_GROUP]);
1927 const minuteOffset = parseInt(match[TIMEZONE_OFFSET_MINUTE_OFFSET_GROUP] || "0");
1928 let timezoneOffset = hourOffset * 60 + minuteOffset;
1929 if (timezoneOffset > 14 * 60) {
1930 return;
1931 }
1932 if (match[TIMEZONE_OFFSET_SIGN_GROUP] === "-") {
1933 timezoneOffset = -timezoneOffset;
1934 }
1935 if (result.end != null) {
1936 result.end.assign("timezoneOffset", timezoneOffset);
1937 }
1938 result.start.assign("timezoneOffset", timezoneOffset);
1939 result.text += match[0];
1940 });
1941 return results;
1942 }
1943}
1944
1945// node_modules/chrono-node/dist/esm/common/refiners/OverlapRemovalRefiner.js
1946class OverlapRemovalRefiner {
1947 refine(context, results) {
1948 if (results.length < 2) {
1949 return results;
1950 }
1951 const filteredResults = [];
1952 let prevResult = results[0];
1953 for (let i = 1;i < results.length; i++) {
1954 const result = results[i];
1955 if (result.index >= prevResult.index + prevResult.text.length) {
1956 filteredResults.push(prevResult);
1957 prevResult = result;
1958 continue;
1959 }
1960 let kept = null;
1961 let removed = null;
1962 if (result.text.length > prevResult.text.length) {
1963 kept = result;
1964 removed = prevResult;
1965 } else {
1966 kept = prevResult;
1967 removed = result;
1968 }
1969 context.debug(() => {
1970 console.log(`${this.constructor.name} remove ${removed} by ${kept}`);
1971 });
1972 prevResult = kept;
1973 }
1974 if (prevResult != null) {
1975 filteredResults.push(prevResult);
1976 }
1977 return filteredResults;
1978 }
1979}
1980
1981// node_modules/chrono-node/dist/esm/calculation/weekdays.js
1982function createParsingComponentsAtWeekday(reference, weekday, modifier) {
1983 const refDate = reference.getDateWithAdjustedTimezone();
1984 const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
1985 let components = new ParsingComponents(reference);
1986 components = components.addDurationAsImplied({ day: daysToWeekday });
1987 components.assign("weekday", weekday);
1988 return components;
1989}
1990function getDaysToWeekday(refDate, weekday, modifier) {
1991 const refWeekday = refDate.getDay();
1992 switch (modifier) {
1993 case "this":
1994 return getDaysForwardToWeekday(refDate, weekday);
1995 case "last":
1996 return getBackwardDaysToWeekday(refDate, weekday);
1997 case "next":
1998 if (refWeekday == Weekday.SUNDAY) {
1999 return weekday == Weekday.SUNDAY ? 7 : weekday;
2000 }
2001 if (refWeekday == Weekday.SATURDAY) {
2002 if (weekday == Weekday.SATURDAY)
2003 return 7;
2004 if (weekday == Weekday.SUNDAY)
2005 return 8;
2006 return 1 + weekday;
2007 }
2008 if (weekday < refWeekday && weekday != Weekday.SUNDAY) {
2009 return getDaysForwardToWeekday(refDate, weekday);
2010 } else {
2011 return getDaysForwardToWeekday(refDate, weekday) + 7;
2012 }
2013 }
2014 return getDaysToWeekdayClosest(refDate, weekday);
2015}
2016function getDaysToWeekdayClosest(refDate, weekday) {
2017 const backward = getBackwardDaysToWeekday(refDate, weekday);
2018 const forward = getDaysForwardToWeekday(refDate, weekday);
2019 return forward < -backward ? forward : backward;
2020}
2021function getDaysForwardToWeekday(refDate, weekday) {
2022 const refWeekday = refDate.getDay();
2023 let forwardCount = weekday - refWeekday;
2024 if (forwardCount < 0) {
2025 forwardCount += 7;
2026 }
2027 return forwardCount;
2028}
2029function getBackwardDaysToWeekday(refDate, weekday) {
2030 const refWeekday = refDate.getDay();
2031 let backwardCount = weekday - refWeekday;
2032 if (backwardCount >= 0) {
2033 backwardCount -= 7;
2034 }
2035 return backwardCount;
2036}
2037
2038// node_modules/chrono-node/dist/esm/common/refiners/ForwardDateRefiner.js
2039class ForwardDateRefiner {
2040 refine(context, results) {
2041 if (!context.option.forwardDate) {
2042 return results;
2043 }
2044 results.forEach((result) => {
2045 let refDate = context.reference.getDateWithAdjustedTimezone();
2046 if (result.start.isOnlyTime() && context.reference.instant > result.start.date()) {
2047 const refDate2 = context.reference.getDateWithAdjustedTimezone();
2048 const refFollowingDay = new Date(refDate2);
2049 refFollowingDay.setDate(refFollowingDay.getDate() + 1);
2050 implySimilarDate(result.start, refFollowingDay);
2051 context.debug(() => {
2052 console.log(`${this.constructor.name} adjusted ${result} time from the ref date (${refDate2}) to the following day (${refFollowingDay})`);
2053 });
2054 if (result.end && result.end.isOnlyTime()) {
2055 implySimilarDate(result.end, refFollowingDay);
2056 if (result.start.date() > result.end.date()) {
2057 refFollowingDay.setDate(refFollowingDay.getDate() + 1);
2058 implySimilarDate(result.end, refFollowingDay);
2059 }
2060 }
2061 }
2062 if (result.start.isOnlyWeekdayComponent() && refDate > result.start.date()) {
2063 let daysToAdd = getDaysForwardToWeekday(refDate, result.start.get("weekday")) || 7;
2064 const forwardedWeekday = addDuration(refDate, { day: daysToAdd });
2065 implySimilarDate(result.start, forwardedWeekday);
2066 context.debug(() => {
2067 console.log(`${this.constructor.name} adjusted ${result} weekday (${result.start})`);
2068 });
2069 if (result.end && result.start.date() > result.end.date()) {
2070 let daysToAdd2 = getDaysForwardToWeekday(refDate, result.start.get("weekday")) || 7;
2071 const forwardedWeekday2 = addDuration(refDate, { day: daysToAdd2 });
2072 implySimilarDate(result.end, forwardedWeekday2);
2073 context.debug(() => {
2074 console.log(`${this.constructor.name} adjusted ${result} weekday (${result.end})`);
2075 });
2076 }
2077 }
2078 if (result.start.isDateWithUnknownYear() && refDate > result.start.date()) {
2079 for (let i = 0;i < 3 && refDate > result.start.date(); i++) {
2080 result.start.imply("year", result.start.get("year") + 1);
2081 context.debug(() => {
2082 console.log(`${this.constructor.name} adjusted ${result} year (${result.start})`);
2083 });
2084 if (result.end && !result.end.isCertain("year")) {
2085 result.end.imply("year", result.end.get("year") + 1);
2086 context.debug(() => {
2087 console.log(`${this.constructor.name} adjusted ${result} month (${result.start})`);
2088 });
2089 }
2090 }
2091 }
2092 });
2093 return results;
2094 }
2095}
2096
2097// node_modules/chrono-node/dist/esm/common/refiners/UnlikelyFormatFilter.js
2098class UnlikelyFormatFilter extends Filter {
2099 strictMode;
2100 constructor(strictMode) {
2101 super();
2102 this.strictMode = strictMode;
2103 }
2104 isValid(context, result) {
2105 if (result.text.replace(" ", "").match(/^\d*(\.\d*)?$/)) {
2106 context.debug(() => {
2107 console.log(`Removing unlikely result '${result.text}'`);
2108 });
2109 return false;
2110 }
2111 if (!result.start.isValidDate()) {
2112 context.debug(() => {
2113 console.log(`Removing invalid result: ${result} (${result.start})`);
2114 });
2115 return false;
2116 }
2117 if (result.end && !result.end.isValidDate()) {
2118 context.debug(() => {
2119 console.log(`Removing invalid result: ${result} (${result.end})`);
2120 });
2121 return false;
2122 }
2123 if (this.strictMode) {
2124 return this.isStrictModeValid(context, result);
2125 }
2126 return true;
2127 }
2128 isStrictModeValid(context, result) {
2129 if (result.start.isOnlyWeekdayComponent()) {
2130 context.debug(() => {
2131 console.log(`(Strict) Removing weekday only component: ${result} (${result.end})`);
2132 });
2133 return false;
2134 }
2135 return true;
2136 }
2137}
2138
2139// node_modules/chrono-node/dist/esm/common/parsers/ISOFormatParser.js
2140var PATTERN8 = new RegExp("([0-9]{4})\\-([0-9]{1,2})\\-([0-9]{1,2})" + "(?:T" + "([0-9]{1,2}):([0-9]{1,2})" + "(?:" + ":([0-9]{1,2})(?:\\.(\\d{1,4}))?" + ")?" + "(" + "Z|([+-]\\d{2}):?(\\d{2})?" + ")?" + ")?" + "(?=\\W|$)", "i");
2141var YEAR_NUMBER_GROUP2 = 1;
2142var MONTH_NUMBER_GROUP2 = 2;
2143var DATE_NUMBER_GROUP2 = 3;
2144var HOUR_NUMBER_GROUP = 4;
2145var MINUTE_NUMBER_GROUP = 5;
2146var SECOND_NUMBER_GROUP = 6;
2147var MILLISECOND_NUMBER_GROUP = 7;
2148var TZD_GROUP = 8;
2149var TZD_HOUR_OFFSET_GROUP = 9;
2150var TZD_MINUTE_OFFSET_GROUP = 10;
2151
2152class ISOFormatParser extends AbstractParserWithWordBoundaryChecking {
2153 innerPattern() {
2154 return PATTERN8;
2155 }
2156 innerExtract(context, match) {
2157 const components = context.createParsingComponents({
2158 year: parseInt(match[YEAR_NUMBER_GROUP2]),
2159 month: parseInt(match[MONTH_NUMBER_GROUP2]),
2160 day: parseInt(match[DATE_NUMBER_GROUP2])
2161 });
2162 if (match[HOUR_NUMBER_GROUP] != null) {
2163 components.assign("hour", parseInt(match[HOUR_NUMBER_GROUP]));
2164 components.assign("minute", parseInt(match[MINUTE_NUMBER_GROUP]));
2165 if (match[SECOND_NUMBER_GROUP] != null) {
2166 components.assign("second", parseInt(match[SECOND_NUMBER_GROUP]));
2167 }
2168 if (match[MILLISECOND_NUMBER_GROUP] != null) {
2169 components.assign("millisecond", parseInt(match[MILLISECOND_NUMBER_GROUP]));
2170 }
2171 if (match[TZD_GROUP] != null) {
2172 let offset = 0;
2173 if (match[TZD_HOUR_OFFSET_GROUP]) {
2174 const hourOffset = parseInt(match[TZD_HOUR_OFFSET_GROUP]);
2175 let minuteOffset = 0;
2176 if (match[TZD_MINUTE_OFFSET_GROUP] != null) {
2177 minuteOffset = parseInt(match[TZD_MINUTE_OFFSET_GROUP]);
2178 }
2179 offset = hourOffset * 60;
2180 if (offset < 0) {
2181 offset -= minuteOffset;
2182 } else {
2183 offset += minuteOffset;
2184 }
2185 }
2186 components.assign("timezoneOffset", offset);
2187 }
2188 }
2189 return components.addTag("parser/ISOFormatParser");
2190 }
2191}
2192
2193// node_modules/chrono-node/dist/esm/common/refiners/MergeWeekdayComponentRefiner.js
2194class MergeWeekdayComponentRefiner extends MergingRefiner {
2195 mergeResults(textBetween, currentResult, nextResult) {
2196 const newResult = nextResult.clone();
2197 newResult.index = currentResult.index;
2198 newResult.text = currentResult.text + textBetween + newResult.text;
2199 newResult.start.assign("weekday", currentResult.start.get("weekday"));
2200 if (newResult.end) {
2201 newResult.end.assign("weekday", currentResult.start.get("weekday"));
2202 }
2203 return newResult;
2204 }
2205 shouldMergeResults(textBetween, currentResult, nextResult) {
2206 const weekdayThenNormalDate = currentResult.start.isOnlyWeekdayComponent() && !currentResult.start.isCertain("hour") && nextResult.start.isCertain("day");
2207 return weekdayThenNormalDate && textBetween.match(/^,?\s*$/) != null;
2208 }
2209}
2210
2211// node_modules/chrono-node/dist/esm/configurations.js
2212function includeCommonConfiguration(configuration, strictMode = false) {
2213 configuration.parsers.unshift(new ISOFormatParser);
2214 configuration.refiners.unshift(new MergeWeekdayComponentRefiner);
2215 configuration.refiners.unshift(new ExtractTimezoneOffsetRefiner);
2216 configuration.refiners.unshift(new OverlapRemovalRefiner);
2217 configuration.refiners.push(new ExtractTimezoneAbbrRefiner);
2218 configuration.refiners.push(new OverlapRemovalRefiner);
2219 configuration.refiners.push(new ForwardDateRefiner);
2220 configuration.refiners.push(new UnlikelyFormatFilter(strictMode));
2221 return configuration;
2222}
2223
2224// node_modules/chrono-node/dist/esm/common/casualReferences.js
2225function now(reference) {
2226 const targetDate = reference.getDateWithAdjustedTimezone();
2227 const component = new ParsingComponents(reference, {});
2228 assignSimilarDate(component, targetDate);
2229 assignSimilarTime(component, targetDate);
2230 component.assign("timezoneOffset", reference.getTimezoneOffset());
2231 component.addTag("casualReference/now");
2232 return component;
2233}
2234function today(reference) {
2235 const targetDate = reference.getDateWithAdjustedTimezone();
2236 const component = new ParsingComponents(reference, {});
2237 assignSimilarDate(component, targetDate);
2238 implySimilarTime(component, targetDate);
2239 component.delete("meridiem");
2240 component.addTag("casualReference/today");
2241 return component;
2242}
2243function yesterday(reference) {
2244 return theDayBefore(reference, 1).addTag("casualReference/yesterday");
2245}
2246function tomorrow(reference) {
2247 return theDayAfter(reference, 1).addTag("casualReference/tomorrow");
2248}
2249function theDayBefore(reference, numDay) {
2250 return theDayAfter(reference, -numDay);
2251}
2252function theDayAfter(reference, nDays) {
2253 const targetDate = reference.getDateWithAdjustedTimezone();
2254 const component = new ParsingComponents(reference, {});
2255 const newDate = new Date(targetDate.getTime());
2256 newDate.setDate(newDate.getDate() + nDays);
2257 assignSimilarDate(component, newDate);
2258 implySimilarTime(component, newDate);
2259 component.delete("meridiem");
2260 return component;
2261}
2262function tonight(reference, implyHour = 22) {
2263 const targetDate = reference.getDateWithAdjustedTimezone();
2264 const component = new ParsingComponents(reference, {});
2265 assignSimilarDate(component, targetDate);
2266 component.imply("hour", implyHour);
2267 component.imply("meridiem", Meridiem.PM);
2268 component.addTag("casualReference/tonight");
2269 return component;
2270}
2271function evening(reference, implyHour = 20) {
2272 const component = new ParsingComponents(reference, {});
2273 component.imply("meridiem", Meridiem.PM);
2274 component.imply("hour", implyHour);
2275 component.addTag("casualReference/evening");
2276 return component;
2277}
2278function midnight(reference) {
2279 const component = new ParsingComponents(reference, {});
2280 if (reference.getDateWithAdjustedTimezone().getHours() > 2) {
2281 component.addDurationAsImplied({ day: 1 });
2282 }
2283 component.assign("hour", 0);
2284 component.imply("minute", 0);
2285 component.imply("second", 0);
2286 component.imply("millisecond", 0);
2287 component.addTag("casualReference/midnight");
2288 return component;
2289}
2290function morning(reference, implyHour = 6) {
2291 const component = new ParsingComponents(reference, {});
2292 component.imply("meridiem", Meridiem.AM);
2293 component.imply("hour", implyHour);
2294 component.imply("minute", 0);
2295 component.imply("second", 0);
2296 component.imply("millisecond", 0);
2297 component.addTag("casualReference/morning");
2298 return component;
2299}
2300function afternoon(reference, implyHour = 15) {
2301 const component = new ParsingComponents(reference, {});
2302 component.imply("meridiem", Meridiem.PM);
2303 component.imply("hour", implyHour);
2304 component.imply("minute", 0);
2305 component.imply("second", 0);
2306 component.imply("millisecond", 0);
2307 component.addTag("casualReference/afternoon");
2308 return component;
2309}
2310function noon(reference) {
2311 const component = new ParsingComponents(reference, {});
2312 component.imply("meridiem", Meridiem.AM);
2313 component.assign("hour", 12);
2314 component.imply("minute", 0);
2315 component.imply("second", 0);
2316 component.imply("millisecond", 0);
2317 component.addTag("casualReference/noon");
2318 return component;
2319}
2320
2321// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualDateParser.js
2322var PATTERN9 = /(now|today|tonight|tomorrow|overmorrow|tmr|tmrw|yesterday|last\s*night)(?=\W|$)/i;
2323
2324class ENCasualDateParser extends AbstractParserWithWordBoundaryChecking {
2325 innerPattern(context) {
2326 return PATTERN9;
2327 }
2328 innerExtract(context, match) {
2329 let targetDate = context.refDate;
2330 const lowerText = match[0].toLowerCase();
2331 let component = context.createParsingComponents();
2332 switch (lowerText) {
2333 case "now":
2334 component = now(context.reference);
2335 break;
2336 case "today":
2337 component = today(context.reference);
2338 break;
2339 case "yesterday":
2340 component = yesterday(context.reference);
2341 break;
2342 case "tomorrow":
2343 case "tmr":
2344 case "tmrw":
2345 component = tomorrow(context.reference);
2346 break;
2347 case "tonight":
2348 component = tonight(context.reference);
2349 break;
2350 case "overmorrow":
2351 component = theDayAfter(context.reference, 2);
2352 break;
2353 default:
2354 if (lowerText.match(/last\s*night/)) {
2355 if (targetDate.getHours() > 6) {
2356 const previousDay = new Date(targetDate.getTime());
2357 previousDay.setDate(previousDay.getDate() - 1);
2358 targetDate = previousDay;
2359 }
2360 assignSimilarDate(component, targetDate);
2361 component.imply("hour", 0);
2362 }
2363 break;
2364 }
2365 component.addTag("parser/ENCasualDateParser");
2366 return component;
2367 }
2368}
2369
2370// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualTimeParser.js
2371var PATTERN10 = /(?:this)?\s{0,3}(morning|afternoon|evening|night|midnight|midday|noon)(?=\W|$)/i;
2372
2373class ENCasualTimeParser extends AbstractParserWithWordBoundaryChecking {
2374 innerPattern() {
2375 return PATTERN10;
2376 }
2377 innerExtract(context, match) {
2378 let component = null;
2379 switch (match[1].toLowerCase()) {
2380 case "afternoon":
2381 component = afternoon(context.reference);
2382 break;
2383 case "evening":
2384 case "night":
2385 component = evening(context.reference);
2386 break;
2387 case "midnight":
2388 component = midnight(context.reference);
2389 break;
2390 case "morning":
2391 component = morning(context.reference);
2392 break;
2393 case "noon":
2394 case "midday":
2395 component = noon(context.reference);
2396 break;
2397 }
2398 if (component) {
2399 component.addTag("parser/ENCasualTimeParser");
2400 }
2401 return component;
2402 }
2403}
2404
2405// node_modules/chrono-node/dist/esm/locales/en/parsers/ENWeekdayParser.js
2406var PATTERN11 = new RegExp("(?:(?:\\,|\\(|\\()\\s*)?" + "(?:on\\s*?)?" + "(?:(this|last|past|next)\\s*)?" + `(${matchAnyPattern(WEEKDAY_DICTIONARY)}|weekend|weekday)` + "(?:\\s*(?:\\,|\\)|\\)))?" + "(?:\\s*(?:of\\s*)?(this|last|past|next)\\s*week)?" + "(?=\\W|$)", "i");
2407var PREFIX_GROUP2 = 1;
2408var WEEKDAY_GROUP = 2;
2409var POSTFIX_GROUP = 3;
2410
2411class ENWeekdayParser extends AbstractParserWithWordBoundaryChecking {
2412 innerPattern() {
2413 return PATTERN11;
2414 }
2415 innerExtract(context, match) {
2416 const prefix = match[PREFIX_GROUP2];
2417 const postfix = match[POSTFIX_GROUP];
2418 let modifierWord = prefix || postfix;
2419 modifierWord = modifierWord || "";
2420 modifierWord = modifierWord.toLowerCase();
2421 let modifier = null;
2422 if (modifierWord == "last" || modifierWord == "past") {
2423 modifier = "last";
2424 } else if (modifierWord == "next") {
2425 modifier = "next";
2426 } else if (modifierWord == "this") {
2427 modifier = "this";
2428 }
2429 const weekday_word = match[WEEKDAY_GROUP].toLowerCase();
2430 let weekday;
2431 if (WEEKDAY_DICTIONARY[weekday_word] !== undefined) {
2432 weekday = WEEKDAY_DICTIONARY[weekday_word];
2433 } else if (weekday_word == "weekend") {
2434 weekday = modifier == "last" ? Weekday.SUNDAY : Weekday.SATURDAY;
2435 } else if (weekday_word == "weekday") {
2436 const refWeekday = context.reference.getDateWithAdjustedTimezone().getDay();
2437 if (refWeekday == Weekday.SUNDAY || refWeekday == Weekday.SATURDAY) {
2438 weekday = modifier == "last" ? Weekday.FRIDAY : Weekday.MONDAY;
2439 } else {
2440 weekday = refWeekday - 1;
2441 weekday = modifier == "last" ? weekday - 1 : weekday + 1;
2442 weekday = weekday % 5 + 1;
2443 }
2444 } else {
2445 return null;
2446 }
2447 return createParsingComponentsAtWeekday(context.reference, weekday, modifier);
2448 }
2449}
2450
2451// node_modules/chrono-node/dist/esm/locales/en/parsers/ENRelativeDateFormatParser.js
2452var PATTERN12 = new RegExp(`(this|last|past|next|after\\s*this)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` + "(?=\\W|$)", "i");
2453var MODIFIER_WORD_GROUP = 1;
2454var RELATIVE_WORD_GROUP = 2;
2455
2456class ENRelativeDateFormatParser extends AbstractParserWithWordBoundaryChecking {
2457 innerPattern() {
2458 return PATTERN12;
2459 }
2460 innerExtract(context, match) {
2461 const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();
2462 const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
2463 const timeunit = TIME_UNIT_DICTIONARY[unitWord];
2464 if (modifier == "next" || modifier.startsWith("after")) {
2465 const timeUnits = {};
2466 timeUnits[timeunit] = 1;
2467 return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
2468 }
2469 if (modifier == "last" || modifier == "past") {
2470 const timeUnits = {};
2471 timeUnits[timeunit] = -1;
2472 return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
2473 }
2474 const components = context.createParsingComponents();
2475 let date = new Date(context.reference.instant.getTime());
2476 if (unitWord.match(/week/i)) {
2477 date.setDate(date.getDate() - date.getDay());
2478 components.imply("day", date.getDate());
2479 components.imply("month", date.getMonth() + 1);
2480 components.imply("year", date.getFullYear());
2481 } else if (unitWord.match(/month/i)) {
2482 date.setDate(1);
2483 components.imply("day", date.getDate());
2484 components.assign("year", date.getFullYear());
2485 components.assign("month", date.getMonth() + 1);
2486 } else if (unitWord.match(/year/i)) {
2487 date.setDate(1);
2488 date.setMonth(0);
2489 components.imply("day", date.getDate());
2490 components.imply("month", date.getMonth() + 1);
2491 components.assign("year", date.getFullYear());
2492 }
2493 return components;
2494 }
2495}
2496
2497// node_modules/chrono-node/dist/esm/common/parsers/SlashDateFormatParser.js
2498var PATTERN13 = new RegExp("([^\\d]|^)" + "([0-3]{0,1}[0-9]{1})[\\/\\.\\-]([0-3]{0,1}[0-9]{1})" + "(?:[\\/\\.\\-]([0-9]{4}|[0-9]{2}))?" + "(\\W|$)", "i");
2499var OPENING_GROUP = 1;
2500var ENDING_GROUP = 5;
2501var FIRST_NUMBERS_GROUP = 2;
2502var SECOND_NUMBERS_GROUP = 3;
2503var YEAR_GROUP5 = 4;
2504
2505class SlashDateFormatParser {
2506 groupNumberMonth;
2507 groupNumberDay;
2508 constructor(littleEndian) {
2509 this.groupNumberMonth = littleEndian ? SECOND_NUMBERS_GROUP : FIRST_NUMBERS_GROUP;
2510 this.groupNumberDay = littleEndian ? FIRST_NUMBERS_GROUP : SECOND_NUMBERS_GROUP;
2511 }
2512 pattern() {
2513 return PATTERN13;
2514 }
2515 extract(context, match) {
2516 const index = match.index + match[OPENING_GROUP].length;
2517 const indexEnd = match.index + match[0].length - match[ENDING_GROUP].length;
2518 if (index > 0) {
2519 const textBefore = context.text.substring(0, index);
2520 if (textBefore.match("\\d/?$")) {
2521 return;
2522 }
2523 }
2524 if (indexEnd < context.text.length) {
2525 const textAfter = context.text.substring(indexEnd);
2526 if (textAfter.match("^/?\\d")) {
2527 return;
2528 }
2529 }
2530 const text = context.text.substring(index, indexEnd);
2531 if (text.match(/^\d\.\d$/) || text.match(/^\d\.\d{1,2}\.\d{1,2}\s*$/)) {
2532 return;
2533 }
2534 if (!match[YEAR_GROUP5] && text.indexOf("/") < 0) {
2535 return;
2536 }
2537 const result = context.createParsingResult(index, text);
2538 let month = parseInt(match[this.groupNumberMonth]);
2539 let day = parseInt(match[this.groupNumberDay]);
2540 if (month < 1 || month > 12) {
2541 if (month > 12) {
2542 if (day >= 1 && day <= 12 && month <= 31) {
2543 [day, month] = [month, day];
2544 } else {
2545 return null;
2546 }
2547 }
2548 }
2549 if (day < 1 || day > 31) {
2550 return null;
2551 }
2552 result.start.assign("day", day);
2553 result.start.assign("month", month);
2554 if (match[YEAR_GROUP5]) {
2555 const rawYearNumber = parseInt(match[YEAR_GROUP5]);
2556 const year = findMostLikelyADYear(rawYearNumber);
2557 result.start.assign("year", year);
2558 } else {
2559 const year = findYearClosestToRef(context.refDate, day, month);
2560 result.start.imply("year", year);
2561 }
2562 return result.addTag("parser/SlashDateFormatParser");
2563 }
2564}
2565
2566// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js
2567var PATTERN14 = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
2568var PATTERN_NO_ABBR = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
2569
2570class ENTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
2571 allowAbbreviations;
2572 constructor(allowAbbreviations = true) {
2573 super();
2574 this.allowAbbreviations = allowAbbreviations;
2575 }
2576 innerPattern() {
2577 return this.allowAbbreviations ? PATTERN14 : PATTERN_NO_ABBR;
2578 }
2579 innerExtract(context, match) {
2580 const prefix = match[1].toLowerCase();
2581 let duration = parseDuration(match[2]);
2582 if (!duration) {
2583 return null;
2584 }
2585 switch (prefix) {
2586 case "last":
2587 case "past":
2588 case "-":
2589 duration = reverseDuration(duration);
2590 break;
2591 }
2592 return ParsingComponents.createRelativeFromReference(context.reference, duration);
2593 }
2594}
2595
2596// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeAfterDateRefiner.js
2597function IsPositiveFollowingReference(result) {
2598 return result.text.match(/^[+-]/i) != null;
2599}
2600function IsNegativeFollowingReference(result) {
2601 return result.text.match(/^-/i) != null;
2602}
2603
2604class ENMergeRelativeAfterDateRefiner extends MergingRefiner {
2605 shouldMergeResults(textBetween, currentResult, nextResult) {
2606 if (!textBetween.match(/^\s*$/i)) {
2607 return false;
2608 }
2609 return IsPositiveFollowingReference(nextResult) || IsNegativeFollowingReference(nextResult);
2610 }
2611 mergeResults(textBetween, currentResult, nextResult, context) {
2612 let timeUnits = parseDuration(nextResult.text);
2613 if (IsNegativeFollowingReference(nextResult)) {
2614 timeUnits = reverseDuration(timeUnits);
2615 }
2616 const components = ParsingComponents.createRelativeFromReference(ReferenceWithTimezone.fromDate(currentResult.start.date()), timeUnits);
2617 return new ParsingResult(currentResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
2618 }
2619}
2620
2621// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeFollowByDateRefiner.js
2622function hasImpliedEarlierReferenceDate(result) {
2623 return result.text.match(/\s+(before|from)$/i) != null;
2624}
2625function hasImpliedLaterReferenceDate(result) {
2626 return result.text.match(/\s+(after|since)$/i) != null;
2627}
2628
2629class ENMergeRelativeFollowByDateRefiner extends MergingRefiner {
2630 patternBetween() {
2631 return /^\s*$/i;
2632 }
2633 shouldMergeResults(textBetween, currentResult, nextResult) {
2634 if (!textBetween.match(this.patternBetween())) {
2635 return false;
2636 }
2637 if (!hasImpliedEarlierReferenceDate(currentResult) && !hasImpliedLaterReferenceDate(currentResult)) {
2638 return false;
2639 }
2640 return !!nextResult.start.get("day") && !!nextResult.start.get("month") && !!nextResult.start.get("year");
2641 }
2642 mergeResults(textBetween, currentResult, nextResult) {
2643 let duration = parseDuration(currentResult.text);
2644 if (hasImpliedEarlierReferenceDate(currentResult)) {
2645 duration = reverseDuration(duration);
2646 }
2647 const components = ParsingComponents.createRelativeFromReference(ReferenceWithTimezone.fromDate(nextResult.start.date()), duration);
2648 return new ParsingResult(nextResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
2649 }
2650}
2651
2652// node_modules/chrono-node/dist/esm/locales/en/refiners/ENExtractYearSuffixRefiner.js
2653var YEAR_SUFFIX_PATTERN = new RegExp(`^\\s*(${YEAR_PATTERN})`, "i");
2654var YEAR_GROUP6 = 1;
2655
2656class ENExtractYearSuffixRefiner {
2657 refine(context, results) {
2658 results.forEach(function(result) {
2659 if (!result.start.isDateWithUnknownYear()) {
2660 return;
2661 }
2662 const suffix = context.text.substring(result.index + result.text.length);
2663 const match = YEAR_SUFFIX_PATTERN.exec(suffix);
2664 if (!match) {
2665 return;
2666 }
2667 if (match[0].trim().length <= 3) {
2668 return;
2669 }
2670 context.debug(() => {
2671 console.log(`Extracting year: '${match[0]}' into : ${result}`);
2672 });
2673 const year = parseYear(match[YEAR_GROUP6]);
2674 if (result.end != null) {
2675 result.end.assign("year", year);
2676 }
2677 result.start.assign("year", year);
2678 result.text += match[0];
2679 });
2680 return results;
2681 }
2682}
2683
2684// node_modules/chrono-node/dist/esm/locales/en/refiners/ENUnlikelyFormatFilter.js
2685class ENUnlikelyFormatFilter extends Filter {
2686 constructor() {
2687 super();
2688 }
2689 isValid(context, result) {
2690 const text = result.text.trim();
2691 if (text === context.text.trim()) {
2692 return true;
2693 }
2694 if (text.toLowerCase() === "may") {
2695 const textBefore = context.text.substring(0, result.index).trim();
2696 if (!textBefore.match(/\b(in)$/i)) {
2697 context.debug(() => {
2698 console.log(`Removing unlikely result: ${result}`);
2699 });
2700 return false;
2701 }
2702 }
2703 if (text.toLowerCase().endsWith("the second")) {
2704 const textAfter = context.text.substring(result.index + result.text.length).trim();
2705 if (textAfter.length > 0) {
2706 context.debug(() => {
2707 console.log(`Removing unlikely result: ${result}`);
2708 });
2709 }
2710 return false;
2711 }
2712 return true;
2713 }
2714}
2715
2716// node_modules/chrono-node/dist/esm/locales/en/configuration.js
2717class ENDefaultConfiguration {
2718 createCasualConfiguration(littleEndian = false) {
2719 const option = this.createConfiguration(false, littleEndian);
2720 option.parsers.push(new ENCasualDateParser);
2721 option.parsers.push(new ENCasualTimeParser);
2722 option.parsers.push(new ENMonthNameParser);
2723 option.parsers.push(new ENRelativeDateFormatParser);
2724 option.parsers.push(new ENTimeUnitCasualRelativeFormatParser);
2725 option.refiners.push(new ENUnlikelyFormatFilter);
2726 return option;
2727 }
2728 createConfiguration(strictMode = true, littleEndian = false) {
2729 const options = includeCommonConfiguration({
2730 parsers: [
2731 new SlashDateFormatParser(littleEndian),
2732 new ENTimeUnitWithinFormatParser(strictMode),
2733 new ENMonthNameLittleEndianParser,
2734 new ENMonthNameMiddleEndianParser(littleEndian),
2735 new ENWeekdayParser,
2736 new ENSlashMonthFormatParser,
2737 new ENTimeExpressionParser(strictMode),
2738 new ENTimeUnitAgoFormatParser(strictMode),
2739 new ENTimeUnitLaterFormatParser(strictMode)
2740 ],
2741 refiners: [new ENMergeDateTimeRefiner]
2742 }, strictMode);
2743 options.parsers.unshift(new ENYearMonthDayParser(strictMode));
2744 options.refiners.unshift(new ENMergeRelativeFollowByDateRefiner);
2745 options.refiners.unshift(new ENMergeRelativeAfterDateRefiner);
2746 options.refiners.unshift(new OverlapRemovalRefiner);
2747 options.refiners.push(new ENMergeDateTimeRefiner);
2748 options.refiners.push(new ENExtractYearSuffixRefiner);
2749 options.refiners.push(new ENMergeDateRangeRefiner);
2750 return options;
2751 }
2752}
2753
2754// node_modules/chrono-node/dist/esm/chrono.js
2755class Chrono {
2756 parsers;
2757 refiners;
2758 defaultConfig = new ENDefaultConfiguration;
2759 constructor(configuration) {
2760 configuration = configuration || this.defaultConfig.createCasualConfiguration();
2761 this.parsers = [...configuration.parsers];
2762 this.refiners = [...configuration.refiners];
2763 }
2764 clone() {
2765 return new Chrono({
2766 parsers: [...this.parsers],
2767 refiners: [...this.refiners]
2768 });
2769 }
2770 parseDate(text, referenceDate, option) {
2771 const results = this.parse(text, referenceDate, option);
2772 return results.length > 0 ? results[0].start.date() : null;
2773 }
2774 parse(text, referenceDate, option) {
2775 const context = new ParsingContext(text, referenceDate, option);
2776 let results = [];
2777 this.parsers.forEach((parser) => {
2778 const parsedResults = Chrono.executeParser(context, parser);
2779 results = results.concat(parsedResults);
2780 });
2781 results.sort((a, b) => {
2782 return a.index - b.index;
2783 });
2784 this.refiners.forEach(function(refiner) {
2785 results = refiner.refine(context, results);
2786 });
2787 return results;
2788 }
2789 static executeParser(context, parser) {
2790 const results = [];
2791 const pattern = parser.pattern(context);
2792 const originalText = context.text;
2793 let remainingText = context.text;
2794 let match = pattern.exec(remainingText);
2795 while (match) {
2796 const index = match.index + originalText.length - remainingText.length;
2797 match.index = index;
2798 const result = parser.extract(context, match);
2799 if (!result) {
2800 remainingText = originalText.substring(match.index + 1);
2801 match = pattern.exec(remainingText);
2802 continue;
2803 }
2804 let parsedResult = null;
2805 if (result instanceof ParsingResult) {
2806 parsedResult = result;
2807 } else if (result instanceof ParsingComponents) {
2808 parsedResult = context.createParsingResult(match.index, match[0]);
2809 parsedResult.start = result;
2810 } else {
2811 parsedResult = context.createParsingResult(match.index, match[0], result);
2812 }
2813 const parsedIndex = parsedResult.index;
2814 const parsedText = parsedResult.text;
2815 context.debug(() => console.log(`${parser.constructor.name} extracted (at index=${parsedIndex}) '${parsedText}'`));
2816 results.push(parsedResult);
2817 remainingText = originalText.substring(parsedIndex + parsedText.length);
2818 match = pattern.exec(remainingText);
2819 }
2820 return results;
2821 }
2822}
2823
2824class ParsingContext {
2825 text;
2826 option;
2827 reference;
2828 refDate;
2829 constructor(text, refDate, option) {
2830 this.text = text;
2831 this.option = option ?? {};
2832 this.reference = ReferenceWithTimezone.fromInput(refDate, this.option.timezones);
2833 this.refDate = this.reference.instant;
2834 }
2835 createParsingComponents(components) {
2836 if (components instanceof ParsingComponents) {
2837 return components;
2838 }
2839 return new ParsingComponents(this.reference, components);
2840 }
2841 createParsingResult(index, textOrEndIndex, startComponents, endComponents) {
2842 const text = typeof textOrEndIndex === "string" ? textOrEndIndex : this.text.substring(index, textOrEndIndex);
2843 const start = startComponents ? this.createParsingComponents(startComponents) : null;
2844 const end = endComponents ? this.createParsingComponents(endComponents) : null;
2845 return new ParsingResult(this.reference, index, text, start, end);
2846 }
2847 debug(block) {
2848 if (this.option.debug) {
2849 if (this.option.debug instanceof Function) {
2850 this.option.debug(block);
2851 } else {
2852 const handler = this.option.debug;
2853 handler.debug(block);
2854 }
2855 }
2856 }
2857}
2858
2859// node_modules/chrono-node/dist/esm/locales/en/index.js
2860var configuration = new ENDefaultConfiguration;
2861var casual = new Chrono(configuration.createCasualConfiguration(false));
2862var strict = new Chrono(configuration.createConfiguration(true, false));
2863var GB = new Chrono(configuration.createCasualConfiguration(true));
2864
2865// node_modules/chrono-node/dist/esm/index.js
2866var casual2 = casual;
2867function parseDate(text, ref, option) {
2868 return casual2.parseDate(text, ref, option);
2869}
2870
2871// index.ts
2872import {
2873 Container,
2874 SelectList,
2875 Text,
2876 fuzzyFilter
2877} from "@earendil-works/pi-tui";
2878var DEFAULT_ORG_FILE = join(homedir(), "desktop/org/todos.org");
2879var INBOX_FILE = join(homedir(), "desktop/org/inbox.org");
2880var DEFAULT_SECTION = "Inbox";
2881function execEmacs(elisp) {
2882 try {
2883 const escaped = elisp.replace(/'/g, "'\\''");
2884 const result = execSync(`emacsclient --eval '${escaped}'`, {
2885 encoding: "utf-8",
2886 timeout: 1e4,
2887 stdio: ["pipe", "pipe", "pipe"]
2888 });
2889 let jsonStr = result.trim();
2890 if (jsonStr.startsWith('"') && jsonStr.endsWith('"')) {
2891 jsonStr = jsonStr.slice(1, -1);
2892 }
2893 jsonStr = jsonStr.replace(/\\"/g, '"');
2894 jsonStr = jsonStr.replace(/\\\\/g, "\\");
2895 return JSON.parse(jsonStr);
2896 } catch (error) {
2897 if (error.message?.includes("emacsclient") || error.status === 1) {
2898 return {
2899 success: false,
2900 error: "Emacs daemon not running. Start with: emacs --daemon"
2901 };
2902 }
2903 return {
2904 success: false,
2905 error: error.message || String(error)
2906 };
2907 }
2908}
2909function stripOrgLinks(text) {
2910 text = text.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
2911 text = text.replace(/\[\[([^\]]*)\]\]/g, "$1");
2912 return text;
2913}
2914function formatTodo(todo) {
2915 const parts = [];
2916 const state = todo.todo || "TODO";
2917 parts.push(`[${state}]`);
2918 if (todo.priority) {
2919 parts.push(`[#${todo.priority}]`);
2920 }
2921 parts.push(stripOrgLinks(todo.heading));
2922 if (todo.tags && todo.tags.length > 0) {
2923 parts.push(`:${todo.tags.join(":")}:`);
2924 }
2925 const dates = [];
2926 if (todo.scheduled) {
2927 dates.push(`SCHEDULED: ${todo.scheduled}`);
2928 }
2929 if (todo.deadline) {
2930 dates.push(`DEADLINE: ${todo.deadline}`);
2931 }
2932 if (dates.length > 0) {
2933 parts.push(`(${dates.join(", ")})`);
2934 }
2935 return parts.join(" ");
2936}
2937function formatTodoMarkdown(todo) {
2938 const parts = [];
2939 const state = todo.todo || "TODO";
2940 parts.push(`**[${state}]**`);
2941 if (todo.priority) {
2942 parts.push(`\`#${todo.priority}\``);
2943 }
2944 parts.push(stripOrgLinks(todo.heading));
2945 if (todo.tags && todo.tags.length > 0) {
2946 const tagStr = todo.tags.map((t) => `\`${t}\``).join(" ");
2947 parts.push(tagStr);
2948 }
2949 const dates = [];
2950 if (todo.scheduled) {
2951 dates.push(`\uD83D\uDCC5 ${todo.scheduled}`);
2952 }
2953 if (todo.deadline) {
2954 dates.push(`⏰ ${todo.deadline}`);
2955 }
2956 let result = parts.join(" ");
2957 if (dates.length > 0) {
2958 result += ` *(${dates.join(", ")})*`;
2959 }
2960 return result;
2961}
2962function parseNaturalDate(text) {
2963 const result = parseDate(text);
2964 if (!result)
2965 return null;
2966 const year = result.getFullYear();
2967 const month = String(result.getMonth() + 1).padStart(2, "0");
2968 const day = String(result.getDate()).padStart(2, "0");
2969 return `${year}-${month}-${day}`;
2970}
2971function parseCommandArgs(args) {
2972 let remaining = args;
2973 let section;
2974 let scheduled;
2975 let deadline;
2976 let priority;
2977 let state;
2978 const sectionMatch = remaining.match(/@(\w+)/);
2979 if (sectionMatch) {
2980 section = sectionMatch[1];
2981 remaining = remaining.replace(/@\w+/, "").trim();
2982 }
2983 const scheduledMatch = remaining.match(/scheduled:([^\s]+(?:\s+[^\s@:]+)*?)(?=\s+(?:deadline:|priority:|state:|@|$)|$)/i);
2984 if (scheduledMatch) {
2985 const dateStr = scheduledMatch[1].trim();
2986 scheduled = parseNaturalDate(dateStr) || dateStr;
2987 remaining = remaining.replace(scheduledMatch[0], "").trim();
2988 }
2989 const deadlineMatch = remaining.match(/deadline:([^\s]+(?:\s+[^\s@:]+)*?)(?=\s+(?:scheduled:|priority:|state:|@|$)|$)/i);
2990 if (deadlineMatch) {
2991 const dateStr = deadlineMatch[1].trim();
2992 deadline = parseNaturalDate(dateStr) || dateStr;
2993 remaining = remaining.replace(deadlineMatch[0], "").trim();
2994 }
2995 const priorityMatch = remaining.match(/priority:(\d)/i);
2996 if (priorityMatch) {
2997 priority = parseInt(priorityMatch[1], 10);
2998 remaining = remaining.replace(priorityMatch[0], "").trim();
2999 }
3000 const stateMatch = remaining.match(/state:(TODO|NEXT|STRT|WAIT|DONE|CANX)/i);
3001 if (stateMatch) {
3002 state = stateMatch[1].toUpperCase();
3003 remaining = remaining.replace(stateMatch[0], "").trim();
3004 }
3005 return {
3006 title: remaining.trim(),
3007 section,
3008 scheduled,
3009 deadline,
3010 priority,
3011 state
3012 };
3013}
3014function org_todos_default(pi) {
3015 const customTypes = [
3016 "org-todos",
3017 "org-todos-search",
3018 "org-todos-add",
3019 "org-todos-done",
3020 "org-todos-next",
3021 "org-todos-upcoming",
3022 "org-todos-update",
3023 "org-todos-note"
3024 ];
3025 for (const customType of customTypes) {
3026 pi.registerMessageRenderer(customType, (message, options, theme) => {
3027 let text = message.content;
3028 text = text.replace(/\[TODO\]/g, theme.bold(theme.fg("mdHeading", "[TODO]")));
3029 text = text.replace(/\[NEXT\]/g, theme.bold(theme.fg("accent", "[NEXT]")));
3030 text = text.replace(/\[STRT\]/g, theme.bold(theme.fg("mdLink", "[STRT]")));
3031 text = text.replace(/\[WAIT\]/g, theme.bold(theme.fg("muted", "[WAIT]")));
3032 text = text.replace(/\[DONE\]/g, theme.bold(theme.fg("success", "[DONE]")));
3033 text = text.replace(/\[CANX\]/g, theme.bold(theme.fg("error", "[CANX]")));
3034 const container = new Container;
3035 container.addChild(new DynamicBorder((s) => theme.fg("borderMuted", s)));
3036 container.addChild(new Text(text, 1, 1));
3037 container.addChild(new DynamicBorder((s) => theme.fg("borderMuted", s)));
3038 return container;
3039 });
3040 }
3041 pi.registerTool({
3042 name: "org_todo",
3043 label: "Org TODO",
3044 promptSnippet: "Manage org-mode TODOs. Actions: list, scheduled, upcoming, overdue, search, get, done, state, schedule, deadline, priority, add, append, inbox-list, inbox-count, inbox-add, refile-targets, refile",
3045 promptGuidelines: [
3046 "NEVER edit .org files directly — always use the org_todo tool for all TODO operations",
3047 "For scheduling, use YYYY-MM-DD date format (natural language dates are NOT supported by this tool)",
3048 "Use inbox-add for quick capture, then refile to the appropriate section later"
3049 ],
3050 description: `Manage org-mode TODOs. Actions:
3051- list: List active TODOs (TODO, NEXT, STRT)
3052- scheduled: Get today's scheduled items
3053- upcoming: Get tasks in next N days (default 7)
3054- overdue: Get overdue tasks
3055- search: Search TODOs by query
3056- get: Get full content of a TODO
3057- done: Mark TODO as DONE
3058- state: Change TODO state (TODO, NEXT, STRT, WAIT, DONE, CANX)
3059- schedule: Set scheduled date
3060- deadline: Set deadline date
3061- priority: Set priority (1-5)
3062- add: Create new TODO
3063- append: Append content to TODO
3064- inbox-list: List all inbox items
3065- inbox-count: Get count of inbox items
3066- inbox-add: Add item to inbox
3067- refile-targets: Get available refile target sections
3068- refile: Refile item from inbox to a section`,
3069 parameters: {
3070 type: "object",
3071 properties: {
3072 action: {
3073 type: "string",
3074 enum: [
3075 "list",
3076 "scheduled",
3077 "upcoming",
3078 "overdue",
3079 "search",
3080 "get",
3081 "done",
3082 "state",
3083 "schedule",
3084 "deadline",
3085 "priority",
3086 "add",
3087 "append",
3088 "sections",
3089 "statistics",
3090 "archive",
3091 "inbox-list",
3092 "inbox-count",
3093 "inbox-add",
3094 "refile-targets",
3095 "refile"
3096 ],
3097 description: "Action to perform"
3098 },
3099 heading: {
3100 type: "string",
3101 description: "TODO heading (for get, done, state, schedule, etc.)"
3102 },
3103 query: {
3104 type: "string",
3105 description: "Search query (for search action)"
3106 },
3107 section: {
3108 type: "string",
3109 description: "Section name (for add action or by-section filter)"
3110 },
3111 state: {
3112 type: "string",
3113 enum: ["TODO", "NEXT", "STRT", "WAIT", "DONE", "CANX"],
3114 description: "TODO state (for state action)"
3115 },
3116 date: {
3117 type: "string",
3118 description: "Date in YYYY-MM-DD format (for schedule/deadline)"
3119 },
3120 days: {
3121 type: "number",
3122 description: "Number of days (for upcoming action, default 7)"
3123 },
3124 priority: {
3125 type: "number",
3126 description: "Priority 1-5 (1=highest)"
3127 },
3128 content: {
3129 type: "string",
3130 description: "Content to append (org-mode format)"
3131 },
3132 tags: {
3133 type: "array",
3134 items: { type: "string" },
3135 description: "Tags for new TODO"
3136 }
3137 },
3138 required: ["action"]
3139 },
3140 execute: async (toolCallId, params, signal, onUpdate, ctx) => {
3141 const { action, heading, query, section, state, date, days, priority, content, tags } = params;
3142 let elisp;
3143 switch (action) {
3144 case "list":
3145 if (section) {
3146 elisp = `(pi/org-todo-by-section "${section}")`;
3147 } else {
3148 elisp = "(pi/org-todo-list)";
3149 }
3150 break;
3151 case "scheduled":
3152 elisp = `(pi/org-todo-scheduled nil "${date || "today"}")`;
3153 break;
3154 case "upcoming":
3155 elisp = `(pi/org-todo-upcoming nil ${days || 7})`;
3156 break;
3157 case "overdue":
3158 elisp = "(pi/org-todo-overdue)";
3159 break;
3160 case "search":
3161 if (!query) {
3162 return {
3163 content: [{ type: "text", text: "Error: query is required for search action" }]
3164 };
3165 }
3166 elisp = `(pi/org-todo-search "${query.replace(/"/g, "\\\"")}")`;
3167 break;
3168 case "get":
3169 if (!heading) {
3170 return {
3171 content: [{ type: "text", text: "Error: heading is required for get action" }]
3172 };
3173 }
3174 elisp = `(pi/org-todo-get "${heading.replace(/"/g, "\\\"")}")`;
3175 break;
3176 case "done":
3177 if (!heading) {
3178 return {
3179 content: [{ type: "text", text: "Error: heading is required for done action" }]
3180 };
3181 }
3182 elisp = `(pi/org-todo-done "${heading.replace(/"/g, "\\\"")}")`;
3183 break;
3184 case "state":
3185 if (!heading || !state) {
3186 return {
3187 content: [{ type: "text", text: "Error: heading and state are required for state action" }]
3188 };
3189 }
3190 elisp = `(pi/org-todo-state "${heading.replace(/"/g, "\\\"")}" "${state}")`;
3191 break;
3192 case "schedule":
3193 if (!heading || !date) {
3194 return {
3195 content: [{ type: "text", text: "Error: heading and date are required for schedule action" }]
3196 };
3197 }
3198 elisp = `(pi/org-todo-schedule "${heading.replace(/"/g, "\\\"")}" "${date}")`;
3199 break;
3200 case "deadline":
3201 if (!heading || !date) {
3202 return {
3203 content: [{ type: "text", text: "Error: heading and date are required for deadline action" }]
3204 };
3205 }
3206 elisp = `(pi/org-todo-deadline "${heading.replace(/"/g, "\\\"")}" "${date}")`;
3207 break;
3208 case "priority":
3209 if (!heading || priority === undefined) {
3210 return {
3211 content: [{ type: "text", text: "Error: heading and priority are required for priority action" }]
3212 };
3213 }
3214 elisp = `(pi/org-todo-priority "${heading.replace(/"/g, "\\\"")}" ${priority})`;
3215 break;
3216 case "add":
3217 if (!heading || !section) {
3218 return {
3219 content: [{ type: "text", text: "Error: heading and section are required for add action" }]
3220 };
3221 }
3222 const schedArg = date ? `"${date}"` : "nil";
3223 const prioArg = priority !== undefined ? priority : "nil";
3224 const tagsArg = tags && tags.length > 0 ? `'(${tags.map((t) => `"${t}"`).join(" ")})` : "nil";
3225 elisp = `(pi/org-todo-add "${heading.replace(/"/g, "\\\"")}" "${section.replace(/"/g, "\\\"")}" nil ${schedArg} ${prioArg} ${tagsArg})`;
3226 if (content) {
3227 const addResult = execEmacs(elisp);
3228 if (!addResult.success) {
3229 return {
3230 content: [{ type: "text", text: `Error: ${addResult.error}` }]
3231 };
3232 }
3233 elisp = `(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${content.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`;
3234 }
3235 break;
3236 case "append":
3237 if (!heading || !content) {
3238 return {
3239 content: [{ type: "text", text: "Error: heading and content are required for append action" }]
3240 };
3241 }
3242 elisp = `(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${content.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`;
3243 break;
3244 case "sections":
3245 elisp = "(pi/org-todo-sections)";
3246 break;
3247 case "statistics":
3248 elisp = "(pi/org-todo-statistics)";
3249 break;
3250 case "archive":
3251 elisp = "(pi/org-todo-archive-done)";
3252 break;
3253 case "inbox-list":
3254 elisp = `(pi/org-todo-list "${INBOX_FILE}" "TODO,NEXT,STRT,WAIT")`;
3255 break;
3256 case "inbox-count":
3257 elisp = `(pi/org-todo-inbox-all)`;
3258 break;
3259 case "inbox-add":
3260 if (!heading) {
3261 return {
3262 content: [{ type: "text", text: "Error: heading is required for inbox-add action" }]
3263 };
3264 }
3265 const schedInbox = date ? `"${date}"` : "nil";
3266 const prioInbox = priority !== undefined ? priority : "nil";
3267 const tagsInbox = tags && tags.length > 0 ? `'(${tags.map((t) => `"${t}"`).join(" ")})` : "nil";
3268 elisp = `(pi/org-todo-add "${heading.replace(/"/g, "\\\"")}" "Inbox" "${INBOX_FILE}" ${schedInbox} ${prioInbox} ${tagsInbox})`;
3269 break;
3270 case "refile-targets":
3271 elisp = "(pi/org-todo-get-refile-targets)";
3272 break;
3273 case "refile":
3274 if (!heading || !section) {
3275 return {
3276 content: [{ type: "text", text: "Error: heading and section are required for refile action" }]
3277 };
3278 }
3279 elisp = `(pi/org-todo-refile "${heading.replace(/"/g, "\\\"")}" "${section.replace(/"/g, "\\\"")}")`;
3280 break;
3281 default:
3282 return {
3283 content: [{ type: "text", text: `Unknown action: ${action}` }]
3284 };
3285 }
3286 const result = execEmacs(elisp);
3287 if (!result.success) {
3288 return {
3289 content: [{ type: "text", text: `Error: ${result.error}` }]
3290 };
3291 }
3292 let text;
3293 if (action === "refile-targets" && Array.isArray(result.data)) {
3294 if (result.data.length === 0) {
3295 text = "No refile targets found.";
3296 } else {
3297 text = result.data.map((t) => {
3298 const indent = " ".repeat((t.level || 1) - 1);
3299 return `${indent}- ${t.section} (${t.file?.replace(/.*\//, "")})`;
3300 }).join(`
3301`);
3302 }
3303 } else if (action === "sections" && Array.isArray(result.data)) {
3304 if (result.data.length === 0) {
3305 text = "No sections found.";
3306 } else {
3307 text = result.data.map((s) => `- ${s.section || s}`).join(`
3308`);
3309 }
3310 } else if (Array.isArray(result.data)) {
3311 if (result.data.length === 0) {
3312 text = "No TODOs found.";
3313 } else {
3314 text = result.data.map(formatTodo).join(`
3315`);
3316 }
3317 } else if (typeof result.data === "object") {
3318 text = JSON.stringify(result.data, null, 2);
3319 } else {
3320 text = String(result.data);
3321 }
3322 return {
3323 content: [{ type: "text", text }]
3324 };
3325 }
3326 });
3327 pi.registerCommand("todos", {
3328 description: "Show today's tasks (scheduled + overdue + NEXT). Usage: /todos [section]",
3329 handler: async (args, ctx) => {
3330 const sectionFilter = (args || "").trim() || null;
3331 let sectionHeadings = null;
3332 if (sectionFilter) {
3333 const sectionResult = execEmacs(`(pi/org-todo-by-section "${sectionFilter.replace(/"/g, "\\\"")}")`);
3334 if (!sectionResult.success) {
3335 const sections = execEmacs("(pi/org-todo-sections)");
3336 const sectionList = sections.success && sections.data ? Array.isArray(sections.data) ? sections.data : Object.values(sections.data) : [];
3337 ctx.ui.notify(`Section "${sectionFilter}" not found. Available: ${sectionList.join(", ")}`, "error");
3338 return;
3339 }
3340 sectionHeadings = new Set((sectionResult.data || []).map((t) => t.heading));
3341 }
3342 function filterBySection(todos) {
3343 if (!sectionHeadings)
3344 return todos;
3345 return todos.filter((t) => sectionHeadings.has(t.heading));
3346 }
3347 const scheduled = execEmacs("(pi/org-todo-scheduled)");
3348 const overdue = execEmacs("(pi/org-todo-overdue)");
3349 const next = execEmacs('(pi/org-todo-list nil "NEXT")');
3350 if (!scheduled.success && !overdue.success && !next.success) {
3351 ctx.ui.notify("Failed to fetch TODOs. Is Emacs daemon running?", "error");
3352 return;
3353 }
3354 const filteredOverdue = filterBySection(overdue.success && overdue.data ? overdue.data : []);
3355 const filteredScheduled = filterBySection(scheduled.success && scheduled.data ? scheduled.data : []);
3356 const filteredNext = filterBySection(next.success && next.data ? next.data : []);
3357 const lines = [];
3358 const title = sectionFilter ? `## \uD83D\uDCCB Today's Tasks — ${sectionFilter}` : "## \uD83D\uDCCB Today's Tasks";
3359 lines.push(title);
3360 lines.push("");
3361 if (filteredOverdue.length > 0) {
3362 lines.push(`### ⚠️ Overdue (${filteredOverdue.length})`);
3363 lines.push("");
3364 for (const todo of filteredOverdue) {
3365 lines.push(`- ${formatTodoMarkdown(todo)}`);
3366 }
3367 lines.push("");
3368 }
3369 if (filteredScheduled.length > 0) {
3370 lines.push(`### \uD83D\uDCC5 Scheduled Today (${filteredScheduled.length})`);
3371 lines.push("");
3372 for (const todo of filteredScheduled) {
3373 lines.push(`- ${formatTodoMarkdown(todo)}`);
3374 }
3375 lines.push("");
3376 }
3377 if (filteredNext.length > 0) {
3378 lines.push(`### ➡️ Next Actions (${filteredNext.length})`);
3379 lines.push("");
3380 for (const todo of filteredNext) {
3381 lines.push(`- ${formatTodoMarkdown(todo)}`);
3382 }
3383 lines.push("");
3384 }
3385 const hasContent = filteredOverdue.length > 0 || filteredScheduled.length > 0 || filteredNext.length > 0;
3386 if (!hasContent) {
3387 if (sectionFilter) {
3388 lines.push(`*No tasks for today in "${sectionFilter}".* \uD83C\uDF89`);
3389 } else {
3390 lines.push("*No tasks for today.* \uD83C\uDF89");
3391 }
3392 }
3393 pi.sendMessage({
3394 customType: "org-todos",
3395 content: lines.join(`
3396`),
3397 display: true
3398 });
3399 }
3400 });
3401 pi.registerCommand("todo-search", {
3402 description: "Search TODOs. Usage: /todo-search <query>",
3403 handler: async (args, ctx) => {
3404 const query = (args || "").trim();
3405 if (!query) {
3406 ctx.ui.notify("Usage: /todo-search <query>", "error");
3407 return;
3408 }
3409 const result = execEmacs(`(pi/org-todo-search "${query.replace(/"/g, "\\\"")}" nil t)`);
3410 if (!result.success) {
3411 ctx.ui.notify(`Search failed: ${result.error}`, "error");
3412 return;
3413 }
3414 if (!result.data || result.data.length === 0) {
3415 ctx.ui.notify(`No TODOs found matching "${query}"`, "info");
3416 return;
3417 }
3418 const lines = [];
3419 lines.push(`## \uD83D\uDD0D Search: "${query}"`);
3420 lines.push("");
3421 lines.push(`*${result.data.length} result(s)*`);
3422 lines.push("");
3423 for (const todo of result.data) {
3424 const matchedIn = todo.matched_in === "heading" ? "" : " *(matched in content)*";
3425 lines.push(`- ${formatTodoMarkdown(todo)}${matchedIn}`);
3426 }
3427 pi.sendMessage({
3428 customType: "org-todos-search",
3429 content: lines.join(`
3430`),
3431 display: true
3432 });
3433 }
3434 });
3435 pi.registerCommand("todo-add", {
3436 description: "Add a new TODO. Usage: /todo-add <title> [@Section] [scheduled:date] [deadline:date] [priority:N]",
3437 handler: async (args, ctx) => {
3438 if (!args?.trim()) {
3439 ctx.ui.notify("Usage: /todo-add <title> [@Section] [scheduled:date] [deadline:date]", "error");
3440 return;
3441 }
3442 const parsed = parseCommandArgs(args);
3443 if (!parsed.title) {
3444 ctx.ui.notify("Error: TODO title is required", "error");
3445 return;
3446 }
3447 const section = parsed.section || DEFAULT_SECTION;
3448 const schedArg = parsed.scheduled ? `"${parsed.scheduled}"` : "nil";
3449 const prioArg = parsed.priority !== undefined ? parsed.priority : "nil";
3450 const sectionsResult = execEmacs("(pi/org-todo-sections)");
3451 if (sectionsResult.success) {
3452 const sections = Array.isArray(sectionsResult.data) ? sectionsResult.data : Object.values(sectionsResult.data || {});
3453 if (!sections.includes(section)) {
3454 ctx.ui.notify(`Section "${section}" not found. Available: ${sections.join(", ")}`, "error");
3455 return;
3456 }
3457 }
3458 const elisp = `(pi/org-todo-add "${parsed.title.replace(/"/g, "\\\"")}" "${section}" nil ${schedArg} ${prioArg} nil)`;
3459 const result = execEmacs(elisp);
3460 if (!result.success) {
3461 ctx.ui.notify(`Failed to add TODO: ${result.error}`, "error");
3462 return;
3463 }
3464 if (parsed.deadline) {
3465 execEmacs(`(pi/org-todo-deadline "${parsed.title.replace(/"/g, "\\\"")}" "${parsed.deadline}")`);
3466 }
3467 const lines = [];
3468 lines.push(`## ✅ TODO Added`);
3469 lines.push("");
3470 lines.push(`**${parsed.title}** added to *${section}*`);
3471 if (parsed.scheduled)
3472 lines.push(`- \uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
3473 if (parsed.deadline)
3474 lines.push(`- ⏰ Deadline: ${parsed.deadline}`);
3475 if (parsed.priority)
3476 lines.push(`- Priority: #${parsed.priority}`);
3477 pi.sendMessage({
3478 customType: "org-todos-add",
3479 content: lines.join(`
3480`),
3481 display: true
3482 });
3483 }
3484 });
3485 async function selectTodo(ctx, title, filterQuery, states) {
3486 const stateFilter = states || "TODO,NEXT,STRT,WAIT";
3487 const listResult = execEmacs(`(pi/org-todo-list nil "${stateFilter}")`);
3488 if (!listResult.success || !listResult.data || listResult.data.length === 0) {
3489 ctx.ui.notify("No active TODOs found.", "info");
3490 return null;
3491 }
3492 const items = listResult.data.map((todo2, i) => ({
3493 value: String(i),
3494 label: stripOrgLinks(formatTodo(todo2)),
3495 description: todo2.scheduled || todo2.deadline || undefined
3496 }));
3497 const filteredItems = filterQuery ? items.filter((item) => {
3498 const searchable = `${item.label} ${item.description || ""}`.toLowerCase();
3499 return filterQuery.toLowerCase().split(/\s+/).every((t) => searchable.includes(t));
3500 }) : items;
3501 if (filteredItems.length === 0) {
3502 ctx.ui.notify(`No TODOs matching "${filterQuery}"`, "info");
3503 return null;
3504 }
3505 const selectedIdx = await showSelectMenu(ctx, title, filteredItems);
3506 if (selectedIdx === null)
3507 return null;
3508 const todo = listResult.data[parseInt(selectedIdx, 10)];
3509 return {
3510 heading: todo.heading,
3511 display: stripOrgLinks(formatTodo(todo))
3512 };
3513 }
3514 pi.registerCommand("todo-done", {
3515 description: "Mark a TODO as done. Usage: /todo-done [filter] (interactive selector)",
3516 handler: async (args, ctx) => {
3517 const filter = (args || "").trim() || undefined;
3518 const selected = await selectTodo(ctx, "Mark as DONE", filter);
3519 if (!selected) {
3520 if (filter)
3521 ctx.ui.notify("Cancelled or no match.", "info");
3522 return;
3523 }
3524 const result = execEmacs(`(pi/org-todo-done "${selected.heading.replace(/"/g, "\\\"")}")`);
3525 if (!result.success) {
3526 ctx.ui.notify(`Failed: ${result.error}`, "error");
3527 return;
3528 }
3529 pi.sendMessage({
3530 customType: "org-todos-done",
3531 content: `## ✅ Done
3532
3533${selected.display}`,
3534 display: true
3535 });
3536 updateTodayStatus(ctx);
3537 }
3538 });
3539 pi.registerCommand("todo-next", {
3540 description: "Mark a TODO as NEXT (prioritized). Usage: /todo-next [filter] (interactive selector)",
3541 handler: async (args, ctx) => {
3542 const filter = (args || "").trim() || undefined;
3543 const selected = await selectTodo(ctx, "Mark as NEXT", filter, "TODO,STRT,WAIT");
3544 if (!selected) {
3545 if (filter)
3546 ctx.ui.notify("Cancelled or no match.", "info");
3547 return;
3548 }
3549 const result = execEmacs(`(pi/org-todo-state "${selected.heading.replace(/"/g, "\\\"")}" "NEXT")`);
3550 if (!result.success) {
3551 ctx.ui.notify(`Failed: ${result.error}`, "error");
3552 return;
3553 }
3554 pi.sendMessage({
3555 customType: "org-todos-next",
3556 content: `## ➡️ Prioritized
3557
3558${selected.display}`,
3559 display: true
3560 });
3561 }
3562 });
3563 pi.registerCommand("todo-upcoming", {
3564 description: "Show upcoming tasks. Usage: /todo-upcoming [days]",
3565 handler: async (args, ctx) => {
3566 const days = parseInt((args || "").trim(), 10) || 7;
3567 const result = execEmacs(`(pi/org-todo-upcoming nil ${days})`);
3568 if (!result.success) {
3569 ctx.ui.notify(`Failed: ${result.error}`, "error");
3570 return;
3571 }
3572 const lines = [];
3573 lines.push(`## \uD83D\uDCC6 Upcoming (next ${days} days)`);
3574 lines.push("");
3575 if (!result.data || result.data.length === 0) {
3576 lines.push("*No upcoming tasks* \uD83C\uDF89");
3577 } else {
3578 for (const todo of result.data) {
3579 lines.push(`- ${formatTodoMarkdown(todo)}`);
3580 }
3581 }
3582 pi.sendMessage({
3583 customType: "org-todos-upcoming",
3584 content: lines.join(`
3585`),
3586 display: true
3587 });
3588 }
3589 });
3590 pi.registerCommand("todo-update", {
3591 description: "Update a TODO. Usage: /todo-update <heading> [scheduled:date] [deadline:date] [priority:N] [state:STATE]",
3592 handler: async (args, ctx) => {
3593 if (!args?.trim()) {
3594 ctx.ui.notify("Usage: /todo-update <heading> [scheduled:date] [deadline:date] [priority:N] [state:STATE]", "error");
3595 return;
3596 }
3597 const parsed = parseCommandArgs(args);
3598 if (!parsed.title) {
3599 ctx.ui.notify("Error: TODO heading is required", "error");
3600 return;
3601 }
3602 const heading = parsed.title;
3603 const updates = [];
3604 if (parsed.scheduled) {
3605 const result = execEmacs(`(pi/org-todo-schedule "${heading.replace(/"/g, "\\\"")}" "${parsed.scheduled}")`);
3606 if (result.success)
3607 updates.push(`\uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
3608 else
3609 ctx.ui.notify(`Failed to set schedule: ${result.error}`, "warning");
3610 }
3611 if (parsed.deadline) {
3612 const result = execEmacs(`(pi/org-todo-deadline "${heading.replace(/"/g, "\\\"")}" "${parsed.deadline}")`);
3613 if (result.success)
3614 updates.push(`⏰ Deadline: ${parsed.deadline}`);
3615 else
3616 ctx.ui.notify(`Failed to set deadline: ${result.error}`, "warning");
3617 }
3618 if (parsed.priority !== undefined) {
3619 const result = execEmacs(`(pi/org-todo-priority "${heading.replace(/"/g, "\\\"")}" ${parsed.priority})`);
3620 if (result.success)
3621 updates.push(`Priority: #${parsed.priority}`);
3622 else
3623 ctx.ui.notify(`Failed to set priority: ${result.error}`, "warning");
3624 }
3625 if (parsed.state) {
3626 const result = execEmacs(`(pi/org-todo-state "${heading.replace(/"/g, "\\\"")}" "${parsed.state}")`);
3627 if (result.success)
3628 updates.push(`State: ${parsed.state}`);
3629 else
3630 ctx.ui.notify(`Failed to set state: ${result.error}`, "warning");
3631 }
3632 if (updates.length === 0) {
3633 ctx.ui.notify("No updates specified. Use scheduled:, deadline:, priority:, or state:", "warning");
3634 return;
3635 }
3636 const lines = [];
3637 lines.push(`## \uD83D\uDCDD Updated`);
3638 lines.push("");
3639 lines.push(`**${heading}**`);
3640 lines.push("");
3641 for (const update of updates) {
3642 lines.push(`- ${update}`);
3643 }
3644 pi.sendMessage({
3645 customType: "org-todos-update",
3646 content: lines.join(`
3647`),
3648 display: true
3649 });
3650 }
3651 });
3652 pi.registerCommand("todo-note", {
3653 description: "Add a note to a TODO. Usage: /todo-note <heading> <note>",
3654 handler: async (args, ctx) => {
3655 if (!args?.trim()) {
3656 ctx.ui.notify("Usage: /todo-note <heading> <note>", "error");
3657 return;
3658 }
3659 const input = args.trim();
3660 let heading;
3661 let note;
3662 const newlineIdx = input.indexOf(`
3663`);
3664 if (newlineIdx > 0) {
3665 heading = input.slice(0, newlineIdx).trim();
3666 note = input.slice(newlineIdx + 1).trim();
3667 } else {
3668 const colonIdx = input.indexOf(": ");
3669 const dashIdx = input.indexOf(" - ");
3670 if (colonIdx > 0 && colonIdx < 60) {
3671 heading = input.slice(0, colonIdx).trim();
3672 note = input.slice(colonIdx + 2).trim();
3673 } else if (dashIdx > 0 && dashIdx < 60) {
3674 heading = input.slice(0, dashIdx).trim();
3675 note = input.slice(dashIdx + 3).trim();
3676 } else {
3677 ctx.ui.notify("Could not parse heading and note. Use format: /todo-note Heading: your note here", "error");
3678 return;
3679 }
3680 }
3681 if (!heading || !note) {
3682 ctx.ui.notify("Both heading and note are required", "error");
3683 return;
3684 }
3685 const timestamp = new Date().toISOString().slice(0, 16).replace("T", " ");
3686 const orgContent = `
3687[${timestamp}] ${note}`;
3688 const result = execEmacs(`(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${orgContent.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`);
3689 if (!result.success) {
3690 ctx.ui.notify(`Failed: ${result.error}`, "error");
3691 return;
3692 }
3693 pi.sendMessage({
3694 customType: "org-todos-note",
3695 content: `## \uD83D\uDCDD Note Added
3696
3697**${heading}**
3698
3699> ${note}`,
3700 display: true
3701 });
3702 }
3703 });
3704 pi.registerCommand("inbox", {
3705 description: "View all inbox items (TODOs and links)",
3706 handler: async (args, ctx) => {
3707 const result = execEmacs(`(pi/org-todo-inbox-all)`);
3708 if (!result.success) {
3709 ctx.ui.notify(`Failed to fetch inbox: ${result.error}`, "error");
3710 return;
3711 }
3712 const todos = result.data?.filter((item) => item.todo) || [];
3713 const links = result.data?.filter((item) => !item.todo) || [];
3714 const lines = [];
3715 lines.push("## \uD83D\uDCE5 Inbox");
3716 lines.push("");
3717 if (!result.data || result.data.length === 0) {
3718 lines.push("*Inbox is empty* ✨");
3719 } else {
3720 lines.push(`*${result.data.length} item(s)* (${todos.length} tasks, ${links.length} links/notes)`);
3721 lines.push("");
3722 if (todos.length > 0) {
3723 lines.push("### ✅ Tasks");
3724 lines.push("");
3725 for (const todo of todos) {
3726 lines.push(`- ${formatTodoMarkdown(todo)}`);
3727 }
3728 lines.push("");
3729 }
3730 if (links.length > 0) {
3731 lines.push("### \uD83D\uDD17 Links & Notes");
3732 lines.push("");
3733 for (const item of links) {
3734 lines.push(`- ${stripOrgLinks(item.heading)}`);
3735 }
3736 }
3737 }
3738 pi.sendMessage({
3739 customType: "org-todos",
3740 content: lines.join(`
3741`),
3742 display: true
3743 });
3744 }
3745 });
3746 pi.registerCommand("inbox-add", {
3747 description: "Quick capture to inbox. Usage: /inbox-add <title> [scheduled:date] [priority:N]",
3748 handler: async (args, ctx) => {
3749 if (!args?.trim()) {
3750 ctx.ui.notify("Usage: /inbox-add <title> [scheduled:date] [priority:N]", "error");
3751 return;
3752 }
3753 const parsed = parseCommandArgs(args);
3754 if (!parsed.title) {
3755 ctx.ui.notify("Error: TODO title is required", "error");
3756 return;
3757 }
3758 const schedArg = parsed.scheduled ? `"${parsed.scheduled}"` : "nil";
3759 const prioArg = parsed.priority !== undefined ? parsed.priority : "nil";
3760 const elisp = `(with-current-buffer (find-file-noselect "${INBOX_FILE}")
3761 (goto-char (point-max))
3762 (insert "\\n* TODO ${parsed.title.replace(/"/g, "\\\"")}")
3763 ${parsed.scheduled ? `(org-schedule nil "${parsed.scheduled}")` : ""}
3764 ${parsed.priority !== undefined ? `(org-priority ${parsed.priority})` : ""}
3765 (save-buffer)
3766 (kill-buffer)
3767 (json-encode (list (cons 'success t))))`;
3768 const result = execEmacs(elisp);
3769 if (!result.success) {
3770 ctx.ui.notify(`Failed to add to inbox: ${result.error}`, "error");
3771 return;
3772 }
3773 const lines = [];
3774 lines.push(`## \uD83D\uDCE5 Added to Inbox`);
3775 lines.push("");
3776 lines.push(`**${parsed.title}**`);
3777 if (parsed.scheduled)
3778 lines.push(`- \uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
3779 if (parsed.priority)
3780 lines.push(`- Priority: #${parsed.priority}`);
3781 pi.sendMessage({
3782 customType: "org-todos-add",
3783 content: lines.join(`
3784`),
3785 display: true
3786 });
3787 updateInboxStatus(ctx);
3788 if (parsed.scheduled) {
3789 updateTodayStatus(ctx);
3790 }
3791 }
3792 });
3793 function fuzzyMatch(item, query) {
3794 if (!query)
3795 return true;
3796 const searchable = `${item.label} ${item.description || ""}`.toLowerCase();
3797 const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
3798 return terms.every((term) => searchable.includes(term));
3799 }
3800 async function showSelectMenu(ctx, title, allItems) {
3801 return ctx.ui.custom((tui, theme, _kb, done) => {
3802 let searchQuery = "";
3803 function getFilteredItems() {
3804 if (!searchQuery)
3805 return allItems;
3806 return allItems.filter((item) => fuzzyMatch(item, searchQuery));
3807 }
3808 let currentItems = getFilteredItems();
3809 const container = new Container;
3810 container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
3811 const headerText = new Text("", 0, 0);
3812 function updateHeader() {
3813 const titleStr = theme.fg("accent", theme.bold(title));
3814 if (searchQuery) {
3815 headerText.setText(`${titleStr} ${theme.fg("warning", `filter: ${searchQuery}`)}`);
3816 } else {
3817 headerText.setText(titleStr);
3818 }
3819 }
3820 updateHeader();
3821 container.addChild(headerText);
3822 const listTheme = {
3823 selectedPrefix: (text) => theme.fg("accent", text),
3824 selectedText: (text) => theme.fg("accent", text),
3825 description: (text) => theme.fg("muted", text),
3826 scrollInfo: (text) => theme.fg("dim", text),
3827 noMatch: (text) => theme.fg("warning", text)
3828 };
3829 let selectList = new SelectList(currentItems, Math.min(currentItems.length, 15), listTheme);
3830 selectList.onSelect = (item) => done(item.value);
3831 selectList.onCancel = () => done(null);
3832 container.addChild(selectList);
3833 container.addChild(new Text(theme.fg("dim", "Type to filter · enter to confirm · esc to cancel")));
3834 container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
3835 function rebuildList() {
3836 currentItems = getFilteredItems();
3837 const newList = new SelectList(currentItems, Math.min(currentItems.length, 15), listTheme);
3838 newList.onSelect = (item) => done(item.value);
3839 newList.onCancel = () => done(null);
3840 const idx = container.children.indexOf(selectList);
3841 if (idx !== -1)
3842 container.children[idx] = newList;
3843 selectList = newList;
3844 updateHeader();
3845 }
3846 return {
3847 render(width) {
3848 return container.render(width);
3849 },
3850 invalidate() {
3851 container.invalidate();
3852 },
3853 handleInput(data) {
3854 if (data === "" || data === "\b") {
3855 if (searchQuery.length > 0) {
3856 searchQuery = searchQuery.slice(0, -1);
3857 rebuildList();
3858 tui.requestRender();
3859 }
3860 return;
3861 }
3862 if (data.length === 1 && data >= " " && data <= "~") {
3863 searchQuery += data;
3864 rebuildList();
3865 tui.requestRender();
3866 return;
3867 }
3868 selectList.handleInput(data);
3869 tui.requestRender();
3870 }
3871 };
3872 });
3873 }
3874 pi.registerCommand("inbox-refile", {
3875 description: "Refile inbox item to a section (interactive)",
3876 handler: async (args, ctx) => {
3877 const inboxResult = execEmacs(`(pi/org-todo-inbox-all)`);
3878 if (!inboxResult.success || !inboxResult.data || inboxResult.data.length === 0) {
3879 ctx.ui.notify("Inbox is empty!", "info");
3880 return;
3881 }
3882 let heading = (args || "").trim();
3883 let sourcePosition = null;
3884 if (!heading) {
3885 const inboxItems = inboxResult.data.map((item, i) => {
3886 const prefix = item.todo ? `[${item.todo}] ` : "";
3887 const label = stripOrgLinks(`${prefix}${item.heading}`);
3888 return {
3889 value: String(i),
3890 label,
3891 description: item.todo ? undefined : "link/note"
3892 };
3893 });
3894 const selectedIdx = await showSelectMenu(ctx, "Select inbox item to refile", inboxItems);
3895 if (selectedIdx === null) {
3896 ctx.ui.notify("Refile cancelled", "info");
3897 return;
3898 }
3899 const sourceItem = inboxResult.data[parseInt(selectedIdx, 10)];
3900 heading = sourceItem.heading;
3901 sourcePosition = sourceItem.position;
3902 }
3903 const targetsResult = execEmacs("(pi/org-todo-get-refile-targets)");
3904 if (!targetsResult.success || !targetsResult.data) {
3905 ctx.ui.notify("Failed to get refile targets", "error");
3906 return;
3907 }
3908 const sectionItems = targetsResult.data.map((t, i) => {
3909 const indent = t.level > 1 ? " ".repeat(t.level - 1) : "";
3910 return {
3911 value: String(i),
3912 label: `${indent}${stripOrgLinks(t.section)}`,
3913 description: t.level > 1 ? stripOrgLinks(t.path) : undefined
3914 };
3915 });
3916 const displayHeading = stripOrgLinks(heading).slice(0, 60);
3917 const targetIdx = await showSelectMenu(ctx, `Refile "${displayHeading}" to:`, sectionItems);
3918 if (targetIdx === null) {
3919 ctx.ui.notify("Refile cancelled", "info");
3920 return;
3921 }
3922 const target = targetsResult.data[parseInt(targetIdx, 10)];
3923 const srcPosArg = sourcePosition ? ` ${sourcePosition}` : "";
3924 const refileResult = execEmacs(`(pi/org-todo-refile "${heading.replace(/"/g, "\\\"")}" "${target.section.replace(/"/g, "\\\"")}" nil nil ${target.position}${srcPosArg})`);
3925 if (!refileResult.success) {
3926 ctx.ui.notify(`Refile failed: ${refileResult.error}`, "error");
3927 return;
3928 }
3929 const displayTarget = stripOrgLinks(target.path || target.section);
3930 pi.sendMessage({
3931 customType: "org-todos",
3932 content: `## ✅ Refiled
3933
3934**${displayHeading}** → *${displayTarget}*`,
3935 display: true
3936 });
3937 updateInboxStatus(ctx);
3938 }
3939 });
3940 function updateInboxStatus(ctx) {
3941 try {
3942 const result = execEmacs(`(pi/org-todo-inbox-all)`);
3943 if (result.success && Array.isArray(result.data)) {
3944 const count = result.data.length;
3945 if (count > 0) {
3946 ctx.ui.setStatus("inbox-count", ctx.ui.theme.fg("warning", `\uD83D\uDCE5 ${count}`));
3947 } else {
3948 ctx.ui.setStatus("inbox-count", undefined);
3949 }
3950 }
3951 } catch (e) {}
3952 }
3953 function updateTodayStatus(ctx) {
3954 try {
3955 const scheduledResult = execEmacs("(pi/org-todo-scheduled)");
3956 const overdueResult = execEmacs("(pi/org-todo-overdue)");
3957 if (scheduledResult.success && overdueResult.success) {
3958 const schedCount = Array.isArray(scheduledResult.data) ? scheduledResult.data.length : 0;
3959 const overdueCount = Array.isArray(overdueResult.data) ? overdueResult.data.length : 0;
3960 if (schedCount === 0 && overdueCount === 0) {
3961 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("success", "✓"));
3962 } else if (overdueCount > 0 && schedCount > 0) {
3963 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("error", `⚠️ ${overdueCount}`) + " " + ctx.ui.theme.fg("accent", `\uD83D\uDCC5 ${schedCount}`));
3964 } else if (overdueCount > 0) {
3965 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("error", `⚠️ ${overdueCount}`));
3966 } else {
3967 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("accent", `\uD83D\uDCC5 ${schedCount}`));
3968 }
3969 }
3970 } catch (e) {}
3971 }
3972 pi.on("session_start", async (_event, ctx) => {
3973 updateInboxStatus(ctx);
3974 updateTodayStatus(ctx);
3975 setupTodoAutocomplete(ctx);
3976 const updateInterval = setInterval(() => {
3977 updateInboxStatus(ctx);
3978 updateTodayStatus(ctx);
3979 }, 5 * 60 * 1000);
3980 updateInterval.unref();
3981 pi.on("session_end", async () => {
3982 clearInterval(updateInterval);
3983 });
3984 });
3985}
3986var TODO_MAX_SUGGESTIONS = 20;
3987function extractTodoToken(textBeforeCursor) {
3988 const match = textBeforeCursor.match(/(?:^|[ \t])t:([^\s]*)$/);
3989 return match?.[1];
3990}
3991function formatTodoItem(item) {
3992 return {
3993 value: item.heading,
3994 label: item.heading,
3995 description: `[${item.todo}]`
3996 };
3997}
3998function filterTodoItems(items, query) {
3999 if (!query.trim()) {
4000 return items.slice(0, TODO_MAX_SUGGESTIONS).map(formatTodoItem);
4001 }
4002 return fuzzyFilter(items, query, (item) => `${item.todo} ${item.heading}`).slice(0, TODO_MAX_SUGGESTIONS).map(formatTodoItem);
4003}
4004function createTodoAutocompleteProvider(current, getItems) {
4005 return {
4006 async getSuggestions(lines, cursorLine, cursorCol, options) {
4007 const currentLine = lines[cursorLine] ?? "";
4008 const textBeforeCursor = currentLine.slice(0, cursorCol);
4009 const query = extractTodoToken(textBeforeCursor);
4010 if (query === undefined) {
4011 return current.getSuggestions(lines, cursorLine, cursorCol, options);
4012 }
4013 const items = await getItems();
4014 if (options.signal.aborted || !items || items.length === 0) {
4015 return current.getSuggestions(lines, cursorLine, cursorCol, options);
4016 }
4017 const suggestions = filterTodoItems(items, query);
4018 if (suggestions.length === 0) {
4019 return current.getSuggestions(lines, cursorLine, cursorCol, options);
4020 }
4021 return { items: suggestions, prefix: `t:${query}` };
4022 },
4023 applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
4024 if (prefix.startsWith("t:")) {
4025 const currentLine = lines[cursorLine] || "";
4026 const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
4027 const afterCursor = currentLine.slice(cursorCol);
4028 const value = item.value.includes(" ") ? `"${item.value}"` : item.value;
4029 const newLine = beforePrefix + value + " " + afterCursor;
4030 const newLines = [...lines];
4031 newLines[cursorLine] = newLine;
4032 return {
4033 lines: newLines,
4034 cursorLine,
4035 cursorCol: beforePrefix.length + value.length + 1
4036 };
4037 }
4038 return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
4039 },
4040 shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
4041 return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
4042 }
4043 };
4044}
4045function setupTodoAutocomplete(ctx) {
4046 let itemsPromise;
4047 const getItems = async () => {
4048 itemsPromise ||= (async () => {
4049 const result = execEmacs("(pi/org-todo-list)");
4050 if (!result.success || !Array.isArray(result.data))
4051 return;
4052 return result.data.map((item) => ({
4053 heading: item.heading,
4054 todo: item.todo || "TODO"
4055 }));
4056 })();
4057 return itemsPromise;
4058 };
4059 getItems();
4060 ctx.ui.addAutocompleteProvider((current) => createTodoAutocompleteProvider(current, getItems));
4061}
4062export {
4063 org_todos_default as default
4064};