@robbeverhelst/do 1.347.0 → 1.348.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 +169 -16
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -14420,7 +14420,7 @@ var configError = (message) => new CliError("config", message, EXIT.CONFIG);
14420
14420
 
14421
14421
  // src/meta.ts
14422
14422
  var CLI_NAME = "do";
14423
- var VERSION = "1.347.0";
14423
+ var VERSION = "1.348.0";
14424
14424
  var API_PREFIX = "/api/v1";
14425
14425
  var DEFAULT_API_URL = "http://localhost:3001";
14426
14426
 
@@ -18030,7 +18030,8 @@ var habits = pgTable("habits", {
18030
18030
  eventSourceId: text("event_source_id"),
18031
18031
  eventMatch: text("event_match"),
18032
18032
  eventOffset: text("event_offset"),
18033
- durationMinutes: integer2("duration_minutes")
18033
+ durationMinutes: integer2("duration_minutes"),
18034
+ activeMonths: integer2("active_months").array()
18034
18035
  }, (t) => [
18035
18036
  index("habits_tenant_idx").on(t.tenantId),
18036
18037
  index("habits_tenant_area_idx").on(t.tenantId, t.areaId),
@@ -18299,7 +18300,8 @@ var challengeParts = pgTable("challenge_parts", {
18299
18300
  challengeId: text("challenge_id").notNull().references(() => challenges.id, { onDelete: "cascade" }),
18300
18301
  kind: challengePartKind("kind").notNull(),
18301
18302
  subjectId: text("subject_id").notNull(),
18302
- position: integer2("position").notNull()
18303
+ position: integer2("position").notNull(),
18304
+ resumeOnStart: boolean4("resume_on_start").notNull().default(false)
18303
18305
  }, (t) => [
18304
18306
  index("challenge_parts_tenant_idx").on(t.tenantId),
18305
18307
  index("challenge_parts_challenge_idx").on(t.challengeId, t.position),
@@ -21397,7 +21399,8 @@ var habitSchema = createSelectSchema(habits, {
21397
21399
  eventSourceId: string2().nullable().default(null),
21398
21400
  eventMatch: string2().nullable().default(null),
21399
21401
  eventOffset: string2().nullable().default(null),
21400
- durationMinutes: number2().int().nullable().default(null)
21402
+ durationMinutes: number2().int().nullable().default(null),
21403
+ activeMonths: array(number2().int()).nullable().default(null)
21401
21404
  });
21402
21405
  var habitEntrySchema = createSelectSchema(habitEntries, {
21403
21406
  occurredAt: exports_coerce.date(),
@@ -21416,6 +21419,12 @@ var habitTimeOfDaySchema = union([
21416
21419
  ]);
21417
21420
  var HABIT_DURATION_MAX_MINUTES = 600;
21418
21421
  var habitDurationMinutesSchema = number2().int().min(1).max(HABIT_DURATION_MAX_MINUTES);
21422
+ var habitActiveMonthsSchema = array(number2().int().min(1).max(12)).max(12).nullable().transform((months) => {
21423
+ if (!months)
21424
+ return null;
21425
+ const set = [...new Set(months)].sort((x, y) => x - y);
21426
+ return set.length === 0 || set.length === 12 ? null : set;
21427
+ });
21419
21428
  var habitAliasSchema = string2().trim().min(1).max(60);
21420
21429
  var habitAliasesSchema = array(habitAliasSchema).max(12).transform((aliases) => aliases.filter((a, i) => aliases.findIndex((b) => b.toLowerCase() === a.toLowerCase()) === i));
21421
21430
  var habitScheduleSchema = discriminatedUnion("kind", [
@@ -21455,7 +21464,8 @@ var createHabitInputSchema = createInsertSchema(habits, {
21455
21464
  aliases: habitAliasesSchema.optional(),
21456
21465
  timeOfDay: habitTimeOfDaySchema.nullish(),
21457
21466
  remind: boolean2().optional(),
21458
- durationMinutes: habitDurationMinutesSchema.nullish()
21467
+ durationMinutes: habitDurationMinutesSchema.nullish(),
21468
+ activeMonths: habitActiveMonthsSchema.optional()
21459
21469
  }).omit({ tenantId: true, recurrenceRuleId: true, retiredAt: true, ...eventColumns }).extend({
21460
21470
  title: string2().min(1),
21461
21471
  schedule: habitScheduleSchema.optional(),
@@ -21472,7 +21482,8 @@ var updateHabitInputSchema = createUpdateSchema(habits, {
21472
21482
  aliases: habitAliasesSchema.optional(),
21473
21483
  timeOfDay: habitTimeOfDaySchema.nullish(),
21474
21484
  remind: boolean2().optional(),
21475
- durationMinutes: habitDurationMinutesSchema.nullish()
21485
+ durationMinutes: habitDurationMinutesSchema.nullish(),
21486
+ activeMonths: habitActiveMonthsSchema.optional()
21476
21487
  }).omit({
21477
21488
  id: true,
21478
21489
  tenantId: true,
@@ -22737,6 +22748,11 @@ var updateRoutineRunInputSchema = object({
22737
22748
  var routineRunsQuerySchema = object({
22738
22749
  limit: exports_coerce.number().int().min(1).max(200).optional()
22739
22750
  });
22751
+ var recentRoutineRunsQuerySchema = object({
22752
+ since: exports_coerce.date().optional(),
22753
+ status: routineRunStatusSchema.optional(),
22754
+ limit: exports_coerce.number().int().min(1).max(200).default(50)
22755
+ });
22740
22756
  var routineDoneInputSchema = object({
22741
22757
  id: ulidSchema.optional(),
22742
22758
  occurrenceDate: exports_coerce.date().nullish()
@@ -22804,7 +22820,11 @@ var routineTodayStepSchema = object({
22804
22820
  minutes: number2().int().nullable(),
22805
22821
  done: boolean2(),
22806
22822
  depth: number2().int().nonnegative().default(0),
22807
- optional: boolean2().default(false)
22823
+ optional: boolean2().default(false),
22824
+ habitId: string2().nullable().default(null),
22825
+ routineId: string2().nullable().default(null),
22826
+ habitToday: _enum2(["due", "not_due", "paused"]).nullable().default(null),
22827
+ amount: object({ value: number2(), target: number2().nullable(), unit: string2().nullable() }).nullable().default(null)
22808
22828
  });
22809
22829
  var routineTodayStateSchema = _enum2(["done", "running", "upcoming", "resting"]);
22810
22830
  var routineTodaySchema = object({
@@ -31326,7 +31346,8 @@ var houseUpkeepSoonSchema = object({
31326
31346
  nextOn: day.nullable(),
31327
31347
  placeTitle: string2().nullable().default(null),
31328
31348
  lastOn: day.nullable().default(null),
31329
- occurrenceId: string2().nullable().default(null)
31349
+ occurrenceId: string2().nullable().default(null),
31350
+ thisMonth: boolean2().optional()
31330
31351
  });
31331
31352
  var housePlaceTileSchema = object({
31332
31353
  id: string2(),
@@ -37345,6 +37366,16 @@ var reviewMarkSchema = object({
37345
37366
  date: progressDateSchema,
37346
37367
  state: reviewMarkStateSchema
37347
37368
  });
37369
+ var reviewTallySchema = object({ done: countSchema2, due: countSchema2 });
37370
+ var reviewHabitsSchema = object({
37371
+ kept30: reviewTallySchema,
37372
+ kept30Before: reviewTallySchema,
37373
+ checkIns: countSchema2,
37374
+ checkInsByAgent: countSchema2,
37375
+ checkInsFromRoutines: countSchema2,
37376
+ paused: countSchema2,
37377
+ grown: countSchema2
37378
+ });
37348
37379
  var reviewNumbersSchema = object({
37349
37380
  tasksDone: countSchema2,
37350
37381
  tasksDoneDelta: number2().int(),
@@ -37367,7 +37398,13 @@ var reviewStreakSchema = object({
37367
37398
  current: countSchema2,
37368
37399
  best: countSchema2,
37369
37400
  unitLabel: string2().min(1),
37370
- marks: array(reviewMarkSchema)
37401
+ marks: array(reviewMarkSchema),
37402
+ state: _enum2(["active", "recovering", "broken"]).optional(),
37403
+ graceUsed: countSchema2.optional(),
37404
+ graceBudget: countSchema2.optional(),
37405
+ kept30: reviewTallySchema.optional(),
37406
+ period: reviewTallySchema.optional(),
37407
+ paused: boolean2().optional()
37371
37408
  });
37372
37409
  var reviewProjectSchema = object({
37373
37410
  projectId: string2().min(1),
@@ -37386,7 +37423,8 @@ var reviewSummarySchema = object({
37386
37423
  numbers: reviewNumbersSchema,
37387
37424
  series: array(reviewSeriesEntrySchema).length(8),
37388
37425
  streaks: array(reviewStreakSchema),
37389
- projects: array(reviewProjectSchema)
37426
+ projects: array(reviewProjectSchema),
37427
+ habits: reviewHabitsSchema.optional()
37390
37428
  }).refine((s) => s.series.at(-1)?.current === true, {
37391
37429
  message: "the last series entry is the asked period",
37392
37430
  path: ["series"]
@@ -37439,7 +37477,7 @@ var progressOperations = defineOperations({
37439
37477
  scopes: ["tasks:read", "habits:read"],
37440
37478
  tag: "Reads",
37441
37479
  summary: "Review summary",
37442
- description: "Plan > Review in one read, for the ISO week (Monday to Sunday) or calendar month holding `date` (default: the one holding today, in the key's member's saved timezone): the headline numbers (tasks done, mine and the agents', against the period before; habit days met and due; projects with a task done; challenges running), tasks done and habit days met for the last 8 periods (oldest first, the last one the asked period), one streak row per live habit (current and best run, `unitLabel` `N days` for a daily habit and `N in a row` otherwise, and a mark per day of the week or per week of the month: done, missed, open, later or not_due, a day it isn't due that never breaks the run), and each open project's tasks done of its total, the most recently active first.",
37480
+ description: "Plan > Review in one read, for the ISO week (Monday to Sunday) or calendar month holding `date` (default: the one holding today, in the key's member's saved timezone): the headline numbers (tasks done, mine and the agents', against the period before; habit days met and due; projects with a task done; challenges running), tasks done and habit days met for the last 8 periods (oldest first, the last one the asked period), one streak row per live habit (current and best run, `unitLabel` `N days` for a daily habit and `N in a row` otherwise, and a mark per day of the week or per week of the month: done, missed, open, later or not_due, a day it isn't due that never breaks the run), and each open project's tasks done of its total, the most recently active first. For Review \u203A Habits each streak row also carries its `state` (active, recovering or broken), grace used of its budget, `kept30` (due days met of due over the last 30 days), `period` (met of due in the asked period) and whether it rests today (`paused`), and `habits` gives the tiles: kept over the last 30 days and the 30 before, the period's check-ins with how many an agent and a routine made, and how many habits rest and have grown.",
37443
37481
  query: [
37444
37482
  {
37445
37483
  name: "span",
@@ -38091,6 +38129,19 @@ var routineOperations = defineOperations({
38091
38129
  response: { description: "`{ today, routines }`." },
38092
38130
  operationId: "routines_today"
38093
38131
  },
38132
+ recentRuns: {
38133
+ method: "get",
38134
+ path: "/routines/runs",
38135
+ scopes: ["routines:read"],
38136
+ tag: "Routines",
38137
+ summary: "Recent runs",
38138
+ description: "The Space's recent routine runs across its live routines, newest first, each with its per-step state: `since` (a day or an instant) keeps those started at or after it, `status` (`in_progress`, `completed`, `abandoned`) keeps one kind, `limit` (1\u2013200, 50 by default) the newest. The Runs page reads the last completed run and the runs left unfinished in the last 7 days from it.",
38139
+ querySchema: recentRoutineRunsQuerySchema,
38140
+ status: 200,
38141
+ response: { description: "The runs, newest first." },
38142
+ errors: { 400: true },
38143
+ operationId: "routines_recent_runs"
38144
+ },
38094
38145
  getRun: {
38095
38146
  method: "get",
38096
38147
  path: "/routines/runs/{runId}",
@@ -48220,9 +48271,14 @@ var SCHEDULE_FLAGS = [
48220
48271
  type: "string",
48221
48272
  description: "How long it takes (15m, 1h, 1h30m; 1\u2013600 minutes); none clears it back to 15m."
48222
48273
  },
48274
+ {
48275
+ name: "season",
48276
+ type: "string",
48277
+ description: "Only in season: a run of months (Oct-Apr) or a list (oct,nov,dec or 10,11,12); off makes it all year."
48278
+ },
48223
48279
  { name: "data", type: "string", description: "JSON for any other field of the contract." }
48224
48280
  ];
48225
- var SCHEDULE_NOTES = 'One of --every-day, --on mo,we,fr, --every 3d|2w [--from YYYY-MM-DD], --monthly 1|last sets its schedule; days it isn\'t due never break the streak. Without one, it keeps `period` + `frequencyCount` (3\xD7 a week). A schedule that follows the waste calendar goes in --data: `{"schedule":{"kind":"event","sourceId":"<its connection id>","match":"bin collection","offset":"day_before_evening"}}` (or `same_day`; a match naming a kind, "PMD", follows only those pickups). The reply\'s `nextDue` lists its next 4 due days. --time 16:30|morning|midday|evening and --for 20m put it on your do calendar at that time on each due day (a part of day at 08:00, 12:30 or 19:00; 15 minutes without --for).';
48281
+ var SCHEDULE_NOTES = 'One of --every-day, --on mo,we,fr, --every 3d|2w [--from YYYY-MM-DD], --monthly 1|last sets its schedule; days it isn\'t due never break the streak. Without one, it keeps `period` + `frequencyCount` (3\xD7 a week). A schedule that follows the waste calendar goes in --data: `{"schedule":{"kind":"event","sourceId":"<its connection id>","match":"bin collection","offset":"day_before_evening"}}` (or `same_day`; a match naming a kind, "PMD", follows only those pickups). The reply\'s `nextDue` lists its next 4 due days. --time 16:30|morning|midday|evening and --for 20m put it on your do calendar at that time on each due day (a part of day at 08:00, 12:30 or 19:00; 15 minutes without --for). --season Oct-Apr keeps it to those months: out of season it isn\'t due, its days never break the streak, and it leaves every list (--season off: all year).';
48226
48282
  function localToday() {
48227
48283
  const now = new Date;
48228
48284
  const pad = (n) => String(n).padStart(2, "0");
@@ -48282,6 +48338,44 @@ function timingFromFlags(flags) {
48282
48338
  }
48283
48339
  return out;
48284
48340
  }
48341
+ var MONTH_NAMES = [
48342
+ "jan",
48343
+ "feb",
48344
+ "mar",
48345
+ "apr",
48346
+ "may",
48347
+ "jun",
48348
+ "jul",
48349
+ "aug",
48350
+ "sep",
48351
+ "oct",
48352
+ "nov",
48353
+ "dec"
48354
+ ];
48355
+ function monthOf(raw) {
48356
+ const text = raw.trim().toLowerCase();
48357
+ const n = /^\d{1,2}$/.test(text) ? Number(text) : MONTH_NAMES.indexOf(text.slice(0, 3)) + 1;
48358
+ if (n < 1 || n > 12)
48359
+ throw usageError(`--season: "${raw}" isn't a month`);
48360
+ return n;
48361
+ }
48362
+ function seasonFromFlag(raw) {
48363
+ const text = raw.trim().toLowerCase();
48364
+ if (text === "off" || text === "none")
48365
+ return null;
48366
+ return text.split(",").flatMap((part) => {
48367
+ const [from, to] = part.split("-");
48368
+ if (to === undefined)
48369
+ return [monthOf(part)];
48370
+ const start = monthOf(from);
48371
+ const length = (monthOf(to) - start + 12) % 12 + 1;
48372
+ return Array.from({ length }, (_, i) => (start - 1 + i) % 12 + 1);
48373
+ });
48374
+ }
48375
+ function seasonFromFlags(flags) {
48376
+ const season = flagString(flags, "season");
48377
+ return season === undefined ? {} : { activeMonths: seasonFromFlag(season) };
48378
+ }
48285
48379
  var commands20 = [
48286
48380
  {
48287
48381
  spec: {
@@ -48302,7 +48396,8 @@ var commands20 = [
48302
48396
  ...title ? { title } : {},
48303
48397
  ...readDataFlag(flags),
48304
48398
  ...schedule ? { schedule } : {},
48305
- ...timingFromFlags(flags)
48399
+ ...timingFromFlags(flags),
48400
+ ...seasonFromFlags(flags)
48306
48401
  });
48307
48402
  if (flagBool(flags, "dry-run")) {
48308
48403
  return {
@@ -48319,7 +48414,7 @@ var commands20 = [
48319
48414
  {
48320
48415
  spec: {
48321
48416
  path: ["habits", "update"],
48322
- summary: "Change a habit: its schedule (--every-day, --on, --every, --monthly), --time, --for or --data.",
48417
+ summary: "Change a habit: its schedule (--every-day, --on, --every, --monthly), --time, --for, --season or --data.",
48323
48418
  args: [{ name: "id", required: true, description: "Habit ULID." }],
48324
48419
  flags: SCHEDULE_FLAGS,
48325
48420
  scopes: [...habitOperations.update.scopes],
@@ -48332,10 +48427,11 @@ var commands20 = [
48332
48427
  const body = parseInput(updateHabitInputSchema, {
48333
48428
  ...readDataFlag(flags),
48334
48429
  ...schedule ? { schedule } : {},
48335
- ...timingFromFlags(flags)
48430
+ ...timingFromFlags(flags),
48431
+ ...seasonFromFlags(flags)
48336
48432
  });
48337
48433
  if (Object.keys(body).length === 0)
48338
- throw usageError("nothing to change \u2014 pass a schedule flag such as --on mo,we, --time, --for, or --data");
48434
+ throw usageError("nothing to change \u2014 pass a schedule flag such as --on mo,we, --time, --for, --season, or --data");
48339
48435
  const client = requireClient();
48340
48436
  if (flagBool(flags, "dry-run")) {
48341
48437
  const before = await client.get(pathOf(habitOperations.get, { id }));
@@ -54960,6 +55056,23 @@ function renderRoutinesToday(data, mode) {
54960
55056
  }).join(`
54961
55057
  `);
54962
55058
  }
55059
+ var RUN_WORDS = {
55060
+ in_progress: "going",
55061
+ completed: "done",
55062
+ abandoned: "left"
55063
+ };
55064
+ function renderRecentRuns(data, mode) {
55065
+ const list = (data.data ?? data) || [];
55066
+ if (list.length === 0)
55067
+ return "No runs.";
55068
+ return list.map((r) => {
55069
+ const steps = r.steps.filter((s) => s.refKind !== "routine");
55070
+ const done = steps.filter((s) => s.status === "done").length;
55071
+ const when = r.startedAt.slice(0, 16).replace("T", " ");
55072
+ return `${when} ${r.routineTitle} ${paint(mode, "dim", `${RUN_WORDS[r.status] ?? r.status} \xB7 ${done} of ${steps.length} done ${r.id}`)}`;
55073
+ }).join(`
55074
+ `);
55075
+ }
54963
55076
  function renderRoutineTaskAdded(data) {
54964
55077
  const run = data;
54965
55078
  const last = run.steps?.at(-1)?.title ?? "the task";
@@ -55377,6 +55490,46 @@ var routineCommands = [
55377
55490
  ];
55378
55491
  var commands42 = [
55379
55492
  ...routineCommands,
55493
+ {
55494
+ spec: {
55495
+ path: ["routines", "runs"],
55496
+ summary: "Recent runs across your routines: when, how far, done or left.",
55497
+ args: [],
55498
+ flags: [
55499
+ {
55500
+ name: "since",
55501
+ type: "string",
55502
+ description: "Only runs started on or after this day (YYYY-MM-DD) or instant."
55503
+ },
55504
+ { name: "status", type: "string", description: "in_progress, completed or abandoned." },
55505
+ {
55506
+ name: "limit",
55507
+ type: "string",
55508
+ description: "How many, newest first (1-200, default 50)."
55509
+ }
55510
+ ],
55511
+ scopes: [...routineOperations.recentRuns.scopes],
55512
+ returns: "RoutineRunView[]",
55513
+ notes: `${routeOf(routineOperations.recentRuns)}. A routine in Trash is left out. "Left" runs (abandoned) can be finished again from the app's Runs page.`
55514
+ },
55515
+ run: async ({ flags, requireClient }) => {
55516
+ const query = parseInput(recentRoutineRunsQuerySchema, {
55517
+ since: flagString(flags, "since"),
55518
+ status: flagString(flags, "status"),
55519
+ limit: flagString(flags, "limit")
55520
+ });
55521
+ return {
55522
+ data: await requireClient().get(pathOf(routineOperations.recentRuns), {
55523
+ query: {
55524
+ since: query.since ? flagString(flags, "since") : undefined,
55525
+ status: query.status,
55526
+ limit: String(query.limit)
55527
+ }
55528
+ }),
55529
+ render: renderRecentRuns
55530
+ };
55531
+ }
55532
+ },
55380
55533
  {
55381
55534
  spec: {
55382
55535
  path: ["routines", "today"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.347.0",
3
+ "version": "1.348.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "homepage": "https://github.com/robbeverhelst/do/tree/main/apps/cli",