main
1// index.ts
2import { DynamicBorder } from "@mariozechner/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 || date.getTime() < 0) {
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-2][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 if (timeComponent.isCertain("meridiem")) {
1817 dateTimeComponent.assign("meridiem", timeComponent.get("meridiem"));
1818 } else if (timeComponent.get("meridiem") != null && dateTimeComponent.get("meridiem") == null) {
1819 dateTimeComponent.imply("meridiem", timeComponent.get("meridiem"));
1820 }
1821 if (dateTimeComponent.get("meridiem") == Meridiem.PM && dateTimeComponent.get("hour") < 12) {
1822 if (timeComponent.isCertain("hour")) {
1823 dateTimeComponent.assign("hour", dateTimeComponent.get("hour") + 12);
1824 } else {
1825 dateTimeComponent.imply("hour", dateTimeComponent.get("hour") + 12);
1826 }
1827 }
1828 dateTimeComponent.addTags(dateComponent.tags());
1829 dateTimeComponent.addTags(timeComponent.tags());
1830 return dateTimeComponent;
1831}
1832
1833// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateTimeRefiner.js
1834class AbstractMergeDateTimeRefiner extends MergingRefiner {
1835 shouldMergeResults(textBetween, currentResult, nextResult) {
1836 return (currentResult.start.isOnlyDate() && nextResult.start.isOnlyTime() || nextResult.start.isOnlyDate() && currentResult.start.isOnlyTime()) && textBetween.match(this.patternBetween()) != null;
1837 }
1838 mergeResults(textBetween, currentResult, nextResult) {
1839 const result = currentResult.start.isOnlyDate() ? mergeDateTimeResult(currentResult, nextResult) : mergeDateTimeResult(nextResult, currentResult);
1840 result.index = currentResult.index;
1841 result.text = currentResult.text + textBetween + nextResult.text;
1842 return result;
1843 }
1844}
1845
1846// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateTimeRefiner.js
1847class ENMergeDateTimeRefiner extends AbstractMergeDateTimeRefiner {
1848 patternBetween() {
1849 return new RegExp("^\\s*(T|at|after|before|on|of|,|-|\\.|∙|:)?\\s*$");
1850 }
1851}
1852
1853// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js
1854var TIMEZONE_NAME_PATTERN = new RegExp("^\\s*,?\\s*\\(?([A-Z]{2,4})\\)?(?=\\W|$)", "i");
1855
1856class ExtractTimezoneAbbrRefiner {
1857 timezoneOverrides;
1858 constructor(timezoneOverrides) {
1859 this.timezoneOverrides = timezoneOverrides;
1860 }
1861 refine(context, results) {
1862 const timezoneOverrides = context.option.timezones ?? {};
1863 results.forEach((result) => {
1864 const suffix = context.text.substring(result.index + result.text.length);
1865 const match = TIMEZONE_NAME_PATTERN.exec(suffix);
1866 if (!match) {
1867 return;
1868 }
1869 const timezoneAbbr = match[1].toUpperCase();
1870 const refDate = result.start.date() ?? result.refDate ?? new Date;
1871 const tzOverrides = { ...this.timezoneOverrides, ...timezoneOverrides };
1872 const extractedTimezoneOffset = toTimezoneOffset(timezoneAbbr, refDate, tzOverrides);
1873 if (extractedTimezoneOffset == null) {
1874 return;
1875 }
1876 context.debug(() => {
1877 console.log(`Extracting timezone: '${timezoneAbbr}' into: ${extractedTimezoneOffset} for: ${result.start}`);
1878 });
1879 const currentTimezoneOffset = result.start.get("timezoneOffset");
1880 if (currentTimezoneOffset !== null && extractedTimezoneOffset != currentTimezoneOffset) {
1881 if (result.start.isCertain("timezoneOffset")) {
1882 return;
1883 }
1884 if (timezoneAbbr != match[1]) {
1885 return;
1886 }
1887 }
1888 if (result.start.isOnlyDate()) {
1889 if (timezoneAbbr != match[1]) {
1890 return;
1891 }
1892 }
1893 result.text += match[0];
1894 if (!result.start.isCertain("timezoneOffset")) {
1895 result.start.assign("timezoneOffset", extractedTimezoneOffset);
1896 }
1897 if (result.end != null && !result.end.isCertain("timezoneOffset")) {
1898 result.end.assign("timezoneOffset", extractedTimezoneOffset);
1899 }
1900 });
1901 return results;
1902 }
1903}
1904
1905// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneOffsetRefiner.js
1906var TIMEZONE_OFFSET_PATTERN = new RegExp("^\\s*(?:\\(?(?:GMT|UTC)\\s?)?([+-])(\\d{1,2})(?::?(\\d{2}))?\\)?", "i");
1907var TIMEZONE_OFFSET_SIGN_GROUP = 1;
1908var TIMEZONE_OFFSET_HOUR_OFFSET_GROUP = 2;
1909var TIMEZONE_OFFSET_MINUTE_OFFSET_GROUP = 3;
1910
1911class ExtractTimezoneOffsetRefiner {
1912 refine(context, results) {
1913 results.forEach(function(result) {
1914 if (result.start.isCertain("timezoneOffset")) {
1915 return;
1916 }
1917 const suffix = context.text.substring(result.index + result.text.length);
1918 const match = TIMEZONE_OFFSET_PATTERN.exec(suffix);
1919 if (!match) {
1920 return;
1921 }
1922 context.debug(() => {
1923 console.log(`Extracting timezone: '${match[0]}' into : ${result}`);
1924 });
1925 const hourOffset = parseInt(match[TIMEZONE_OFFSET_HOUR_OFFSET_GROUP]);
1926 const minuteOffset = parseInt(match[TIMEZONE_OFFSET_MINUTE_OFFSET_GROUP] || "0");
1927 let timezoneOffset = hourOffset * 60 + minuteOffset;
1928 if (timezoneOffset > 14 * 60) {
1929 return;
1930 }
1931 if (match[TIMEZONE_OFFSET_SIGN_GROUP] === "-") {
1932 timezoneOffset = -timezoneOffset;
1933 }
1934 if (result.end != null) {
1935 result.end.assign("timezoneOffset", timezoneOffset);
1936 }
1937 result.start.assign("timezoneOffset", timezoneOffset);
1938 result.text += match[0];
1939 });
1940 return results;
1941 }
1942}
1943
1944// node_modules/chrono-node/dist/esm/common/refiners/OverlapRemovalRefiner.js
1945class OverlapRemovalRefiner {
1946 refine(context, results) {
1947 if (results.length < 2) {
1948 return results;
1949 }
1950 const filteredResults = [];
1951 let prevResult = results[0];
1952 for (let i = 1;i < results.length; i++) {
1953 const result = results[i];
1954 if (result.index >= prevResult.index + prevResult.text.length) {
1955 filteredResults.push(prevResult);
1956 prevResult = result;
1957 continue;
1958 }
1959 let kept = null;
1960 let removed = null;
1961 if (result.text.length > prevResult.text.length) {
1962 kept = result;
1963 removed = prevResult;
1964 } else {
1965 kept = prevResult;
1966 removed = result;
1967 }
1968 context.debug(() => {
1969 console.log(`${this.constructor.name} remove ${removed} by ${kept}`);
1970 });
1971 prevResult = kept;
1972 }
1973 if (prevResult != null) {
1974 filteredResults.push(prevResult);
1975 }
1976 return filteredResults;
1977 }
1978}
1979
1980// node_modules/chrono-node/dist/esm/common/refiners/ForwardDateRefiner.js
1981class ForwardDateRefiner {
1982 refine(context, results) {
1983 if (!context.option.forwardDate) {
1984 return results;
1985 }
1986 results.forEach((result) => {
1987 let refDate = context.reference.getDateWithAdjustedTimezone();
1988 if (result.start.isOnlyTime() && context.reference.instant > result.start.date()) {
1989 const refDate2 = context.reference.getDateWithAdjustedTimezone();
1990 const refFollowingDay = new Date(refDate2);
1991 refFollowingDay.setDate(refFollowingDay.getDate() + 1);
1992 implySimilarDate(result.start, refFollowingDay);
1993 context.debug(() => {
1994 console.log(`${this.constructor.name} adjusted ${result} time from the ref date (${refDate2}) to the following day (${refFollowingDay})`);
1995 });
1996 if (result.end && result.end.isOnlyTime()) {
1997 implySimilarDate(result.end, refFollowingDay);
1998 if (result.start.date() > result.end.date()) {
1999 refFollowingDay.setDate(refFollowingDay.getDate() + 1);
2000 implySimilarDate(result.end, refFollowingDay);
2001 }
2002 }
2003 }
2004 if (result.start.isOnlyWeekdayComponent() && refDate > result.start.date()) {
2005 let daysToAdd = result.start.get("weekday") - refDate.getDay();
2006 if (daysToAdd <= 0) {
2007 daysToAdd += 7;
2008 }
2009 refDate = addDuration(refDate, { day: daysToAdd });
2010 implySimilarDate(result.start, refDate);
2011 context.debug(() => {
2012 console.log(`${this.constructor.name} adjusted ${result} weekday (${result.start})`);
2013 });
2014 if (result.end && result.end.isOnlyWeekdayComponent()) {
2015 let daysToAdd2 = result.end.get("weekday") - refDate.getDay();
2016 if (daysToAdd2 <= 0) {
2017 daysToAdd2 += 7;
2018 }
2019 refDate = addDuration(refDate, { day: daysToAdd2 });
2020 implySimilarDate(result.end, refDate);
2021 context.debug(() => {
2022 console.log(`${this.constructor.name} adjusted ${result} weekday (${result.end})`);
2023 });
2024 }
2025 }
2026 if (result.start.isDateWithUnknownYear() && refDate > result.start.date()) {
2027 for (let i = 0;i < 3 && refDate > result.start.date(); i++) {
2028 result.start.imply("year", result.start.get("year") + 1);
2029 context.debug(() => {
2030 console.log(`${this.constructor.name} adjusted ${result} year (${result.start})`);
2031 });
2032 if (result.end && !result.end.isCertain("year")) {
2033 result.end.imply("year", result.end.get("year") + 1);
2034 context.debug(() => {
2035 console.log(`${this.constructor.name} adjusted ${result} month (${result.start})`);
2036 });
2037 }
2038 }
2039 }
2040 });
2041 return results;
2042 }
2043}
2044
2045// node_modules/chrono-node/dist/esm/common/refiners/UnlikelyFormatFilter.js
2046class UnlikelyFormatFilter extends Filter {
2047 strictMode;
2048 constructor(strictMode) {
2049 super();
2050 this.strictMode = strictMode;
2051 }
2052 isValid(context, result) {
2053 if (result.text.replace(" ", "").match(/^\d*(\.\d*)?$/)) {
2054 context.debug(() => {
2055 console.log(`Removing unlikely result '${result.text}'`);
2056 });
2057 return false;
2058 }
2059 if (!result.start.isValidDate()) {
2060 context.debug(() => {
2061 console.log(`Removing invalid result: ${result} (${result.start})`);
2062 });
2063 return false;
2064 }
2065 if (result.end && !result.end.isValidDate()) {
2066 context.debug(() => {
2067 console.log(`Removing invalid result: ${result} (${result.end})`);
2068 });
2069 return false;
2070 }
2071 if (this.strictMode) {
2072 return this.isStrictModeValid(context, result);
2073 }
2074 return true;
2075 }
2076 isStrictModeValid(context, result) {
2077 if (result.start.isOnlyWeekdayComponent()) {
2078 context.debug(() => {
2079 console.log(`(Strict) Removing weekday only component: ${result} (${result.end})`);
2080 });
2081 return false;
2082 }
2083 return true;
2084 }
2085}
2086
2087// node_modules/chrono-node/dist/esm/common/parsers/ISOFormatParser.js
2088var 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");
2089var YEAR_NUMBER_GROUP2 = 1;
2090var MONTH_NUMBER_GROUP2 = 2;
2091var DATE_NUMBER_GROUP2 = 3;
2092var HOUR_NUMBER_GROUP = 4;
2093var MINUTE_NUMBER_GROUP = 5;
2094var SECOND_NUMBER_GROUP = 6;
2095var MILLISECOND_NUMBER_GROUP = 7;
2096var TZD_GROUP = 8;
2097var TZD_HOUR_OFFSET_GROUP = 9;
2098var TZD_MINUTE_OFFSET_GROUP = 10;
2099
2100class ISOFormatParser extends AbstractParserWithWordBoundaryChecking {
2101 innerPattern() {
2102 return PATTERN8;
2103 }
2104 innerExtract(context, match) {
2105 const components = context.createParsingComponents({
2106 year: parseInt(match[YEAR_NUMBER_GROUP2]),
2107 month: parseInt(match[MONTH_NUMBER_GROUP2]),
2108 day: parseInt(match[DATE_NUMBER_GROUP2])
2109 });
2110 if (match[HOUR_NUMBER_GROUP] != null) {
2111 components.assign("hour", parseInt(match[HOUR_NUMBER_GROUP]));
2112 components.assign("minute", parseInt(match[MINUTE_NUMBER_GROUP]));
2113 if (match[SECOND_NUMBER_GROUP] != null) {
2114 components.assign("second", parseInt(match[SECOND_NUMBER_GROUP]));
2115 }
2116 if (match[MILLISECOND_NUMBER_GROUP] != null) {
2117 components.assign("millisecond", parseInt(match[MILLISECOND_NUMBER_GROUP]));
2118 }
2119 if (match[TZD_GROUP] != null) {
2120 let offset = 0;
2121 if (match[TZD_HOUR_OFFSET_GROUP]) {
2122 const hourOffset = parseInt(match[TZD_HOUR_OFFSET_GROUP]);
2123 let minuteOffset = 0;
2124 if (match[TZD_MINUTE_OFFSET_GROUP] != null) {
2125 minuteOffset = parseInt(match[TZD_MINUTE_OFFSET_GROUP]);
2126 }
2127 offset = hourOffset * 60;
2128 if (offset < 0) {
2129 offset -= minuteOffset;
2130 } else {
2131 offset += minuteOffset;
2132 }
2133 }
2134 components.assign("timezoneOffset", offset);
2135 }
2136 }
2137 return components.addTag("parser/ISOFormatParser");
2138 }
2139}
2140
2141// node_modules/chrono-node/dist/esm/common/refiners/MergeWeekdayComponentRefiner.js
2142class MergeWeekdayComponentRefiner extends MergingRefiner {
2143 mergeResults(textBetween, currentResult, nextResult) {
2144 const newResult = nextResult.clone();
2145 newResult.index = currentResult.index;
2146 newResult.text = currentResult.text + textBetween + newResult.text;
2147 newResult.start.assign("weekday", currentResult.start.get("weekday"));
2148 if (newResult.end) {
2149 newResult.end.assign("weekday", currentResult.start.get("weekday"));
2150 }
2151 return newResult;
2152 }
2153 shouldMergeResults(textBetween, currentResult, nextResult) {
2154 const weekdayThenNormalDate = currentResult.start.isOnlyWeekdayComponent() && !currentResult.start.isCertain("hour") && nextResult.start.isCertain("day");
2155 return weekdayThenNormalDate && textBetween.match(/^,?\s*$/) != null;
2156 }
2157}
2158
2159// node_modules/chrono-node/dist/esm/configurations.js
2160function includeCommonConfiguration(configuration, strictMode = false) {
2161 configuration.parsers.unshift(new ISOFormatParser);
2162 configuration.refiners.unshift(new MergeWeekdayComponentRefiner);
2163 configuration.refiners.unshift(new ExtractTimezoneOffsetRefiner);
2164 configuration.refiners.unshift(new OverlapRemovalRefiner);
2165 configuration.refiners.push(new ExtractTimezoneAbbrRefiner);
2166 configuration.refiners.push(new OverlapRemovalRefiner);
2167 configuration.refiners.push(new ForwardDateRefiner);
2168 configuration.refiners.push(new UnlikelyFormatFilter(strictMode));
2169 return configuration;
2170}
2171
2172// node_modules/chrono-node/dist/esm/common/casualReferences.js
2173function now(reference) {
2174 const targetDate = reference.getDateWithAdjustedTimezone();
2175 const component = new ParsingComponents(reference, {});
2176 assignSimilarDate(component, targetDate);
2177 assignSimilarTime(component, targetDate);
2178 component.assign("timezoneOffset", reference.getTimezoneOffset());
2179 component.addTag("casualReference/now");
2180 return component;
2181}
2182function today(reference) {
2183 const targetDate = reference.getDateWithAdjustedTimezone();
2184 const component = new ParsingComponents(reference, {});
2185 assignSimilarDate(component, targetDate);
2186 implySimilarTime(component, targetDate);
2187 component.delete("meridiem");
2188 component.addTag("casualReference/today");
2189 return component;
2190}
2191function yesterday(reference) {
2192 return theDayBefore(reference, 1).addTag("casualReference/yesterday");
2193}
2194function tomorrow(reference) {
2195 return theDayAfter(reference, 1).addTag("casualReference/tomorrow");
2196}
2197function theDayBefore(reference, numDay) {
2198 return theDayAfter(reference, -numDay);
2199}
2200function theDayAfter(reference, nDays) {
2201 const targetDate = reference.getDateWithAdjustedTimezone();
2202 const component = new ParsingComponents(reference, {});
2203 const newDate = new Date(targetDate.getTime());
2204 newDate.setDate(newDate.getDate() + nDays);
2205 assignSimilarDate(component, newDate);
2206 implySimilarTime(component, newDate);
2207 component.delete("meridiem");
2208 return component;
2209}
2210function tonight(reference, implyHour = 22) {
2211 const targetDate = reference.getDateWithAdjustedTimezone();
2212 const component = new ParsingComponents(reference, {});
2213 assignSimilarDate(component, targetDate);
2214 component.imply("hour", implyHour);
2215 component.imply("meridiem", Meridiem.PM);
2216 component.addTag("casualReference/tonight");
2217 return component;
2218}
2219function evening(reference, implyHour = 20) {
2220 const component = new ParsingComponents(reference, {});
2221 component.imply("meridiem", Meridiem.PM);
2222 component.imply("hour", implyHour);
2223 component.addTag("casualReference/evening");
2224 return component;
2225}
2226function midnight(reference) {
2227 const component = new ParsingComponents(reference, {});
2228 if (reference.getDateWithAdjustedTimezone().getHours() > 2) {
2229 component.addDurationAsImplied({ day: 1 });
2230 }
2231 component.assign("hour", 0);
2232 component.imply("minute", 0);
2233 component.imply("second", 0);
2234 component.imply("millisecond", 0);
2235 component.addTag("casualReference/midnight");
2236 return component;
2237}
2238function morning(reference, implyHour = 6) {
2239 const component = new ParsingComponents(reference, {});
2240 component.imply("meridiem", Meridiem.AM);
2241 component.imply("hour", implyHour);
2242 component.imply("minute", 0);
2243 component.imply("second", 0);
2244 component.imply("millisecond", 0);
2245 component.addTag("casualReference/morning");
2246 return component;
2247}
2248function afternoon(reference, implyHour = 15) {
2249 const component = new ParsingComponents(reference, {});
2250 component.imply("meridiem", Meridiem.PM);
2251 component.imply("hour", implyHour);
2252 component.imply("minute", 0);
2253 component.imply("second", 0);
2254 component.imply("millisecond", 0);
2255 component.addTag("casualReference/afternoon");
2256 return component;
2257}
2258function noon(reference) {
2259 const component = new ParsingComponents(reference, {});
2260 component.imply("meridiem", Meridiem.AM);
2261 component.assign("hour", 12);
2262 component.imply("minute", 0);
2263 component.imply("second", 0);
2264 component.imply("millisecond", 0);
2265 component.addTag("casualReference/noon");
2266 return component;
2267}
2268
2269// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualDateParser.js
2270var PATTERN9 = /(now|today|tonight|tomorrow|overmorrow|tmr|tmrw|yesterday|last\s*night)(?=\W|$)/i;
2271
2272class ENCasualDateParser extends AbstractParserWithWordBoundaryChecking {
2273 innerPattern(context) {
2274 return PATTERN9;
2275 }
2276 innerExtract(context, match) {
2277 let targetDate = context.refDate;
2278 const lowerText = match[0].toLowerCase();
2279 let component = context.createParsingComponents();
2280 switch (lowerText) {
2281 case "now":
2282 component = now(context.reference);
2283 break;
2284 case "today":
2285 component = today(context.reference);
2286 break;
2287 case "yesterday":
2288 component = yesterday(context.reference);
2289 break;
2290 case "tomorrow":
2291 case "tmr":
2292 case "tmrw":
2293 component = tomorrow(context.reference);
2294 break;
2295 case "tonight":
2296 component = tonight(context.reference);
2297 break;
2298 case "overmorrow":
2299 component = theDayAfter(context.reference, 2);
2300 break;
2301 default:
2302 if (lowerText.match(/last\s*night/)) {
2303 if (targetDate.getHours() > 6) {
2304 const previousDay = new Date(targetDate.getTime());
2305 previousDay.setDate(previousDay.getDate() - 1);
2306 targetDate = previousDay;
2307 }
2308 assignSimilarDate(component, targetDate);
2309 component.imply("hour", 0);
2310 }
2311 break;
2312 }
2313 component.addTag("parser/ENCasualDateParser");
2314 return component;
2315 }
2316}
2317
2318// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualTimeParser.js
2319var PATTERN10 = /(?:this)?\s{0,3}(morning|afternoon|evening|night|midnight|midday|noon)(?=\W|$)/i;
2320
2321class ENCasualTimeParser extends AbstractParserWithWordBoundaryChecking {
2322 innerPattern() {
2323 return PATTERN10;
2324 }
2325 innerExtract(context, match) {
2326 let component = null;
2327 switch (match[1].toLowerCase()) {
2328 case "afternoon":
2329 component = afternoon(context.reference);
2330 break;
2331 case "evening":
2332 case "night":
2333 component = evening(context.reference);
2334 break;
2335 case "midnight":
2336 component = midnight(context.reference);
2337 break;
2338 case "morning":
2339 component = morning(context.reference);
2340 break;
2341 case "noon":
2342 case "midday":
2343 component = noon(context.reference);
2344 break;
2345 }
2346 if (component) {
2347 component.addTag("parser/ENCasualTimeParser");
2348 }
2349 return component;
2350 }
2351}
2352
2353// node_modules/chrono-node/dist/esm/calculation/weekdays.js
2354function createParsingComponentsAtWeekday(reference, weekday, modifier) {
2355 const refDate = reference.getDateWithAdjustedTimezone();
2356 const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
2357 let components = new ParsingComponents(reference);
2358 components = components.addDurationAsImplied({ day: daysToWeekday });
2359 components.assign("weekday", weekday);
2360 return components;
2361}
2362function getDaysToWeekday(refDate, weekday, modifier) {
2363 const refWeekday = refDate.getDay();
2364 switch (modifier) {
2365 case "this":
2366 return getDaysForwardToWeekday(refDate, weekday);
2367 case "last":
2368 return getBackwardDaysToWeekday(refDate, weekday);
2369 case "next":
2370 if (refWeekday == Weekday.SUNDAY) {
2371 return weekday == Weekday.SUNDAY ? 7 : weekday;
2372 }
2373 if (refWeekday == Weekday.SATURDAY) {
2374 if (weekday == Weekday.SATURDAY)
2375 return 7;
2376 if (weekday == Weekday.SUNDAY)
2377 return 8;
2378 return 1 + weekday;
2379 }
2380 if (weekday < refWeekday && weekday != Weekday.SUNDAY) {
2381 return getDaysForwardToWeekday(refDate, weekday);
2382 } else {
2383 return getDaysForwardToWeekday(refDate, weekday) + 7;
2384 }
2385 }
2386 return getDaysToWeekdayClosest(refDate, weekday);
2387}
2388function getDaysToWeekdayClosest(refDate, weekday) {
2389 const backward = getBackwardDaysToWeekday(refDate, weekday);
2390 const forward = getDaysForwardToWeekday(refDate, weekday);
2391 return forward < -backward ? forward : backward;
2392}
2393function getDaysForwardToWeekday(refDate, weekday) {
2394 const refWeekday = refDate.getDay();
2395 let forwardCount = weekday - refWeekday;
2396 if (forwardCount < 0) {
2397 forwardCount += 7;
2398 }
2399 return forwardCount;
2400}
2401function getBackwardDaysToWeekday(refDate, weekday) {
2402 const refWeekday = refDate.getDay();
2403 let backwardCount = weekday - refWeekday;
2404 if (backwardCount >= 0) {
2405 backwardCount -= 7;
2406 }
2407 return backwardCount;
2408}
2409
2410// node_modules/chrono-node/dist/esm/locales/en/parsers/ENWeekdayParser.js
2411var PATTERN11 = new RegExp("(?:(?:\\,|\\(|\\()\\s*)?" + "(?:on\\s*?)?" + "(?:(this|last|past|next)\\s*)?" + `(${matchAnyPattern(WEEKDAY_DICTIONARY)}|weekend|weekday)` + "(?:\\s*(?:\\,|\\)|\\)))?" + "(?:\\s*(this|last|past|next)\\s*week)?" + "(?=\\W|$)", "i");
2412var PREFIX_GROUP2 = 1;
2413var WEEKDAY_GROUP = 2;
2414var POSTFIX_GROUP = 3;
2415
2416class ENWeekdayParser extends AbstractParserWithWordBoundaryChecking {
2417 innerPattern() {
2418 return PATTERN11;
2419 }
2420 innerExtract(context, match) {
2421 const prefix = match[PREFIX_GROUP2];
2422 const postfix = match[POSTFIX_GROUP];
2423 let modifierWord = prefix || postfix;
2424 modifierWord = modifierWord || "";
2425 modifierWord = modifierWord.toLowerCase();
2426 let modifier = null;
2427 if (modifierWord == "last" || modifierWord == "past") {
2428 modifier = "last";
2429 } else if (modifierWord == "next") {
2430 modifier = "next";
2431 } else if (modifierWord == "this") {
2432 modifier = "this";
2433 }
2434 const weekday_word = match[WEEKDAY_GROUP].toLowerCase();
2435 let weekday;
2436 if (WEEKDAY_DICTIONARY[weekday_word] !== undefined) {
2437 weekday = WEEKDAY_DICTIONARY[weekday_word];
2438 } else if (weekday_word == "weekend") {
2439 weekday = modifier == "last" ? Weekday.SUNDAY : Weekday.SATURDAY;
2440 } else if (weekday_word == "weekday") {
2441 const refWeekday = context.reference.getDateWithAdjustedTimezone().getDay();
2442 if (refWeekday == Weekday.SUNDAY || refWeekday == Weekday.SATURDAY) {
2443 weekday = modifier == "last" ? Weekday.FRIDAY : Weekday.MONDAY;
2444 } else {
2445 weekday = refWeekday - 1;
2446 weekday = modifier == "last" ? weekday - 1 : weekday + 1;
2447 weekday = weekday % 5 + 1;
2448 }
2449 } else {
2450 return null;
2451 }
2452 return createParsingComponentsAtWeekday(context.reference, weekday, modifier);
2453 }
2454}
2455
2456// node_modules/chrono-node/dist/esm/locales/en/parsers/ENRelativeDateFormatParser.js
2457var PATTERN12 = new RegExp(`(this|last|past|next|after\\s*this)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` + "(?=\\W|$)", "i");
2458var MODIFIER_WORD_GROUP = 1;
2459var RELATIVE_WORD_GROUP = 2;
2460
2461class ENRelativeDateFormatParser extends AbstractParserWithWordBoundaryChecking {
2462 innerPattern() {
2463 return PATTERN12;
2464 }
2465 innerExtract(context, match) {
2466 const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();
2467 const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
2468 const timeunit = TIME_UNIT_DICTIONARY[unitWord];
2469 if (modifier == "next" || modifier.startsWith("after")) {
2470 const timeUnits = {};
2471 timeUnits[timeunit] = 1;
2472 return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
2473 }
2474 if (modifier == "last" || modifier == "past") {
2475 const timeUnits = {};
2476 timeUnits[timeunit] = -1;
2477 return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
2478 }
2479 const components = context.createParsingComponents();
2480 let date = new Date(context.reference.instant.getTime());
2481 if (unitWord.match(/week/i)) {
2482 date.setDate(date.getDate() - date.getDay());
2483 components.imply("day", date.getDate());
2484 components.imply("month", date.getMonth() + 1);
2485 components.imply("year", date.getFullYear());
2486 } else if (unitWord.match(/month/i)) {
2487 date.setDate(1);
2488 components.imply("day", date.getDate());
2489 components.assign("year", date.getFullYear());
2490 components.assign("month", date.getMonth() + 1);
2491 } else if (unitWord.match(/year/i)) {
2492 date.setDate(1);
2493 date.setMonth(0);
2494 components.imply("day", date.getDate());
2495 components.imply("month", date.getMonth() + 1);
2496 components.assign("year", date.getFullYear());
2497 }
2498 return components;
2499 }
2500}
2501
2502// node_modules/chrono-node/dist/esm/common/parsers/SlashDateFormatParser.js
2503var 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");
2504var OPENING_GROUP = 1;
2505var ENDING_GROUP = 5;
2506var FIRST_NUMBERS_GROUP = 2;
2507var SECOND_NUMBERS_GROUP = 3;
2508var YEAR_GROUP5 = 4;
2509
2510class SlashDateFormatParser {
2511 groupNumberMonth;
2512 groupNumberDay;
2513 constructor(littleEndian) {
2514 this.groupNumberMonth = littleEndian ? SECOND_NUMBERS_GROUP : FIRST_NUMBERS_GROUP;
2515 this.groupNumberDay = littleEndian ? FIRST_NUMBERS_GROUP : SECOND_NUMBERS_GROUP;
2516 }
2517 pattern() {
2518 return PATTERN13;
2519 }
2520 extract(context, match) {
2521 const index = match.index + match[OPENING_GROUP].length;
2522 const indexEnd = match.index + match[0].length - match[ENDING_GROUP].length;
2523 if (index > 0) {
2524 const textBefore = context.text.substring(0, index);
2525 if (textBefore.match("\\d/?$")) {
2526 return;
2527 }
2528 }
2529 if (indexEnd < context.text.length) {
2530 const textAfter = context.text.substring(indexEnd);
2531 if (textAfter.match("^/?\\d")) {
2532 return;
2533 }
2534 }
2535 const text = context.text.substring(index, indexEnd);
2536 if (text.match(/^\d\.\d$/) || text.match(/^\d\.\d{1,2}\.\d{1,2}\s*$/)) {
2537 return;
2538 }
2539 if (!match[YEAR_GROUP5] && text.indexOf("/") < 0) {
2540 return;
2541 }
2542 const result = context.createParsingResult(index, text);
2543 let month = parseInt(match[this.groupNumberMonth]);
2544 let day = parseInt(match[this.groupNumberDay]);
2545 if (month < 1 || month > 12) {
2546 if (month > 12) {
2547 if (day >= 1 && day <= 12 && month <= 31) {
2548 [day, month] = [month, day];
2549 } else {
2550 return null;
2551 }
2552 }
2553 }
2554 if (day < 1 || day > 31) {
2555 return null;
2556 }
2557 result.start.assign("day", day);
2558 result.start.assign("month", month);
2559 if (match[YEAR_GROUP5]) {
2560 const rawYearNumber = parseInt(match[YEAR_GROUP5]);
2561 const year = findMostLikelyADYear(rawYearNumber);
2562 result.start.assign("year", year);
2563 } else {
2564 const year = findYearClosestToRef(context.refDate, day, month);
2565 result.start.imply("year", year);
2566 }
2567 return result.addTag("parser/SlashDateFormatParser");
2568 }
2569}
2570
2571// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js
2572var PATTERN14 = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
2573var PATTERN_NO_ABBR = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
2574
2575class ENTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
2576 allowAbbreviations;
2577 constructor(allowAbbreviations = true) {
2578 super();
2579 this.allowAbbreviations = allowAbbreviations;
2580 }
2581 innerPattern() {
2582 return this.allowAbbreviations ? PATTERN14 : PATTERN_NO_ABBR;
2583 }
2584 innerExtract(context, match) {
2585 const prefix = match[1].toLowerCase();
2586 let duration = parseDuration(match[2]);
2587 if (!duration) {
2588 return null;
2589 }
2590 switch (prefix) {
2591 case "last":
2592 case "past":
2593 case "-":
2594 duration = reverseDuration(duration);
2595 break;
2596 }
2597 return ParsingComponents.createRelativeFromReference(context.reference, duration);
2598 }
2599}
2600
2601// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeAfterDateRefiner.js
2602function IsPositiveFollowingReference(result) {
2603 return result.text.match(/^[+-]/i) != null;
2604}
2605function IsNegativeFollowingReference(result) {
2606 return result.text.match(/^-/i) != null;
2607}
2608
2609class ENMergeRelativeAfterDateRefiner extends MergingRefiner {
2610 shouldMergeResults(textBetween, currentResult, nextResult) {
2611 if (!textBetween.match(/^\s*$/i)) {
2612 return false;
2613 }
2614 return IsPositiveFollowingReference(nextResult) || IsNegativeFollowingReference(nextResult);
2615 }
2616 mergeResults(textBetween, currentResult, nextResult, context) {
2617 let timeUnits = parseDuration(nextResult.text);
2618 if (IsNegativeFollowingReference(nextResult)) {
2619 timeUnits = reverseDuration(timeUnits);
2620 }
2621 const components = ParsingComponents.createRelativeFromReference(ReferenceWithTimezone.fromDate(currentResult.start.date()), timeUnits);
2622 return new ParsingResult(currentResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
2623 }
2624}
2625
2626// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeFollowByDateRefiner.js
2627function hasImpliedEarlierReferenceDate(result) {
2628 return result.text.match(/\s+(before|from)$/i) != null;
2629}
2630function hasImpliedLaterReferenceDate(result) {
2631 return result.text.match(/\s+(after|since)$/i) != null;
2632}
2633
2634class ENMergeRelativeFollowByDateRefiner extends MergingRefiner {
2635 patternBetween() {
2636 return /^\s*$/i;
2637 }
2638 shouldMergeResults(textBetween, currentResult, nextResult) {
2639 if (!textBetween.match(this.patternBetween())) {
2640 return false;
2641 }
2642 if (!hasImpliedEarlierReferenceDate(currentResult) && !hasImpliedLaterReferenceDate(currentResult)) {
2643 return false;
2644 }
2645 return !!nextResult.start.get("day") && !!nextResult.start.get("month") && !!nextResult.start.get("year");
2646 }
2647 mergeResults(textBetween, currentResult, nextResult) {
2648 let duration = parseDuration(currentResult.text);
2649 if (hasImpliedEarlierReferenceDate(currentResult)) {
2650 duration = reverseDuration(duration);
2651 }
2652 const components = ParsingComponents.createRelativeFromReference(ReferenceWithTimezone.fromDate(nextResult.start.date()), duration);
2653 return new ParsingResult(nextResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
2654 }
2655}
2656
2657// node_modules/chrono-node/dist/esm/locales/en/refiners/ENExtractYearSuffixRefiner.js
2658var YEAR_SUFFIX_PATTERN = new RegExp(`^\\s*(${YEAR_PATTERN})`, "i");
2659var YEAR_GROUP6 = 1;
2660
2661class ENExtractYearSuffixRefiner {
2662 refine(context, results) {
2663 results.forEach(function(result) {
2664 if (!result.start.isDateWithUnknownYear()) {
2665 return;
2666 }
2667 const suffix = context.text.substring(result.index + result.text.length);
2668 const match = YEAR_SUFFIX_PATTERN.exec(suffix);
2669 if (!match) {
2670 return;
2671 }
2672 if (match[0].trim().length <= 3) {
2673 return;
2674 }
2675 context.debug(() => {
2676 console.log(`Extracting year: '${match[0]}' into : ${result}`);
2677 });
2678 const year = parseYear(match[YEAR_GROUP6]);
2679 if (result.end != null) {
2680 result.end.assign("year", year);
2681 }
2682 result.start.assign("year", year);
2683 result.text += match[0];
2684 });
2685 return results;
2686 }
2687}
2688
2689// node_modules/chrono-node/dist/esm/locales/en/refiners/ENUnlikelyFormatFilter.js
2690class ENUnlikelyFormatFilter extends Filter {
2691 constructor() {
2692 super();
2693 }
2694 isValid(context, result) {
2695 const text = result.text.trim();
2696 if (text === context.text.trim()) {
2697 return true;
2698 }
2699 if (text.toLowerCase() === "may") {
2700 const textBefore = context.text.substring(0, result.index).trim();
2701 if (!textBefore.match(/\b(in)$/i)) {
2702 context.debug(() => {
2703 console.log(`Removing unlikely result: ${result}`);
2704 });
2705 return false;
2706 }
2707 }
2708 if (text.toLowerCase().endsWith("the second")) {
2709 const textAfter = context.text.substring(result.index + result.text.length).trim();
2710 if (textAfter.length > 0) {
2711 context.debug(() => {
2712 console.log(`Removing unlikely result: ${result}`);
2713 });
2714 }
2715 return false;
2716 }
2717 return true;
2718 }
2719}
2720
2721// node_modules/chrono-node/dist/esm/locales/en/configuration.js
2722class ENDefaultConfiguration {
2723 createCasualConfiguration(littleEndian = false) {
2724 const option = this.createConfiguration(false, littleEndian);
2725 option.parsers.push(new ENCasualDateParser);
2726 option.parsers.push(new ENCasualTimeParser);
2727 option.parsers.push(new ENMonthNameParser);
2728 option.parsers.push(new ENRelativeDateFormatParser);
2729 option.parsers.push(new ENTimeUnitCasualRelativeFormatParser);
2730 option.refiners.push(new ENUnlikelyFormatFilter);
2731 return option;
2732 }
2733 createConfiguration(strictMode = true, littleEndian = false) {
2734 const options = includeCommonConfiguration({
2735 parsers: [
2736 new SlashDateFormatParser(littleEndian),
2737 new ENTimeUnitWithinFormatParser(strictMode),
2738 new ENMonthNameLittleEndianParser,
2739 new ENMonthNameMiddleEndianParser(littleEndian),
2740 new ENWeekdayParser,
2741 new ENSlashMonthFormatParser,
2742 new ENTimeExpressionParser(strictMode),
2743 new ENTimeUnitAgoFormatParser(strictMode),
2744 new ENTimeUnitLaterFormatParser(strictMode)
2745 ],
2746 refiners: [new ENMergeDateTimeRefiner]
2747 }, strictMode);
2748 options.parsers.unshift(new ENYearMonthDayParser(strictMode));
2749 options.refiners.unshift(new ENMergeRelativeFollowByDateRefiner);
2750 options.refiners.unshift(new ENMergeRelativeAfterDateRefiner);
2751 options.refiners.unshift(new OverlapRemovalRefiner);
2752 options.refiners.push(new ENMergeDateTimeRefiner);
2753 options.refiners.push(new ENExtractYearSuffixRefiner);
2754 options.refiners.push(new ENMergeDateRangeRefiner);
2755 return options;
2756 }
2757}
2758
2759// node_modules/chrono-node/dist/esm/chrono.js
2760class Chrono {
2761 parsers;
2762 refiners;
2763 defaultConfig = new ENDefaultConfiguration;
2764 constructor(configuration) {
2765 configuration = configuration || this.defaultConfig.createCasualConfiguration();
2766 this.parsers = [...configuration.parsers];
2767 this.refiners = [...configuration.refiners];
2768 }
2769 clone() {
2770 return new Chrono({
2771 parsers: [...this.parsers],
2772 refiners: [...this.refiners]
2773 });
2774 }
2775 parseDate(text, referenceDate, option) {
2776 const results = this.parse(text, referenceDate, option);
2777 return results.length > 0 ? results[0].start.date() : null;
2778 }
2779 parse(text, referenceDate, option) {
2780 const context = new ParsingContext(text, referenceDate, option);
2781 let results = [];
2782 this.parsers.forEach((parser) => {
2783 const parsedResults = Chrono.executeParser(context, parser);
2784 results = results.concat(parsedResults);
2785 });
2786 results.sort((a, b) => {
2787 return a.index - b.index;
2788 });
2789 this.refiners.forEach(function(refiner) {
2790 results = refiner.refine(context, results);
2791 });
2792 return results;
2793 }
2794 static executeParser(context, parser) {
2795 const results = [];
2796 const pattern = parser.pattern(context);
2797 const originalText = context.text;
2798 let remainingText = context.text;
2799 let match = pattern.exec(remainingText);
2800 while (match) {
2801 const index = match.index + originalText.length - remainingText.length;
2802 match.index = index;
2803 const result = parser.extract(context, match);
2804 if (!result) {
2805 remainingText = originalText.substring(match.index + 1);
2806 match = pattern.exec(remainingText);
2807 continue;
2808 }
2809 let parsedResult = null;
2810 if (result instanceof ParsingResult) {
2811 parsedResult = result;
2812 } else if (result instanceof ParsingComponents) {
2813 parsedResult = context.createParsingResult(match.index, match[0]);
2814 parsedResult.start = result;
2815 } else {
2816 parsedResult = context.createParsingResult(match.index, match[0], result);
2817 }
2818 const parsedIndex = parsedResult.index;
2819 const parsedText = parsedResult.text;
2820 context.debug(() => console.log(`${parser.constructor.name} extracted (at index=${parsedIndex}) '${parsedText}'`));
2821 results.push(parsedResult);
2822 remainingText = originalText.substring(parsedIndex + parsedText.length);
2823 match = pattern.exec(remainingText);
2824 }
2825 return results;
2826 }
2827}
2828
2829class ParsingContext {
2830 text;
2831 option;
2832 reference;
2833 refDate;
2834 constructor(text, refDate, option) {
2835 this.text = text;
2836 this.option = option ?? {};
2837 this.reference = ReferenceWithTimezone.fromInput(refDate, this.option.timezones);
2838 this.refDate = this.reference.instant;
2839 }
2840 createParsingComponents(components) {
2841 if (components instanceof ParsingComponents) {
2842 return components;
2843 }
2844 return new ParsingComponents(this.reference, components);
2845 }
2846 createParsingResult(index, textOrEndIndex, startComponents, endComponents) {
2847 const text = typeof textOrEndIndex === "string" ? textOrEndIndex : this.text.substring(index, textOrEndIndex);
2848 const start = startComponents ? this.createParsingComponents(startComponents) : null;
2849 const end = endComponents ? this.createParsingComponents(endComponents) : null;
2850 return new ParsingResult(this.reference, index, text, start, end);
2851 }
2852 debug(block) {
2853 if (this.option.debug) {
2854 if (this.option.debug instanceof Function) {
2855 this.option.debug(block);
2856 } else {
2857 const handler = this.option.debug;
2858 handler.debug(block);
2859 }
2860 }
2861 }
2862}
2863
2864// node_modules/chrono-node/dist/esm/locales/en/index.js
2865var configuration = new ENDefaultConfiguration;
2866var casual = new Chrono(configuration.createCasualConfiguration(false));
2867var strict = new Chrono(configuration.createConfiguration(true, false));
2868var GB = new Chrono(configuration.createCasualConfiguration(true));
2869
2870// node_modules/chrono-node/dist/esm/index.js
2871var casual2 = casual;
2872function parseDate(text, ref, option) {
2873 return casual2.parseDate(text, ref, option);
2874}
2875
2876// index.ts
2877import { Container, SelectList, Text } from "@mariozechner/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 description: `Manage org-mode TODOs. Actions:
3045- list: List active TODOs (TODO, NEXT, STRT)
3046- scheduled: Get today's scheduled items
3047- upcoming: Get tasks in next N days (default 7)
3048- overdue: Get overdue tasks
3049- search: Search TODOs by query
3050- get: Get full content of a TODO
3051- done: Mark TODO as DONE
3052- state: Change TODO state (TODO, NEXT, STRT, WAIT, DONE, CANX)
3053- schedule: Set scheduled date
3054- deadline: Set deadline date
3055- priority: Set priority (1-5)
3056- add: Create new TODO
3057- append: Append content to TODO
3058- inbox-list: List all inbox items
3059- inbox-count: Get count of inbox items
3060- inbox-add: Add item to inbox
3061- refile-targets: Get available refile target sections
3062- refile: Refile item from inbox to a section`,
3063 parameters: {
3064 type: "object",
3065 properties: {
3066 action: {
3067 type: "string",
3068 enum: [
3069 "list",
3070 "scheduled",
3071 "upcoming",
3072 "overdue",
3073 "search",
3074 "get",
3075 "done",
3076 "state",
3077 "schedule",
3078 "deadline",
3079 "priority",
3080 "add",
3081 "append",
3082 "sections",
3083 "statistics",
3084 "archive",
3085 "inbox-list",
3086 "inbox-count",
3087 "inbox-add",
3088 "refile-targets",
3089 "refile"
3090 ],
3091 description: "Action to perform"
3092 },
3093 heading: {
3094 type: "string",
3095 description: "TODO heading (for get, done, state, schedule, etc.)"
3096 },
3097 query: {
3098 type: "string",
3099 description: "Search query (for search action)"
3100 },
3101 section: {
3102 type: "string",
3103 description: "Section name (for add action or by-section filter)"
3104 },
3105 state: {
3106 type: "string",
3107 enum: ["TODO", "NEXT", "STRT", "WAIT", "DONE", "CANX"],
3108 description: "TODO state (for state action)"
3109 },
3110 date: {
3111 type: "string",
3112 description: "Date in YYYY-MM-DD format (for schedule/deadline)"
3113 },
3114 days: {
3115 type: "number",
3116 description: "Number of days (for upcoming action, default 7)"
3117 },
3118 priority: {
3119 type: "number",
3120 description: "Priority 1-5 (1=highest)"
3121 },
3122 content: {
3123 type: "string",
3124 description: "Content to append (org-mode format)"
3125 },
3126 tags: {
3127 type: "array",
3128 items: { type: "string" },
3129 description: "Tags for new TODO"
3130 }
3131 },
3132 required: ["action"]
3133 },
3134 execute: async (toolCallId, params, signal, onUpdate, ctx) => {
3135 const { action, heading, query, section, state, date, days, priority, content, tags } = params;
3136 let elisp;
3137 switch (action) {
3138 case "list":
3139 if (section) {
3140 elisp = `(pi/org-todo-by-section "${section}")`;
3141 } else {
3142 elisp = "(pi/org-todo-list)";
3143 }
3144 break;
3145 case "scheduled":
3146 elisp = `(pi/org-todo-scheduled nil "${date || "today"}")`;
3147 break;
3148 case "upcoming":
3149 elisp = `(pi/org-todo-upcoming nil ${days || 7})`;
3150 break;
3151 case "overdue":
3152 elisp = "(pi/org-todo-overdue)";
3153 break;
3154 case "search":
3155 if (!query) {
3156 return {
3157 content: [{ type: "text", text: "Error: query is required for search action" }]
3158 };
3159 }
3160 elisp = `(pi/org-todo-search "${query.replace(/"/g, "\\\"")}")`;
3161 break;
3162 case "get":
3163 if (!heading) {
3164 return {
3165 content: [{ type: "text", text: "Error: heading is required for get action" }]
3166 };
3167 }
3168 elisp = `(pi/org-todo-get "${heading.replace(/"/g, "\\\"")}")`;
3169 break;
3170 case "done":
3171 if (!heading) {
3172 return {
3173 content: [{ type: "text", text: "Error: heading is required for done action" }]
3174 };
3175 }
3176 elisp = `(pi/org-todo-done "${heading.replace(/"/g, "\\\"")}")`;
3177 break;
3178 case "state":
3179 if (!heading || !state) {
3180 return {
3181 content: [{ type: "text", text: "Error: heading and state are required for state action" }]
3182 };
3183 }
3184 elisp = `(pi/org-todo-state "${heading.replace(/"/g, "\\\"")}" "${state}")`;
3185 break;
3186 case "schedule":
3187 if (!heading || !date) {
3188 return {
3189 content: [{ type: "text", text: "Error: heading and date are required for schedule action" }]
3190 };
3191 }
3192 elisp = `(pi/org-todo-schedule "${heading.replace(/"/g, "\\\"")}" "${date}")`;
3193 break;
3194 case "deadline":
3195 if (!heading || !date) {
3196 return {
3197 content: [{ type: "text", text: "Error: heading and date are required for deadline action" }]
3198 };
3199 }
3200 elisp = `(pi/org-todo-deadline "${heading.replace(/"/g, "\\\"")}" "${date}")`;
3201 break;
3202 case "priority":
3203 if (!heading || priority === undefined) {
3204 return {
3205 content: [{ type: "text", text: "Error: heading and priority are required for priority action" }]
3206 };
3207 }
3208 elisp = `(pi/org-todo-priority "${heading.replace(/"/g, "\\\"")}" ${priority})`;
3209 break;
3210 case "add":
3211 if (!heading || !section) {
3212 return {
3213 content: [{ type: "text", text: "Error: heading and section are required for add action" }]
3214 };
3215 }
3216 const schedArg = date ? `"${date}"` : "nil";
3217 const prioArg = priority !== undefined ? priority : "nil";
3218 const tagsArg = tags && tags.length > 0 ? `'(${tags.map((t) => `"${t}"`).join(" ")})` : "nil";
3219 elisp = `(pi/org-todo-add "${heading.replace(/"/g, "\\\"")}" "${section.replace(/"/g, "\\\"")}" nil ${schedArg} ${prioArg} ${tagsArg})`;
3220 if (content) {
3221 const addResult = execEmacs(elisp);
3222 if (!addResult.success) {
3223 return {
3224 content: [{ type: "text", text: `Error: ${addResult.error}` }]
3225 };
3226 }
3227 elisp = `(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${content.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`;
3228 }
3229 break;
3230 case "append":
3231 if (!heading || !content) {
3232 return {
3233 content: [{ type: "text", text: "Error: heading and content are required for append action" }]
3234 };
3235 }
3236 elisp = `(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${content.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`;
3237 break;
3238 case "sections":
3239 elisp = "(pi/org-todo-sections)";
3240 break;
3241 case "statistics":
3242 elisp = "(pi/org-todo-statistics)";
3243 break;
3244 case "archive":
3245 elisp = "(pi/org-todo-archive-done)";
3246 break;
3247 case "inbox-list":
3248 elisp = `(pi/org-todo-list "${INBOX_FILE}" "TODO,NEXT,STRT,WAIT")`;
3249 break;
3250 case "inbox-count":
3251 elisp = `(pi/org-todo-inbox-all)`;
3252 break;
3253 case "inbox-add":
3254 if (!heading) {
3255 return {
3256 content: [{ type: "text", text: "Error: heading is required for inbox-add action" }]
3257 };
3258 }
3259 const schedInbox = date ? `"${date}"` : "nil";
3260 const prioInbox = priority !== undefined ? priority : "nil";
3261 const tagsInbox = tags && tags.length > 0 ? `'(${tags.map((t) => `"${t}"`).join(" ")})` : "nil";
3262 elisp = `(pi/org-todo-add "${heading.replace(/"/g, "\\\"")}" "Inbox" "${INBOX_FILE}" ${schedInbox} ${prioInbox} ${tagsInbox})`;
3263 break;
3264 case "refile-targets":
3265 elisp = "(pi/org-todo-get-refile-targets)";
3266 break;
3267 case "refile":
3268 if (!heading || !section) {
3269 return {
3270 content: [{ type: "text", text: "Error: heading and section are required for refile action" }]
3271 };
3272 }
3273 elisp = `(pi/org-todo-refile "${heading.replace(/"/g, "\\\"")}" "${section.replace(/"/g, "\\\"")}")`;
3274 break;
3275 default:
3276 return {
3277 content: [{ type: "text", text: `Unknown action: ${action}` }]
3278 };
3279 }
3280 const result = execEmacs(elisp);
3281 if (!result.success) {
3282 return {
3283 content: [{ type: "text", text: `Error: ${result.error}` }]
3284 };
3285 }
3286 let text;
3287 if (action === "refile-targets" && Array.isArray(result.data)) {
3288 if (result.data.length === 0) {
3289 text = "No refile targets found.";
3290 } else {
3291 text = result.data.map((t) => {
3292 const indent = " ".repeat((t.level || 1) - 1);
3293 return `${indent}- ${t.section} (${t.file?.replace(/.*\//, "")})`;
3294 }).join(`
3295`);
3296 }
3297 } else if (action === "sections" && Array.isArray(result.data)) {
3298 if (result.data.length === 0) {
3299 text = "No sections found.";
3300 } else {
3301 text = result.data.map((s) => `- ${s.section || s}`).join(`
3302`);
3303 }
3304 } else if (Array.isArray(result.data)) {
3305 if (result.data.length === 0) {
3306 text = "No TODOs found.";
3307 } else {
3308 text = result.data.map(formatTodo).join(`
3309`);
3310 }
3311 } else if (typeof result.data === "object") {
3312 text = JSON.stringify(result.data, null, 2);
3313 } else {
3314 text = String(result.data);
3315 }
3316 return {
3317 content: [{ type: "text", text }]
3318 };
3319 }
3320 });
3321 pi.registerCommand("todos", {
3322 description: "Show today's tasks (scheduled + overdue + NEXT). Usage: /todos [section]",
3323 handler: async (args, ctx) => {
3324 const sectionFilter = (args || "").trim() || null;
3325 let sectionHeadings = null;
3326 if (sectionFilter) {
3327 const sectionResult = execEmacs(`(pi/org-todo-by-section "${sectionFilter.replace(/"/g, "\\\"")}")`);
3328 if (!sectionResult.success) {
3329 const sections = execEmacs("(pi/org-todo-sections)");
3330 const sectionList = sections.success && sections.data ? Array.isArray(sections.data) ? sections.data : Object.values(sections.data) : [];
3331 ctx.ui.notify(`Section "${sectionFilter}" not found. Available: ${sectionList.join(", ")}`, "error");
3332 return;
3333 }
3334 sectionHeadings = new Set((sectionResult.data || []).map((t) => t.heading));
3335 }
3336 function filterBySection(todos) {
3337 if (!sectionHeadings)
3338 return todos;
3339 return todos.filter((t) => sectionHeadings.has(t.heading));
3340 }
3341 const scheduled = execEmacs("(pi/org-todo-scheduled)");
3342 const overdue = execEmacs("(pi/org-todo-overdue)");
3343 const next = execEmacs('(pi/org-todo-list nil "NEXT")');
3344 if (!scheduled.success && !overdue.success && !next.success) {
3345 ctx.ui.notify("Failed to fetch TODOs. Is Emacs daemon running?", "error");
3346 return;
3347 }
3348 const filteredOverdue = filterBySection(overdue.success && overdue.data ? overdue.data : []);
3349 const filteredScheduled = filterBySection(scheduled.success && scheduled.data ? scheduled.data : []);
3350 const filteredNext = filterBySection(next.success && next.data ? next.data : []);
3351 const lines = [];
3352 const title = sectionFilter ? `## \uD83D\uDCCB Today's Tasks — ${sectionFilter}` : "## \uD83D\uDCCB Today's Tasks";
3353 lines.push(title);
3354 lines.push("");
3355 if (filteredOverdue.length > 0) {
3356 lines.push(`### ⚠️ Overdue (${filteredOverdue.length})`);
3357 lines.push("");
3358 for (const todo of filteredOverdue) {
3359 lines.push(`- ${formatTodoMarkdown(todo)}`);
3360 }
3361 lines.push("");
3362 }
3363 if (filteredScheduled.length > 0) {
3364 lines.push(`### \uD83D\uDCC5 Scheduled Today (${filteredScheduled.length})`);
3365 lines.push("");
3366 for (const todo of filteredScheduled) {
3367 lines.push(`- ${formatTodoMarkdown(todo)}`);
3368 }
3369 lines.push("");
3370 }
3371 if (filteredNext.length > 0) {
3372 lines.push(`### ➡️ Next Actions (${filteredNext.length})`);
3373 lines.push("");
3374 for (const todo of filteredNext) {
3375 lines.push(`- ${formatTodoMarkdown(todo)}`);
3376 }
3377 lines.push("");
3378 }
3379 const hasContent = filteredOverdue.length > 0 || filteredScheduled.length > 0 || filteredNext.length > 0;
3380 if (!hasContent) {
3381 if (sectionFilter) {
3382 lines.push(`*No tasks for today in "${sectionFilter}".* \uD83C\uDF89`);
3383 } else {
3384 lines.push("*No tasks for today.* \uD83C\uDF89");
3385 }
3386 }
3387 pi.sendMessage({
3388 customType: "org-todos",
3389 content: lines.join(`
3390`),
3391 display: true
3392 });
3393 }
3394 });
3395 pi.registerCommand("todo-search", {
3396 description: "Search TODOs. Usage: /todo-search <query>",
3397 handler: async (args, ctx) => {
3398 const query = (args || "").trim();
3399 if (!query) {
3400 ctx.ui.notify("Usage: /todo-search <query>", "error");
3401 return;
3402 }
3403 const result = execEmacs(`(pi/org-todo-search "${query.replace(/"/g, "\\\"")}" nil t)`);
3404 if (!result.success) {
3405 ctx.ui.notify(`Search failed: ${result.error}`, "error");
3406 return;
3407 }
3408 if (!result.data || result.data.length === 0) {
3409 ctx.ui.notify(`No TODOs found matching "${query}"`, "info");
3410 return;
3411 }
3412 const lines = [];
3413 lines.push(`## \uD83D\uDD0D Search: "${query}"`);
3414 lines.push("");
3415 lines.push(`*${result.data.length} result(s)*`);
3416 lines.push("");
3417 for (const todo of result.data) {
3418 const matchedIn = todo.matched_in === "heading" ? "" : " *(matched in content)*";
3419 lines.push(`- ${formatTodoMarkdown(todo)}${matchedIn}`);
3420 }
3421 pi.sendMessage({
3422 customType: "org-todos-search",
3423 content: lines.join(`
3424`),
3425 display: true
3426 });
3427 }
3428 });
3429 pi.registerCommand("todo-add", {
3430 description: "Add a new TODO. Usage: /todo-add <title> [@Section] [scheduled:date] [deadline:date] [priority:N]",
3431 handler: async (args, ctx) => {
3432 if (!args?.trim()) {
3433 ctx.ui.notify("Usage: /todo-add <title> [@Section] [scheduled:date] [deadline:date]", "error");
3434 return;
3435 }
3436 const parsed = parseCommandArgs(args);
3437 if (!parsed.title) {
3438 ctx.ui.notify("Error: TODO title is required", "error");
3439 return;
3440 }
3441 const section = parsed.section || DEFAULT_SECTION;
3442 const schedArg = parsed.scheduled ? `"${parsed.scheduled}"` : "nil";
3443 const prioArg = parsed.priority !== undefined ? parsed.priority : "nil";
3444 const sectionsResult = execEmacs("(pi/org-todo-sections)");
3445 if (sectionsResult.success) {
3446 const sections = Array.isArray(sectionsResult.data) ? sectionsResult.data : Object.values(sectionsResult.data || {});
3447 if (!sections.includes(section)) {
3448 ctx.ui.notify(`Section "${section}" not found. Available: ${sections.join(", ")}`, "error");
3449 return;
3450 }
3451 }
3452 const elisp = `(pi/org-todo-add "${parsed.title.replace(/"/g, "\\\"")}" "${section}" nil ${schedArg} ${prioArg} nil)`;
3453 const result = execEmacs(elisp);
3454 if (!result.success) {
3455 ctx.ui.notify(`Failed to add TODO: ${result.error}`, "error");
3456 return;
3457 }
3458 if (parsed.deadline) {
3459 execEmacs(`(pi/org-todo-deadline "${parsed.title.replace(/"/g, "\\\"")}" "${parsed.deadline}")`);
3460 }
3461 const lines = [];
3462 lines.push(`## ✅ TODO Added`);
3463 lines.push("");
3464 lines.push(`**${parsed.title}** added to *${section}*`);
3465 if (parsed.scheduled)
3466 lines.push(`- \uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
3467 if (parsed.deadline)
3468 lines.push(`- ⏰ Deadline: ${parsed.deadline}`);
3469 if (parsed.priority)
3470 lines.push(`- Priority: #${parsed.priority}`);
3471 pi.sendMessage({
3472 customType: "org-todos-add",
3473 content: lines.join(`
3474`),
3475 display: true
3476 });
3477 }
3478 });
3479 async function selectTodo(ctx, title, filterQuery, states) {
3480 const stateFilter = states || "TODO,NEXT,STRT,WAIT";
3481 const listResult = execEmacs(`(pi/org-todo-list nil "${stateFilter}")`);
3482 if (!listResult.success || !listResult.data || listResult.data.length === 0) {
3483 ctx.ui.notify("No active TODOs found.", "info");
3484 return null;
3485 }
3486 const items = listResult.data.map((todo2, i) => ({
3487 value: String(i),
3488 label: stripOrgLinks(formatTodo(todo2)),
3489 description: todo2.scheduled || todo2.deadline || undefined
3490 }));
3491 const filteredItems = filterQuery ? items.filter((item) => {
3492 const searchable = `${item.label} ${item.description || ""}`.toLowerCase();
3493 return filterQuery.toLowerCase().split(/\s+/).every((t) => searchable.includes(t));
3494 }) : items;
3495 if (filteredItems.length === 0) {
3496 ctx.ui.notify(`No TODOs matching "${filterQuery}"`, "info");
3497 return null;
3498 }
3499 const selectedIdx = await showSelectMenu(ctx, title, filteredItems);
3500 if (selectedIdx === null)
3501 return null;
3502 const todo = listResult.data[parseInt(selectedIdx, 10)];
3503 return {
3504 heading: todo.heading,
3505 display: stripOrgLinks(formatTodo(todo))
3506 };
3507 }
3508 pi.registerCommand("todo-done", {
3509 description: "Mark a TODO as done. Usage: /todo-done [filter] (interactive selector)",
3510 handler: async (args, ctx) => {
3511 const filter = (args || "").trim() || undefined;
3512 const selected = await selectTodo(ctx, "Mark as DONE", filter);
3513 if (!selected) {
3514 if (filter)
3515 ctx.ui.notify("Cancelled or no match.", "info");
3516 return;
3517 }
3518 const result = execEmacs(`(pi/org-todo-done "${selected.heading.replace(/"/g, "\\\"")}")`);
3519 if (!result.success) {
3520 ctx.ui.notify(`Failed: ${result.error}`, "error");
3521 return;
3522 }
3523 pi.sendMessage({
3524 customType: "org-todos-done",
3525 content: `## ✅ Done
3526
3527${selected.display}`,
3528 display: true
3529 });
3530 updateTodayStatus(ctx);
3531 }
3532 });
3533 pi.registerCommand("todo-next", {
3534 description: "Mark a TODO as NEXT (prioritized). Usage: /todo-next [filter] (interactive selector)",
3535 handler: async (args, ctx) => {
3536 const filter = (args || "").trim() || undefined;
3537 const selected = await selectTodo(ctx, "Mark as NEXT", filter, "TODO,STRT,WAIT");
3538 if (!selected) {
3539 if (filter)
3540 ctx.ui.notify("Cancelled or no match.", "info");
3541 return;
3542 }
3543 const result = execEmacs(`(pi/org-todo-state "${selected.heading.replace(/"/g, "\\\"")}" "NEXT")`);
3544 if (!result.success) {
3545 ctx.ui.notify(`Failed: ${result.error}`, "error");
3546 return;
3547 }
3548 pi.sendMessage({
3549 customType: "org-todos-next",
3550 content: `## ➡️ Prioritized
3551
3552${selected.display}`,
3553 display: true
3554 });
3555 }
3556 });
3557 pi.registerCommand("todo-upcoming", {
3558 description: "Show upcoming tasks. Usage: /todo-upcoming [days]",
3559 handler: async (args, ctx) => {
3560 const days = parseInt((args || "").trim(), 10) || 7;
3561 const result = execEmacs(`(pi/org-todo-upcoming nil ${days})`);
3562 if (!result.success) {
3563 ctx.ui.notify(`Failed: ${result.error}`, "error");
3564 return;
3565 }
3566 const lines = [];
3567 lines.push(`## \uD83D\uDCC6 Upcoming (next ${days} days)`);
3568 lines.push("");
3569 if (!result.data || result.data.length === 0) {
3570 lines.push("*No upcoming tasks* \uD83C\uDF89");
3571 } else {
3572 for (const todo of result.data) {
3573 lines.push(`- ${formatTodoMarkdown(todo)}`);
3574 }
3575 }
3576 pi.sendMessage({
3577 customType: "org-todos-upcoming",
3578 content: lines.join(`
3579`),
3580 display: true
3581 });
3582 }
3583 });
3584 pi.registerCommand("todo-update", {
3585 description: "Update a TODO. Usage: /todo-update <heading> [scheduled:date] [deadline:date] [priority:N] [state:STATE]",
3586 handler: async (args, ctx) => {
3587 if (!args?.trim()) {
3588 ctx.ui.notify("Usage: /todo-update <heading> [scheduled:date] [deadline:date] [priority:N] [state:STATE]", "error");
3589 return;
3590 }
3591 const parsed = parseCommandArgs(args);
3592 if (!parsed.title) {
3593 ctx.ui.notify("Error: TODO heading is required", "error");
3594 return;
3595 }
3596 const heading = parsed.title;
3597 const updates = [];
3598 if (parsed.scheduled) {
3599 const result = execEmacs(`(pi/org-todo-schedule "${heading.replace(/"/g, "\\\"")}" "${parsed.scheduled}")`);
3600 if (result.success)
3601 updates.push(`\uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
3602 else
3603 ctx.ui.notify(`Failed to set schedule: ${result.error}`, "warning");
3604 }
3605 if (parsed.deadline) {
3606 const result = execEmacs(`(pi/org-todo-deadline "${heading.replace(/"/g, "\\\"")}" "${parsed.deadline}")`);
3607 if (result.success)
3608 updates.push(`⏰ Deadline: ${parsed.deadline}`);
3609 else
3610 ctx.ui.notify(`Failed to set deadline: ${result.error}`, "warning");
3611 }
3612 if (parsed.priority !== undefined) {
3613 const result = execEmacs(`(pi/org-todo-priority "${heading.replace(/"/g, "\\\"")}" ${parsed.priority})`);
3614 if (result.success)
3615 updates.push(`Priority: #${parsed.priority}`);
3616 else
3617 ctx.ui.notify(`Failed to set priority: ${result.error}`, "warning");
3618 }
3619 if (parsed.state) {
3620 const result = execEmacs(`(pi/org-todo-state "${heading.replace(/"/g, "\\\"")}" "${parsed.state}")`);
3621 if (result.success)
3622 updates.push(`State: ${parsed.state}`);
3623 else
3624 ctx.ui.notify(`Failed to set state: ${result.error}`, "warning");
3625 }
3626 if (updates.length === 0) {
3627 ctx.ui.notify("No updates specified. Use scheduled:, deadline:, priority:, or state:", "warning");
3628 return;
3629 }
3630 const lines = [];
3631 lines.push(`## \uD83D\uDCDD Updated`);
3632 lines.push("");
3633 lines.push(`**${heading}**`);
3634 lines.push("");
3635 for (const update of updates) {
3636 lines.push(`- ${update}`);
3637 }
3638 pi.sendMessage({
3639 customType: "org-todos-update",
3640 content: lines.join(`
3641`),
3642 display: true
3643 });
3644 }
3645 });
3646 pi.registerCommand("todo-note", {
3647 description: "Add a note to a TODO. Usage: /todo-note <heading> <note>",
3648 handler: async (args, ctx) => {
3649 if (!args?.trim()) {
3650 ctx.ui.notify("Usage: /todo-note <heading> <note>", "error");
3651 return;
3652 }
3653 const input = args.trim();
3654 let heading;
3655 let note;
3656 const newlineIdx = input.indexOf(`
3657`);
3658 if (newlineIdx > 0) {
3659 heading = input.slice(0, newlineIdx).trim();
3660 note = input.slice(newlineIdx + 1).trim();
3661 } else {
3662 const colonIdx = input.indexOf(": ");
3663 const dashIdx = input.indexOf(" - ");
3664 if (colonIdx > 0 && colonIdx < 60) {
3665 heading = input.slice(0, colonIdx).trim();
3666 note = input.slice(colonIdx + 2).trim();
3667 } else if (dashIdx > 0 && dashIdx < 60) {
3668 heading = input.slice(0, dashIdx).trim();
3669 note = input.slice(dashIdx + 3).trim();
3670 } else {
3671 ctx.ui.notify("Could not parse heading and note. Use format: /todo-note Heading: your note here", "error");
3672 return;
3673 }
3674 }
3675 if (!heading || !note) {
3676 ctx.ui.notify("Both heading and note are required", "error");
3677 return;
3678 }
3679 const timestamp = new Date().toISOString().slice(0, 16).replace("T", " ");
3680 const orgContent = `
3681[${timestamp}] ${note}`;
3682 const result = execEmacs(`(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${orgContent.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`);
3683 if (!result.success) {
3684 ctx.ui.notify(`Failed: ${result.error}`, "error");
3685 return;
3686 }
3687 pi.sendMessage({
3688 customType: "org-todos-note",
3689 content: `## \uD83D\uDCDD Note Added
3690
3691**${heading}**
3692
3693> ${note}`,
3694 display: true
3695 });
3696 }
3697 });
3698 pi.registerCommand("inbox", {
3699 description: "View all inbox items (TODOs and links)",
3700 handler: async (args, ctx) => {
3701 const result = execEmacs(`(pi/org-todo-inbox-all)`);
3702 if (!result.success) {
3703 ctx.ui.notify(`Failed to fetch inbox: ${result.error}`, "error");
3704 return;
3705 }
3706 const todos = result.data?.filter((item) => item.todo) || [];
3707 const links = result.data?.filter((item) => !item.todo) || [];
3708 const lines = [];
3709 lines.push("## \uD83D\uDCE5 Inbox");
3710 lines.push("");
3711 if (!result.data || result.data.length === 0) {
3712 lines.push("*Inbox is empty* ✨");
3713 } else {
3714 lines.push(`*${result.data.length} item(s)* (${todos.length} tasks, ${links.length} links/notes)`);
3715 lines.push("");
3716 if (todos.length > 0) {
3717 lines.push("### ✅ Tasks");
3718 lines.push("");
3719 for (const todo of todos) {
3720 lines.push(`- ${formatTodoMarkdown(todo)}`);
3721 }
3722 lines.push("");
3723 }
3724 if (links.length > 0) {
3725 lines.push("### \uD83D\uDD17 Links & Notes");
3726 lines.push("");
3727 for (const item of links) {
3728 lines.push(`- ${stripOrgLinks(item.heading)}`);
3729 }
3730 }
3731 }
3732 pi.sendMessage({
3733 customType: "org-todos",
3734 content: lines.join(`
3735`),
3736 display: true
3737 });
3738 }
3739 });
3740 pi.registerCommand("inbox-add", {
3741 description: "Quick capture to inbox. Usage: /inbox-add <title> [scheduled:date] [priority:N]",
3742 handler: async (args, ctx) => {
3743 if (!args?.trim()) {
3744 ctx.ui.notify("Usage: /inbox-add <title> [scheduled:date] [priority:N]", "error");
3745 return;
3746 }
3747 const parsed = parseCommandArgs(args);
3748 if (!parsed.title) {
3749 ctx.ui.notify("Error: TODO title is required", "error");
3750 return;
3751 }
3752 const schedArg = parsed.scheduled ? `"${parsed.scheduled}"` : "nil";
3753 const prioArg = parsed.priority !== undefined ? parsed.priority : "nil";
3754 const elisp = `(with-current-buffer (find-file-noselect "${INBOX_FILE}")
3755 (goto-char (point-max))
3756 (insert "\\n* TODO ${parsed.title.replace(/"/g, "\\\"")}")
3757 ${parsed.scheduled ? `(org-schedule nil "${parsed.scheduled}")` : ""}
3758 ${parsed.priority !== undefined ? `(org-priority ${parsed.priority})` : ""}
3759 (save-buffer)
3760 (kill-buffer)
3761 (json-encode (list (cons 'success t))))`;
3762 const result = execEmacs(elisp);
3763 if (!result.success) {
3764 ctx.ui.notify(`Failed to add to inbox: ${result.error}`, "error");
3765 return;
3766 }
3767 const lines = [];
3768 lines.push(`## \uD83D\uDCE5 Added to Inbox`);
3769 lines.push("");
3770 lines.push(`**${parsed.title}**`);
3771 if (parsed.scheduled)
3772 lines.push(`- \uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
3773 if (parsed.priority)
3774 lines.push(`- Priority: #${parsed.priority}`);
3775 pi.sendMessage({
3776 customType: "org-todos-add",
3777 content: lines.join(`
3778`),
3779 display: true
3780 });
3781 updateInboxStatus(ctx);
3782 if (parsed.scheduled) {
3783 updateTodayStatus(ctx);
3784 }
3785 }
3786 });
3787 function fuzzyMatch(item, query) {
3788 if (!query)
3789 return true;
3790 const searchable = `${item.label} ${item.description || ""}`.toLowerCase();
3791 const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
3792 return terms.every((term) => searchable.includes(term));
3793 }
3794 async function showSelectMenu(ctx, title, allItems) {
3795 return ctx.ui.custom((tui, theme, _kb, done) => {
3796 let searchQuery = "";
3797 function getFilteredItems() {
3798 if (!searchQuery)
3799 return allItems;
3800 return allItems.filter((item) => fuzzyMatch(item, searchQuery));
3801 }
3802 let currentItems = getFilteredItems();
3803 const container = new Container;
3804 container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
3805 const headerText = new Text("", 0, 0);
3806 function updateHeader() {
3807 const titleStr = theme.fg("accent", theme.bold(title));
3808 if (searchQuery) {
3809 headerText.setText(`${titleStr} ${theme.fg("warning", `filter: ${searchQuery}`)}`);
3810 } else {
3811 headerText.setText(titleStr);
3812 }
3813 }
3814 updateHeader();
3815 container.addChild(headerText);
3816 const listTheme = {
3817 selectedPrefix: (text) => theme.fg("accent", text),
3818 selectedText: (text) => theme.fg("accent", text),
3819 description: (text) => theme.fg("muted", text),
3820 scrollInfo: (text) => theme.fg("dim", text),
3821 noMatch: (text) => theme.fg("warning", text)
3822 };
3823 let selectList = new SelectList(currentItems, Math.min(currentItems.length, 15), listTheme);
3824 selectList.onSelect = (item) => done(item.value);
3825 selectList.onCancel = () => done(null);
3826 container.addChild(selectList);
3827 container.addChild(new Text(theme.fg("dim", "Type to filter · enter to confirm · esc to cancel")));
3828 container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
3829 function rebuildList() {
3830 currentItems = getFilteredItems();
3831 const newList = new SelectList(currentItems, Math.min(currentItems.length, 15), listTheme);
3832 newList.onSelect = (item) => done(item.value);
3833 newList.onCancel = () => done(null);
3834 const idx = container.children.indexOf(selectList);
3835 if (idx !== -1)
3836 container.children[idx] = newList;
3837 selectList = newList;
3838 updateHeader();
3839 }
3840 return {
3841 render(width) {
3842 return container.render(width);
3843 },
3844 invalidate() {
3845 container.invalidate();
3846 },
3847 handleInput(data) {
3848 if (data === "" || data === "\b") {
3849 if (searchQuery.length > 0) {
3850 searchQuery = searchQuery.slice(0, -1);
3851 rebuildList();
3852 tui.requestRender();
3853 }
3854 return;
3855 }
3856 if (data.length === 1 && data >= " " && data <= "~") {
3857 searchQuery += data;
3858 rebuildList();
3859 tui.requestRender();
3860 return;
3861 }
3862 selectList.handleInput(data);
3863 tui.requestRender();
3864 }
3865 };
3866 });
3867 }
3868 pi.registerCommand("inbox-refile", {
3869 description: "Refile inbox item to a section (interactive)",
3870 handler: async (args, ctx) => {
3871 const inboxResult = execEmacs(`(pi/org-todo-inbox-all)`);
3872 if (!inboxResult.success || !inboxResult.data || inboxResult.data.length === 0) {
3873 ctx.ui.notify("Inbox is empty!", "info");
3874 return;
3875 }
3876 let heading = (args || "").trim();
3877 let sourcePosition = null;
3878 if (!heading) {
3879 const inboxItems = inboxResult.data.map((item, i) => {
3880 const prefix = item.todo ? `[${item.todo}] ` : "";
3881 const label = stripOrgLinks(`${prefix}${item.heading}`);
3882 return {
3883 value: String(i),
3884 label,
3885 description: item.todo ? undefined : "link/note"
3886 };
3887 });
3888 const selectedIdx = await showSelectMenu(ctx, "Select inbox item to refile", inboxItems);
3889 if (selectedIdx === null) {
3890 ctx.ui.notify("Refile cancelled", "info");
3891 return;
3892 }
3893 const sourceItem = inboxResult.data[parseInt(selectedIdx, 10)];
3894 heading = sourceItem.heading;
3895 sourcePosition = sourceItem.position;
3896 }
3897 const targetsResult = execEmacs("(pi/org-todo-get-refile-targets)");
3898 if (!targetsResult.success || !targetsResult.data) {
3899 ctx.ui.notify("Failed to get refile targets", "error");
3900 return;
3901 }
3902 const sectionItems = targetsResult.data.map((t, i) => {
3903 const indent = t.level > 1 ? " ".repeat(t.level - 1) : "";
3904 return {
3905 value: String(i),
3906 label: `${indent}${stripOrgLinks(t.section)}`,
3907 description: t.level > 1 ? stripOrgLinks(t.path) : undefined
3908 };
3909 });
3910 const displayHeading = stripOrgLinks(heading).slice(0, 60);
3911 const targetIdx = await showSelectMenu(ctx, `Refile "${displayHeading}" to:`, sectionItems);
3912 if (targetIdx === null) {
3913 ctx.ui.notify("Refile cancelled", "info");
3914 return;
3915 }
3916 const target = targetsResult.data[parseInt(targetIdx, 10)];
3917 const srcPosArg = sourcePosition ? ` ${sourcePosition}` : "";
3918 const refileResult = execEmacs(`(pi/org-todo-refile "${heading.replace(/"/g, "\\\"")}" "${target.section.replace(/"/g, "\\\"")}" nil nil ${target.position}${srcPosArg})`);
3919 if (!refileResult.success) {
3920 ctx.ui.notify(`Refile failed: ${refileResult.error}`, "error");
3921 return;
3922 }
3923 const displayTarget = stripOrgLinks(target.path || target.section);
3924 pi.sendMessage({
3925 customType: "org-todos",
3926 content: `## ✅ Refiled
3927
3928**${displayHeading}** → *${displayTarget}*`,
3929 display: true
3930 });
3931 updateInboxStatus(ctx);
3932 }
3933 });
3934 function updateInboxStatus(ctx) {
3935 try {
3936 const result = execEmacs(`(pi/org-todo-inbox-all)`);
3937 if (result.success && Array.isArray(result.data)) {
3938 const count = result.data.length;
3939 if (count > 0) {
3940 ctx.ui.setStatus("inbox-count", ctx.ui.theme.fg("warning", `\uD83D\uDCE5 ${count}`));
3941 } else {
3942 ctx.ui.setStatus("inbox-count", undefined);
3943 }
3944 }
3945 } catch (e) {}
3946 }
3947 function updateTodayStatus(ctx) {
3948 try {
3949 const scheduledResult = execEmacs("(pi/org-todo-scheduled)");
3950 const overdueResult = execEmacs("(pi/org-todo-overdue)");
3951 if (scheduledResult.success && overdueResult.success) {
3952 const schedCount = Array.isArray(scheduledResult.data) ? scheduledResult.data.length : 0;
3953 const overdueCount = Array.isArray(overdueResult.data) ? overdueResult.data.length : 0;
3954 if (schedCount === 0 && overdueCount === 0) {
3955 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("success", "✓"));
3956 } else if (overdueCount > 0 && schedCount > 0) {
3957 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("error", `⚠️ ${overdueCount}`) + " " + ctx.ui.theme.fg("accent", `\uD83D\uDCC5 ${schedCount}`));
3958 } else if (overdueCount > 0) {
3959 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("error", `⚠️ ${overdueCount}`));
3960 } else {
3961 ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("accent", `\uD83D\uDCC5 ${schedCount}`));
3962 }
3963 }
3964 } catch (e) {}
3965 }
3966 pi.on("session_start", async (_event, ctx) => {
3967 updateInboxStatus(ctx);
3968 updateTodayStatus(ctx);
3969 const updateInterval = setInterval(() => {
3970 updateInboxStatus(ctx);
3971 updateTodayStatus(ctx);
3972 }, 5 * 60 * 1000);
3973 pi.on("session_end", async () => {
3974 clearInterval(updateInterval);
3975 });
3976 });
3977}
3978export {
3979 org_todos_default as default
3980};