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