@robbeverhelst/do 1.63.0 → 1.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/do.js +270 -15
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -20603,6 +20603,11 @@ var createCollectionEntryInputSchema = createInsertSchema(collectionEntries, {
20603
20603
  rating: exports_external.number().int().min(1).max(5).nullish(),
20604
20604
  note: exports_external.string().max(2000).nullish()
20605
20605
  }).omit({ tenantId: true, itemId: true, createdAt: true });
20606
+ var updateCollectionEntryInputSchema = exports_external.object({
20607
+ occurredOn: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be an ISO date (YYYY-MM-DD)").optional(),
20608
+ rating: exports_external.number().int().min(1).max(5).nullish(),
20609
+ note: exports_external.string().max(2000).nullish()
20610
+ }).strict();
20606
20611
  var collectionItemViewSchema = collectionItemSchema.extend({
20607
20612
  title: exports_external.string(),
20608
20613
  parentId: ulidSchema.nullable(),
@@ -21787,6 +21792,215 @@ var insightsOverviewSchema = exports_external.object({
21787
21792
  correlations: exports_external.array(insightsCorrelationSchema),
21788
21793
  streaks: exports_external.array(insightsStreakSchema)
21789
21794
  });
21795
+ // ../../packages/schema/src/when.ts
21796
+ var WEEKDAYS = [
21797
+ { name: "sunday", dow: 0, code: "SU" },
21798
+ { name: "monday", dow: 1, code: "MO" },
21799
+ { name: "tuesday", dow: 2, code: "TU" },
21800
+ { name: "wednesday", dow: 3, code: "WE" },
21801
+ { name: "thursday", dow: 4, code: "TH" },
21802
+ { name: "friday", dow: 5, code: "FR" },
21803
+ { name: "saturday", dow: 6, code: "SA" }
21804
+ ];
21805
+ var WEEKDAY_ALT = "monday|tuesday|wednesday|thursday|friday|saturday|sunday";
21806
+ var EVERY_N_RE = /\bevery\s+(\d+)\s+(day|days|week|weeks|month|months|year|years)\b/i;
21807
+ var EVERY_WEEKDAYS_RE = /\bevery\s+(?:weekday|weekdays|work\s?day|work\s?days)\b/i;
21808
+ var EVERY_WEEKDAY_RE = new RegExp(`\\bevery\\s+(${WEEKDAY_ALT})\\b`, "i");
21809
+ var EVERY_UNIT_RE = /\bevery\s+(day|week|month|year)\b/i;
21810
+ var ADVERB_RE = /\b(daily|weekly|monthly|yearly|annually)\b/i;
21811
+ var RELATIVE_RE = /\b(today|tonight|tomorrow|tmrw|tmr)\b/i;
21812
+ var IN_N_RE = /\bin\s+(\d+)\s+(day|days|week|weeks|month|months)\b/i;
21813
+ var NEXT_UNIT_RE = /\bnext\s+(week|month|year)\b/i;
21814
+ var NEXT_WEEKDAY_RE = new RegExp(`\\bnext\\s+(${WEEKDAY_ALT})\\b`, "i");
21815
+ var THIS_WEEKDAY_RE = new RegExp(`\\bthis\\s+(${WEEKDAY_ALT})\\b`, "i");
21816
+ var WEEKDAY_RE = new RegExp(`\\b(${WEEKDAY_ALT})\\b`, "i");
21817
+ var TIME_AMPM_RE = /\b(?:at\s+)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)\b/i;
21818
+ var TIME_24H_RE = /\b(?:at\s+)?([01]?\d|2[0-3]):([0-5]\d)\b/;
21819
+ var NOON_RE = /\b(noon|midday)\b/i;
21820
+ var MIDNIGHT_RE = /\bmidnight\b/i;
21821
+ function blank(s, start, end) {
21822
+ return s.slice(0, start) + " ".repeat(end - start) + s.slice(end);
21823
+ }
21824
+ function startOfDay(now) {
21825
+ const d = new Date(now);
21826
+ d.setHours(0, 0, 0, 0);
21827
+ return d;
21828
+ }
21829
+ function addDays(d, days) {
21830
+ const next = new Date(d);
21831
+ next.setDate(next.getDate() + days);
21832
+ return next;
21833
+ }
21834
+ function daysUntilWeekday(now, dow) {
21835
+ return (dow - now.getDay() + 7) % 7;
21836
+ }
21837
+ var MONDAY = { name: "monday", dow: 1, code: "MO" };
21838
+ function weekdayByName(name) {
21839
+ const hit = WEEKDAYS.find((w) => w.name === (name ?? "").toLowerCase());
21840
+ return hit ?? MONDAY;
21841
+ }
21842
+ function parseWhen(input, now) {
21843
+ const spans = [];
21844
+ let scan = input;
21845
+ const take = (m, kind) => {
21846
+ if (!m || m.index === undefined)
21847
+ return null;
21848
+ const start = m.index;
21849
+ const end = start + m[0].length;
21850
+ const span = { start, end, kind };
21851
+ spans.push(span);
21852
+ scan = blank(scan, start, end);
21853
+ return { span, index: start };
21854
+ };
21855
+ const recurrence2 = takeRecurrence(scan, take);
21856
+ const date6 = takeDate(scan, take, now);
21857
+ const time4 = takeTime(scan, take);
21858
+ const { scheduledAt, allDay } = resolveWhen({ now, recurrence: recurrence2, date: date6, time: time4 });
21859
+ spans.sort((a, b) => a.start - b.start);
21860
+ return {
21861
+ ...scheduledAt ? { scheduledAt } : {},
21862
+ allDay,
21863
+ ...recurrence2 ? { recurrence: recurrence2 } : {},
21864
+ spans
21865
+ };
21866
+ }
21867
+ function takeRecurrence(scan, take) {
21868
+ const everyN = scan.match(EVERY_N_RE);
21869
+ if (everyN && take(everyN, "recurrence")) {
21870
+ const [, nStr = "1", unitRaw = "day"] = everyN;
21871
+ const interval2 = Number(nStr);
21872
+ const unit2 = unitRaw.toLowerCase();
21873
+ const freq = unit2.startsWith("day") ? "daily" : unit2.startsWith("week") ? "weekly" : unit2.startsWith("month") ? "monthly" : "yearly";
21874
+ return { mode: "rigid", freq, interval: interval2 > 0 ? interval2 : 1 };
21875
+ }
21876
+ const weekdays = scan.match(EVERY_WEEKDAYS_RE);
21877
+ if (weekdays && take(weekdays, "recurrence")) {
21878
+ return { mode: "rigid", freq: "weekly", interval: 1, byDay: ["MO", "TU", "WE", "TH", "FR"] };
21879
+ }
21880
+ const weekday = scan.match(EVERY_WEEKDAY_RE);
21881
+ if (weekday && take(weekday, "recurrence")) {
21882
+ return { mode: "rigid", freq: "weekly", interval: 1, byDay: [weekdayByName(weekday[1]).code] };
21883
+ }
21884
+ const unit = scan.match(EVERY_UNIT_RE);
21885
+ if (unit && take(unit, "recurrence")) {
21886
+ const u = (unit[1] ?? "").toLowerCase();
21887
+ const freq = u === "day" ? "daily" : u === "week" ? "weekly" : u === "month" ? "monthly" : "yearly";
21888
+ return { mode: "rigid", freq, interval: 1 };
21889
+ }
21890
+ const adverb = scan.match(ADVERB_RE);
21891
+ if (adverb && take(adverb, "recurrence")) {
21892
+ const word = (adverb[1] ?? "").toLowerCase();
21893
+ const freq = word === "daily" ? "daily" : word === "weekly" ? "weekly" : word === "monthly" ? "monthly" : "yearly";
21894
+ return { mode: "rigid", freq, interval: 1 };
21895
+ }
21896
+ return;
21897
+ }
21898
+ function takeDate(scan, take, now) {
21899
+ const today2 = startOfDay(now);
21900
+ const on = (day, tonight = false) => ({ day, tonight });
21901
+ const inN = scan.match(IN_N_RE);
21902
+ if (inN && take(inN, "date")) {
21903
+ const [, nStr = "1", unitRaw = "day"] = inN;
21904
+ const n = Number(nStr);
21905
+ const unit = unitRaw.toLowerCase();
21906
+ if (unit.startsWith("day"))
21907
+ return on(addDays(today2, n));
21908
+ if (unit.startsWith("week"))
21909
+ return on(addDays(today2, n * 7));
21910
+ return on(addMonths(today2, n));
21911
+ }
21912
+ const nextWeekday = scan.match(NEXT_WEEKDAY_RE);
21913
+ if (nextWeekday && take(nextWeekday, "date")) {
21914
+ const { dow } = weekdayByName(nextWeekday[1]);
21915
+ const delta = daysUntilWeekday(now, dow) || 7;
21916
+ return on(addDays(today2, delta));
21917
+ }
21918
+ const thisWeekday = scan.match(THIS_WEEKDAY_RE);
21919
+ if (thisWeekday && take(thisWeekday, "date")) {
21920
+ return on(addDays(today2, daysUntilWeekday(now, weekdayByName(thisWeekday[1]).dow)));
21921
+ }
21922
+ const nextUnit = scan.match(NEXT_UNIT_RE);
21923
+ if (nextUnit && take(nextUnit, "date")) {
21924
+ const u = (nextUnit[1] ?? "").toLowerCase();
21925
+ if (u === "week")
21926
+ return on(addDays(today2, 7));
21927
+ if (u === "month")
21928
+ return on(addMonths(today2, 1));
21929
+ return on(addMonths(today2, 12));
21930
+ }
21931
+ const relative = scan.match(RELATIVE_RE);
21932
+ if (relative && take(relative, "date")) {
21933
+ const word = (relative[1] ?? "").toLowerCase();
21934
+ if (word === "tonight")
21935
+ return on(today2, true);
21936
+ return on(word === "today" ? today2 : addDays(today2, 1));
21937
+ }
21938
+ const weekday = scan.match(WEEKDAY_RE);
21939
+ if (weekday && take(weekday, "date")) {
21940
+ return on(addDays(today2, daysUntilWeekday(now, weekdayByName(weekday[1]).dow)));
21941
+ }
21942
+ return null;
21943
+ }
21944
+ function takeTime(scan, take) {
21945
+ const ampm = scan.match(TIME_AMPM_RE);
21946
+ if (ampm && take(ampm, "time")) {
21947
+ const [, hRaw = "0", mRaw, meridiem = "am"] = ampm;
21948
+ let hours = Number(hRaw) % 12;
21949
+ if (meridiem.toLowerCase() === "pm")
21950
+ hours += 12;
21951
+ return { hours, minutes: mRaw ? Number(mRaw) : 0 };
21952
+ }
21953
+ const h24 = scan.match(TIME_24H_RE);
21954
+ if (h24 && take(h24, "time")) {
21955
+ const [, hRaw = "0", mRaw = "0"] = h24;
21956
+ return { hours: Number(hRaw), minutes: Number(mRaw) };
21957
+ }
21958
+ const noon = scan.match(NOON_RE);
21959
+ if (noon && take(noon, "time"))
21960
+ return { hours: 12, minutes: 0 };
21961
+ const midnight = scan.match(MIDNIGHT_RE);
21962
+ if (midnight && take(midnight, "time"))
21963
+ return { hours: 0, minutes: 0 };
21964
+ return null;
21965
+ }
21966
+ function resolveWhen(args) {
21967
+ const { now, recurrence: recurrence2, date: date6, time: time4 } = args;
21968
+ let day = date6?.day ?? null;
21969
+ if (!day && recurrence2)
21970
+ day = firstOccurrence(recurrence2, now);
21971
+ if (!day && time4)
21972
+ day = startOfDay(now);
21973
+ if (!day)
21974
+ return { allDay: true };
21975
+ if (time4) {
21976
+ const at = new Date(day);
21977
+ at.setHours(time4.hours, time4.minutes, 0, 0);
21978
+ return { scheduledAt: at, allDay: false };
21979
+ }
21980
+ if (date6?.tonight) {
21981
+ const at = new Date(day);
21982
+ at.setHours(18, 0, 0, 0);
21983
+ return { scheduledAt: at, allDay: false };
21984
+ }
21985
+ return { scheduledAt: day, allDay: true };
21986
+ }
21987
+ function firstOccurrence(rule, now) {
21988
+ const today2 = startOfDay(now);
21989
+ if (rule.freq === "weekly" && rule.byDay && rule.byDay.length > 0) {
21990
+ const deltas = rule.byDay.map((code) => {
21991
+ const dow = WEEKDAYS.find((w) => w.code === code)?.dow ?? 1;
21992
+ return daysUntilWeekday(now, dow);
21993
+ });
21994
+ return addDays(today2, Math.min(...deltas));
21995
+ }
21996
+ return today2;
21997
+ }
21998
+ function addMonths(d, months) {
21999
+ const next = new Date(d);
22000
+ next.setMonth(next.getMonth() + months);
22001
+ return next;
22002
+ }
22003
+
21790
22004
  // ../../packages/schema/src/capture.ts
21791
22005
  var TAG_RE = /(?:^|\s)#([\p{L}\p{N}][\p{L}\p{N}_-]*)/gu;
21792
22006
  var PRIORITY_RE = /(?:^|\s)!([\p{L}]+)/gu;
@@ -21803,7 +22017,7 @@ var PRIORITY_ALIASES = {
21803
22017
  h: "high",
21804
22018
  hi: "high"
21805
22019
  };
21806
- function parseCapture(input) {
22020
+ function parseCapture(input, now) {
21807
22021
  const tags = [];
21808
22022
  for (const match of input.matchAll(TAG_RE)) {
21809
22023
  const tag = match[1]?.toLowerCase();
@@ -21825,8 +22039,29 @@ function parseCapture(input) {
21825
22039
  break;
21826
22040
  }
21827
22041
  }
21828
- const title = input.replace(TAG_RE, " ").replace(PRIORITY_RE, " ").replace(AREA_RE, " ").replace(/\s+/g, " ").trim();
21829
- return { title, tags, ...priority ? { priority } : {}, ...area ? { area } : {} };
22042
+ let stripped = blankMatches(input, [TAG_RE, PRIORITY_RE, AREA_RE]);
22043
+ const when = now ? parseWhen(stripped, now) : undefined;
22044
+ if (when) {
22045
+ for (const span of [...when.spans].sort((a, b) => b.start - a.start)) {
22046
+ stripped = stripped.slice(0, span.start) + " ".repeat(span.end - span.start) + stripped.slice(span.end);
22047
+ }
22048
+ }
22049
+ const title = stripped.replace(/\s+/g, " ").trim();
22050
+ return {
22051
+ title,
22052
+ tags,
22053
+ ...priority ? { priority } : {},
22054
+ ...area ? { area } : {},
22055
+ ...when?.scheduledAt ? { scheduledAt: when.scheduledAt, allDay: when.allDay } : {},
22056
+ ...when?.recurrence ? { recurrence: when.recurrence } : {}
22057
+ };
22058
+ }
22059
+ function blankMatches(input, regexes) {
22060
+ let out = input;
22061
+ for (const re of regexes) {
22062
+ out = out.replace(re, (m) => " ".repeat(m.length));
22063
+ }
22064
+ return out;
21830
22065
  }
21831
22066
  // ../../packages/schema/src/export.ts
21832
22067
  var EXPORT_FORMAT = "do.export";
@@ -23981,8 +24216,8 @@ var usageError = (message, detail) => new CliError("usage", message, EXIT.USAGE,
23981
24216
  var configError = (message) => new CliError("config", message, EXIT.CONFIG);
23982
24217
 
23983
24218
  // src/capture.ts
23984
- function parseCapture2(input) {
23985
- const parsed = parseCapture(input);
24219
+ function parseCapture2(input, now) {
24220
+ const parsed = parseCapture(input, now);
23986
24221
  if (parsed.title.length === 0) {
23987
24222
  throw usageError("capture needs some text for the task title");
23988
24223
  }
@@ -23991,7 +24226,7 @@ function parseCapture2(input) {
23991
24226
 
23992
24227
  // src/meta.ts
23993
24228
  var CLI_NAME = "do";
23994
- var VERSION = "1.63.0";
24229
+ var VERSION = "1.65.0";
23995
24230
  var API_PREFIX = "/api/v1";
23996
24231
  var DEFAULT_API_URL = "http://localhost:3001";
23997
24232
 
@@ -24062,8 +24297,16 @@ class DoClient {
24062
24297
  if (!res.ok) {
24063
24298
  throw httpError(res.status, parsed);
24064
24299
  }
24065
- return parsed;
24300
+ return unwrapEnvelope(parsed);
24301
+ }
24302
+ }
24303
+ function unwrapEnvelope(body) {
24304
+ if (typeof body === "object" && body !== null && !Array.isArray(body) && "nextCursor" in body) {
24305
+ const record2 = body;
24306
+ if (Array.isArray(record2.data))
24307
+ return record2.data;
24066
24308
  }
24309
+ return body;
24067
24310
  }
24068
24311
  function safeJson(text2) {
24069
24312
  try {
@@ -25110,8 +25353,8 @@ function renderTask(data, mode) {
25110
25353
  const t = data;
25111
25354
  const mark = STATUS_MARK[t.status ?? ""] ?? "\u2022";
25112
25355
  const prio = t.priority && t.priority !== "none" ? paint(mode, "yellow", `!${t.priority} `) : "";
25113
- const when = t.scheduledAt ?? t.dueAt;
25114
- const date6 = when ? paint(mode, "dim", ` (${shortDate(when)})`) : "";
25356
+ const when2 = t.scheduledAt ?? t.dueAt;
25357
+ const date6 = when2 ? paint(mode, "dim", ` (${shortDate(when2)})`) : "";
25115
25358
  return `${mark} ${prio}${t.title ?? "(untitled)"}${date6}
25116
25359
  ${paint(mode, "dim", t.id)}`;
25117
25360
  }
@@ -25399,19 +25642,30 @@ function runFixed(spec, ctx) {
25399
25642
  return handler(ctx);
25400
25643
  }
25401
25644
  var HANDLERS = {
25402
- capture: async ({ args, requireClient }) => {
25645
+ capture: async ({ args, io, requireClient }) => {
25403
25646
  const text2 = args.join(" ").trim();
25404
25647
  if (text2.length === 0)
25405
25648
  throw usageError('capture needs some text, e.g. `do capture "call mum #family"`');
25406
- const parsed = parseCapture2(text2);
25649
+ const parsed = parseCapture2(text2, io.now());
25650
+ const id = ulid3();
25407
25651
  const input = parseInput(createTaskInputSchema, {
25408
- id: ulid3(),
25652
+ id,
25409
25653
  title: parsed.title,
25410
25654
  tags: parsed.tags,
25411
25655
  ...parsed.priority ? { priority: parsed.priority } : {},
25412
- ...parsed.area ? { areaName: parsed.area } : {}
25656
+ ...parsed.area ? { areaName: parsed.area } : {},
25657
+ ...parsed.scheduledAt ? { scheduledAt: parsed.scheduledAt, allDay: parsed.allDay } : {}
25413
25658
  });
25414
- return { data: await requireClient().post("/tasks", input), render: renderTask };
25659
+ const client = requireClient();
25660
+ const task = await client.post("/tasks", input);
25661
+ if (parsed.recurrence) {
25662
+ const rule = parseInput(createRecurrenceRuleInputSchema, {
25663
+ templateId: id,
25664
+ ...parsed.recurrence
25665
+ });
25666
+ await client.post("/recurrence/rules", rule);
25667
+ }
25668
+ return { data: task, render: renderTask };
25415
25669
  },
25416
25670
  today: async ({ io, requireClient }) => {
25417
25671
  const tasks3 = await requireClient().get("/tasks", { query: { filter: "all" } });
@@ -25658,7 +25912,8 @@ var HANDLERS = {
25658
25912
  "link revoke": async ({ args, flags, requireClient }) => {
25659
25913
  const id = requireArg(args, 0, "share-link id");
25660
25914
  assertDestructiveAllowed(flags, `revoke share-link ${id}`);
25661
- return { data: await requireClient().delete(`/share-links/${id}`), render: renderRecord };
25915
+ await requireClient().delete(`/share-links/${id}`);
25916
+ return { data: { ok: true, revoked: id }, render: renderRecord };
25662
25917
  },
25663
25918
  whoami: async ({ requireClient }) => ({
25664
25919
  data: await requireClient().get("/whoami"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.63.0",
3
+ "version": "1.65.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "type": "module",