Commit 7d5c5faabd2f
dots/pi/agent/extensions/org-todos/dist/index.js
@@ -1,8 +1,2883 @@
// index.ts
+import { DynamicBorder } from "@mariozechner/pi-coding-agent";
import { execSync } from "node:child_process";
import { homedir } from "node:os";
import { join } from "node:path";
+
+// node_modules/chrono-node/dist/esm/types.js
+var Meridiem;
+(function(Meridiem2) {
+ Meridiem2[Meridiem2["AM"] = 0] = "AM";
+ Meridiem2[Meridiem2["PM"] = 1] = "PM";
+})(Meridiem || (Meridiem = {}));
+var Weekday;
+(function(Weekday2) {
+ Weekday2[Weekday2["SUNDAY"] = 0] = "SUNDAY";
+ Weekday2[Weekday2["MONDAY"] = 1] = "MONDAY";
+ Weekday2[Weekday2["TUESDAY"] = 2] = "TUESDAY";
+ Weekday2[Weekday2["WEDNESDAY"] = 3] = "WEDNESDAY";
+ Weekday2[Weekday2["THURSDAY"] = 4] = "THURSDAY";
+ Weekday2[Weekday2["FRIDAY"] = 5] = "FRIDAY";
+ Weekday2[Weekday2["SATURDAY"] = 6] = "SATURDAY";
+})(Weekday || (Weekday = {}));
+var Month;
+(function(Month2) {
+ Month2[Month2["JANUARY"] = 1] = "JANUARY";
+ Month2[Month2["FEBRUARY"] = 2] = "FEBRUARY";
+ Month2[Month2["MARCH"] = 3] = "MARCH";
+ Month2[Month2["APRIL"] = 4] = "APRIL";
+ Month2[Month2["MAY"] = 5] = "MAY";
+ Month2[Month2["JUNE"] = 6] = "JUNE";
+ Month2[Month2["JULY"] = 7] = "JULY";
+ Month2[Month2["AUGUST"] = 8] = "AUGUST";
+ Month2[Month2["SEPTEMBER"] = 9] = "SEPTEMBER";
+ Month2[Month2["OCTOBER"] = 10] = "OCTOBER";
+ Month2[Month2["NOVEMBER"] = 11] = "NOVEMBER";
+ Month2[Month2["DECEMBER"] = 12] = "DECEMBER";
+})(Month || (Month = {}));
+
+// node_modules/chrono-node/dist/esm/utils/dates.js
+function assignSimilarDate(component, target) {
+ component.assign("day", target.getDate());
+ component.assign("month", target.getMonth() + 1);
+ component.assign("year", target.getFullYear());
+}
+function assignSimilarTime(component, target) {
+ component.assign("hour", target.getHours());
+ component.assign("minute", target.getMinutes());
+ component.assign("second", target.getSeconds());
+ component.assign("millisecond", target.getMilliseconds());
+ component.assign("meridiem", target.getHours() < 12 ? Meridiem.AM : Meridiem.PM);
+}
+function implySimilarDate(component, target) {
+ component.imply("day", target.getDate());
+ component.imply("month", target.getMonth() + 1);
+ component.imply("year", target.getFullYear());
+}
+function implySimilarTime(component, target) {
+ component.imply("hour", target.getHours());
+ component.imply("minute", target.getMinutes());
+ component.imply("second", target.getSeconds());
+ component.imply("millisecond", target.getMilliseconds());
+ component.imply("meridiem", target.getHours() < 12 ? Meridiem.AM : Meridiem.PM);
+}
+
+// node_modules/chrono-node/dist/esm/timezone.js
+var TIMEZONE_ABBR_MAP = {
+ ACDT: 630,
+ ACST: 570,
+ ADT: -180,
+ AEDT: 660,
+ AEST: 600,
+ AFT: 270,
+ AKDT: -480,
+ AKST: -540,
+ ALMT: 360,
+ AMST: -180,
+ AMT: -240,
+ ANAST: 720,
+ ANAT: 720,
+ AQTT: 300,
+ ART: -180,
+ AST: -240,
+ AWDT: 540,
+ AWST: 480,
+ AZOST: 0,
+ AZOT: -60,
+ AZST: 300,
+ AZT: 240,
+ BNT: 480,
+ BOT: -240,
+ BRST: -120,
+ BRT: -180,
+ BST: 60,
+ BTT: 360,
+ CAST: 480,
+ CAT: 120,
+ CCT: 390,
+ CDT: -300,
+ CEST: 120,
+ CET: {
+ timezoneOffsetDuringDst: 2 * 60,
+ timezoneOffsetNonDst: 60,
+ dstStart: (year) => getLastWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2),
+ dstEnd: (year) => getLastWeekdayOfMonth(year, Month.OCTOBER, Weekday.SUNDAY, 3)
+ },
+ CHADT: 825,
+ CHAST: 765,
+ CKT: -600,
+ CLST: -180,
+ CLT: -240,
+ COT: -300,
+ CST: -360,
+ CT: {
+ timezoneOffsetDuringDst: -5 * 60,
+ timezoneOffsetNonDst: -6 * 60,
+ dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
+ dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
+ },
+ CVT: -60,
+ CXT: 420,
+ ChST: 600,
+ DAVT: 420,
+ EASST: -300,
+ EAST: -360,
+ EAT: 180,
+ ECT: -300,
+ EDT: -240,
+ EEST: 180,
+ EET: 120,
+ EGST: 0,
+ EGT: -60,
+ EST: -300,
+ ET: {
+ timezoneOffsetDuringDst: -4 * 60,
+ timezoneOffsetNonDst: -5 * 60,
+ dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
+ dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
+ },
+ FJST: 780,
+ FJT: 720,
+ FKST: -180,
+ FKT: -240,
+ FNT: -120,
+ GALT: -360,
+ GAMT: -540,
+ GET: 240,
+ GFT: -180,
+ GILT: 720,
+ GMT: 0,
+ GST: 240,
+ GYT: -240,
+ HAA: -180,
+ HAC: -300,
+ HADT: -540,
+ HAE: -240,
+ HAP: -420,
+ HAR: -360,
+ HAST: -600,
+ HAT: -90,
+ HAY: -480,
+ HKT: 480,
+ HLV: -210,
+ HNA: -240,
+ HNC: -360,
+ HNE: -300,
+ HNP: -480,
+ HNR: -420,
+ HNT: -150,
+ HNY: -540,
+ HOVT: 420,
+ ICT: 420,
+ IDT: 180,
+ IOT: 360,
+ IRDT: 270,
+ IRKST: 540,
+ IRKT: 540,
+ IRST: 210,
+ IST: 330,
+ JST: 540,
+ KGT: 360,
+ KRAST: 480,
+ KRAT: 480,
+ KST: 540,
+ KUYT: 240,
+ LHDT: 660,
+ LHST: 630,
+ LINT: 840,
+ MAGST: 720,
+ MAGT: 720,
+ MART: -510,
+ MAWT: 300,
+ MDT: -360,
+ MESZ: 120,
+ MEZ: 60,
+ MHT: 720,
+ MMT: 390,
+ MSD: 240,
+ MSK: 180,
+ MST: -420,
+ MT: {
+ timezoneOffsetDuringDst: -6 * 60,
+ timezoneOffsetNonDst: -7 * 60,
+ dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
+ dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
+ },
+ MUT: 240,
+ MVT: 300,
+ MYT: 480,
+ NCT: 660,
+ NDT: -90,
+ NFT: 690,
+ NOVST: 420,
+ NOVT: 360,
+ NPT: 345,
+ NST: -150,
+ NUT: -660,
+ NZDT: 780,
+ NZST: 720,
+ OMSST: 420,
+ OMST: 420,
+ PDT: -420,
+ PET: -300,
+ PETST: 720,
+ PETT: 720,
+ PGT: 600,
+ PHOT: 780,
+ PHT: 480,
+ PKT: 300,
+ PMDT: -120,
+ PMST: -180,
+ PONT: 660,
+ PST: -480,
+ PT: {
+ timezoneOffsetDuringDst: -7 * 60,
+ timezoneOffsetNonDst: -8 * 60,
+ dstStart: (year) => getNthWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2, 2),
+ dstEnd: (year) => getNthWeekdayOfMonth(year, Month.NOVEMBER, Weekday.SUNDAY, 1, 2)
+ },
+ PWT: 540,
+ PYST: -180,
+ PYT: -240,
+ RET: 240,
+ SAMT: 240,
+ SAST: 120,
+ SBT: 660,
+ SCT: 240,
+ SGT: 480,
+ SRT: -180,
+ SST: -660,
+ TAHT: -600,
+ TFT: 300,
+ TJT: 300,
+ TKT: 780,
+ TLT: 540,
+ TMT: 300,
+ TVT: 720,
+ ULAT: 480,
+ UTC: 0,
+ UYST: -120,
+ UYT: -180,
+ UZT: 300,
+ VET: -210,
+ VLAST: 660,
+ VLAT: 660,
+ VUT: 660,
+ WAST: 120,
+ WAT: 60,
+ WEST: 60,
+ WESZ: 60,
+ WET: 0,
+ WEZ: 0,
+ WFT: 720,
+ WGST: -120,
+ WGT: -180,
+ WIB: 420,
+ WIT: 540,
+ WITA: 480,
+ WST: 780,
+ WT: 0,
+ YAKST: 600,
+ YAKT: 600,
+ YAPT: 600,
+ YEKST: 360,
+ YEKT: 360
+};
+function getNthWeekdayOfMonth(year, month, weekday, n, hour = 0) {
+ let dayOfMonth = 0;
+ let i = 0;
+ while (i < n) {
+ dayOfMonth++;
+ const date = new Date(year, month - 1, dayOfMonth);
+ if (date.getDay() === weekday)
+ i++;
+ }
+ return new Date(year, month - 1, dayOfMonth, hour);
+}
+function getLastWeekdayOfMonth(year, month, weekday, hour = 0) {
+ const oneIndexedWeekday = weekday === 0 ? 7 : weekday;
+ const date = new Date(year, month - 1 + 1, 1, 12);
+ const firstWeekdayNextMonth = date.getDay() === 0 ? 7 : date.getDay();
+ let dayDiff;
+ if (firstWeekdayNextMonth === oneIndexedWeekday)
+ dayDiff = 7;
+ else if (firstWeekdayNextMonth < oneIndexedWeekday)
+ dayDiff = 7 + firstWeekdayNextMonth - oneIndexedWeekday;
+ else
+ dayDiff = firstWeekdayNextMonth - oneIndexedWeekday;
+ date.setDate(date.getDate() - dayDiff);
+ return new Date(year, month - 1, date.getDate(), hour);
+}
+function toTimezoneOffset(timezoneInput, date, timezoneOverrides = {}) {
+ if (timezoneInput == null) {
+ return null;
+ }
+ if (typeof timezoneInput === "number") {
+ return timezoneInput;
+ }
+ const matchedTimezone = timezoneOverrides[timezoneInput] ?? TIMEZONE_ABBR_MAP[timezoneInput];
+ if (matchedTimezone == null) {
+ return null;
+ }
+ if (typeof matchedTimezone == "number") {
+ return matchedTimezone;
+ }
+ if (date == null) {
+ return null;
+ }
+ if (date > matchedTimezone.dstStart(date.getFullYear()) && !(date > matchedTimezone.dstEnd(date.getFullYear()))) {
+ return matchedTimezone.timezoneOffsetDuringDst;
+ }
+ return matchedTimezone.timezoneOffsetNonDst;
+}
+
+// node_modules/chrono-node/dist/esm/calculation/duration.js
+var EmptyDuration = {
+ day: 0,
+ second: 0,
+ millisecond: 0
+};
+function addDuration(ref, duration) {
+ let date = new Date(ref);
+ if (duration["y"]) {
+ duration["year"] = duration["y"];
+ delete duration["y"];
+ }
+ if (duration["mo"]) {
+ duration["month"] = duration["mo"];
+ delete duration["mo"];
+ }
+ if (duration["M"]) {
+ duration["month"] = duration["M"];
+ delete duration["M"];
+ }
+ if (duration["w"]) {
+ duration["week"] = duration["w"];
+ delete duration["w"];
+ }
+ if (duration["d"]) {
+ duration["day"] = duration["d"];
+ delete duration["d"];
+ }
+ if (duration["h"]) {
+ duration["hour"] = duration["h"];
+ delete duration["h"];
+ }
+ if (duration["m"]) {
+ duration["minute"] = duration["m"];
+ delete duration["m"];
+ }
+ if (duration["s"]) {
+ duration["second"] = duration["s"];
+ delete duration["s"];
+ }
+ if (duration["ms"]) {
+ duration["millisecond"] = duration["ms"];
+ delete duration["ms"];
+ }
+ if ("year" in duration) {
+ const floor = Math.floor(duration["year"]);
+ date.setFullYear(date.getFullYear() + floor);
+ const remainingFraction = duration["year"] - floor;
+ if (remainingFraction > 0) {
+ duration.month = duration?.month ?? 0;
+ duration.month += remainingFraction * 12;
+ }
+ }
+ if ("quarter" in duration) {
+ const floor = Math.floor(duration["quarter"]);
+ date.setMonth(date.getMonth() + floor * 3);
+ }
+ if ("month" in duration) {
+ const floor = Math.floor(duration["month"]);
+ date.setMonth(date.getMonth() + floor);
+ const remainingFraction = duration["month"] - floor;
+ if (remainingFraction > 0) {
+ duration.week = duration?.week ?? 0;
+ duration.week += remainingFraction * 4;
+ }
+ }
+ if ("week" in duration) {
+ const floor = Math.floor(duration["week"]);
+ date.setDate(date.getDate() + floor * 7);
+ const remainingFraction = duration["week"] - floor;
+ if (remainingFraction > 0) {
+ duration.day = duration?.day ?? 0;
+ duration.day += Math.round(remainingFraction * 7);
+ }
+ }
+ if ("day" in duration) {
+ const floor = Math.floor(duration["day"]);
+ date.setDate(date.getDate() + floor);
+ const remainingFraction = duration["day"] - floor;
+ if (remainingFraction > 0) {
+ duration.hour = duration?.hour ?? 0;
+ duration.hour += Math.round(remainingFraction * 24);
+ }
+ }
+ if ("hour" in duration) {
+ const floor = Math.floor(duration["hour"]);
+ date.setHours(date.getHours() + floor);
+ const remainingFraction = duration["hour"] - floor;
+ if (remainingFraction > 0) {
+ duration.minute = duration?.minute ?? 0;
+ duration.minute += Math.round(remainingFraction * 60);
+ }
+ }
+ if ("minute" in duration) {
+ const floor = Math.floor(duration["minute"]);
+ date.setMinutes(date.getMinutes() + floor);
+ const remainingFraction = duration["minute"] - floor;
+ if (remainingFraction > 0) {
+ duration.second = duration?.second ?? 0;
+ duration.second += Math.round(remainingFraction * 60);
+ }
+ }
+ if ("second" in duration) {
+ const floor = Math.floor(duration["second"]);
+ date.setSeconds(date.getSeconds() + floor);
+ const remainingFraction = duration["second"] - floor;
+ if (remainingFraction > 0) {
+ duration.millisecond = duration?.millisecond ?? 0;
+ duration.millisecond += Math.round(remainingFraction * 1000);
+ }
+ }
+ if ("millisecond" in duration) {
+ const floor = Math.floor(duration["millisecond"]);
+ date.setMilliseconds(date.getMilliseconds() + floor);
+ }
+ return date;
+}
+function reverseDuration(duration) {
+ const reversed = {};
+ for (const key in duration) {
+ reversed[key] = -duration[key];
+ }
+ return reversed;
+}
+
+// node_modules/chrono-node/dist/esm/results.js
+class ReferenceWithTimezone {
+ instant;
+ timezoneOffset;
+ constructor(instant, timezoneOffset) {
+ this.instant = instant ?? new Date;
+ this.timezoneOffset = timezoneOffset ?? null;
+ }
+ static fromDate(date) {
+ return new ReferenceWithTimezone(date);
+ }
+ static fromInput(input, timezoneOverrides) {
+ if (input instanceof Date) {
+ return ReferenceWithTimezone.fromDate(input);
+ }
+ const instant = input?.instant ?? new Date;
+ const timezoneOffset = toTimezoneOffset(input?.timezone, instant, timezoneOverrides);
+ return new ReferenceWithTimezone(instant, timezoneOffset);
+ }
+ getDateWithAdjustedTimezone() {
+ const date = new Date(this.instant);
+ if (this.timezoneOffset !== null) {
+ date.setMinutes(date.getMinutes() - this.getSystemTimezoneAdjustmentMinute(this.instant));
+ }
+ return date;
+ }
+ getSystemTimezoneAdjustmentMinute(date, overrideTimezoneOffset) {
+ if (!date || date.getTime() < 0) {
+ date = new Date;
+ }
+ const currentTimezoneOffset = -date.getTimezoneOffset();
+ const targetTimezoneOffset = overrideTimezoneOffset ?? this.timezoneOffset ?? currentTimezoneOffset;
+ return currentTimezoneOffset - targetTimezoneOffset;
+ }
+ getTimezoneOffset() {
+ return this.timezoneOffset ?? -this.instant.getTimezoneOffset();
+ }
+}
+
+class ParsingComponents {
+ knownValues;
+ impliedValues;
+ reference;
+ _tags = new Set;
+ constructor(reference, knownComponents) {
+ this.reference = reference;
+ this.knownValues = {};
+ this.impliedValues = {};
+ if (knownComponents) {
+ for (const key in knownComponents) {
+ this.knownValues[key] = knownComponents[key];
+ }
+ }
+ const date = reference.getDateWithAdjustedTimezone();
+ this.imply("day", date.getDate());
+ this.imply("month", date.getMonth() + 1);
+ this.imply("year", date.getFullYear());
+ this.imply("hour", 12);
+ this.imply("minute", 0);
+ this.imply("second", 0);
+ this.imply("millisecond", 0);
+ }
+ static createRelativeFromReference(reference, duration = EmptyDuration) {
+ let date = addDuration(reference.getDateWithAdjustedTimezone(), duration);
+ const components = new ParsingComponents(reference);
+ components.addTag("result/relativeDate");
+ if ("hour" in duration || "minute" in duration || "second" in duration || "millisecond" in duration) {
+ components.addTag("result/relativeDateAndTime");
+ assignSimilarTime(components, date);
+ assignSimilarDate(components, date);
+ components.assign("timezoneOffset", reference.getTimezoneOffset());
+ } else {
+ implySimilarTime(components, date);
+ components.imply("timezoneOffset", reference.getTimezoneOffset());
+ if ("day" in duration) {
+ components.assign("day", date.getDate());
+ components.assign("month", date.getMonth() + 1);
+ components.assign("year", date.getFullYear());
+ components.assign("weekday", date.getDay());
+ } else if ("week" in duration) {
+ components.assign("day", date.getDate());
+ components.assign("month", date.getMonth() + 1);
+ components.assign("year", date.getFullYear());
+ components.imply("weekday", date.getDay());
+ } else {
+ components.imply("day", date.getDate());
+ if ("month" in duration) {
+ components.assign("month", date.getMonth() + 1);
+ components.assign("year", date.getFullYear());
+ } else {
+ components.imply("month", date.getMonth() + 1);
+ if ("year" in duration) {
+ components.assign("year", date.getFullYear());
+ } else {
+ components.imply("year", date.getFullYear());
+ }
+ }
+ }
+ }
+ return components;
+ }
+ get(component) {
+ if (component in this.knownValues) {
+ return this.knownValues[component];
+ }
+ if (component in this.impliedValues) {
+ return this.impliedValues[component];
+ }
+ return null;
+ }
+ isCertain(component) {
+ return component in this.knownValues;
+ }
+ getCertainComponents() {
+ return Object.keys(this.knownValues);
+ }
+ imply(component, value) {
+ if (component in this.knownValues) {
+ return this;
+ }
+ this.impliedValues[component] = value;
+ return this;
+ }
+ assign(component, value) {
+ this.knownValues[component] = value;
+ delete this.impliedValues[component];
+ return this;
+ }
+ addDurationAsImplied(duration) {
+ const currentDate = this.dateWithoutTimezoneAdjustment();
+ const date = addDuration(currentDate, duration);
+ if ("day" in duration || "week" in duration || "month" in duration || "year" in duration) {
+ this.delete(["day", "weekday", "month", "year"]);
+ this.imply("day", date.getDate());
+ this.imply("weekday", date.getDay());
+ this.imply("month", date.getMonth() + 1);
+ this.imply("year", date.getFullYear());
+ }
+ if ("second" in duration || "minute" in duration || "hour" in duration) {
+ this.delete(["second", "minute", "hour"]);
+ this.imply("second", date.getSeconds());
+ this.imply("minute", date.getMinutes());
+ this.imply("hour", date.getHours());
+ }
+ return this;
+ }
+ delete(components) {
+ if (typeof components === "string") {
+ components = [components];
+ }
+ for (const component of components) {
+ delete this.knownValues[component];
+ delete this.impliedValues[component];
+ }
+ }
+ clone() {
+ const component = new ParsingComponents(this.reference);
+ component.knownValues = {};
+ component.impliedValues = {};
+ for (const key in this.knownValues) {
+ component.knownValues[key] = this.knownValues[key];
+ }
+ for (const key in this.impliedValues) {
+ component.impliedValues[key] = this.impliedValues[key];
+ }
+ return component;
+ }
+ isOnlyDate() {
+ return !this.isCertain("hour") && !this.isCertain("minute") && !this.isCertain("second");
+ }
+ isOnlyTime() {
+ return !this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month") && !this.isCertain("year");
+ }
+ isOnlyWeekdayComponent() {
+ return this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month");
+ }
+ isDateWithUnknownYear() {
+ return this.isCertain("month") && !this.isCertain("year");
+ }
+ isValidDate() {
+ const date = this.dateWithoutTimezoneAdjustment();
+ if (date.getFullYear() !== this.get("year"))
+ return false;
+ if (date.getMonth() !== this.get("month") - 1)
+ return false;
+ if (date.getDate() !== this.get("day"))
+ return false;
+ if (this.get("hour") != null && date.getHours() != this.get("hour"))
+ return false;
+ if (this.get("minute") != null && date.getMinutes() != this.get("minute"))
+ return false;
+ return true;
+ }
+ toString() {
+ return `[ParsingComponents {
+ tags: ${JSON.stringify(Array.from(this._tags).sort())},
+ knownValues: ${JSON.stringify(this.knownValues)},
+ impliedValues: ${JSON.stringify(this.impliedValues)}},
+ reference: ${JSON.stringify(this.reference)}]`;
+ }
+ date() {
+ const date = this.dateWithoutTimezoneAdjustment();
+ const timezoneAdjustment = this.reference.getSystemTimezoneAdjustmentMinute(date, this.get("timezoneOffset"));
+ return new Date(date.getTime() + timezoneAdjustment * 60000);
+ }
+ addTag(tag) {
+ this._tags.add(tag);
+ return this;
+ }
+ addTags(tags) {
+ for (const tag of tags) {
+ this._tags.add(tag);
+ }
+ return this;
+ }
+ tags() {
+ return new Set(this._tags);
+ }
+ dateWithoutTimezoneAdjustment() {
+ 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"));
+ date.setFullYear(this.get("year"));
+ return date;
+ }
+}
+
+class ParsingResult {
+ refDate;
+ index;
+ text;
+ reference;
+ start;
+ end;
+ constructor(reference, index, text, start, end) {
+ this.reference = reference;
+ this.refDate = reference.instant;
+ this.index = index;
+ this.text = text;
+ this.start = start || new ParsingComponents(reference);
+ this.end = end;
+ }
+ clone() {
+ const result = new ParsingResult(this.reference, this.index, this.text);
+ result.start = this.start ? this.start.clone() : null;
+ result.end = this.end ? this.end.clone() : null;
+ return result;
+ }
+ date() {
+ return this.start.date();
+ }
+ addTag(tag) {
+ this.start.addTag(tag);
+ if (this.end) {
+ this.end.addTag(tag);
+ }
+ return this;
+ }
+ addTags(tags) {
+ this.start.addTags(tags);
+ if (this.end) {
+ this.end.addTags(tags);
+ }
+ return this;
+ }
+ tags() {
+ const combinedTags = new Set(this.start.tags());
+ if (this.end) {
+ for (const tag of this.end.tags()) {
+ combinedTags.add(tag);
+ }
+ }
+ return combinedTags;
+ }
+ toString() {
+ const tags = Array.from(this.tags()).sort();
+ return `[ParsingResult {index: ${this.index}, text: '${this.text}', tags: ${JSON.stringify(tags)} ...}]`;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/utils/pattern.js
+function repeatedTimeunitPattern(prefix, singleTimeunitPattern, connectorPattern = "\\s{0,5},?\\s{0,5}") {
+ const singleTimeunitPatternNoCapture = singleTimeunitPattern.replace(/\((?!\?)/g, "(?:");
+ return `${prefix}${singleTimeunitPatternNoCapture}(?:${connectorPattern}${singleTimeunitPatternNoCapture}){0,10}`;
+}
+function extractTerms(dictionary) {
+ let keys;
+ if (dictionary instanceof Array) {
+ keys = [...dictionary];
+ } else if (dictionary instanceof Map) {
+ keys = Array.from(dictionary.keys());
+ } else {
+ keys = Object.keys(dictionary);
+ }
+ return keys;
+}
+function matchAnyPattern(dictionary) {
+ const joinedTerms = extractTerms(dictionary).sort((a, b) => b.length - a.length).join("|").replace(/\./g, "\\.");
+ return `(?:${joinedTerms})`;
+}
+
+// node_modules/chrono-node/dist/esm/calculation/years.js
+function findMostLikelyADYear(yearNumber) {
+ if (yearNumber < 100) {
+ if (yearNumber > 50) {
+ yearNumber = yearNumber + 1900;
+ } else {
+ yearNumber = yearNumber + 2000;
+ }
+ }
+ return yearNumber;
+}
+function findYearClosestToRef(refDate, day, month) {
+ let date = new Date(refDate);
+ date.setMonth(month - 1);
+ date.setDate(day);
+ const nextYear = addDuration(date, { year: 1 });
+ const lastYear = addDuration(date, { year: -1 });
+ if (Math.abs(nextYear.getTime() - refDate.getTime()) < Math.abs(date.getTime() - refDate.getTime())) {
+ date = nextYear;
+ } else if (Math.abs(lastYear.getTime() - refDate.getTime()) < Math.abs(date.getTime() - refDate.getTime())) {
+ date = lastYear;
+ }
+ return date.getFullYear();
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/constants.js
+var WEEKDAY_DICTIONARY = {
+ sunday: 0,
+ sun: 0,
+ "sun.": 0,
+ monday: 1,
+ mon: 1,
+ "mon.": 1,
+ tuesday: 2,
+ tue: 2,
+ "tue.": 2,
+ wednesday: 3,
+ wed: 3,
+ "wed.": 3,
+ thursday: 4,
+ thurs: 4,
+ "thurs.": 4,
+ thur: 4,
+ "thur.": 4,
+ thu: 4,
+ "thu.": 4,
+ friday: 5,
+ fri: 5,
+ "fri.": 5,
+ saturday: 6,
+ sat: 6,
+ "sat.": 6
+};
+var FULL_MONTH_NAME_DICTIONARY = {
+ january: 1,
+ february: 2,
+ march: 3,
+ april: 4,
+ may: 5,
+ june: 6,
+ july: 7,
+ august: 8,
+ september: 9,
+ october: 10,
+ november: 11,
+ december: 12
+};
+var MONTH_DICTIONARY = {
+ ...FULL_MONTH_NAME_DICTIONARY,
+ jan: 1,
+ "jan.": 1,
+ feb: 2,
+ "feb.": 2,
+ mar: 3,
+ "mar.": 3,
+ apr: 4,
+ "apr.": 4,
+ jun: 6,
+ "jun.": 6,
+ jul: 7,
+ "jul.": 7,
+ aug: 8,
+ "aug.": 8,
+ sep: 9,
+ "sep.": 9,
+ sept: 9,
+ "sept.": 9,
+ oct: 10,
+ "oct.": 10,
+ nov: 11,
+ "nov.": 11,
+ dec: 12,
+ "dec.": 12
+};
+var INTEGER_WORD_DICTIONARY = {
+ one: 1,
+ two: 2,
+ three: 3,
+ four: 4,
+ five: 5,
+ six: 6,
+ seven: 7,
+ eight: 8,
+ nine: 9,
+ ten: 10,
+ eleven: 11,
+ twelve: 12
+};
+var ORDINAL_WORD_DICTIONARY = {
+ first: 1,
+ second: 2,
+ third: 3,
+ fourth: 4,
+ fifth: 5,
+ sixth: 6,
+ seventh: 7,
+ eighth: 8,
+ ninth: 9,
+ tenth: 10,
+ eleventh: 11,
+ twelfth: 12,
+ thirteenth: 13,
+ fourteenth: 14,
+ fifteenth: 15,
+ sixteenth: 16,
+ seventeenth: 17,
+ eighteenth: 18,
+ nineteenth: 19,
+ twentieth: 20,
+ "twenty first": 21,
+ "twenty-first": 21,
+ "twenty second": 22,
+ "twenty-second": 22,
+ "twenty third": 23,
+ "twenty-third": 23,
+ "twenty fourth": 24,
+ "twenty-fourth": 24,
+ "twenty fifth": 25,
+ "twenty-fifth": 25,
+ "twenty sixth": 26,
+ "twenty-sixth": 26,
+ "twenty seventh": 27,
+ "twenty-seventh": 27,
+ "twenty eighth": 28,
+ "twenty-eighth": 28,
+ "twenty ninth": 29,
+ "twenty-ninth": 29,
+ thirtieth: 30,
+ "thirty first": 31,
+ "thirty-first": 31
+};
+var TIME_UNIT_DICTIONARY_NO_ABBR = {
+ second: "second",
+ seconds: "second",
+ minute: "minute",
+ minutes: "minute",
+ hour: "hour",
+ hours: "hour",
+ day: "day",
+ days: "day",
+ week: "week",
+ weeks: "week",
+ month: "month",
+ months: "month",
+ quarter: "quarter",
+ quarters: "quarter",
+ year: "year",
+ years: "year"
+};
+var TIME_UNIT_DICTIONARY = {
+ s: "second",
+ sec: "second",
+ second: "second",
+ seconds: "second",
+ m: "minute",
+ min: "minute",
+ mins: "minute",
+ minute: "minute",
+ minutes: "minute",
+ h: "hour",
+ hr: "hour",
+ hrs: "hour",
+ hour: "hour",
+ hours: "hour",
+ d: "day",
+ day: "day",
+ days: "day",
+ w: "week",
+ week: "week",
+ weeks: "week",
+ mo: "month",
+ mon: "month",
+ mos: "month",
+ month: "month",
+ months: "month",
+ qtr: "quarter",
+ quarter: "quarter",
+ quarters: "quarter",
+ y: "year",
+ yr: "year",
+ year: "year",
+ years: "year",
+ ...TIME_UNIT_DICTIONARY_NO_ABBR
+};
+var 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)?)`;
+function parseNumberPattern(match) {
+ const num = match.toLowerCase();
+ if (INTEGER_WORD_DICTIONARY[num] !== undefined) {
+ return INTEGER_WORD_DICTIONARY[num];
+ } else if (num === "a" || num === "an" || num == "the") {
+ return 1;
+ } else if (num.match(/few/)) {
+ return 3;
+ } else if (num.match(/half/)) {
+ return 0.5;
+ } else if (num.match(/couple/)) {
+ return 2;
+ } else if (num.match(/several/)) {
+ return 7;
+ }
+ return parseFloat(num);
+}
+var ORDINAL_NUMBER_PATTERN = `(?:${matchAnyPattern(ORDINAL_WORD_DICTIONARY)}|[0-9]{1,2}(?:st|nd|rd|th)?)`;
+function parseOrdinalNumberPattern(match) {
+ let num = match.toLowerCase();
+ if (ORDINAL_WORD_DICTIONARY[num] !== undefined) {
+ return ORDINAL_WORD_DICTIONARY[num];
+ }
+ num = num.replace(/(?:st|nd|rd|th)$/i, "");
+ return parseInt(num);
+}
+var 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])`;
+function parseYear(match) {
+ if (/BE/i.test(match)) {
+ match = match.replace(/BE/i, "");
+ return parseInt(match) - 543;
+ }
+ if (/BCE?/i.test(match)) {
+ match = match.replace(/BCE?/i, "");
+ return -parseInt(match);
+ }
+ if (/(AD|CE)/i.test(match)) {
+ match = match.replace(/(AD|CE)/i, "");
+ return parseInt(match);
+ }
+ const rawYearNumber = parseInt(match);
+ return findMostLikelyADYear(rawYearNumber);
+}
+var SINGLE_TIME_UNIT_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(TIME_UNIT_DICTIONARY)})`;
+var SINGLE_TIME_UNIT_REGEX = new RegExp(SINGLE_TIME_UNIT_PATTERN, "i");
+var SINGLE_TIME_UNIT_NO_ABBR_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(TIME_UNIT_DICTIONARY_NO_ABBR)})`;
+var TIME_UNIT_CONNECTOR_PATTERN = `\\s{0,5},?(?:\\s*and)?\\s{0,5}`;
+var TIME_UNITS_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s{0,3})?`, SINGLE_TIME_UNIT_PATTERN, TIME_UNIT_CONNECTOR_PATTERN);
+var TIME_UNITS_NO_ABBR_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s{0,3})?`, SINGLE_TIME_UNIT_NO_ABBR_PATTERN, TIME_UNIT_CONNECTOR_PATTERN);
+function parseDuration(timeunitText) {
+ const fragments = {};
+ let remainingText = timeunitText;
+ let match = SINGLE_TIME_UNIT_REGEX.exec(remainingText);
+ while (match) {
+ collectDateTimeFragment(fragments, match);
+ remainingText = remainingText.substring(match[0].length).trim();
+ match = SINGLE_TIME_UNIT_REGEX.exec(remainingText);
+ }
+ if (Object.keys(fragments).length == 0) {
+ return null;
+ }
+ return fragments;
+}
+function collectDateTimeFragment(fragments, match) {
+ if (match[0].match(/^[a-zA-Z]+$/)) {
+ return;
+ }
+ const num = parseNumberPattern(match[1]);
+ const unit = TIME_UNIT_DICTIONARY[match[2].toLowerCase()];
+ fragments[unit] = num;
+}
+
+// node_modules/chrono-node/dist/esm/common/parsers/AbstractParserWithWordBoundary.js
+class AbstractParserWithWordBoundaryChecking {
+ innerPatternHasChange(context, currentInnerPattern) {
+ return this.innerPattern(context) !== currentInnerPattern;
+ }
+ patternLeftBoundary() {
+ return `(\\W|^)`;
+ }
+ cachedInnerPattern = null;
+ cachedPattern = null;
+ pattern(context) {
+ if (this.cachedInnerPattern) {
+ if (!this.innerPatternHasChange(context, this.cachedInnerPattern)) {
+ return this.cachedPattern;
+ }
+ }
+ this.cachedInnerPattern = this.innerPattern(context);
+ this.cachedPattern = new RegExp(`${this.patternLeftBoundary()}${this.cachedInnerPattern.source}`, this.cachedInnerPattern.flags);
+ return this.cachedPattern;
+ }
+ extract(context, match) {
+ const header = match[1] ?? "";
+ match.index = match.index + header.length;
+ match[0] = match[0].substring(header.length);
+ for (let i = 2;i < match.length; i++) {
+ match[i - 1] = match[i];
+ }
+ return this.innerExtract(context, match);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.js
+var PATTERN_WITH_OPTIONAL_PREFIX = new RegExp(`(?:(?:within|in|for)\\s*)?` + `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
+var PATTERN_WITH_PREFIX = new RegExp(`(?:within|in|for)\\s*` + `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
+var PATTERN_WITH_PREFIX_STRICT = new RegExp(`(?:within|in|for)\\s*` + `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
+
+class ENTimeUnitWithinFormatParser extends AbstractParserWithWordBoundaryChecking {
+ strictMode;
+ constructor(strictMode) {
+ super();
+ this.strictMode = strictMode;
+ }
+ innerPattern(context) {
+ if (this.strictMode) {
+ return PATTERN_WITH_PREFIX_STRICT;
+ }
+ return context.option.forwardDate ? PATTERN_WITH_OPTIONAL_PREFIX : PATTERN_WITH_PREFIX;
+ }
+ innerExtract(context, match) {
+ if (match[0].match(/^for\s*the\s*\w+/)) {
+ return null;
+ }
+ const timeUnits = parseDuration(match[1]);
+ if (!timeUnits) {
+ return null;
+ }
+ return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameLittleEndianParser.js
+var 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");
+var DATE_GROUP = 1;
+var DATE_TO_GROUP = 2;
+var MONTH_NAME_GROUP = 3;
+var YEAR_GROUP = 4;
+
+class ENMonthNameLittleEndianParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN;
+ }
+ innerExtract(context, match) {
+ const result = context.createParsingResult(match.index, match[0]);
+ const month = MONTH_DICTIONARY[match[MONTH_NAME_GROUP].toLowerCase()];
+ const day = parseOrdinalNumberPattern(match[DATE_GROUP]);
+ if (day > 31) {
+ match.index = match.index + match[DATE_GROUP].length;
+ return null;
+ }
+ result.start.assign("month", month);
+ result.start.assign("day", day);
+ if (match[YEAR_GROUP]) {
+ const yearNumber = parseYear(match[YEAR_GROUP]);
+ result.start.assign("year", yearNumber);
+ } else {
+ const year = findYearClosestToRef(context.refDate, day, month);
+ result.start.imply("year", year);
+ }
+ if (match[DATE_TO_GROUP]) {
+ const endDate = parseOrdinalNumberPattern(match[DATE_TO_GROUP]);
+ result.end = result.start.clone();
+ result.end.assign("day", endDate);
+ }
+ return result;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameMiddleEndianParser.js
+var 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");
+var MONTH_NAME_GROUP2 = 1;
+var DATE_GROUP2 = 2;
+var DATE_TO_GROUP2 = 3;
+var YEAR_GROUP2 = 4;
+
+class ENMonthNameMiddleEndianParser extends AbstractParserWithWordBoundaryChecking {
+ shouldSkipYearLikeDate;
+ constructor(shouldSkipYearLikeDate) {
+ super();
+ this.shouldSkipYearLikeDate = shouldSkipYearLikeDate;
+ }
+ innerPattern() {
+ return PATTERN2;
+ }
+ innerExtract(context, match) {
+ const month = MONTH_DICTIONARY[match[MONTH_NAME_GROUP2].toLowerCase()];
+ const day = parseOrdinalNumberPattern(match[DATE_GROUP2]);
+ if (day > 31) {
+ return null;
+ }
+ if (this.shouldSkipYearLikeDate) {
+ if (!match[DATE_TO_GROUP2] && !match[YEAR_GROUP2] && match[DATE_GROUP2].match(/^2[0-5]$/)) {
+ return null;
+ }
+ }
+ const components = context.createParsingComponents({
+ day,
+ month
+ }).addTag("parser/ENMonthNameMiddleEndianParser");
+ if (match[YEAR_GROUP2]) {
+ const year = parseYear(match[YEAR_GROUP2]);
+ components.assign("year", year);
+ } else {
+ const year = findYearClosestToRef(context.refDate, day, month);
+ components.imply("year", year);
+ }
+ if (!match[DATE_TO_GROUP2]) {
+ return components;
+ }
+ const endDate = parseOrdinalNumberPattern(match[DATE_TO_GROUP2]);
+ const result = context.createParsingResult(match.index, match[0]);
+ result.start = components;
+ result.end = components.clone();
+ result.end.assign("day", endDate);
+ return result;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameParser.js
+var PATTERN3 = new RegExp(`((?:in)\\s*)?` + `(${matchAnyPattern(MONTH_DICTIONARY)})` + `\\s*` + `(?:` + `(?:,|-|of)?\\s*(${YEAR_PATTERN})?` + ")?" + "(?=[^\\s\\w]|\\s+[^0-9]|\\s+$|$)", "i");
+var PREFIX_GROUP = 1;
+var MONTH_NAME_GROUP3 = 2;
+var YEAR_GROUP3 = 3;
+
+class ENMonthNameParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN3;
+ }
+ innerExtract(context, match) {
+ const monthName = match[MONTH_NAME_GROUP3].toLowerCase();
+ if (match[0].length <= 3 && !FULL_MONTH_NAME_DICTIONARY[monthName]) {
+ return null;
+ }
+ const result = context.createParsingResult(match.index + (match[PREFIX_GROUP] || "").length, match.index + match[0].length);
+ result.start.imply("day", 1);
+ result.start.addTag("parser/ENMonthNameParser");
+ const month = MONTH_DICTIONARY[monthName];
+ result.start.assign("month", month);
+ if (match[YEAR_GROUP3]) {
+ const year = parseYear(match[YEAR_GROUP3]);
+ result.start.assign("year", year);
+ } else {
+ const year = findYearClosestToRef(context.refDate, 1, month);
+ result.start.imply("year", year);
+ }
+ return result;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENYearMonthDayParser.js
+var PATTERN4 = new RegExp(`([0-9]{4})[-\\.\\/\\s]` + `(?:(${matchAnyPattern(MONTH_DICTIONARY)})|([0-9]{1,2}))[-\\.\\/\\s]` + `([0-9]{1,2})` + "(?=\\W|$)", "i");
+var YEAR_NUMBER_GROUP = 1;
+var MONTH_NAME_GROUP4 = 2;
+var MONTH_NUMBER_GROUP = 3;
+var DATE_NUMBER_GROUP = 4;
+
+class ENYearMonthDayParser extends AbstractParserWithWordBoundaryChecking {
+ strictMonthDateOrder;
+ constructor(strictMonthDateOrder) {
+ super();
+ this.strictMonthDateOrder = strictMonthDateOrder;
+ }
+ innerPattern() {
+ return PATTERN4;
+ }
+ innerExtract(context, match) {
+ const year = parseInt(match[YEAR_NUMBER_GROUP]);
+ let day = parseInt(match[DATE_NUMBER_GROUP]);
+ let month = match[MONTH_NUMBER_GROUP] ? parseInt(match[MONTH_NUMBER_GROUP]) : MONTH_DICTIONARY[match[MONTH_NAME_GROUP4].toLowerCase()];
+ if (month < 1 || month > 12) {
+ if (this.strictMonthDateOrder) {
+ return null;
+ }
+ if (day >= 1 && day <= 12) {
+ [month, day] = [day, month];
+ }
+ }
+ if (day < 1 || day > 31) {
+ return null;
+ }
+ return {
+ day,
+ month,
+ year
+ };
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENSlashMonthFormatParser.js
+var PATTERN5 = new RegExp("([0-9]|0[1-9]|1[012])/([0-9]{4})" + "", "i");
+var MONTH_GROUP = 1;
+var YEAR_GROUP4 = 2;
+
+class ENSlashMonthFormatParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN5;
+ }
+ innerExtract(context, match) {
+ const year = parseInt(match[YEAR_GROUP4]);
+ const month = parseInt(match[MONTH_GROUP]);
+ return context.createParsingComponents().imply("day", 1).assign("month", month).assign("year", year);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/parsers/AbstractTimeExpressionParser.js
+function primaryTimePattern(leftBoundary, primaryPrefix, primarySuffix, flags) {
+ 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);
+}
+function followingTimePatten(followingPhase, followingSuffix) {
+ 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");
+}
+var HOUR_GROUP = 2;
+var MINUTE_GROUP = 3;
+var SECOND_GROUP = 4;
+var MILLI_SECOND_GROUP = 5;
+var AM_PM_HOUR_GROUP = 6;
+
+class AbstractTimeExpressionParser {
+ strictMode;
+ constructor(strictMode = false) {
+ this.strictMode = strictMode;
+ }
+ patternFlags() {
+ return "i";
+ }
+ primaryPatternLeftBoundary() {
+ return `(^|\\s|T|\\b)`;
+ }
+ primarySuffix() {
+ return `(?!/)(?=\\W|$)`;
+ }
+ followingSuffix() {
+ return `(?!/)(?=\\W|$)`;
+ }
+ pattern(context) {
+ return this.getPrimaryTimePatternThroughCache();
+ }
+ extract(context, match) {
+ const startComponents = this.extractPrimaryTimeComponents(context, match);
+ if (!startComponents) {
+ if (match[0].match(/^\d{4}/)) {
+ match.index += 4;
+ return null;
+ }
+ match.index += match[0].length;
+ return null;
+ }
+ const index = match.index + match[1].length;
+ const text = match[0].substring(match[1].length);
+ const result = context.createParsingResult(index, text, startComponents);
+ match.index += match[0].length;
+ const remainingText = context.text.substring(match.index);
+ const followingPattern = this.getFollowingTimePatternThroughCache();
+ const followingMatch = followingPattern.exec(remainingText);
+ if (text.match(/^\d{3,4}/) && followingMatch) {
+ if (followingMatch[0].match(/^\s*([+-])\s*\d{2,4}$/)) {
+ return null;
+ }
+ if (followingMatch[0].match(/^\s*([+-])\s*\d{2}\W\d{2}/)) {
+ return null;
+ }
+ }
+ if (!followingMatch || followingMatch[0].match(/^\s*([+-])\s*\d{3,4}$/)) {
+ return this.checkAndReturnWithoutFollowingPattern(result);
+ }
+ result.end = this.extractFollowingTimeComponents(context, followingMatch, result);
+ if (result.end) {
+ result.text += followingMatch[0];
+ }
+ return this.checkAndReturnWithFollowingPattern(result);
+ }
+ extractPrimaryTimeComponents(context, match, strict = false) {
+ const components = context.createParsingComponents();
+ let minute = 0;
+ let meridiem = null;
+ let hour = parseInt(match[HOUR_GROUP]);
+ if (hour > 100) {
+ if (match[HOUR_GROUP].length == 4 && match[MINUTE_GROUP] == null && !match[AM_PM_HOUR_GROUP]) {
+ return null;
+ }
+ if (this.strictMode || match[MINUTE_GROUP] != null) {
+ return null;
+ }
+ minute = hour % 100;
+ hour = Math.floor(hour / 100);
+ }
+ if (hour > 24) {
+ return null;
+ }
+ if (match[MINUTE_GROUP] != null) {
+ if (match[MINUTE_GROUP].length == 1 && !match[AM_PM_HOUR_GROUP]) {
+ return null;
+ }
+ minute = parseInt(match[MINUTE_GROUP]);
+ }
+ if (minute >= 60) {
+ return null;
+ }
+ if (hour > 12) {
+ meridiem = Meridiem.PM;
+ }
+ if (match[AM_PM_HOUR_GROUP] != null) {
+ if (hour > 12)
+ return null;
+ const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase();
+ if (ampm == "a") {
+ meridiem = Meridiem.AM;
+ if (hour == 12) {
+ hour = 0;
+ }
+ }
+ if (ampm == "p") {
+ meridiem = Meridiem.PM;
+ if (hour != 12) {
+ hour += 12;
+ }
+ }
+ }
+ components.assign("hour", hour);
+ components.assign("minute", minute);
+ if (meridiem !== null) {
+ components.assign("meridiem", meridiem);
+ } else {
+ if (hour < 12) {
+ components.imply("meridiem", Meridiem.AM);
+ } else {
+ components.imply("meridiem", Meridiem.PM);
+ }
+ }
+ if (match[MILLI_SECOND_GROUP] != null) {
+ const millisecond = parseInt(match[MILLI_SECOND_GROUP].substring(0, 3));
+ if (millisecond >= 1000)
+ return null;
+ components.assign("millisecond", millisecond);
+ }
+ if (match[SECOND_GROUP] != null) {
+ const second = parseInt(match[SECOND_GROUP]);
+ if (second >= 60)
+ return null;
+ components.assign("second", second);
+ }
+ return components;
+ }
+ extractFollowingTimeComponents(context, match, result) {
+ const components = context.createParsingComponents();
+ if (match[MILLI_SECOND_GROUP] != null) {
+ const millisecond = parseInt(match[MILLI_SECOND_GROUP].substring(0, 3));
+ if (millisecond >= 1000)
+ return null;
+ components.assign("millisecond", millisecond);
+ }
+ if (match[SECOND_GROUP] != null) {
+ const second = parseInt(match[SECOND_GROUP]);
+ if (second >= 60)
+ return null;
+ components.assign("second", second);
+ }
+ let hour = parseInt(match[HOUR_GROUP]);
+ let minute = 0;
+ let meridiem = -1;
+ if (match[MINUTE_GROUP] != null) {
+ minute = parseInt(match[MINUTE_GROUP]);
+ } else if (hour > 100) {
+ minute = hour % 100;
+ hour = Math.floor(hour / 100);
+ }
+ if (minute >= 60 || hour > 24) {
+ return null;
+ }
+ if (hour >= 12) {
+ meridiem = Meridiem.PM;
+ }
+ if (match[AM_PM_HOUR_GROUP] != null) {
+ if (hour > 12) {
+ return null;
+ }
+ const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase();
+ if (ampm == "a") {
+ meridiem = Meridiem.AM;
+ if (hour == 12) {
+ hour = 0;
+ if (!components.isCertain("day")) {
+ components.imply("day", components.get("day") + 1);
+ }
+ }
+ }
+ if (ampm == "p") {
+ meridiem = Meridiem.PM;
+ if (hour != 12)
+ hour += 12;
+ }
+ if (!result.start.isCertain("meridiem")) {
+ if (meridiem == Meridiem.AM) {
+ result.start.imply("meridiem", Meridiem.AM);
+ if (result.start.get("hour") == 12) {
+ result.start.assign("hour", 0);
+ }
+ } else {
+ result.start.imply("meridiem", Meridiem.PM);
+ if (result.start.get("hour") != 12) {
+ result.start.assign("hour", result.start.get("hour") + 12);
+ }
+ }
+ }
+ }
+ components.assign("hour", hour);
+ components.assign("minute", minute);
+ if (meridiem >= 0) {
+ components.assign("meridiem", meridiem);
+ } else {
+ const startAtPM = result.start.isCertain("meridiem") && result.start.get("hour") > 12;
+ if (startAtPM) {
+ if (result.start.get("hour") - 12 > hour) {
+ components.imply("meridiem", Meridiem.AM);
+ } else if (hour <= 12) {
+ components.assign("hour", hour + 12);
+ components.assign("meridiem", Meridiem.PM);
+ }
+ } else if (hour > 12) {
+ components.imply("meridiem", Meridiem.PM);
+ } else if (hour <= 12) {
+ components.imply("meridiem", Meridiem.AM);
+ }
+ }
+ if (components.date().getTime() < result.start.date().getTime()) {
+ components.imply("day", components.get("day") + 1);
+ }
+ return components;
+ }
+ checkAndReturnWithoutFollowingPattern(result) {
+ if (result.text.match(/^\d$/)) {
+ return null;
+ }
+ if (result.text.match(/^\d\d\d+$/)) {
+ return null;
+ }
+ if (result.text.match(/\d[apAP]$/)) {
+ return null;
+ }
+ const endingWithNumbers = result.text.match(/[^\d:.](\d[\d.]+)$/);
+ if (endingWithNumbers) {
+ const endingNumbers = endingWithNumbers[1];
+ if (this.strictMode) {
+ return null;
+ }
+ if (endingNumbers.includes(".") && !endingNumbers.match(/\d(\.\d{2})+$/)) {
+ return null;
+ }
+ const endingNumberVal = parseInt(endingNumbers);
+ if (endingNumberVal > 24) {
+ return null;
+ }
+ }
+ return result;
+ }
+ checkAndReturnWithFollowingPattern(result) {
+ if (result.text.match(/^\d+-\d+$/)) {
+ return null;
+ }
+ const endingWithNumbers = result.text.match(/[^\d:.](\d[\d.]+)\s*-\s*(\d[\d.]+)$/);
+ if (endingWithNumbers) {
+ if (this.strictMode) {
+ return null;
+ }
+ const startingNumbers = endingWithNumbers[1];
+ const endingNumbers = endingWithNumbers[2];
+ if (endingNumbers.includes(".") && !endingNumbers.match(/\d(\.\d{2})+$/)) {
+ return null;
+ }
+ const endingNumberVal = parseInt(endingNumbers);
+ const startingNumberVal = parseInt(startingNumbers);
+ if (endingNumberVal > 24 || startingNumberVal > 24) {
+ return null;
+ }
+ }
+ return result;
+ }
+ cachedPrimaryPrefix = null;
+ cachedPrimarySuffix = null;
+ cachedPrimaryTimePattern = null;
+ getPrimaryTimePatternThroughCache() {
+ const primaryPrefix = this.primaryPrefix();
+ const primarySuffix = this.primarySuffix();
+ if (this.cachedPrimaryPrefix === primaryPrefix && this.cachedPrimarySuffix === primarySuffix) {
+ return this.cachedPrimaryTimePattern;
+ }
+ this.cachedPrimaryTimePattern = primaryTimePattern(this.primaryPatternLeftBoundary(), primaryPrefix, primarySuffix, this.patternFlags());
+ this.cachedPrimaryPrefix = primaryPrefix;
+ this.cachedPrimarySuffix = primarySuffix;
+ return this.cachedPrimaryTimePattern;
+ }
+ cachedFollowingPhase = null;
+ cachedFollowingSuffix = null;
+ cachedFollowingTimePatten = null;
+ getFollowingTimePatternThroughCache() {
+ const followingPhase = this.followingPhase();
+ const followingSuffix = this.followingSuffix();
+ if (this.cachedFollowingPhase === followingPhase && this.cachedFollowingSuffix === followingSuffix) {
+ return this.cachedFollowingTimePatten;
+ }
+ this.cachedFollowingTimePatten = followingTimePatten(followingPhase, followingSuffix);
+ this.cachedFollowingPhase = followingPhase;
+ this.cachedFollowingSuffix = followingSuffix;
+ return this.cachedFollowingTimePatten;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeExpressionParser.js
+class ENTimeExpressionParser extends AbstractTimeExpressionParser {
+ constructor(strictMode) {
+ super(strictMode);
+ }
+ followingPhase() {
+ return "\\s*(?:\\-|\\–|\\~|\\〜|to|until|through|till|\\?)\\s*";
+ }
+ primaryPrefix() {
+ return "(?:(?:at|from)\\s*)??";
+ }
+ primarySuffix() {
+ return "(?:\\s*(?:o\\W*clock|at\\s*night|in\\s*the\\s*(?:morning|afternoon)))?(?!/)(?=\\W|$)";
+ }
+ extractPrimaryTimeComponents(context, match) {
+ const components = super.extractPrimaryTimeComponents(context, match);
+ if (!components) {
+ return components;
+ }
+ if (match[0].endsWith("night")) {
+ const hour = components.get("hour");
+ if (hour >= 6 && hour < 12) {
+ components.assign("hour", components.get("hour") + 12);
+ components.assign("meridiem", Meridiem.PM);
+ } else if (hour < 6) {
+ components.assign("meridiem", Meridiem.AM);
+ }
+ }
+ if (match[0].endsWith("afternoon")) {
+ components.assign("meridiem", Meridiem.PM);
+ const hour = components.get("hour");
+ if (hour >= 0 && hour <= 6) {
+ components.assign("hour", components.get("hour") + 12);
+ }
+ }
+ if (match[0].endsWith("morning")) {
+ components.assign("meridiem", Meridiem.AM);
+ const hour = components.get("hour");
+ if (hour < 12) {
+ components.assign("hour", components.get("hour"));
+ }
+ }
+ return components.addTag("parser/ENTimeExpressionParser");
+ }
+ extractFollowingTimeComponents(context, match, result) {
+ const followingComponents = super.extractFollowingTimeComponents(context, match, result);
+ if (followingComponents) {
+ followingComponents.addTag("parser/ENTimeExpressionParser");
+ }
+ return followingComponents;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitAgoFormatParser.js
+var PATTERN6 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
+var STRICT_PATTERN = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
+
+class ENTimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
+ strictMode;
+ constructor(strictMode) {
+ super();
+ this.strictMode = strictMode;
+ }
+ innerPattern() {
+ return this.strictMode ? STRICT_PATTERN : PATTERN6;
+ }
+ innerExtract(context, match) {
+ const duration = parseDuration(match[1]);
+ if (!duration) {
+ return null;
+ }
+ return ParsingComponents.createRelativeFromReference(context.reference, reverseDuration(duration));
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitLaterFormatParser.js
+var PATTERN7 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:later|after|from now|henceforth|forward|out)` + "(?=(?:\\W|$))", "i");
+var STRICT_PATTERN2 = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(later|after|from now)(?=\\W|$)`, "i");
+var GROUP_NUM_TIMEUNITS = 1;
+
+class ENTimeUnitLaterFormatParser extends AbstractParserWithWordBoundaryChecking {
+ strictMode;
+ constructor(strictMode) {
+ super();
+ this.strictMode = strictMode;
+ }
+ innerPattern() {
+ return this.strictMode ? STRICT_PATTERN2 : PATTERN7;
+ }
+ innerExtract(context, match) {
+ const timeUnits = parseDuration(match[GROUP_NUM_TIMEUNITS]);
+ if (!timeUnits) {
+ return null;
+ }
+ return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/abstractRefiners.js
+class Filter {
+ refine(context, results) {
+ return results.filter((r) => this.isValid(context, r));
+ }
+}
+
+class MergingRefiner {
+ refine(context, results) {
+ if (results.length < 2) {
+ return results;
+ }
+ const mergedResults = [];
+ let curResult = results[0];
+ let nextResult = null;
+ for (let i = 1;i < results.length; i++) {
+ nextResult = results[i];
+ const textBetween = context.text.substring(curResult.index + curResult.text.length, nextResult.index);
+ if (!this.shouldMergeResults(textBetween, curResult, nextResult, context)) {
+ mergedResults.push(curResult);
+ curResult = nextResult;
+ } else {
+ const left = curResult;
+ const right = nextResult;
+ const mergedResult = this.mergeResults(textBetween, left, right, context);
+ context.debug(() => {
+ console.log(`${this.constructor.name} merged ${left} and ${right} into ${mergedResult}`);
+ });
+ curResult = mergedResult;
+ }
+ }
+ if (curResult != null) {
+ mergedResults.push(curResult);
+ }
+ return mergedResults;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateRangeRefiner.js
+class AbstractMergeDateRangeRefiner extends MergingRefiner {
+ shouldMergeResults(textBetween, currentResult, nextResult) {
+ return !currentResult.end && !nextResult.end && textBetween.match(this.patternBetween()) != null;
+ }
+ mergeResults(textBetween, fromResult, toResult) {
+ if (!fromResult.start.isOnlyWeekdayComponent() && !toResult.start.isOnlyWeekdayComponent()) {
+ toResult.start.getCertainComponents().forEach((key) => {
+ if (!fromResult.start.isCertain(key)) {
+ fromResult.start.imply(key, toResult.start.get(key));
+ }
+ });
+ fromResult.start.getCertainComponents().forEach((key) => {
+ if (!toResult.start.isCertain(key)) {
+ toResult.start.imply(key, fromResult.start.get(key));
+ }
+ });
+ }
+ if (fromResult.start.date() > toResult.start.date()) {
+ let fromDate = fromResult.start.date();
+ let toDate = toResult.start.date();
+ if (toResult.start.isOnlyWeekdayComponent() && addDuration(toDate, { day: 7 }) > fromDate) {
+ toDate = addDuration(toDate, { day: 7 });
+ toResult.start.imply("day", toDate.getDate());
+ toResult.start.imply("month", toDate.getMonth() + 1);
+ toResult.start.imply("year", toDate.getFullYear());
+ } else if (fromResult.start.isOnlyWeekdayComponent() && addDuration(fromDate, { day: -7 }) < toDate) {
+ fromDate = addDuration(fromDate, { day: -7 });
+ fromResult.start.imply("day", fromDate.getDate());
+ fromResult.start.imply("month", fromDate.getMonth() + 1);
+ fromResult.start.imply("year", fromDate.getFullYear());
+ } else if (toResult.start.isDateWithUnknownYear() && addDuration(toDate, { year: 1 }) > fromDate) {
+ toDate = addDuration(toDate, { year: 1 });
+ toResult.start.imply("year", toDate.getFullYear());
+ } else if (fromResult.start.isDateWithUnknownYear() && addDuration(fromDate, { year: -1 }) < toDate) {
+ fromDate = addDuration(fromDate, { year: -1 });
+ fromResult.start.imply("year", fromDate.getFullYear());
+ } else {
+ [toResult, fromResult] = [fromResult, toResult];
+ }
+ }
+ const result = fromResult.clone();
+ result.start = fromResult.start;
+ result.end = toResult.start;
+ result.index = Math.min(fromResult.index, toResult.index);
+ if (fromResult.index < toResult.index) {
+ result.text = fromResult.text + textBetween + toResult.text;
+ } else {
+ result.text = toResult.text + textBetween + fromResult.text;
+ }
+ return result;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateRangeRefiner.js
+class ENMergeDateRangeRefiner extends AbstractMergeDateRangeRefiner {
+ patternBetween() {
+ return /^\s*(to|-|–|until|through|till)\s*$/i;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/calculation/mergingCalculation.js
+function mergeDateTimeResult(dateResult, timeResult) {
+ const result = dateResult.clone();
+ const beginDate = dateResult.start;
+ const beginTime = timeResult.start;
+ result.start = mergeDateTimeComponent(beginDate, beginTime);
+ if (dateResult.end != null || timeResult.end != null) {
+ const endDate = dateResult.end == null ? dateResult.start : dateResult.end;
+ const endTime = timeResult.end == null ? timeResult.start : timeResult.end;
+ const endDateTime = mergeDateTimeComponent(endDate, endTime);
+ if (dateResult.end == null && endDateTime.date().getTime() < result.start.date().getTime()) {
+ const nextDay = new Date(endDateTime.date().getTime());
+ nextDay.setDate(nextDay.getDate() + 1);
+ if (endDateTime.isCertain("day")) {
+ assignSimilarDate(endDateTime, nextDay);
+ } else {
+ implySimilarDate(endDateTime, nextDay);
+ }
+ }
+ result.end = endDateTime;
+ }
+ return result;
+}
+function mergeDateTimeComponent(dateComponent, timeComponent) {
+ const dateTimeComponent = dateComponent.clone();
+ if (timeComponent.isCertain("hour")) {
+ dateTimeComponent.assign("hour", timeComponent.get("hour"));
+ dateTimeComponent.assign("minute", timeComponent.get("minute"));
+ if (timeComponent.isCertain("second")) {
+ dateTimeComponent.assign("second", timeComponent.get("second"));
+ if (timeComponent.isCertain("millisecond")) {
+ dateTimeComponent.assign("millisecond", timeComponent.get("millisecond"));
+ } else {
+ dateTimeComponent.imply("millisecond", timeComponent.get("millisecond"));
+ }
+ } else {
+ dateTimeComponent.imply("second", timeComponent.get("second"));
+ dateTimeComponent.imply("millisecond", timeComponent.get("millisecond"));
+ }
+ } else {
+ dateTimeComponent.imply("hour", timeComponent.get("hour"));
+ dateTimeComponent.imply("minute", timeComponent.get("minute"));
+ dateTimeComponent.imply("second", timeComponent.get("second"));
+ dateTimeComponent.imply("millisecond", timeComponent.get("millisecond"));
+ }
+ if (timeComponent.isCertain("timezoneOffset")) {
+ dateTimeComponent.assign("timezoneOffset", timeComponent.get("timezoneOffset"));
+ }
+ if (timeComponent.isCertain("meridiem")) {
+ dateTimeComponent.assign("meridiem", timeComponent.get("meridiem"));
+ } else if (timeComponent.get("meridiem") != null && dateTimeComponent.get("meridiem") == null) {
+ dateTimeComponent.imply("meridiem", timeComponent.get("meridiem"));
+ }
+ if (dateTimeComponent.get("meridiem") == Meridiem.PM && dateTimeComponent.get("hour") < 12) {
+ if (timeComponent.isCertain("hour")) {
+ dateTimeComponent.assign("hour", dateTimeComponent.get("hour") + 12);
+ } else {
+ dateTimeComponent.imply("hour", dateTimeComponent.get("hour") + 12);
+ }
+ }
+ dateTimeComponent.addTags(dateComponent.tags());
+ dateTimeComponent.addTags(timeComponent.tags());
+ return dateTimeComponent;
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateTimeRefiner.js
+class AbstractMergeDateTimeRefiner extends MergingRefiner {
+ shouldMergeResults(textBetween, currentResult, nextResult) {
+ return (currentResult.start.isOnlyDate() && nextResult.start.isOnlyTime() || nextResult.start.isOnlyDate() && currentResult.start.isOnlyTime()) && textBetween.match(this.patternBetween()) != null;
+ }
+ mergeResults(textBetween, currentResult, nextResult) {
+ const result = currentResult.start.isOnlyDate() ? mergeDateTimeResult(currentResult, nextResult) : mergeDateTimeResult(nextResult, currentResult);
+ result.index = currentResult.index;
+ result.text = currentResult.text + textBetween + nextResult.text;
+ return result;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateTimeRefiner.js
+class ENMergeDateTimeRefiner extends AbstractMergeDateTimeRefiner {
+ patternBetween() {
+ return new RegExp("^\\s*(T|at|after|before|on|of|,|-|\\.|∙|:)?\\s*$");
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js
+var TIMEZONE_NAME_PATTERN = new RegExp("^\\s*,?\\s*\\(?([A-Z]{2,4})\\)?(?=\\W|$)", "i");
+
+class ExtractTimezoneAbbrRefiner {
+ timezoneOverrides;
+ constructor(timezoneOverrides) {
+ this.timezoneOverrides = timezoneOverrides;
+ }
+ refine(context, results) {
+ const timezoneOverrides = context.option.timezones ?? {};
+ results.forEach((result) => {
+ const suffix = context.text.substring(result.index + result.text.length);
+ const match = TIMEZONE_NAME_PATTERN.exec(suffix);
+ if (!match) {
+ return;
+ }
+ const timezoneAbbr = match[1].toUpperCase();
+ const refDate = result.start.date() ?? result.refDate ?? new Date;
+ const tzOverrides = { ...this.timezoneOverrides, ...timezoneOverrides };
+ const extractedTimezoneOffset = toTimezoneOffset(timezoneAbbr, refDate, tzOverrides);
+ if (extractedTimezoneOffset == null) {
+ return;
+ }
+ context.debug(() => {
+ console.log(`Extracting timezone: '${timezoneAbbr}' into: ${extractedTimezoneOffset} for: ${result.start}`);
+ });
+ const currentTimezoneOffset = result.start.get("timezoneOffset");
+ if (currentTimezoneOffset !== null && extractedTimezoneOffset != currentTimezoneOffset) {
+ if (result.start.isCertain("timezoneOffset")) {
+ return;
+ }
+ if (timezoneAbbr != match[1]) {
+ return;
+ }
+ }
+ if (result.start.isOnlyDate()) {
+ if (timezoneAbbr != match[1]) {
+ return;
+ }
+ }
+ result.text += match[0];
+ if (!result.start.isCertain("timezoneOffset")) {
+ result.start.assign("timezoneOffset", extractedTimezoneOffset);
+ }
+ if (result.end != null && !result.end.isCertain("timezoneOffset")) {
+ result.end.assign("timezoneOffset", extractedTimezoneOffset);
+ }
+ });
+ return results;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneOffsetRefiner.js
+var TIMEZONE_OFFSET_PATTERN = new RegExp("^\\s*(?:\\(?(?:GMT|UTC)\\s?)?([+-])(\\d{1,2})(?::?(\\d{2}))?\\)?", "i");
+var TIMEZONE_OFFSET_SIGN_GROUP = 1;
+var TIMEZONE_OFFSET_HOUR_OFFSET_GROUP = 2;
+var TIMEZONE_OFFSET_MINUTE_OFFSET_GROUP = 3;
+
+class ExtractTimezoneOffsetRefiner {
+ refine(context, results) {
+ results.forEach(function(result) {
+ if (result.start.isCertain("timezoneOffset")) {
+ return;
+ }
+ const suffix = context.text.substring(result.index + result.text.length);
+ const match = TIMEZONE_OFFSET_PATTERN.exec(suffix);
+ if (!match) {
+ return;
+ }
+ context.debug(() => {
+ console.log(`Extracting timezone: '${match[0]}' into : ${result}`);
+ });
+ const hourOffset = parseInt(match[TIMEZONE_OFFSET_HOUR_OFFSET_GROUP]);
+ const minuteOffset = parseInt(match[TIMEZONE_OFFSET_MINUTE_OFFSET_GROUP] || "0");
+ let timezoneOffset = hourOffset * 60 + minuteOffset;
+ if (timezoneOffset > 14 * 60) {
+ return;
+ }
+ if (match[TIMEZONE_OFFSET_SIGN_GROUP] === "-") {
+ timezoneOffset = -timezoneOffset;
+ }
+ if (result.end != null) {
+ result.end.assign("timezoneOffset", timezoneOffset);
+ }
+ result.start.assign("timezoneOffset", timezoneOffset);
+ result.text += match[0];
+ });
+ return results;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/OverlapRemovalRefiner.js
+class OverlapRemovalRefiner {
+ refine(context, results) {
+ if (results.length < 2) {
+ return results;
+ }
+ const filteredResults = [];
+ let prevResult = results[0];
+ for (let i = 1;i < results.length; i++) {
+ const result = results[i];
+ if (result.index >= prevResult.index + prevResult.text.length) {
+ filteredResults.push(prevResult);
+ prevResult = result;
+ continue;
+ }
+ let kept = null;
+ let removed = null;
+ if (result.text.length > prevResult.text.length) {
+ kept = result;
+ removed = prevResult;
+ } else {
+ kept = prevResult;
+ removed = result;
+ }
+ context.debug(() => {
+ console.log(`${this.constructor.name} remove ${removed} by ${kept}`);
+ });
+ prevResult = kept;
+ }
+ if (prevResult != null) {
+ filteredResults.push(prevResult);
+ }
+ return filteredResults;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/ForwardDateRefiner.js
+class ForwardDateRefiner {
+ refine(context, results) {
+ if (!context.option.forwardDate) {
+ return results;
+ }
+ results.forEach((result) => {
+ let refDate = context.reference.getDateWithAdjustedTimezone();
+ if (result.start.isOnlyTime() && context.reference.instant > result.start.date()) {
+ const refDate2 = context.reference.getDateWithAdjustedTimezone();
+ const refFollowingDay = new Date(refDate2);
+ refFollowingDay.setDate(refFollowingDay.getDate() + 1);
+ implySimilarDate(result.start, refFollowingDay);
+ context.debug(() => {
+ console.log(`${this.constructor.name} adjusted ${result} time from the ref date (${refDate2}) to the following day (${refFollowingDay})`);
+ });
+ if (result.end && result.end.isOnlyTime()) {
+ implySimilarDate(result.end, refFollowingDay);
+ if (result.start.date() > result.end.date()) {
+ refFollowingDay.setDate(refFollowingDay.getDate() + 1);
+ implySimilarDate(result.end, refFollowingDay);
+ }
+ }
+ }
+ if (result.start.isOnlyWeekdayComponent() && refDate > result.start.date()) {
+ let daysToAdd = result.start.get("weekday") - refDate.getDay();
+ if (daysToAdd <= 0) {
+ daysToAdd += 7;
+ }
+ refDate = addDuration(refDate, { day: daysToAdd });
+ implySimilarDate(result.start, refDate);
+ context.debug(() => {
+ console.log(`${this.constructor.name} adjusted ${result} weekday (${result.start})`);
+ });
+ if (result.end && result.end.isOnlyWeekdayComponent()) {
+ let daysToAdd2 = result.end.get("weekday") - refDate.getDay();
+ if (daysToAdd2 <= 0) {
+ daysToAdd2 += 7;
+ }
+ refDate = addDuration(refDate, { day: daysToAdd2 });
+ implySimilarDate(result.end, refDate);
+ context.debug(() => {
+ console.log(`${this.constructor.name} adjusted ${result} weekday (${result.end})`);
+ });
+ }
+ }
+ if (result.start.isDateWithUnknownYear() && refDate > result.start.date()) {
+ for (let i = 0;i < 3 && refDate > result.start.date(); i++) {
+ result.start.imply("year", result.start.get("year") + 1);
+ context.debug(() => {
+ console.log(`${this.constructor.name} adjusted ${result} year (${result.start})`);
+ });
+ if (result.end && !result.end.isCertain("year")) {
+ result.end.imply("year", result.end.get("year") + 1);
+ context.debug(() => {
+ console.log(`${this.constructor.name} adjusted ${result} month (${result.start})`);
+ });
+ }
+ }
+ }
+ });
+ return results;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/UnlikelyFormatFilter.js
+class UnlikelyFormatFilter extends Filter {
+ strictMode;
+ constructor(strictMode) {
+ super();
+ this.strictMode = strictMode;
+ }
+ isValid(context, result) {
+ if (result.text.replace(" ", "").match(/^\d*(\.\d*)?$/)) {
+ context.debug(() => {
+ console.log(`Removing unlikely result '${result.text}'`);
+ });
+ return false;
+ }
+ if (!result.start.isValidDate()) {
+ context.debug(() => {
+ console.log(`Removing invalid result: ${result} (${result.start})`);
+ });
+ return false;
+ }
+ if (result.end && !result.end.isValidDate()) {
+ context.debug(() => {
+ console.log(`Removing invalid result: ${result} (${result.end})`);
+ });
+ return false;
+ }
+ if (this.strictMode) {
+ return this.isStrictModeValid(context, result);
+ }
+ return true;
+ }
+ isStrictModeValid(context, result) {
+ if (result.start.isOnlyWeekdayComponent()) {
+ context.debug(() => {
+ console.log(`(Strict) Removing weekday only component: ${result} (${result.end})`);
+ });
+ return false;
+ }
+ return true;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/parsers/ISOFormatParser.js
+var 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");
+var YEAR_NUMBER_GROUP2 = 1;
+var MONTH_NUMBER_GROUP2 = 2;
+var DATE_NUMBER_GROUP2 = 3;
+var HOUR_NUMBER_GROUP = 4;
+var MINUTE_NUMBER_GROUP = 5;
+var SECOND_NUMBER_GROUP = 6;
+var MILLISECOND_NUMBER_GROUP = 7;
+var TZD_GROUP = 8;
+var TZD_HOUR_OFFSET_GROUP = 9;
+var TZD_MINUTE_OFFSET_GROUP = 10;
+
+class ISOFormatParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN8;
+ }
+ innerExtract(context, match) {
+ const components = context.createParsingComponents({
+ year: parseInt(match[YEAR_NUMBER_GROUP2]),
+ month: parseInt(match[MONTH_NUMBER_GROUP2]),
+ day: parseInt(match[DATE_NUMBER_GROUP2])
+ });
+ if (match[HOUR_NUMBER_GROUP] != null) {
+ components.assign("hour", parseInt(match[HOUR_NUMBER_GROUP]));
+ components.assign("minute", parseInt(match[MINUTE_NUMBER_GROUP]));
+ if (match[SECOND_NUMBER_GROUP] != null) {
+ components.assign("second", parseInt(match[SECOND_NUMBER_GROUP]));
+ }
+ if (match[MILLISECOND_NUMBER_GROUP] != null) {
+ components.assign("millisecond", parseInt(match[MILLISECOND_NUMBER_GROUP]));
+ }
+ if (match[TZD_GROUP] != null) {
+ let offset = 0;
+ if (match[TZD_HOUR_OFFSET_GROUP]) {
+ const hourOffset = parseInt(match[TZD_HOUR_OFFSET_GROUP]);
+ let minuteOffset = 0;
+ if (match[TZD_MINUTE_OFFSET_GROUP] != null) {
+ minuteOffset = parseInt(match[TZD_MINUTE_OFFSET_GROUP]);
+ }
+ offset = hourOffset * 60;
+ if (offset < 0) {
+ offset -= minuteOffset;
+ } else {
+ offset += minuteOffset;
+ }
+ }
+ components.assign("timezoneOffset", offset);
+ }
+ }
+ return components.addTag("parser/ISOFormatParser");
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/refiners/MergeWeekdayComponentRefiner.js
+class MergeWeekdayComponentRefiner extends MergingRefiner {
+ mergeResults(textBetween, currentResult, nextResult) {
+ const newResult = nextResult.clone();
+ newResult.index = currentResult.index;
+ newResult.text = currentResult.text + textBetween + newResult.text;
+ newResult.start.assign("weekday", currentResult.start.get("weekday"));
+ if (newResult.end) {
+ newResult.end.assign("weekday", currentResult.start.get("weekday"));
+ }
+ return newResult;
+ }
+ shouldMergeResults(textBetween, currentResult, nextResult) {
+ const weekdayThenNormalDate = currentResult.start.isOnlyWeekdayComponent() && !currentResult.start.isCertain("hour") && nextResult.start.isCertain("day");
+ return weekdayThenNormalDate && textBetween.match(/^,?\s*$/) != null;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/configurations.js
+function includeCommonConfiguration(configuration, strictMode = false) {
+ configuration.parsers.unshift(new ISOFormatParser);
+ configuration.refiners.unshift(new MergeWeekdayComponentRefiner);
+ configuration.refiners.unshift(new ExtractTimezoneOffsetRefiner);
+ configuration.refiners.unshift(new OverlapRemovalRefiner);
+ configuration.refiners.push(new ExtractTimezoneAbbrRefiner);
+ configuration.refiners.push(new OverlapRemovalRefiner);
+ configuration.refiners.push(new ForwardDateRefiner);
+ configuration.refiners.push(new UnlikelyFormatFilter(strictMode));
+ return configuration;
+}
+
+// node_modules/chrono-node/dist/esm/common/casualReferences.js
+function now(reference) {
+ const targetDate = reference.getDateWithAdjustedTimezone();
+ const component = new ParsingComponents(reference, {});
+ assignSimilarDate(component, targetDate);
+ assignSimilarTime(component, targetDate);
+ component.assign("timezoneOffset", reference.getTimezoneOffset());
+ component.addTag("casualReference/now");
+ return component;
+}
+function today(reference) {
+ const targetDate = reference.getDateWithAdjustedTimezone();
+ const component = new ParsingComponents(reference, {});
+ assignSimilarDate(component, targetDate);
+ implySimilarTime(component, targetDate);
+ component.delete("meridiem");
+ component.addTag("casualReference/today");
+ return component;
+}
+function yesterday(reference) {
+ return theDayBefore(reference, 1).addTag("casualReference/yesterday");
+}
+function tomorrow(reference) {
+ return theDayAfter(reference, 1).addTag("casualReference/tomorrow");
+}
+function theDayBefore(reference, numDay) {
+ return theDayAfter(reference, -numDay);
+}
+function theDayAfter(reference, nDays) {
+ const targetDate = reference.getDateWithAdjustedTimezone();
+ const component = new ParsingComponents(reference, {});
+ const newDate = new Date(targetDate.getTime());
+ newDate.setDate(newDate.getDate() + nDays);
+ assignSimilarDate(component, newDate);
+ implySimilarTime(component, newDate);
+ component.delete("meridiem");
+ return component;
+}
+function tonight(reference, implyHour = 22) {
+ const targetDate = reference.getDateWithAdjustedTimezone();
+ const component = new ParsingComponents(reference, {});
+ assignSimilarDate(component, targetDate);
+ component.imply("hour", implyHour);
+ component.imply("meridiem", Meridiem.PM);
+ component.addTag("casualReference/tonight");
+ return component;
+}
+function evening(reference, implyHour = 20) {
+ const component = new ParsingComponents(reference, {});
+ component.imply("meridiem", Meridiem.PM);
+ component.imply("hour", implyHour);
+ component.addTag("casualReference/evening");
+ return component;
+}
+function midnight(reference) {
+ const component = new ParsingComponents(reference, {});
+ if (reference.getDateWithAdjustedTimezone().getHours() > 2) {
+ component.addDurationAsImplied({ day: 1 });
+ }
+ component.assign("hour", 0);
+ component.imply("minute", 0);
+ component.imply("second", 0);
+ component.imply("millisecond", 0);
+ component.addTag("casualReference/midnight");
+ return component;
+}
+function morning(reference, implyHour = 6) {
+ const component = new ParsingComponents(reference, {});
+ component.imply("meridiem", Meridiem.AM);
+ component.imply("hour", implyHour);
+ component.imply("minute", 0);
+ component.imply("second", 0);
+ component.imply("millisecond", 0);
+ component.addTag("casualReference/morning");
+ return component;
+}
+function afternoon(reference, implyHour = 15) {
+ const component = new ParsingComponents(reference, {});
+ component.imply("meridiem", Meridiem.PM);
+ component.imply("hour", implyHour);
+ component.imply("minute", 0);
+ component.imply("second", 0);
+ component.imply("millisecond", 0);
+ component.addTag("casualReference/afternoon");
+ return component;
+}
+function noon(reference) {
+ const component = new ParsingComponents(reference, {});
+ component.imply("meridiem", Meridiem.AM);
+ component.assign("hour", 12);
+ component.imply("minute", 0);
+ component.imply("second", 0);
+ component.imply("millisecond", 0);
+ component.addTag("casualReference/noon");
+ return component;
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualDateParser.js
+var PATTERN9 = /(now|today|tonight|tomorrow|overmorrow|tmr|tmrw|yesterday|last\s*night)(?=\W|$)/i;
+
+class ENCasualDateParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern(context) {
+ return PATTERN9;
+ }
+ innerExtract(context, match) {
+ let targetDate = context.refDate;
+ const lowerText = match[0].toLowerCase();
+ let component = context.createParsingComponents();
+ switch (lowerText) {
+ case "now":
+ component = now(context.reference);
+ break;
+ case "today":
+ component = today(context.reference);
+ break;
+ case "yesterday":
+ component = yesterday(context.reference);
+ break;
+ case "tomorrow":
+ case "tmr":
+ case "tmrw":
+ component = tomorrow(context.reference);
+ break;
+ case "tonight":
+ component = tonight(context.reference);
+ break;
+ case "overmorrow":
+ component = theDayAfter(context.reference, 2);
+ break;
+ default:
+ if (lowerText.match(/last\s*night/)) {
+ if (targetDate.getHours() > 6) {
+ const previousDay = new Date(targetDate.getTime());
+ previousDay.setDate(previousDay.getDate() - 1);
+ targetDate = previousDay;
+ }
+ assignSimilarDate(component, targetDate);
+ component.imply("hour", 0);
+ }
+ break;
+ }
+ component.addTag("parser/ENCasualDateParser");
+ return component;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualTimeParser.js
+var PATTERN10 = /(?:this)?\s{0,3}(morning|afternoon|evening|night|midnight|midday|noon)(?=\W|$)/i;
+
+class ENCasualTimeParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN10;
+ }
+ innerExtract(context, match) {
+ let component = null;
+ switch (match[1].toLowerCase()) {
+ case "afternoon":
+ component = afternoon(context.reference);
+ break;
+ case "evening":
+ case "night":
+ component = evening(context.reference);
+ break;
+ case "midnight":
+ component = midnight(context.reference);
+ break;
+ case "morning":
+ component = morning(context.reference);
+ break;
+ case "noon":
+ case "midday":
+ component = noon(context.reference);
+ break;
+ }
+ if (component) {
+ component.addTag("parser/ENCasualTimeParser");
+ }
+ return component;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/calculation/weekdays.js
+function createParsingComponentsAtWeekday(reference, weekday, modifier) {
+ const refDate = reference.getDateWithAdjustedTimezone();
+ const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
+ let components = new ParsingComponents(reference);
+ components = components.addDurationAsImplied({ day: daysToWeekday });
+ components.assign("weekday", weekday);
+ return components;
+}
+function getDaysToWeekday(refDate, weekday, modifier) {
+ const refWeekday = refDate.getDay();
+ switch (modifier) {
+ case "this":
+ return getDaysForwardToWeekday(refDate, weekday);
+ case "last":
+ return getBackwardDaysToWeekday(refDate, weekday);
+ case "next":
+ if (refWeekday == Weekday.SUNDAY) {
+ return weekday == Weekday.SUNDAY ? 7 : weekday;
+ }
+ if (refWeekday == Weekday.SATURDAY) {
+ if (weekday == Weekday.SATURDAY)
+ return 7;
+ if (weekday == Weekday.SUNDAY)
+ return 8;
+ return 1 + weekday;
+ }
+ if (weekday < refWeekday && weekday != Weekday.SUNDAY) {
+ return getDaysForwardToWeekday(refDate, weekday);
+ } else {
+ return getDaysForwardToWeekday(refDate, weekday) + 7;
+ }
+ }
+ return getDaysToWeekdayClosest(refDate, weekday);
+}
+function getDaysToWeekdayClosest(refDate, weekday) {
+ const backward = getBackwardDaysToWeekday(refDate, weekday);
+ const forward = getDaysForwardToWeekday(refDate, weekday);
+ return forward < -backward ? forward : backward;
+}
+function getDaysForwardToWeekday(refDate, weekday) {
+ const refWeekday = refDate.getDay();
+ let forwardCount = weekday - refWeekday;
+ if (forwardCount < 0) {
+ forwardCount += 7;
+ }
+ return forwardCount;
+}
+function getBackwardDaysToWeekday(refDate, weekday) {
+ const refWeekday = refDate.getDay();
+ let backwardCount = weekday - refWeekday;
+ if (backwardCount >= 0) {
+ backwardCount -= 7;
+ }
+ return backwardCount;
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENWeekdayParser.js
+var 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");
+var PREFIX_GROUP2 = 1;
+var WEEKDAY_GROUP = 2;
+var POSTFIX_GROUP = 3;
+
+class ENWeekdayParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN11;
+ }
+ innerExtract(context, match) {
+ const prefix = match[PREFIX_GROUP2];
+ const postfix = match[POSTFIX_GROUP];
+ let modifierWord = prefix || postfix;
+ modifierWord = modifierWord || "";
+ modifierWord = modifierWord.toLowerCase();
+ let modifier = null;
+ if (modifierWord == "last" || modifierWord == "past") {
+ modifier = "last";
+ } else if (modifierWord == "next") {
+ modifier = "next";
+ } else if (modifierWord == "this") {
+ modifier = "this";
+ }
+ const weekday_word = match[WEEKDAY_GROUP].toLowerCase();
+ let weekday;
+ if (WEEKDAY_DICTIONARY[weekday_word] !== undefined) {
+ weekday = WEEKDAY_DICTIONARY[weekday_word];
+ } else if (weekday_word == "weekend") {
+ weekday = modifier == "last" ? Weekday.SUNDAY : Weekday.SATURDAY;
+ } else if (weekday_word == "weekday") {
+ const refWeekday = context.reference.getDateWithAdjustedTimezone().getDay();
+ if (refWeekday == Weekday.SUNDAY || refWeekday == Weekday.SATURDAY) {
+ weekday = modifier == "last" ? Weekday.FRIDAY : Weekday.MONDAY;
+ } else {
+ weekday = refWeekday - 1;
+ weekday = modifier == "last" ? weekday - 1 : weekday + 1;
+ weekday = weekday % 5 + 1;
+ }
+ } else {
+ return null;
+ }
+ return createParsingComponentsAtWeekday(context.reference, weekday, modifier);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENRelativeDateFormatParser.js
+var PATTERN12 = new RegExp(`(this|last|past|next|after\\s*this)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` + "(?=\\W|$)", "i");
+var MODIFIER_WORD_GROUP = 1;
+var RELATIVE_WORD_GROUP = 2;
+
+class ENRelativeDateFormatParser extends AbstractParserWithWordBoundaryChecking {
+ innerPattern() {
+ return PATTERN12;
+ }
+ innerExtract(context, match) {
+ const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();
+ const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
+ const timeunit = TIME_UNIT_DICTIONARY[unitWord];
+ if (modifier == "next" || modifier.startsWith("after")) {
+ const timeUnits = {};
+ timeUnits[timeunit] = 1;
+ return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
+ }
+ if (modifier == "last" || modifier == "past") {
+ const timeUnits = {};
+ timeUnits[timeunit] = -1;
+ return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
+ }
+ const components = context.createParsingComponents();
+ let date = new Date(context.reference.instant.getTime());
+ if (unitWord.match(/week/i)) {
+ date.setDate(date.getDate() - date.getDay());
+ components.imply("day", date.getDate());
+ components.imply("month", date.getMonth() + 1);
+ components.imply("year", date.getFullYear());
+ } else if (unitWord.match(/month/i)) {
+ date.setDate(1);
+ components.imply("day", date.getDate());
+ components.assign("year", date.getFullYear());
+ components.assign("month", date.getMonth() + 1);
+ } else if (unitWord.match(/year/i)) {
+ date.setDate(1);
+ date.setMonth(0);
+ components.imply("day", date.getDate());
+ components.imply("month", date.getMonth() + 1);
+ components.assign("year", date.getFullYear());
+ }
+ return components;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/common/parsers/SlashDateFormatParser.js
+var 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");
+var OPENING_GROUP = 1;
+var ENDING_GROUP = 5;
+var FIRST_NUMBERS_GROUP = 2;
+var SECOND_NUMBERS_GROUP = 3;
+var YEAR_GROUP5 = 4;
+
+class SlashDateFormatParser {
+ groupNumberMonth;
+ groupNumberDay;
+ constructor(littleEndian) {
+ this.groupNumberMonth = littleEndian ? SECOND_NUMBERS_GROUP : FIRST_NUMBERS_GROUP;
+ this.groupNumberDay = littleEndian ? FIRST_NUMBERS_GROUP : SECOND_NUMBERS_GROUP;
+ }
+ pattern() {
+ return PATTERN13;
+ }
+ extract(context, match) {
+ const index = match.index + match[OPENING_GROUP].length;
+ const indexEnd = match.index + match[0].length - match[ENDING_GROUP].length;
+ if (index > 0) {
+ const textBefore = context.text.substring(0, index);
+ if (textBefore.match("\\d/?$")) {
+ return;
+ }
+ }
+ if (indexEnd < context.text.length) {
+ const textAfter = context.text.substring(indexEnd);
+ if (textAfter.match("^/?\\d")) {
+ return;
+ }
+ }
+ const text = context.text.substring(index, indexEnd);
+ if (text.match(/^\d\.\d$/) || text.match(/^\d\.\d{1,2}\.\d{1,2}\s*$/)) {
+ return;
+ }
+ if (!match[YEAR_GROUP5] && text.indexOf("/") < 0) {
+ return;
+ }
+ const result = context.createParsingResult(index, text);
+ let month = parseInt(match[this.groupNumberMonth]);
+ let day = parseInt(match[this.groupNumberDay]);
+ if (month < 1 || month > 12) {
+ if (month > 12) {
+ if (day >= 1 && day <= 12 && month <= 31) {
+ [day, month] = [month, day];
+ } else {
+ return null;
+ }
+ }
+ }
+ if (day < 1 || day > 31) {
+ return null;
+ }
+ result.start.assign("day", day);
+ result.start.assign("month", month);
+ if (match[YEAR_GROUP5]) {
+ const rawYearNumber = parseInt(match[YEAR_GROUP5]);
+ const year = findMostLikelyADYear(rawYearNumber);
+ result.start.assign("year", year);
+ } else {
+ const year = findYearClosestToRef(context.refDate, day, month);
+ result.start.imply("year", year);
+ }
+ return result.addTag("parser/SlashDateFormatParser");
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js
+var PATTERN14 = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
+var PATTERN_NO_ABBR = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
+
+class ENTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
+ allowAbbreviations;
+ constructor(allowAbbreviations = true) {
+ super();
+ this.allowAbbreviations = allowAbbreviations;
+ }
+ innerPattern() {
+ return this.allowAbbreviations ? PATTERN14 : PATTERN_NO_ABBR;
+ }
+ innerExtract(context, match) {
+ const prefix = match[1].toLowerCase();
+ let duration = parseDuration(match[2]);
+ if (!duration) {
+ return null;
+ }
+ switch (prefix) {
+ case "last":
+ case "past":
+ case "-":
+ duration = reverseDuration(duration);
+ break;
+ }
+ return ParsingComponents.createRelativeFromReference(context.reference, duration);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeAfterDateRefiner.js
+function IsPositiveFollowingReference(result) {
+ return result.text.match(/^[+-]/i) != null;
+}
+function IsNegativeFollowingReference(result) {
+ return result.text.match(/^-/i) != null;
+}
+
+class ENMergeRelativeAfterDateRefiner extends MergingRefiner {
+ shouldMergeResults(textBetween, currentResult, nextResult) {
+ if (!textBetween.match(/^\s*$/i)) {
+ return false;
+ }
+ return IsPositiveFollowingReference(nextResult) || IsNegativeFollowingReference(nextResult);
+ }
+ mergeResults(textBetween, currentResult, nextResult, context) {
+ let timeUnits = parseDuration(nextResult.text);
+ if (IsNegativeFollowingReference(nextResult)) {
+ timeUnits = reverseDuration(timeUnits);
+ }
+ const components = ParsingComponents.createRelativeFromReference(ReferenceWithTimezone.fromDate(currentResult.start.date()), timeUnits);
+ return new ParsingResult(currentResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeFollowByDateRefiner.js
+function hasImpliedEarlierReferenceDate(result) {
+ return result.text.match(/\s+(before|from)$/i) != null;
+}
+function hasImpliedLaterReferenceDate(result) {
+ return result.text.match(/\s+(after|since)$/i) != null;
+}
+
+class ENMergeRelativeFollowByDateRefiner extends MergingRefiner {
+ patternBetween() {
+ return /^\s*$/i;
+ }
+ shouldMergeResults(textBetween, currentResult, nextResult) {
+ if (!textBetween.match(this.patternBetween())) {
+ return false;
+ }
+ if (!hasImpliedEarlierReferenceDate(currentResult) && !hasImpliedLaterReferenceDate(currentResult)) {
+ return false;
+ }
+ return !!nextResult.start.get("day") && !!nextResult.start.get("month") && !!nextResult.start.get("year");
+ }
+ mergeResults(textBetween, currentResult, nextResult) {
+ let duration = parseDuration(currentResult.text);
+ if (hasImpliedEarlierReferenceDate(currentResult)) {
+ duration = reverseDuration(duration);
+ }
+ const components = ParsingComponents.createRelativeFromReference(ReferenceWithTimezone.fromDate(nextResult.start.date()), duration);
+ return new ParsingResult(nextResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/refiners/ENExtractYearSuffixRefiner.js
+var YEAR_SUFFIX_PATTERN = new RegExp(`^\\s*(${YEAR_PATTERN})`, "i");
+var YEAR_GROUP6 = 1;
+
+class ENExtractYearSuffixRefiner {
+ refine(context, results) {
+ results.forEach(function(result) {
+ if (!result.start.isDateWithUnknownYear()) {
+ return;
+ }
+ const suffix = context.text.substring(result.index + result.text.length);
+ const match = YEAR_SUFFIX_PATTERN.exec(suffix);
+ if (!match) {
+ return;
+ }
+ if (match[0].trim().length <= 3) {
+ return;
+ }
+ context.debug(() => {
+ console.log(`Extracting year: '${match[0]}' into : ${result}`);
+ });
+ const year = parseYear(match[YEAR_GROUP6]);
+ if (result.end != null) {
+ result.end.assign("year", year);
+ }
+ result.start.assign("year", year);
+ result.text += match[0];
+ });
+ return results;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/refiners/ENUnlikelyFormatFilter.js
+class ENUnlikelyFormatFilter extends Filter {
+ constructor() {
+ super();
+ }
+ isValid(context, result) {
+ const text = result.text.trim();
+ if (text === context.text.trim()) {
+ return true;
+ }
+ if (text.toLowerCase() === "may") {
+ const textBefore = context.text.substring(0, result.index).trim();
+ if (!textBefore.match(/\b(in)$/i)) {
+ context.debug(() => {
+ console.log(`Removing unlikely result: ${result}`);
+ });
+ return false;
+ }
+ }
+ if (text.toLowerCase().endsWith("the second")) {
+ const textAfter = context.text.substring(result.index + result.text.length).trim();
+ if (textAfter.length > 0) {
+ context.debug(() => {
+ console.log(`Removing unlikely result: ${result}`);
+ });
+ }
+ return false;
+ }
+ return true;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/configuration.js
+class ENDefaultConfiguration {
+ createCasualConfiguration(littleEndian = false) {
+ const option = this.createConfiguration(false, littleEndian);
+ option.parsers.push(new ENCasualDateParser);
+ option.parsers.push(new ENCasualTimeParser);
+ option.parsers.push(new ENMonthNameParser);
+ option.parsers.push(new ENRelativeDateFormatParser);
+ option.parsers.push(new ENTimeUnitCasualRelativeFormatParser);
+ option.refiners.push(new ENUnlikelyFormatFilter);
+ return option;
+ }
+ createConfiguration(strictMode = true, littleEndian = false) {
+ const options = includeCommonConfiguration({
+ parsers: [
+ new SlashDateFormatParser(littleEndian),
+ new ENTimeUnitWithinFormatParser(strictMode),
+ new ENMonthNameLittleEndianParser,
+ new ENMonthNameMiddleEndianParser(littleEndian),
+ new ENWeekdayParser,
+ new ENSlashMonthFormatParser,
+ new ENTimeExpressionParser(strictMode),
+ new ENTimeUnitAgoFormatParser(strictMode),
+ new ENTimeUnitLaterFormatParser(strictMode)
+ ],
+ refiners: [new ENMergeDateTimeRefiner]
+ }, strictMode);
+ options.parsers.unshift(new ENYearMonthDayParser(strictMode));
+ options.refiners.unshift(new ENMergeRelativeFollowByDateRefiner);
+ options.refiners.unshift(new ENMergeRelativeAfterDateRefiner);
+ options.refiners.unshift(new OverlapRemovalRefiner);
+ options.refiners.push(new ENMergeDateTimeRefiner);
+ options.refiners.push(new ENExtractYearSuffixRefiner);
+ options.refiners.push(new ENMergeDateRangeRefiner);
+ return options;
+ }
+}
+
+// node_modules/chrono-node/dist/esm/chrono.js
+class Chrono {
+ parsers;
+ refiners;
+ defaultConfig = new ENDefaultConfiguration;
+ constructor(configuration) {
+ configuration = configuration || this.defaultConfig.createCasualConfiguration();
+ this.parsers = [...configuration.parsers];
+ this.refiners = [...configuration.refiners];
+ }
+ clone() {
+ return new Chrono({
+ parsers: [...this.parsers],
+ refiners: [...this.refiners]
+ });
+ }
+ parseDate(text, referenceDate, option) {
+ const results = this.parse(text, referenceDate, option);
+ return results.length > 0 ? results[0].start.date() : null;
+ }
+ parse(text, referenceDate, option) {
+ const context = new ParsingContext(text, referenceDate, option);
+ let results = [];
+ this.parsers.forEach((parser) => {
+ const parsedResults = Chrono.executeParser(context, parser);
+ results = results.concat(parsedResults);
+ });
+ results.sort((a, b) => {
+ return a.index - b.index;
+ });
+ this.refiners.forEach(function(refiner) {
+ results = refiner.refine(context, results);
+ });
+ return results;
+ }
+ static executeParser(context, parser) {
+ const results = [];
+ const pattern = parser.pattern(context);
+ const originalText = context.text;
+ let remainingText = context.text;
+ let match = pattern.exec(remainingText);
+ while (match) {
+ const index = match.index + originalText.length - remainingText.length;
+ match.index = index;
+ const result = parser.extract(context, match);
+ if (!result) {
+ remainingText = originalText.substring(match.index + 1);
+ match = pattern.exec(remainingText);
+ continue;
+ }
+ let parsedResult = null;
+ if (result instanceof ParsingResult) {
+ parsedResult = result;
+ } else if (result instanceof ParsingComponents) {
+ parsedResult = context.createParsingResult(match.index, match[0]);
+ parsedResult.start = result;
+ } else {
+ parsedResult = context.createParsingResult(match.index, match[0], result);
+ }
+ const parsedIndex = parsedResult.index;
+ const parsedText = parsedResult.text;
+ context.debug(() => console.log(`${parser.constructor.name} extracted (at index=${parsedIndex}) '${parsedText}'`));
+ results.push(parsedResult);
+ remainingText = originalText.substring(parsedIndex + parsedText.length);
+ match = pattern.exec(remainingText);
+ }
+ return results;
+ }
+}
+
+class ParsingContext {
+ text;
+ option;
+ reference;
+ refDate;
+ constructor(text, refDate, option) {
+ this.text = text;
+ this.option = option ?? {};
+ this.reference = ReferenceWithTimezone.fromInput(refDate, this.option.timezones);
+ this.refDate = this.reference.instant;
+ }
+ createParsingComponents(components) {
+ if (components instanceof ParsingComponents) {
+ return components;
+ }
+ return new ParsingComponents(this.reference, components);
+ }
+ createParsingResult(index, textOrEndIndex, startComponents, endComponents) {
+ const text = typeof textOrEndIndex === "string" ? textOrEndIndex : this.text.substring(index, textOrEndIndex);
+ const start = startComponents ? this.createParsingComponents(startComponents) : null;
+ const end = endComponents ? this.createParsingComponents(endComponents) : null;
+ return new ParsingResult(this.reference, index, text, start, end);
+ }
+ debug(block) {
+ if (this.option.debug) {
+ if (this.option.debug instanceof Function) {
+ this.option.debug(block);
+ } else {
+ const handler = this.option.debug;
+ handler.debug(block);
+ }
+ }
+ }
+}
+
+// node_modules/chrono-node/dist/esm/locales/en/index.js
+var configuration = new ENDefaultConfiguration;
+var casual = new Chrono(configuration.createCasualConfiguration(false));
+var strict = new Chrono(configuration.createConfiguration(true, false));
+var GB = new Chrono(configuration.createCasualConfiguration(true));
+
+// node_modules/chrono-node/dist/esm/index.js
+var casual2 = casual;
+function parseDate(text, ref, option) {
+ return casual2.parseDate(text, ref, option);
+}
+
+// index.ts
+import { Container, SelectList, Text } from "@mariozechner/pi-tui";
var DEFAULT_ORG_FILE = join(homedir(), "desktop/org/todos.org");
+var INBOX_FILE = join(homedir(), "desktop/org/inbox.org");
+var DEFAULT_SECTION = "Inbox";
function execEmacs(elisp) {
try {
const escaped = elisp.replace(/'/g, "'\\''");
@@ -31,6 +2906,11 @@ function execEmacs(elisp) {
};
}
}
+function stripOrgLinks(text) {
+ text = text.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
+ text = text.replace(/\[\[([^\]]*)\]\]/g, "$1");
+ return text;
+}
function formatTodo(todo) {
const parts = [];
const state = todo.todo || "TODO";
@@ -38,7 +2918,7 @@ function formatTodo(todo) {
if (todo.priority) {
parts.push(`[#${todo.priority}]`);
}
- parts.push(todo.heading);
+ parts.push(stripOrgLinks(todo.heading));
if (todo.tags && todo.tags.length > 0) {
parts.push(`:${todo.tags.join(":")}:`);
}
@@ -54,7 +2934,110 @@ function formatTodo(todo) {
}
return parts.join(" ");
}
+function formatTodoMarkdown(todo) {
+ const parts = [];
+ const state = todo.todo || "TODO";
+ parts.push(`**[${state}]**`);
+ if (todo.priority) {
+ parts.push(`\`#${todo.priority}\``);
+ }
+ parts.push(stripOrgLinks(todo.heading));
+ if (todo.tags && todo.tags.length > 0) {
+ const tagStr = todo.tags.map((t) => `\`${t}\``).join(" ");
+ parts.push(tagStr);
+ }
+ const dates = [];
+ if (todo.scheduled) {
+ dates.push(`\uD83D\uDCC5 ${todo.scheduled}`);
+ }
+ if (todo.deadline) {
+ dates.push(`⏰ ${todo.deadline}`);
+ }
+ let result = parts.join(" ");
+ if (dates.length > 0) {
+ result += ` *(${dates.join(", ")})*`;
+ }
+ return result;
+}
+function parseNaturalDate(text) {
+ const result = parseDate(text);
+ if (!result)
+ return null;
+ const year = result.getFullYear();
+ const month = String(result.getMonth() + 1).padStart(2, "0");
+ const day = String(result.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+}
+function parseCommandArgs(args) {
+ let remaining = args;
+ let section;
+ let scheduled;
+ let deadline;
+ let priority;
+ let state;
+ const sectionMatch = remaining.match(/@(\w+)/);
+ if (sectionMatch) {
+ section = sectionMatch[1];
+ remaining = remaining.replace(/@\w+/, "").trim();
+ }
+ const scheduledMatch = remaining.match(/scheduled:([^\s]+(?:\s+[^\s@:]+)*?)(?=\s+(?:deadline:|priority:|state:|@|$)|$)/i);
+ if (scheduledMatch) {
+ const dateStr = scheduledMatch[1].trim();
+ scheduled = parseNaturalDate(dateStr) || dateStr;
+ remaining = remaining.replace(scheduledMatch[0], "").trim();
+ }
+ const deadlineMatch = remaining.match(/deadline:([^\s]+(?:\s+[^\s@:]+)*?)(?=\s+(?:scheduled:|priority:|state:|@|$)|$)/i);
+ if (deadlineMatch) {
+ const dateStr = deadlineMatch[1].trim();
+ deadline = parseNaturalDate(dateStr) || dateStr;
+ remaining = remaining.replace(deadlineMatch[0], "").trim();
+ }
+ const priorityMatch = remaining.match(/priority:(\d)/i);
+ if (priorityMatch) {
+ priority = parseInt(priorityMatch[1], 10);
+ remaining = remaining.replace(priorityMatch[0], "").trim();
+ }
+ const stateMatch = remaining.match(/state:(TODO|NEXT|STRT|WAIT|DONE|CANX)/i);
+ if (stateMatch) {
+ state = stateMatch[1].toUpperCase();
+ remaining = remaining.replace(stateMatch[0], "").trim();
+ }
+ return {
+ title: remaining.trim(),
+ section,
+ scheduled,
+ deadline,
+ priority,
+ state
+ };
+}
function org_todos_default(pi) {
+ const customTypes = [
+ "org-todos",
+ "org-todos-search",
+ "org-todos-add",
+ "org-todos-done",
+ "org-todos-next",
+ "org-todos-upcoming",
+ "org-todos-update",
+ "org-todos-note"
+ ];
+ for (const customType of customTypes) {
+ pi.registerMessageRenderer(customType, (message, options, theme) => {
+ let text = message.content;
+ text = text.replace(/\[TODO\]/g, theme.bold(theme.fg("mdHeading", "[TODO]")));
+ text = text.replace(/\[NEXT\]/g, theme.bold(theme.fg("accent", "[NEXT]")));
+ text = text.replace(/\[STRT\]/g, theme.bold(theme.fg("mdLink", "[STRT]")));
+ text = text.replace(/\[WAIT\]/g, theme.bold(theme.fg("muted", "[WAIT]")));
+ text = text.replace(/\[DONE\]/g, theme.bold(theme.fg("success", "[DONE]")));
+ text = text.replace(/\[CANX\]/g, theme.bold(theme.fg("error", "[CANX]")));
+ const container = new Container;
+ container.addChild(new DynamicBorder((s) => theme.fg("borderMuted", s)));
+ container.addChild(new Text(text, 1, 1));
+ container.addChild(new DynamicBorder((s) => theme.fg("borderMuted", s)));
+ return container;
+ });
+ }
pi.registerTool({
name: "org_todo",
label: "Org TODO",
@@ -71,7 +3054,12 @@ function org_todos_default(pi) {
- deadline: Set deadline date
- priority: Set priority (1-5)
- add: Create new TODO
-- append: Append content to TODO`,
+- append: Append content to TODO
+- inbox-list: List all inbox items
+- inbox-count: Get count of inbox items
+- inbox-add: Add item to inbox
+- refile-targets: Get available refile target sections
+- refile: Refile item from inbox to a section`,
parameters: {
type: "object",
properties: {
@@ -93,7 +3081,12 @@ function org_todos_default(pi) {
"append",
"sections",
"statistics",
- "archive"
+ "archive",
+ "inbox-list",
+ "inbox-count",
+ "inbox-add",
+ "refile-targets",
+ "refile"
],
description: "Action to perform"
},
@@ -242,6 +3235,34 @@ function org_todos_default(pi) {
case "archive":
elisp = "(pi/org-todo-archive-done)";
break;
+ case "inbox-list":
+ elisp = `(pi/org-todo-list "${INBOX_FILE}" "TODO,NEXT,STRT,WAIT")`;
+ break;
+ case "inbox-count":
+ elisp = `(pi/org-todo-inbox-all)`;
+ break;
+ case "inbox-add":
+ if (!heading) {
+ return {
+ content: [{ type: "text", text: "Error: heading is required for inbox-add action" }]
+ };
+ }
+ const schedInbox = date ? `"${date}"` : "nil";
+ const prioInbox = priority !== undefined ? priority : "nil";
+ const tagsInbox = tags && tags.length > 0 ? `'(${tags.map((t) => `"${t}"`).join(" ")})` : "nil";
+ elisp = `(pi/org-todo-add "${heading.replace(/"/g, "\\\"")}" "Inbox" "${INBOX_FILE}" ${schedInbox} ${prioInbox} ${tagsInbox})`;
+ break;
+ case "refile-targets":
+ elisp = "(pi/org-todo-get-refile-targets)";
+ break;
+ case "refile":
+ if (!heading || !section) {
+ return {
+ content: [{ type: "text", text: "Error: heading and section are required for refile action" }]
+ };
+ }
+ elisp = `(pi/org-todo-refile "${heading.replace(/"/g, "\\\"")}" "${section.replace(/"/g, "\\\"")}")`;
+ break;
default:
return {
content: [{ type: "text", text: `Unknown action: ${action}` }]
@@ -272,9 +3293,25 @@ function org_todos_default(pi) {
}
});
pi.registerCommand("todos", {
- description: "Show today's tasks (scheduled + overdue + NEXT)",
+ description: "Show today's tasks (scheduled + overdue + NEXT). Usage: /todos [section]",
handler: async (args, ctx) => {
- const theme = ctx.ui.theme;
+ const sectionFilter = (args || "").trim() || null;
+ let sectionHeadings = null;
+ if (sectionFilter) {
+ const sectionResult = execEmacs(`(pi/org-todo-by-section "${sectionFilter.replace(/"/g, "\\\"")}")`);
+ if (!sectionResult.success) {
+ const sections = execEmacs("(pi/org-todo-sections)");
+ const sectionList = sections.success && sections.data ? Array.isArray(sections.data) ? sections.data : Object.values(sections.data) : [];
+ ctx.ui.notify(`Section "${sectionFilter}" not found. Available: ${sectionList.join(", ")}`, "error");
+ return;
+ }
+ sectionHeadings = new Set((sectionResult.data || []).map((t) => t.heading));
+ }
+ function filterBySection(todos) {
+ if (!sectionHeadings)
+ return todos;
+ return todos.filter((t) => sectionHeadings.has(t.heading));
+ }
const scheduled = execEmacs("(pi/org-todo-scheduled)");
const overdue = execEmacs("(pi/org-todo-overdue)");
const next = execEmacs('(pi/org-todo-list nil "NEXT")');
@@ -282,47 +3319,51 @@ function org_todos_default(pi) {
ctx.ui.notify("Failed to fetch TODOs. Is Emacs daemon running?", "error");
return;
}
+ const filteredOverdue = filterBySection(overdue.success && overdue.data ? overdue.data : []);
+ const filteredScheduled = filterBySection(scheduled.success && scheduled.data ? scheduled.data : []);
+ const filteredNext = filterBySection(next.success && next.data ? next.data : []);
const lines = [];
- lines.push(theme.bold("\uD83D\uDCCB Today's Tasks"));
- lines.push(theme.fg("dim", "─".repeat(50)));
- if (overdue.success && overdue.data && overdue.data.length > 0) {
+ const title = sectionFilter ? `## \uD83D\uDCCB Today's Tasks — ${sectionFilter}` : "## \uD83D\uDCCB Today's Tasks";
+ lines.push(title);
+ lines.push("");
+ if (filteredOverdue.length > 0) {
+ lines.push(`### ⚠️ Overdue (${filteredOverdue.length})`);
lines.push("");
- lines.push(theme.fg("error", `⚠️ Overdue (${overdue.data.length})`));
- for (const todo of overdue.data.slice(0, 5)) {
- lines.push(` ${theme.fg("error", "•")} ${formatTodo(todo)}`);
+ for (const todo of filteredOverdue) {
+ lines.push(`- ${formatTodoMarkdown(todo)}`);
}
- if (overdue.data.length > 5) {
- lines.push(theme.fg("dim", ` ... and ${overdue.data.length - 5} more`));
+ lines.push("");
+ }
+ if (filteredScheduled.length > 0) {
+ lines.push(`### \uD83D\uDCC5 Scheduled Today (${filteredScheduled.length})`);
+ lines.push("");
+ for (const todo of filteredScheduled) {
+ lines.push(`- ${formatTodoMarkdown(todo)}`);
+ }
+ lines.push("");
+ }
+ if (filteredNext.length > 0) {
+ lines.push(`### ➡️ Next Actions (${filteredNext.length})`);
+ lines.push("");
+ for (const todo of filteredNext) {
+ lines.push(`- ${formatTodoMarkdown(todo)}`);
+ }
+ lines.push("");
+ }
+ const hasContent = filteredOverdue.length > 0 || filteredScheduled.length > 0 || filteredNext.length > 0;
+ if (!hasContent) {
+ if (sectionFilter) {
+ lines.push(`*No tasks for today in "${sectionFilter}".* \uD83C\uDF89`);
+ } else {
+ lines.push("*No tasks for today.* \uD83C\uDF89");
}
}
- if (scheduled.success && scheduled.data && scheduled.data.length > 0) {
- lines.push("");
- lines.push(theme.fg("accent", `\uD83D\uDCC5 Scheduled Today (${scheduled.data.length})`));
- for (const todo of scheduled.data.slice(0, 5)) {
- lines.push(` ${theme.fg("accent", "•")} ${formatTodo(todo)}`);
- }
- if (scheduled.data.length > 5) {
- lines.push(theme.fg("dim", ` ... and ${scheduled.data.length - 5} more`));
- }
- }
- if (next.success && next.data && next.data.length > 0) {
- lines.push("");
- lines.push(theme.fg("success", `➡️ Next Actions (${next.data.length})`));
- for (const todo of next.data.slice(0, 5)) {
- lines.push(` ${theme.fg("success", "•")} ${formatTodo(todo)}`);
- }
- if (next.data.length > 5) {
- lines.push(theme.fg("dim", ` ... and ${next.data.length - 5} more`));
- }
- }
- if (lines.length === 2) {
- lines.push("");
- lines.push(theme.fg("dim", "No tasks for today. \uD83C\uDF89"));
- }
- ctx.ui.setWidget("todos", lines);
- setTimeout(() => {
- ctx.ui.setWidget("todos", undefined);
- }, 15000);
+ pi.sendMessage({
+ customType: "org-todos",
+ content: lines.join(`
+`),
+ display: true
+ });
}
});
pi.registerCommand("todo-search", {
@@ -333,7 +3374,6 @@ function org_todos_default(pi) {
ctx.ui.notify("Usage: /todo-search <query>", "error");
return;
}
- const theme = ctx.ui.theme;
const result = execEmacs(`(pi/org-todo-search "${query.replace(/"/g, "\\\"")}" nil t)`);
if (!result.success) {
ctx.ui.notify(`Search failed: ${result.error}`, "error");
@@ -344,21 +3384,538 @@ function org_todos_default(pi) {
return;
}
const lines = [];
- lines.push(theme.bold(`\uD83D\uDD0D Search: "${query}" (${result.data.length} results)`));
- lines.push(theme.fg("dim", "─".repeat(50)));
- for (const todo of result.data.slice(0, 10)) {
- const matchedIn = todo.matched_in === "heading" ? "" : theme.fg("dim", " (in content)");
- lines.push(` ${theme.fg("accent", "•")} ${formatTodo(todo)}${matchedIn}`);
+ lines.push(`## \uD83D\uDD0D Search: "${query}"`);
+ lines.push("");
+ lines.push(`*${result.data.length} result(s)*`);
+ lines.push("");
+ for (const todo of result.data) {
+ const matchedIn = todo.matched_in === "heading" ? "" : " *(matched in content)*";
+ lines.push(`- ${formatTodoMarkdown(todo)}${matchedIn}`);
}
- if (result.data.length > 10) {
- lines.push(theme.fg("dim", ` ... and ${result.data.length - 10} more`));
- }
- ctx.ui.setWidget("todo-search", lines);
- setTimeout(() => {
- ctx.ui.setWidget("todo-search", undefined);
- }, 20000);
+ pi.sendMessage({
+ customType: "org-todos-search",
+ content: lines.join(`
+`),
+ display: true
+ });
}
});
+ pi.registerCommand("todo-add", {
+ description: "Add a new TODO. Usage: /todo-add <title> [@Section] [scheduled:date] [deadline:date] [priority:N]",
+ handler: async (args, ctx) => {
+ if (!args?.trim()) {
+ ctx.ui.notify("Usage: /todo-add <title> [@Section] [scheduled:date] [deadline:date]", "error");
+ return;
+ }
+ const parsed = parseCommandArgs(args);
+ if (!parsed.title) {
+ ctx.ui.notify("Error: TODO title is required", "error");
+ return;
+ }
+ const section = parsed.section || DEFAULT_SECTION;
+ const schedArg = parsed.scheduled ? `"${parsed.scheduled}"` : "nil";
+ const prioArg = parsed.priority !== undefined ? parsed.priority : "nil";
+ const sectionsResult = execEmacs("(pi/org-todo-sections)");
+ if (sectionsResult.success) {
+ const sections = Array.isArray(sectionsResult.data) ? sectionsResult.data : Object.values(sectionsResult.data || {});
+ if (!sections.includes(section)) {
+ ctx.ui.notify(`Section "${section}" not found. Available: ${sections.join(", ")}`, "error");
+ return;
+ }
+ }
+ const elisp = `(pi/org-todo-add "${parsed.title.replace(/"/g, "\\\"")}" "${section}" nil ${schedArg} ${prioArg} nil)`;
+ const result = execEmacs(elisp);
+ if (!result.success) {
+ ctx.ui.notify(`Failed to add TODO: ${result.error}`, "error");
+ return;
+ }
+ if (parsed.deadline) {
+ execEmacs(`(pi/org-todo-deadline "${parsed.title.replace(/"/g, "\\\"")}" "${parsed.deadline}")`);
+ }
+ const lines = [];
+ lines.push(`## ✅ TODO Added`);
+ lines.push("");
+ lines.push(`**${parsed.title}** added to *${section}*`);
+ if (parsed.scheduled)
+ lines.push(`- \uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
+ if (parsed.deadline)
+ lines.push(`- ⏰ Deadline: ${parsed.deadline}`);
+ if (parsed.priority)
+ lines.push(`- Priority: #${parsed.priority}`);
+ pi.sendMessage({
+ customType: "org-todos-add",
+ content: lines.join(`
+`),
+ display: true
+ });
+ }
+ });
+ pi.registerCommand("todo-done", {
+ description: "Mark a TODO as done. Usage: /todo-done <heading>",
+ handler: async (args, ctx) => {
+ const heading = (args || "").trim();
+ if (!heading) {
+ ctx.ui.notify("Usage: /todo-done <heading>", "error");
+ return;
+ }
+ const result = execEmacs(`(pi/org-todo-done "${heading.replace(/"/g, "\\\"")}")`);
+ if (!result.success) {
+ ctx.ui.notify(`Failed: ${result.error}`, "error");
+ return;
+ }
+ pi.sendMessage({
+ customType: "org-todos-done",
+ content: `## ✅ Done
+
+**${heading}** marked as DONE`,
+ display: true
+ });
+ updateTodayStatus(ctx);
+ }
+ });
+ pi.registerCommand("todo-next", {
+ description: "Mark a TODO as NEXT (prioritized). Usage: /todo-next <heading>",
+ handler: async (args, ctx) => {
+ const heading = (args || "").trim();
+ if (!heading) {
+ ctx.ui.notify("Usage: /todo-next <heading>", "error");
+ return;
+ }
+ const result = execEmacs(`(pi/org-todo-state "${heading.replace(/"/g, "\\\"")}" "NEXT")`);
+ if (!result.success) {
+ ctx.ui.notify(`Failed: ${result.error}`, "error");
+ return;
+ }
+ pi.sendMessage({
+ customType: "org-todos-next",
+ content: `## ➡️ Prioritized
+
+**${heading}** marked as NEXT`,
+ display: true
+ });
+ }
+ });
+ pi.registerCommand("todo-upcoming", {
+ description: "Show upcoming tasks. Usage: /todo-upcoming [days]",
+ handler: async (args, ctx) => {
+ const days = parseInt((args || "").trim(), 10) || 7;
+ const result = execEmacs(`(pi/org-todo-upcoming nil ${days})`);
+ if (!result.success) {
+ ctx.ui.notify(`Failed: ${result.error}`, "error");
+ return;
+ }
+ const lines = [];
+ lines.push(`## \uD83D\uDCC6 Upcoming (next ${days} days)`);
+ lines.push("");
+ if (!result.data || result.data.length === 0) {
+ lines.push("*No upcoming tasks* \uD83C\uDF89");
+ } else {
+ for (const todo of result.data) {
+ lines.push(`- ${formatTodoMarkdown(todo)}`);
+ }
+ }
+ pi.sendMessage({
+ customType: "org-todos-upcoming",
+ content: lines.join(`
+`),
+ display: true
+ });
+ }
+ });
+ pi.registerCommand("todo-update", {
+ description: "Update a TODO. Usage: /todo-update <heading> [scheduled:date] [deadline:date] [priority:N] [state:STATE]",
+ handler: async (args, ctx) => {
+ if (!args?.trim()) {
+ ctx.ui.notify("Usage: /todo-update <heading> [scheduled:date] [deadline:date] [priority:N] [state:STATE]", "error");
+ return;
+ }
+ const parsed = parseCommandArgs(args);
+ if (!parsed.title) {
+ ctx.ui.notify("Error: TODO heading is required", "error");
+ return;
+ }
+ const heading = parsed.title;
+ const updates = [];
+ if (parsed.scheduled) {
+ const result = execEmacs(`(pi/org-todo-schedule "${heading.replace(/"/g, "\\\"")}" "${parsed.scheduled}")`);
+ if (result.success)
+ updates.push(`\uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
+ else
+ ctx.ui.notify(`Failed to set schedule: ${result.error}`, "warning");
+ }
+ if (parsed.deadline) {
+ const result = execEmacs(`(pi/org-todo-deadline "${heading.replace(/"/g, "\\\"")}" "${parsed.deadline}")`);
+ if (result.success)
+ updates.push(`⏰ Deadline: ${parsed.deadline}`);
+ else
+ ctx.ui.notify(`Failed to set deadline: ${result.error}`, "warning");
+ }
+ if (parsed.priority !== undefined) {
+ const result = execEmacs(`(pi/org-todo-priority "${heading.replace(/"/g, "\\\"")}" ${parsed.priority})`);
+ if (result.success)
+ updates.push(`Priority: #${parsed.priority}`);
+ else
+ ctx.ui.notify(`Failed to set priority: ${result.error}`, "warning");
+ }
+ if (parsed.state) {
+ const result = execEmacs(`(pi/org-todo-state "${heading.replace(/"/g, "\\\"")}" "${parsed.state}")`);
+ if (result.success)
+ updates.push(`State: ${parsed.state}`);
+ else
+ ctx.ui.notify(`Failed to set state: ${result.error}`, "warning");
+ }
+ if (updates.length === 0) {
+ ctx.ui.notify("No updates specified. Use scheduled:, deadline:, priority:, or state:", "warning");
+ return;
+ }
+ const lines = [];
+ lines.push(`## \uD83D\uDCDD Updated`);
+ lines.push("");
+ lines.push(`**${heading}**`);
+ lines.push("");
+ for (const update of updates) {
+ lines.push(`- ${update}`);
+ }
+ pi.sendMessage({
+ customType: "org-todos-update",
+ content: lines.join(`
+`),
+ display: true
+ });
+ }
+ });
+ pi.registerCommand("todo-note", {
+ description: "Add a note to a TODO. Usage: /todo-note <heading> <note>",
+ handler: async (args, ctx) => {
+ if (!args?.trim()) {
+ ctx.ui.notify("Usage: /todo-note <heading> <note>", "error");
+ return;
+ }
+ const input = args.trim();
+ let heading;
+ let note;
+ const newlineIdx = input.indexOf(`
+`);
+ if (newlineIdx > 0) {
+ heading = input.slice(0, newlineIdx).trim();
+ note = input.slice(newlineIdx + 1).trim();
+ } else {
+ const colonIdx = input.indexOf(": ");
+ const dashIdx = input.indexOf(" - ");
+ if (colonIdx > 0 && colonIdx < 60) {
+ heading = input.slice(0, colonIdx).trim();
+ note = input.slice(colonIdx + 2).trim();
+ } else if (dashIdx > 0 && dashIdx < 60) {
+ heading = input.slice(0, dashIdx).trim();
+ note = input.slice(dashIdx + 3).trim();
+ } else {
+ ctx.ui.notify("Could not parse heading and note. Use format: /todo-note Heading: your note here", "error");
+ return;
+ }
+ }
+ if (!heading || !note) {
+ ctx.ui.notify("Both heading and note are required", "error");
+ return;
+ }
+ const timestamp = new Date().toISOString().slice(0, 16).replace("T", " ");
+ const orgContent = `
+[${timestamp}] ${note}`;
+ const result = execEmacs(`(pi/org-todo-append "${heading.replace(/"/g, "\\\"")}" "${orgContent.replace(/"/g, "\\\"").replace(/\n/g, "\\n")}")`);
+ if (!result.success) {
+ ctx.ui.notify(`Failed: ${result.error}`, "error");
+ return;
+ }
+ pi.sendMessage({
+ customType: "org-todos-note",
+ content: `## \uD83D\uDCDD Note Added
+
+**${heading}**
+
+> ${note}`,
+ display: true
+ });
+ }
+ });
+ pi.registerCommand("inbox", {
+ description: "View all inbox items (TODOs and links)",
+ handler: async (args, ctx) => {
+ const result = execEmacs(`(pi/org-todo-inbox-all)`);
+ if (!result.success) {
+ ctx.ui.notify(`Failed to fetch inbox: ${result.error}`, "error");
+ return;
+ }
+ const todos = result.data?.filter((item) => item.todo) || [];
+ const links = result.data?.filter((item) => !item.todo) || [];
+ const lines = [];
+ lines.push("## \uD83D\uDCE5 Inbox");
+ lines.push("");
+ if (!result.data || result.data.length === 0) {
+ lines.push("*Inbox is empty* ✨");
+ } else {
+ lines.push(`*${result.data.length} item(s)* (${todos.length} tasks, ${links.length} links/notes)`);
+ lines.push("");
+ if (todos.length > 0) {
+ lines.push("### ✅ Tasks");
+ lines.push("");
+ for (const todo of todos) {
+ lines.push(`- ${formatTodoMarkdown(todo)}`);
+ }
+ lines.push("");
+ }
+ if (links.length > 0) {
+ lines.push("### \uD83D\uDD17 Links & Notes");
+ lines.push("");
+ for (const item of links) {
+ lines.push(`- ${stripOrgLinks(item.heading)}`);
+ }
+ }
+ }
+ pi.sendMessage({
+ customType: "org-todos",
+ content: lines.join(`
+`),
+ display: true
+ });
+ }
+ });
+ pi.registerCommand("inbox-add", {
+ description: "Quick capture to inbox. Usage: /inbox-add <title> [scheduled:date] [priority:N]",
+ handler: async (args, ctx) => {
+ if (!args?.trim()) {
+ ctx.ui.notify("Usage: /inbox-add <title> [scheduled:date] [priority:N]", "error");
+ return;
+ }
+ const parsed = parseCommandArgs(args);
+ if (!parsed.title) {
+ ctx.ui.notify("Error: TODO title is required", "error");
+ return;
+ }
+ const schedArg = parsed.scheduled ? `"${parsed.scheduled}"` : "nil";
+ const prioArg = parsed.priority !== undefined ? parsed.priority : "nil";
+ const tagsArg = parsed.tags && parsed.tags.length > 0 ? `'(${parsed.tags.map((t) => `"${t}"`).join(" ")})` : "nil";
+ const elisp = `(with-current-buffer (find-file-noselect "${INBOX_FILE}")
+ (goto-char (point-max))
+ (insert "\\n* TODO ${parsed.title.replace(/"/g, "\\\"")}")
+ ${parsed.scheduled ? `(org-schedule nil "${parsed.scheduled}")` : ""}
+ ${parsed.priority !== undefined ? `(org-priority ${parsed.priority})` : ""}
+ (save-buffer)
+ (kill-buffer)
+ "Added")`;
+ const result = execEmacs(elisp);
+ if (!result.success) {
+ ctx.ui.notify(`Failed to add to inbox: ${result.error}`, "error");
+ return;
+ }
+ const lines = [];
+ lines.push(`## \uD83D\uDCE5 Added to Inbox`);
+ lines.push("");
+ lines.push(`**${parsed.title}**`);
+ if (parsed.scheduled)
+ lines.push(`- \uD83D\uDCC5 Scheduled: ${parsed.scheduled}`);
+ if (parsed.priority)
+ lines.push(`- Priority: #${parsed.priority}`);
+ pi.sendMessage({
+ customType: "org-todos-add",
+ content: lines.join(`
+`),
+ display: true
+ });
+ updateInboxStatus(ctx);
+ if (parsed.scheduled) {
+ updateTodayStatus(ctx);
+ }
+ }
+ });
+ function fuzzyMatch(item, query) {
+ if (!query)
+ return true;
+ const searchable = `${item.label} ${item.description || ""}`.toLowerCase();
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
+ return terms.every((term) => searchable.includes(term));
+ }
+ async function showSelectMenu(ctx, title, allItems) {
+ return ctx.ui.custom((tui, theme, _kb, done) => {
+ let searchQuery = "";
+ function getFilteredItems() {
+ if (!searchQuery)
+ return allItems;
+ return allItems.filter((item) => fuzzyMatch(item, searchQuery));
+ }
+ let currentItems = getFilteredItems();
+ const container = new Container;
+ container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
+ const headerText = new Text("", 0, 0);
+ function updateHeader() {
+ const titleStr = theme.fg("accent", theme.bold(title));
+ if (searchQuery) {
+ headerText.setText(`${titleStr} ${theme.fg("warning", `filter: ${searchQuery}`)}`);
+ } else {
+ headerText.setText(titleStr);
+ }
+ }
+ updateHeader();
+ container.addChild(headerText);
+ const listTheme = {
+ selectedPrefix: (text) => theme.fg("accent", text),
+ selectedText: (text) => theme.fg("accent", text),
+ description: (text) => theme.fg("muted", text),
+ scrollInfo: (text) => theme.fg("dim", text),
+ noMatch: (text) => theme.fg("warning", text)
+ };
+ let selectList = new SelectList(currentItems, Math.min(currentItems.length, 15), listTheme);
+ selectList.onSelect = (item) => done(item.value);
+ selectList.onCancel = () => done(null);
+ container.addChild(selectList);
+ container.addChild(new Text(theme.fg("dim", "Type to filter · enter to confirm · esc to cancel")));
+ container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
+ function rebuildList() {
+ currentItems = getFilteredItems();
+ const newList = new SelectList(currentItems, Math.min(currentItems.length, 15), listTheme);
+ newList.onSelect = (item) => done(item.value);
+ newList.onCancel = () => done(null);
+ const idx = container.children.indexOf(selectList);
+ if (idx !== -1)
+ container.children[idx] = newList;
+ selectList = newList;
+ updateHeader();
+ }
+ return {
+ render(width) {
+ return container.render(width);
+ },
+ invalidate() {
+ container.invalidate();
+ },
+ handleInput(data) {
+ if (data === "" || data === "\b") {
+ if (searchQuery.length > 0) {
+ searchQuery = searchQuery.slice(0, -1);
+ rebuildList();
+ tui.requestRender();
+ }
+ return;
+ }
+ if (data.length === 1 && data >= " " && data <= "~") {
+ searchQuery += data;
+ rebuildList();
+ tui.requestRender();
+ return;
+ }
+ selectList.handleInput(data);
+ tui.requestRender();
+ }
+ };
+ });
+ }
+ pi.registerCommand("inbox-refile", {
+ description: "Refile inbox item to a section (interactive)",
+ handler: async (args, ctx) => {
+ const inboxResult = execEmacs(`(pi/org-todo-inbox-all)`);
+ if (!inboxResult.success || !inboxResult.data || inboxResult.data.length === 0) {
+ ctx.ui.notify("Inbox is empty!", "info");
+ return;
+ }
+ let heading = (args || "").trim();
+ let sourcePosition = null;
+ if (!heading) {
+ const inboxItems = inboxResult.data.map((item, i) => {
+ const prefix = item.todo ? `[${item.todo}] ` : "";
+ const label = stripOrgLinks(`${prefix}${item.heading}`);
+ return {
+ value: String(i),
+ label,
+ description: item.todo ? undefined : "link/note"
+ };
+ });
+ const selectedIdx = await showSelectMenu(ctx, "Select inbox item to refile", inboxItems);
+ if (selectedIdx === null) {
+ ctx.ui.notify("Refile cancelled", "info");
+ return;
+ }
+ const sourceItem = inboxResult.data[parseInt(selectedIdx, 10)];
+ heading = sourceItem.heading;
+ sourcePosition = sourceItem.position;
+ }
+ const targetsResult = execEmacs("(pi/org-todo-get-refile-targets)");
+ if (!targetsResult.success || !targetsResult.data) {
+ ctx.ui.notify("Failed to get refile targets", "error");
+ return;
+ }
+ const sectionItems = targetsResult.data.map((t, i) => {
+ const indent = t.level > 1 ? " ".repeat(t.level - 1) : "";
+ return {
+ value: String(i),
+ label: `${indent}${stripOrgLinks(t.section)}`,
+ description: t.level > 1 ? stripOrgLinks(t.path) : undefined
+ };
+ });
+ const displayHeading = stripOrgLinks(heading).slice(0, 60);
+ const targetIdx = await showSelectMenu(ctx, `Refile "${displayHeading}" to:`, sectionItems);
+ if (targetIdx === null) {
+ ctx.ui.notify("Refile cancelled", "info");
+ return;
+ }
+ const target = targetsResult.data[parseInt(targetIdx, 10)];
+ const srcPosArg = sourcePosition ? ` ${sourcePosition}` : "";
+ const refileResult = execEmacs(`(pi/org-todo-refile "${heading.replace(/"/g, "\\\"")}" "${target.section.replace(/"/g, "\\\"")}" nil nil ${target.position}${srcPosArg})`);
+ if (!refileResult.success) {
+ ctx.ui.notify(`Refile failed: ${refileResult.error}`, "error");
+ return;
+ }
+ const displayTarget = stripOrgLinks(target.path || target.section);
+ pi.sendMessage({
+ customType: "org-todos",
+ content: `## ✅ Refiled
+
+**${displayHeading}** → *${displayTarget}*`,
+ display: true
+ });
+ updateInboxStatus(ctx);
+ }
+ });
+ function updateInboxStatus(ctx) {
+ try {
+ const result = execEmacs(`(pi/org-todo-inbox-all)`);
+ if (result.success && Array.isArray(result.data)) {
+ const count = result.data.length;
+ if (count > 0) {
+ ctx.ui.setStatus("inbox-count", ctx.ui.theme.fg("warning", `\uD83D\uDCE5 ${count}`));
+ } else {
+ ctx.ui.setStatus("inbox-count", undefined);
+ }
+ }
+ } catch (e) {}
+ }
+ function updateTodayStatus(ctx) {
+ try {
+ const scheduledResult = execEmacs("(pi/org-todo-scheduled)");
+ const overdueResult = execEmacs("(pi/org-todo-overdue)");
+ if (scheduledResult.success && overdueResult.success) {
+ const schedCount = Array.isArray(scheduledResult.data) ? scheduledResult.data.length : 0;
+ const overdueCount = Array.isArray(overdueResult.data) ? overdueResult.data.length : 0;
+ if (schedCount === 0 && overdueCount === 0) {
+ ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("success", "✓"));
+ } else if (overdueCount > 0 && schedCount > 0) {
+ ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("error", `⚠️ ${overdueCount}`) + " " + ctx.ui.theme.fg("accent", `\uD83D\uDCC5 ${schedCount}`));
+ } else if (overdueCount > 0) {
+ ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("error", `⚠️ ${overdueCount}`));
+ } else {
+ ctx.ui.setStatus("today-todos", ctx.ui.theme.fg("accent", `\uD83D\uDCC5 ${schedCount}`));
+ }
+ }
+ } catch (e) {}
+ }
+ pi.on("session_start", async (_event, ctx) => {
+ updateInboxStatus(ctx);
+ updateTodayStatus(ctx);
+ const updateInterval = setInterval(() => {
+ updateInboxStatus(ctx);
+ updateTodayStatus(ctx);
+ }, 5 * 60 * 1000);
+ pi.on("session_end", async () => {
+ clearInterval(updateInterval);
+ });
+ });
}
export {
org_todos_default as default
dots/pi/agent/extensions/org-todos/index.ts
@@ -95,6 +95,17 @@ function execEmacs(elisp: string): OrgTodoResult {
}
}
+/**
+ * Strip org-mode link markup [[url][title]] → title, [[url]] → url
+ */
+function stripOrgLinks(text: string): string {
+ // [[url][title]] → title
+ text = text.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
+ // [[url]] → url
+ text = text.replace(/\[\[([^\]]*)\]\]/g, "$1");
+ return text;
+}
+
/**
* Format TODO item for display
*/
@@ -110,8 +121,8 @@ function formatTodo(todo: any): string {
parts.push(`[#${todo.priority}]`);
}
- // Heading
- parts.push(todo.heading);
+ // Heading (strip org links)
+ parts.push(stripOrgLinks(todo.heading));
// Tags
if (todo.tags && todo.tags.length > 0) {
@@ -148,8 +159,8 @@ function formatTodoMarkdown(todo: any): string {
parts.push(`\`#${todo.priority}\``);
}
- // Heading
- parts.push(todo.heading);
+ // Heading (strip org links)
+ parts.push(stripOrgLinks(todo.heading));
// Tags
if (todo.tags && todo.tags.length > 0) {
@@ -563,8 +574,36 @@ export default function (pi: ExtensionAPI) {
// Register /todos command
pi.registerCommand("todos", {
- description: "Show today's tasks (scheduled + overdue + NEXT)",
+ description: "Show today's tasks (scheduled + overdue + NEXT). Usage: /todos [section]",
handler: async (args, ctx) => {
+ const sectionFilter = (args || "").trim() || null;
+
+ // When a section filter is provided, we need to get the headings in that
+ // section first, then intersect with the scheduled/overdue/next results.
+ // We compare by heading text since the elisp doesn't return section info.
+ let sectionHeadings: Set<string> | null = null;
+ if (sectionFilter) {
+ const sectionResult = execEmacs(`(pi/org-todo-by-section "${sectionFilter.replace(/"/g, '\\"')}")`);
+ if (!sectionResult.success) {
+ // Section might not exist — show available sections
+ const sections = execEmacs("(pi/org-todo-sections)");
+ const sectionList = sections.success && sections.data
+ ? (Array.isArray(sections.data) ? sections.data : Object.values(sections.data)) as string[]
+ : [];
+ ctx.ui.notify(`Section "${sectionFilter}" not found. Available: ${sectionList.join(", ")}`, "error");
+ return;
+ }
+ sectionHeadings = new Set(
+ (sectionResult.data || []).map((t: any) => t.heading)
+ );
+ }
+
+ // Helper: filter todos by section headings set
+ function filterBySection(todos: any[]): any[] {
+ if (!sectionHeadings) return todos;
+ return todos.filter((t: any) => sectionHeadings!.has(t.heading));
+ }
+
// Fetch scheduled, overdue, and NEXT items
const scheduled = execEmacs("(pi/org-todo-scheduled)");
const overdue = execEmacs("(pi/org-todo-overdue)");
@@ -575,50 +614,59 @@ export default function (pi: ExtensionAPI) {
return;
}
+ // Apply section filter
+ const filteredOverdue = filterBySection(overdue.success && overdue.data ? overdue.data : []);
+ const filteredScheduled = filterBySection(scheduled.success && scheduled.data ? scheduled.data : []);
+ const filteredNext = filterBySection(next.success && next.data ? next.data : []);
+
// Build markdown content
const lines: string[] = [];
- lines.push("## 📋 Today's Tasks");
+ const title = sectionFilter
+ ? `## 📋 Today's Tasks — ${sectionFilter}`
+ : "## 📋 Today's Tasks";
+ lines.push(title);
lines.push("");
// Overdue section
- if (overdue.success && overdue.data && overdue.data.length > 0) {
- lines.push(`### ⚠️ Overdue (${overdue.data.length})`);
+ if (filteredOverdue.length > 0) {
+ lines.push(`### ⚠️ Overdue (${filteredOverdue.length})`);
lines.push("");
- for (const todo of overdue.data) {
+ for (const todo of filteredOverdue) {
lines.push(`- ${formatTodoMarkdown(todo)}`);
}
lines.push("");
}
// Scheduled section
- if (scheduled.success && scheduled.data && scheduled.data.length > 0) {
- lines.push(`### 📅 Scheduled Today (${scheduled.data.length})`);
+ if (filteredScheduled.length > 0) {
+ lines.push(`### 📅 Scheduled Today (${filteredScheduled.length})`);
lines.push("");
- for (const todo of scheduled.data) {
+ for (const todo of filteredScheduled) {
lines.push(`- ${formatTodoMarkdown(todo)}`);
}
lines.push("");
}
// NEXT section
- if (next.success && next.data && next.data.length > 0) {
- lines.push(`### ➡️ Next Actions (${next.data.length})`);
+ if (filteredNext.length > 0) {
+ lines.push(`### ➡️ Next Actions (${filteredNext.length})`);
lines.push("");
- for (const todo of next.data) {
+ for (const todo of filteredNext) {
lines.push(`- ${formatTodoMarkdown(todo)}`);
}
lines.push("");
}
// Empty state
- const hasContent =
- (overdue.success && overdue.data?.length > 0) ||
- (scheduled.success && scheduled.data?.length > 0) ||
- (next.success && next.data?.length > 0);
+ const hasContent = filteredOverdue.length > 0 || filteredScheduled.length > 0 || filteredNext.length > 0;
if (!hasContent) {
- lines.push("*No tasks for today.* 🎉");
+ if (sectionFilter) {
+ lines.push(`*No tasks for today in "${sectionFilter}".* 🎉`);
+ } else {
+ lines.push("*No tasks for today.* 🎉");
+ }
}
// Send as a message that appears in the conversation
@@ -990,7 +1038,7 @@ export default function (pi: ExtensionAPI) {
lines.push("### 🔗 Links & Notes");
lines.push("");
for (const item of links) {
- lines.push(`- ${item.heading}`);
+ lines.push(`- ${stripOrgLinks(item.heading)}`);
}
}
}
@@ -1172,12 +1220,10 @@ export default function (pi: ExtensionAPI) {
if (!heading) {
const inboxItems: SelectItem[] = inboxResult.data.map((item: any, i: number) => {
const prefix = item.todo ? `[${item.todo}] ` : "";
- const label = `${prefix}${item.heading}`;
- // Strip org link markup for cleaner display
- const cleanLabel = label.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
+ const label = stripOrgLinks(`${prefix}${item.heading}`);
return {
value: String(i), // index into inboxResult.data
- label: cleanLabel,
+ label,
description: item.todo ? undefined : "link/note",
};
});
@@ -1203,16 +1249,15 @@ export default function (pi: ExtensionAPI) {
// Build flat list with path for display, use index to identify target
const sectionItems: SelectItem[] = targetsResult.data.map((t: any, i: number) => {
const indent = t.level > 1 ? " ".repeat(t.level - 1) : "";
- const cleanSection = t.section.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
return {
value: String(i), // index into targetsResult.data
- label: `${indent}${cleanSection}`,
- description: t.level > 1 ? t.path : undefined,
+ label: `${indent}${stripOrgLinks(t.section)}`,
+ description: t.level > 1 ? stripOrgLinks(t.path) : undefined,
};
});
// Clean heading for display
- const displayHeading = heading.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2").slice(0, 60);
+ const displayHeading = stripOrgLinks(heading).slice(0, 60);
const targetIdx = await showSelectMenu(ctx, `Refile "${displayHeading}" to:`, sectionItems);
if (targetIdx === null) {
ctx.ui.notify("Refile cancelled", "info");
@@ -1229,7 +1274,7 @@ export default function (pi: ExtensionAPI) {
return;
}
- const displayTarget = target.path || target.section;
+ const displayTarget = stripOrgLinks(target.path || target.section);
pi.sendMessage({
customType: "org-todos",
content: `## ✅ Refiled\n\n**${displayHeading}** → *${displayTarget}*`,
dots/pi/agent/extensions/org-todos/Makefile
@@ -71,7 +71,7 @@ test-unit:
# Build TypeScript
build:
@echo -e "$(YELLOW)Building TypeScript...$(NC)"
- @bun build index.ts --target=node --outdir dist
+ @bun build index.ts --target=node --outdir dist --external @mariozechner/pi-coding-agent --external @mariozechner/pi-tui
@echo -e "$(GREEN)✓ Build complete: dist/index.js$(NC)"
# Check if Emacs daemon is running