@robbeverhelst/do 1.342.0 → 1.343.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.
- package/dist/do.js +661 -25
- package/package.json +1 -1
package/dist/do.js
CHANGED
|
@@ -971,7 +971,9 @@ var GLOBAL_BOOLEAN_FLAGS = [
|
|
|
971
971
|
"allow-destructive",
|
|
972
972
|
"dry-run",
|
|
973
973
|
"draft",
|
|
974
|
-
"no-year"
|
|
974
|
+
"no-year",
|
|
975
|
+
"after-done",
|
|
976
|
+
"no-repeat"
|
|
975
977
|
];
|
|
976
978
|
var SHORT_ALIASES = {
|
|
977
979
|
h: "help",
|
|
@@ -14418,7 +14420,7 @@ var configError = (message) => new CliError("config", message, EXIT.CONFIG);
|
|
|
14418
14420
|
|
|
14419
14421
|
// src/meta.ts
|
|
14420
14422
|
var CLI_NAME = "do";
|
|
14421
|
-
var VERSION = "1.
|
|
14423
|
+
var VERSION = "1.343.0";
|
|
14422
14424
|
var API_PREFIX = "/api/v1";
|
|
14423
14425
|
var DEFAULT_API_URL = "http://localhost:3001";
|
|
14424
14426
|
|
|
@@ -21582,12 +21584,78 @@ var habitTodayRoutineSchema = object({
|
|
|
21582
21584
|
stepsDone: number2().int().nonnegative(),
|
|
21583
21585
|
stepsTotal: number2().int().nonnegative()
|
|
21584
21586
|
});
|
|
21585
|
-
var
|
|
21587
|
+
var habitsTodayBaseSchema = object({
|
|
21586
21588
|
date: string2().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
21587
21589
|
timezone: string2().min(1),
|
|
21588
21590
|
groups: array(object({ part: habitDayPartSchema, habits: array(habitTodayItemSchema) })),
|
|
21589
21591
|
routines: array(habitTodayRoutineSchema)
|
|
21590
21592
|
});
|
|
21593
|
+
var habitMomentSchema = _enum2(["morning", "day", "evening"]);
|
|
21594
|
+
var habitMomentStateSchema = _enum2(["done", "going", "tonight", "open"]);
|
|
21595
|
+
var habitRowKindSchema = _enum2(["tick", "amount", "timer", "days-without"]);
|
|
21596
|
+
var HABIT_STRIP_DAYS = 14;
|
|
21597
|
+
var habitStripDaySchema = _enum2([
|
|
21598
|
+
"done",
|
|
21599
|
+
"missed",
|
|
21600
|
+
"not_due",
|
|
21601
|
+
"today_open",
|
|
21602
|
+
"today_done"
|
|
21603
|
+
]);
|
|
21604
|
+
var habitStreakUnitSchema = _enum2(["days", "in_a_row", "since"]);
|
|
21605
|
+
var habitSectionStreakSchema = object({
|
|
21606
|
+
current: number2().int().nonnegative(),
|
|
21607
|
+
best: number2().int().nonnegative(),
|
|
21608
|
+
unit: habitStreakUnitSchema,
|
|
21609
|
+
since: exports_iso.date().nullable().default(null)
|
|
21610
|
+
});
|
|
21611
|
+
var habitSectionRowSchema = object({
|
|
21612
|
+
habitId: ulidSchema,
|
|
21613
|
+
title: string2(),
|
|
21614
|
+
kind: habitRowKindSchema,
|
|
21615
|
+
schedule: habitScheduleSchema,
|
|
21616
|
+
scheduleWords: string2(),
|
|
21617
|
+
sourceWords: string2().nullable(),
|
|
21618
|
+
done: boolean2(),
|
|
21619
|
+
amount: object({ value: number2(), target: number2().nullable(), unit: string2().nullable() }).nullable(),
|
|
21620
|
+
strip: array(habitStripDaySchema).length(HABIT_STRIP_DAYS),
|
|
21621
|
+
streak: habitSectionStreakSchema,
|
|
21622
|
+
routineStep: object({
|
|
21623
|
+
routineId: ulidSchema,
|
|
21624
|
+
title: string2(),
|
|
21625
|
+
step: number2().int().positive(),
|
|
21626
|
+
of: number2().int().positive()
|
|
21627
|
+
}).nullable()
|
|
21628
|
+
});
|
|
21629
|
+
var habitSectionMomentSchema = object({
|
|
21630
|
+
moment: habitMomentSchema,
|
|
21631
|
+
label: string2(),
|
|
21632
|
+
routines: array(object({ id: ulidSchema, title: string2(), time: string2().nullable() })),
|
|
21633
|
+
state: habitMomentStateSchema,
|
|
21634
|
+
going: number2().int().nonnegative().default(0),
|
|
21635
|
+
rows: array(habitSectionRowSchema)
|
|
21636
|
+
});
|
|
21637
|
+
var habitNotDueSchema = object({
|
|
21638
|
+
id: ulidSchema,
|
|
21639
|
+
title: string2(),
|
|
21640
|
+
nextDue: exports_iso.date().nullable()
|
|
21641
|
+
});
|
|
21642
|
+
var habitWeekDaySchema = object({
|
|
21643
|
+
day: exports_iso.date(),
|
|
21644
|
+
done: number2().int().nonnegative().nullable(),
|
|
21645
|
+
due: number2().int().nonnegative()
|
|
21646
|
+
});
|
|
21647
|
+
var habitSectionTotalsSchema = object({
|
|
21648
|
+
dueToday: number2().int().nonnegative(),
|
|
21649
|
+
doneToday: number2().int().nonnegative(),
|
|
21650
|
+
weekDone: number2().int().nonnegative(),
|
|
21651
|
+
weekDue: number2().int().nonnegative()
|
|
21652
|
+
});
|
|
21653
|
+
var habitsTodayResponseSchema = habitsTodayBaseSchema.extend({
|
|
21654
|
+
moments: array(habitSectionMomentSchema).default([]),
|
|
21655
|
+
notDue: array(habitNotDueSchema).default([]),
|
|
21656
|
+
week: array(habitWeekDaySchema).default([]),
|
|
21657
|
+
totals: habitSectionTotalsSchema.default({ dueToday: 0, doneToday: 0, weekDone: 0, weekDue: 0 })
|
|
21658
|
+
});
|
|
21591
21659
|
|
|
21592
21660
|
// ../../packages/schema/src/contracts/metrics.ts
|
|
21593
21661
|
var METRICS = {
|
|
@@ -22055,6 +22123,55 @@ var taskFilingSuggestionSchema = object({
|
|
|
22055
22123
|
var completedAtInputSchema = exports_coerce.date().refine((d) => d.getTime() <= Date.now() + 60000, "completedAt can't be in the future").nullish();
|
|
22056
22124
|
var taskNextStepSchema = string2().trim().min(1).max(500);
|
|
22057
22125
|
var taskDeliverableSchema = string2().trim().min(1).max(500);
|
|
22126
|
+
var taskRecurrenceAnchorSchema = _enum2(["fixed", "from_completion"]);
|
|
22127
|
+
function isTimeZone(zone) {
|
|
22128
|
+
try {
|
|
22129
|
+
new Intl.DateTimeFormat("en", { timeZone: zone });
|
|
22130
|
+
return true;
|
|
22131
|
+
} catch {
|
|
22132
|
+
return false;
|
|
22133
|
+
}
|
|
22134
|
+
}
|
|
22135
|
+
var taskRecurrenceInputSchema = object({
|
|
22136
|
+
freq: recurrenceFreqSchema,
|
|
22137
|
+
interval: number2().int().positive().default(1),
|
|
22138
|
+
byDay: array(byDaySchema).min(1).optional(),
|
|
22139
|
+
byMonthDay: number2().int().min(-1).max(31).refine((d) => d !== 0, "byMonthDay is 1\u201331, or -1 for the last day").optional(),
|
|
22140
|
+
anchor: taskRecurrenceAnchorSchema.default("fixed"),
|
|
22141
|
+
timezone: string2().refine(isTimeZone, "an IANA time zone, e.g. Europe/Brussels").optional()
|
|
22142
|
+
}).strict().superRefine((v, ctx) => {
|
|
22143
|
+
const issue = (path, message) => ctx.addIssue({ code: "custom", path, message });
|
|
22144
|
+
if (v.anchor === "from_completion") {
|
|
22145
|
+
if (v.freq === "yearly")
|
|
22146
|
+
issue(["freq"], "a from_completion rule is daily, weekly or monthly");
|
|
22147
|
+
if (v.byDay)
|
|
22148
|
+
issue(["byDay"], "a from_completion rule takes no byDay");
|
|
22149
|
+
if (v.byMonthDay !== undefined)
|
|
22150
|
+
issue(["byMonthDay"], "a from_completion rule takes no byMonthDay");
|
|
22151
|
+
return;
|
|
22152
|
+
}
|
|
22153
|
+
if (v.byDay && v.freq === "daily")
|
|
22154
|
+
issue(["byDay"], "byDay needs a weekly or monthly freq");
|
|
22155
|
+
const ordinal = v.byDay?.findIndex((code) => parseByDay(code)?.ordinal != null) ?? -1;
|
|
22156
|
+
if (ordinal >= 0 && v.freq !== "monthly" && v.freq !== "yearly") {
|
|
22157
|
+
issue(["byDay", ordinal], "an ordinal weekday (1SU, -1FR) needs a monthly or yearly freq");
|
|
22158
|
+
}
|
|
22159
|
+
if (v.byMonthDay !== undefined && v.freq !== "monthly") {
|
|
22160
|
+
issue(["byMonthDay"], "byMonthDay needs a monthly freq");
|
|
22161
|
+
}
|
|
22162
|
+
if (v.byMonthDay !== undefined && v.byDay) {
|
|
22163
|
+
issue(["byMonthDay"], "pass byDay or byMonthDay, not both");
|
|
22164
|
+
}
|
|
22165
|
+
});
|
|
22166
|
+
var taskRecurrenceSchema = object({
|
|
22167
|
+
ruleId: ulidSchema,
|
|
22168
|
+
freq: recurrenceFreqSchema,
|
|
22169
|
+
interval: number2().int().positive(),
|
|
22170
|
+
byDay: array(string2()).nullable(),
|
|
22171
|
+
byMonthDay: number2().int().nullable(),
|
|
22172
|
+
anchor: taskRecurrenceAnchorSchema,
|
|
22173
|
+
timezone: string2()
|
|
22174
|
+
});
|
|
22058
22175
|
var taskViewSchema = taskSchema.omit({
|
|
22059
22176
|
ownerUserId: true,
|
|
22060
22177
|
ownerAgentId: true,
|
|
@@ -22089,7 +22206,8 @@ var taskViewSchema = taskSchema.omit({
|
|
|
22089
22206
|
filingSuggestion: taskFilingSuggestionSchema.nullable().default(null),
|
|
22090
22207
|
blockedBy: array(taskBlockerSchema).default([]),
|
|
22091
22208
|
blocks: number2().int().nonnegative().default(0),
|
|
22092
|
-
isBlocked: boolean2().default(false)
|
|
22209
|
+
isBlocked: boolean2().default(false),
|
|
22210
|
+
recurrence: taskRecurrenceSchema.nullable().default(null)
|
|
22093
22211
|
});
|
|
22094
22212
|
var taskProjectIdSchema = ulidSchema.nullish();
|
|
22095
22213
|
var taskAreaIdSchema = ulidSchema.nullish();
|
|
@@ -22143,7 +22261,8 @@ var createTaskInputSchema = createInsertSchema(tasks, {
|
|
|
22143
22261
|
source: taskSourceSchema.nullish(),
|
|
22144
22262
|
nextStep: taskNextStepSchema.nullish(),
|
|
22145
22263
|
proposed: boolean2().optional(),
|
|
22146
|
-
fromDesk: deskSchema.nullish()
|
|
22264
|
+
fromDesk: deskSchema.nullish(),
|
|
22265
|
+
recurrence: taskRecurrenceInputSchema.nullish()
|
|
22147
22266
|
});
|
|
22148
22267
|
var taskReadyNoteSchema = string2().trim().min(1).max(500);
|
|
22149
22268
|
var markTaskReadyInputSchema = object({ note: taskReadyNoteSchema.nullish() }).strict();
|
|
@@ -22196,7 +22315,8 @@ var updateTaskInputSchema = createUpdateSchema(tasks, {
|
|
|
22196
22315
|
proposed: boolean2().optional(),
|
|
22197
22316
|
notNowUntil: exports_coerce.date().nullish(),
|
|
22198
22317
|
ready: boolean2().optional(),
|
|
22199
|
-
readyNote: taskReadyNoteSchema.nullish()
|
|
22318
|
+
readyNote: taskReadyNoteSchema.nullish(),
|
|
22319
|
+
recurrence: taskRecurrenceInputSchema.nullish()
|
|
22200
22320
|
}).strict();
|
|
22201
22321
|
var taskListFilterSchema = _enum2(["all", "unsorted", "starred"]);
|
|
22202
22322
|
|
|
@@ -29751,7 +29871,7 @@ var habitOperations = defineOperations({
|
|
|
29751
29871
|
scopes: ["habits:read"],
|
|
29752
29872
|
tag: "Habits",
|
|
29753
29873
|
summary: "What's left today",
|
|
29754
|
-
description: "\"What's left?\" (#866): the habits due on the key's member's day (their saved time zone), already filtered by schedule, grouped by part of day (morning, midday, evening, anytime). Each says its value today, `done`/`open`/`skipped` (never overdue: habits don't carry over), the week for an N-per-week habit, and its last check-in (`by` names the Agent). Also today's routines with how many steps are done. Quit habits are left out
|
|
29874
|
+
description: "\"What's left?\" (#866): the habits due on the key's member's day (their saved time zone), already filtered by schedule, grouped by part of day (morning, midday, evening, anytime). Each says its value today, `done`/`open`/`skipped` (never overdue: habits don't carry over), the week for an N-per-week habit, and its last check-in (`by` names the Agent). Also today's routines with how many steps are done. Quit habits are left out of `groups`. The Habits section (V3-47) rides along: `moments` (morning, day, evening; habits inside their routine, each with its schedule in words, today's amount, a 14-day strip where a day it isn't due is `not_due`, never `missed`, and its streak in days, in a row or since), `notDue` (live, unpaused habits off today, by next due day), this Monday-to-Sunday `week` counting only due habits, and `totals`.",
|
|
29755
29875
|
status: 200,
|
|
29756
29876
|
response: { description: "Today's habits and routines." },
|
|
29757
29877
|
operationId: "habits_today"
|
|
@@ -30003,9 +30123,9 @@ var habitOperations = defineOperations({
|
|
|
30003
30123
|
scopes: ["habits:write"],
|
|
30004
30124
|
tag: "Habits",
|
|
30005
30125
|
summary: "Undo a check-in",
|
|
30006
|
-
description: "Delete a check-in; fires `habit.updated`.",
|
|
30126
|
+
description: "Delete a wrong or test check-in (or a skip): the streak, today state and heatmap recompute from what's left. The entry must be this habit's: another habit's, another Space's or one already gone is a 404. Find the id with `GET /habits/{id}/check-ins` or `lastCheckIn.id`. Returns the refreshed habit; fires `habit.updated`.",
|
|
30007
30127
|
status: 200,
|
|
30008
|
-
response: { description: "The refreshed habit." },
|
|
30128
|
+
response: { description: "The refreshed habit, its streak recomputed." },
|
|
30009
30129
|
errors: { 404: true },
|
|
30010
30130
|
webhook: "habit.updated",
|
|
30011
30131
|
operationId: "undo_habit_check_in"
|
|
@@ -37646,7 +37766,7 @@ var routineOperations = defineOperations({
|
|
|
37646
37766
|
scopes: ["routines:write"],
|
|
37647
37767
|
tag: "Routines",
|
|
37648
37768
|
summary: "Update a routine",
|
|
37649
|
-
description: "Patch a routine; fires `routine.updated`.",
|
|
37769
|
+
description: "Patch a routine; fires `routine.updated`. `steps`, when present, replaces the template in one call, in the order given: a step whose `id` is already the routine's is updated in place (its runs keep pointing at it), one left out is dropped (past runs keep their snapshot), a new `id` is added. `steps: []` empties the routine. The habits and tasks the steps link are never changed.",
|
|
37650
37770
|
body: updateRoutineInputSchema,
|
|
37651
37771
|
status: 200,
|
|
37652
37772
|
response: { description: "The updated routine." },
|
|
@@ -37659,12 +37779,25 @@ var routineOperations = defineOperations({
|
|
|
37659
37779
|
scopes: ["routines:write"],
|
|
37660
37780
|
tag: "Routines",
|
|
37661
37781
|
summary: "Delete a routine",
|
|
37662
|
-
description: "Soft-delete a routine;
|
|
37782
|
+
description: "Soft-delete a routine into Trash; its steps and runs stay for history, and the habits and tasks its steps link are untouched. Fires `routine.deleted`.",
|
|
37663
37783
|
status: 204,
|
|
37664
37784
|
response: { description: "Deleted." },
|
|
37665
37785
|
errors: { 404: true },
|
|
37666
37786
|
webhook: "routine.deleted"
|
|
37667
37787
|
},
|
|
37788
|
+
archive: {
|
|
37789
|
+
method: "post",
|
|
37790
|
+
path: "/routines/{id}/archive",
|
|
37791
|
+
scopes: ["routines:write"],
|
|
37792
|
+
tag: "Routines",
|
|
37793
|
+
summary: "Archive a routine",
|
|
37794
|
+
description: "Put the routine in Trash: the same soft delete as `DELETE /api/v1/routines/{id}`, restorable like any other node. Its steps and runs stay for history; the habits and tasks its steps link are untouched. Fires `routine.deleted`.",
|
|
37795
|
+
status: 204,
|
|
37796
|
+
response: { description: "Archived." },
|
|
37797
|
+
errors: { 404: true },
|
|
37798
|
+
webhook: "routine.deleted",
|
|
37799
|
+
operationId: "archive_routine"
|
|
37800
|
+
},
|
|
37668
37801
|
attachRecurrence: {
|
|
37669
37802
|
method: "put",
|
|
37670
37803
|
path: "/routines/{id}/recurrence",
|
|
@@ -39498,7 +39631,7 @@ var taskOperations = defineOperations({
|
|
|
39498
39631
|
scopes: ["tasks:read"],
|
|
39499
39632
|
tag: "Tasks",
|
|
39500
39633
|
summary: "Get a task",
|
|
39501
|
-
description: "Fetch one task by id (ULID) or by its ref (`DO-142`, case-insensitive).",
|
|
39634
|
+
description: "Fetch one task by id (ULID) or by its ref (`DO-142`, case-insensitive). A repeating task carries its rule as `recurrence` (null for a one-off).",
|
|
39502
39635
|
status: 200,
|
|
39503
39636
|
response: { description: "The task." },
|
|
39504
39637
|
errors: { 404: true }
|
|
@@ -39509,7 +39642,7 @@ var taskOperations = defineOperations({
|
|
|
39509
39642
|
scopes: ["tasks:write"],
|
|
39510
39643
|
tag: "Tasks",
|
|
39511
39644
|
summary: "Create a task",
|
|
39512
|
-
description: "Create a task; fires the `task.created` webhook.",
|
|
39645
|
+
description: "Create a task; fires the `task.created` webhook. An optional `recurrence` makes it repeat: `freq` daily/weekly/monthly, `interval`, `byDay` / `byMonthDay` for a `fixed` (calendar) series, or `anchor: from_completion` (the next one is due `interval` after the last was done).",
|
|
39513
39646
|
body: createTaskInputSchema,
|
|
39514
39647
|
status: 201,
|
|
39515
39648
|
response: { description: "The created task." },
|
|
@@ -39522,7 +39655,7 @@ var taskOperations = defineOperations({
|
|
|
39522
39655
|
scopes: ["tasks:write"],
|
|
39523
39656
|
tag: "Tasks",
|
|
39524
39657
|
summary: "Update a task",
|
|
39525
|
-
description:
|
|
39658
|
+
description: 'Patch a task; fires the `task.updated` webhook. `recurrence` sets or changes its repeat rule and `recurrence: null` ends the series (the done history stays). On a repeating task `status: "done"` finishes the open occurrence; the task stays open for the next one.',
|
|
39526
39659
|
body: updateTaskInputSchema,
|
|
39527
39660
|
status: 200,
|
|
39528
39661
|
response: { description: "The updated task." },
|
|
@@ -47322,7 +47455,7 @@ var commands20 = [
|
|
|
47322
47455
|
flags: [],
|
|
47323
47456
|
scopes: [...habitOperations.today.scopes],
|
|
47324
47457
|
returns: "HabitsTodayResponse",
|
|
47325
|
-
notes: `${routeOf(habitOperations.today)}. Your saved time zone's day; already filtered by schedule. Each habit says done/open/skipped (never overdue), its value today, the week for an N-per-week habit and its last check-in (\`by\` names the Agent). Quit habits are left out
|
|
47458
|
+
notes: `${routeOf(habitOperations.today)}. Your saved time zone's day; already filtered by schedule. Each habit says done/open/skipped (never overdue), its value today, the week for an N-per-week habit and its last check-in (\`by\` names the Agent). Quit habits are left out of \`groups\`. The Habits section rides along: \`moments\` (morning, day, evening; each habit inside its routine, with \`scheduleWords\`, today's \`amount\`, a 14-day \`strip\` oldest first where a day it isn't due is \`not_due\`, never \`missed\`, its \`streak\` in days, in a row or since, and its \`routineStep\`), \`notDue\` (every live, unpaused habit off today, by \`nextDue\`), this Monday-to-Sunday \`week\` (due habits only; a day after today has \`done: null\`) and \`totals\`.`
|
|
47326
47459
|
},
|
|
47327
47460
|
run: readOperation(habitOperations.today, renderHabitsToday)
|
|
47328
47461
|
},
|
|
@@ -47388,6 +47521,32 @@ var commands20 = [
|
|
|
47388
47521
|
};
|
|
47389
47522
|
}
|
|
47390
47523
|
},
|
|
47524
|
+
{
|
|
47525
|
+
spec: {
|
|
47526
|
+
path: ["habit", "undo"],
|
|
47527
|
+
summary: "Undo a wrong or test check-in (or a skip); the streak recomputes.",
|
|
47528
|
+
args: [
|
|
47529
|
+
{ name: "id", required: true, description: "Habit ULID." },
|
|
47530
|
+
{
|
|
47531
|
+
name: "entry-id",
|
|
47532
|
+
required: true,
|
|
47533
|
+
description: "The check-in's ULID (`do habit check-ins <id>`, or lastCheckIn.id)."
|
|
47534
|
+
}
|
|
47535
|
+
],
|
|
47536
|
+
flags: [],
|
|
47537
|
+
scopes: [...habitOperations.removeCheckIn.scopes],
|
|
47538
|
+
returns: "HabitSummary",
|
|
47539
|
+
notes: `${routeOf(habitOperations.removeCheckIn)}. The check-in must be this habit's: another habit's, or one already gone, is a 404. Prints the refreshed habit.`
|
|
47540
|
+
},
|
|
47541
|
+
run: async ({ args, requireClient }) => {
|
|
47542
|
+
const id = requireArg(args, 0, "habit id");
|
|
47543
|
+
const entryId = requireArg(args, 1, "check-in id");
|
|
47544
|
+
return {
|
|
47545
|
+
data: await requireClient().delete(pathOf(habitOperations.removeCheckIn, { id, entryId })),
|
|
47546
|
+
render: renderHabit
|
|
47547
|
+
};
|
|
47548
|
+
}
|
|
47549
|
+
},
|
|
47391
47550
|
{
|
|
47392
47551
|
spec: {
|
|
47393
47552
|
path: ["habit", "skip"],
|
|
@@ -53634,7 +53793,327 @@ function renderRoutineTaskAdded(data) {
|
|
|
53634
53793
|
const last = run.steps?.at(-1)?.title ?? "the task";
|
|
53635
53794
|
return `${run.routineTitle ?? "(routine)"}: ${last} is the last step of today's run.`;
|
|
53636
53795
|
}
|
|
53796
|
+
var STEP_GRAMMAR = "A step is `habit:<habit id>` (a habit step, titled after the habit), `task:<task id>` (a Work task step, titled after the task) or any other text (a plain step with that title).";
|
|
53797
|
+
var ROUTINE_FLAGS = [
|
|
53798
|
+
{ name: "kind", type: "string", description: "sequence, time_blocked or checklist." },
|
|
53799
|
+
{ name: "area", type: "string", description: "The Area's ULID (`none` unfiles it)." },
|
|
53800
|
+
{ name: "notes", type: "string", description: "The routine's notes." },
|
|
53801
|
+
{ name: "data", type: "string", description: "JSON (or @file) with the rest of the input." }
|
|
53802
|
+
];
|
|
53803
|
+
function routineFields(flags) {
|
|
53804
|
+
const out = { ...readDataFlag(flags) };
|
|
53805
|
+
const title = flagString(flags, "title");
|
|
53806
|
+
const kind = flagString(flags, "kind");
|
|
53807
|
+
const area = flagString(flags, "area");
|
|
53808
|
+
const notes = flagString(flags, "notes");
|
|
53809
|
+
if (title !== undefined)
|
|
53810
|
+
out.title = title;
|
|
53811
|
+
if (kind !== undefined)
|
|
53812
|
+
out.kind = kind;
|
|
53813
|
+
if (area !== undefined)
|
|
53814
|
+
out.areaId = area === "none" ? null : area;
|
|
53815
|
+
if (notes !== undefined)
|
|
53816
|
+
out.notes = notes;
|
|
53817
|
+
return out;
|
|
53818
|
+
}
|
|
53819
|
+
async function stepFrom(client, item, title) {
|
|
53820
|
+
const link = /^(habit|task):(\S+)$/.exec(item.trim());
|
|
53821
|
+
if (!link) {
|
|
53822
|
+
const text = (title ?? item).trim();
|
|
53823
|
+
if (!text)
|
|
53824
|
+
throw usageError("a step needs a title");
|
|
53825
|
+
return { id: ulid3(), title: text, refKind: "free" };
|
|
53826
|
+
}
|
|
53827
|
+
const kind = link[1];
|
|
53828
|
+
const id = link[2];
|
|
53829
|
+
const named = title ?? (await client.get(kind === "habit" ? pathOf(habitOperations.get, { id }) : pathOf(taskOperations.get, { id }))).title ?? kind;
|
|
53830
|
+
return kind === "habit" ? { id: ulid3(), title: named, refKind: "habit", habitId: id } : { id: ulid3(), title: named, refKind: "task", taskId: id };
|
|
53831
|
+
}
|
|
53832
|
+
function stepInput(step, sortOrder) {
|
|
53833
|
+
return {
|
|
53834
|
+
id: step.id,
|
|
53835
|
+
title: step.title,
|
|
53836
|
+
refKind: step.refKind,
|
|
53837
|
+
habitId: step.refKind === "habit" ? step.habitId : null,
|
|
53838
|
+
taskId: step.refKind === "task" ? step.taskId : null,
|
|
53839
|
+
sortOrder,
|
|
53840
|
+
durationMinutes: step.durationMinutes,
|
|
53841
|
+
notes: step.notes
|
|
53842
|
+
};
|
|
53843
|
+
}
|
|
53844
|
+
function stepIndex(steps, token) {
|
|
53845
|
+
const byId = steps.findIndex((s) => s.id === token);
|
|
53846
|
+
if (byId >= 0)
|
|
53847
|
+
return byId;
|
|
53848
|
+
const n = Number(token);
|
|
53849
|
+
if (Number.isInteger(n) && n >= 1 && n <= steps.length)
|
|
53850
|
+
return n - 1;
|
|
53851
|
+
throw usageError(`no step ${token} in this routine \u2014 name its id or its number`);
|
|
53852
|
+
}
|
|
53853
|
+
function positionOf(raw, max, name) {
|
|
53854
|
+
const n = Number(raw);
|
|
53855
|
+
if (!Number.isInteger(n) || n < 1 || n > max) {
|
|
53856
|
+
throw usageError(`${name} must be a number from 1 to ${max}`);
|
|
53857
|
+
}
|
|
53858
|
+
return n - 1;
|
|
53859
|
+
}
|
|
53860
|
+
function renderRoutine(data) {
|
|
53861
|
+
const r = data;
|
|
53862
|
+
const steps = (r.steps ?? []).map((s, i) => {
|
|
53863
|
+
const kind = s.refKind === "free" ? "" : ` [${s.refKind}]`;
|
|
53864
|
+
return ` ${i + 1}. ${s.title}${kind} ${s.id}`;
|
|
53865
|
+
});
|
|
53866
|
+
return [
|
|
53867
|
+
`${r.title ?? "(routine)"} ${r.id}`,
|
|
53868
|
+
...steps.length ? steps : [" (no steps)"]
|
|
53869
|
+
].join(`
|
|
53870
|
+
`);
|
|
53871
|
+
}
|
|
53872
|
+
function putSteps(client, id, steps) {
|
|
53873
|
+
const body = parseInput(updateRoutineInputSchema, {
|
|
53874
|
+
steps: steps.map((s, i) => ({ ...s, sortOrder: i }))
|
|
53875
|
+
});
|
|
53876
|
+
return client.patch(pathOf(routineOperations.update, { id }), body);
|
|
53877
|
+
}
|
|
53878
|
+
function readRoutine(client, id) {
|
|
53879
|
+
return client.get(pathOf(routineOperations.get, { id }));
|
|
53880
|
+
}
|
|
53881
|
+
var routineCommands = [
|
|
53882
|
+
{
|
|
53883
|
+
spec: {
|
|
53884
|
+
path: ["routines", "create"],
|
|
53885
|
+
summary: "Create a routine with its steps.",
|
|
53886
|
+
args: [{ name: "title", required: false, description: "The routine's title." }],
|
|
53887
|
+
flags: [
|
|
53888
|
+
...ROUTINE_FLAGS,
|
|
53889
|
+
{ name: "step", type: "string", description: "A step, in order; repeat it. See grammar." }
|
|
53890
|
+
],
|
|
53891
|
+
scopes: [...routineOperations.create.scopes],
|
|
53892
|
+
returns: "RoutineView",
|
|
53893
|
+
grammar: { step: STEP_GRAMMAR },
|
|
53894
|
+
notes: `${routeOf(routineOperations.create)}. The id is minted when omitted; --data carries the rest of a CreateRoutineInput. A linked step's title is read from its habit or task. e.g. \`do routines create "Wind down" --step "Lights low" --step habit:<id>\`.`
|
|
53895
|
+
},
|
|
53896
|
+
run: async ({ args, flags, requireClient }) => {
|
|
53897
|
+
const title = args.join(" ").trim();
|
|
53898
|
+
const client = requireClient();
|
|
53899
|
+
const steps = [];
|
|
53900
|
+
for (const item of flagStrings(flags, "step"))
|
|
53901
|
+
steps.push(await stepFrom(client, item));
|
|
53902
|
+
const body = parseInput(createRoutineInputSchema, {
|
|
53903
|
+
id: ulid3(),
|
|
53904
|
+
...title ? { title } : {},
|
|
53905
|
+
...routineFields(flags),
|
|
53906
|
+
...steps.length ? { steps: steps.map((s, i) => ({ ...s, sortOrder: i })) } : {}
|
|
53907
|
+
});
|
|
53908
|
+
if (flagBool(flags, "dry-run")) {
|
|
53909
|
+
return {
|
|
53910
|
+
data: { dryRun: true, op: "create", entity: "routines", after: body },
|
|
53911
|
+
render: renderRecord
|
|
53912
|
+
};
|
|
53913
|
+
}
|
|
53914
|
+
return {
|
|
53915
|
+
data: await client.post(pathOf(routineOperations.create), body),
|
|
53916
|
+
render: renderRoutine
|
|
53917
|
+
};
|
|
53918
|
+
}
|
|
53919
|
+
},
|
|
53920
|
+
{
|
|
53921
|
+
spec: {
|
|
53922
|
+
path: ["routines", "update"],
|
|
53923
|
+
summary: "Change a routine: its title, kind, Area, notes, or --data.",
|
|
53924
|
+
args: [{ name: "id", required: true, description: "Routine ULID." }],
|
|
53925
|
+
flags: [{ name: "title", type: "string", description: "A new title." }, ...ROUTINE_FLAGS],
|
|
53926
|
+
scopes: [...routineOperations.update.scopes],
|
|
53927
|
+
returns: "RoutineView",
|
|
53928
|
+
notes: `${routeOf(routineOperations.update)}. Only what you pass changes; its steps change with \`do routines steps\`.`
|
|
53929
|
+
},
|
|
53930
|
+
run: async ({ args, flags, requireClient }) => {
|
|
53931
|
+
const id = requireArg(args, 0, "routine id");
|
|
53932
|
+
const body = parseInput(updateRoutineInputSchema, routineFields(flags));
|
|
53933
|
+
if (Object.keys(body).length === 0) {
|
|
53934
|
+
throw usageError("nothing to change \u2014 pass --title, --kind, --area, --notes or --data");
|
|
53935
|
+
}
|
|
53936
|
+
const client = requireClient();
|
|
53937
|
+
if (flagBool(flags, "dry-run")) {
|
|
53938
|
+
const before = await readRoutine(client, id);
|
|
53939
|
+
return {
|
|
53940
|
+
data: {
|
|
53941
|
+
dryRun: true,
|
|
53942
|
+
op: "update",
|
|
53943
|
+
entity: "routines",
|
|
53944
|
+
id,
|
|
53945
|
+
before,
|
|
53946
|
+
after: mergePreview(before, body),
|
|
53947
|
+
changes: body
|
|
53948
|
+
},
|
|
53949
|
+
render: renderRecord
|
|
53950
|
+
};
|
|
53951
|
+
}
|
|
53952
|
+
return {
|
|
53953
|
+
data: await client.patch(pathOf(routineOperations.update, { id }), body),
|
|
53954
|
+
render: renderRoutine
|
|
53955
|
+
};
|
|
53956
|
+
}
|
|
53957
|
+
},
|
|
53958
|
+
{
|
|
53959
|
+
spec: {
|
|
53960
|
+
path: ["routines", "steps", "set"],
|
|
53961
|
+
summary: "Replace a routine's steps with a new ordered list, or --clear them all.",
|
|
53962
|
+
args: [
|
|
53963
|
+
{ name: "id", required: true, description: "Routine ULID." },
|
|
53964
|
+
{ name: "steps", required: false, description: "The steps, in order. See grammar." }
|
|
53965
|
+
],
|
|
53966
|
+
flags: [{ name: "clear", type: "boolean", description: "Empty the routine (no steps)." }],
|
|
53967
|
+
scopes: [...routineOperations.update.scopes],
|
|
53968
|
+
returns: "RoutineView",
|
|
53969
|
+
grammar: { step: STEP_GRAMMAR },
|
|
53970
|
+
notes: `${routeOf(routineOperations.update)} with \`steps\`, in one call. The habits and tasks the old steps linked are untouched; past runs keep their snapshot.`
|
|
53971
|
+
},
|
|
53972
|
+
run: async ({ args, flags, requireClient }) => {
|
|
53973
|
+
const id = requireArg(args, 0, "routine id");
|
|
53974
|
+
const items = args.slice(1);
|
|
53975
|
+
const clear = flagBool(flags, "clear");
|
|
53976
|
+
if (items.length === 0 && !clear) {
|
|
53977
|
+
throw usageError("routines steps set needs the steps in order, or --clear to empty it");
|
|
53978
|
+
}
|
|
53979
|
+
if (items.length > 0 && clear)
|
|
53980
|
+
throw usageError("--clear takes no steps");
|
|
53981
|
+
const client = requireClient();
|
|
53982
|
+
const steps = [];
|
|
53983
|
+
for (const item of items)
|
|
53984
|
+
steps.push(await stepFrom(client, item));
|
|
53985
|
+
return { data: await putSteps(client, id, steps), render: renderRoutine };
|
|
53986
|
+
}
|
|
53987
|
+
},
|
|
53988
|
+
{
|
|
53989
|
+
spec: {
|
|
53990
|
+
path: ["routines", "steps", "add"],
|
|
53991
|
+
summary: "Add a step to a routine, at the end or --at a position.",
|
|
53992
|
+
args: [
|
|
53993
|
+
{ name: "id", required: true, description: "Routine ULID." },
|
|
53994
|
+
{ name: "step", required: true, description: "The step. See grammar." }
|
|
53995
|
+
],
|
|
53996
|
+
flags: [
|
|
53997
|
+
{ name: "at", type: "string", description: "Its position, 1-based (default: last)." },
|
|
53998
|
+
{ name: "title", type: "string", description: "Its title (instead of the linked name)." }
|
|
53999
|
+
],
|
|
54000
|
+
scopes: [...routineOperations.get.scopes, ...routineOperations.update.scopes],
|
|
54001
|
+
returns: "RoutineView",
|
|
54002
|
+
grammar: { step: STEP_GRAMMAR },
|
|
54003
|
+
notes: `${routeOf(routineOperations.get)}, then ${routeOf(routineOperations.update)} with every step (the others keep their ids).`
|
|
54004
|
+
},
|
|
54005
|
+
run: async ({ args, flags, requireClient }) => {
|
|
54006
|
+
const id = requireArg(args, 0, "routine id");
|
|
54007
|
+
const item = args.slice(1).join(" ").trim();
|
|
54008
|
+
if (!item)
|
|
54009
|
+
throw usageError("routines steps add needs the step: `habit:<id>`, `task:<id>` or a title");
|
|
54010
|
+
const client = requireClient();
|
|
54011
|
+
const routine = await readRoutine(client, id);
|
|
54012
|
+
const steps = routine.steps.map((s, i) => stepInput(s, i));
|
|
54013
|
+
const at = flagString(flags, "at");
|
|
54014
|
+
const index = at === undefined ? steps.length : positionOf(at, steps.length + 1, "--at");
|
|
54015
|
+
steps.splice(index, 0, await stepFrom(client, item, flagString(flags, "title")));
|
|
54016
|
+
return { data: await putSteps(client, id, steps), render: renderRoutine };
|
|
54017
|
+
}
|
|
54018
|
+
},
|
|
54019
|
+
{
|
|
54020
|
+
spec: {
|
|
54021
|
+
path: ["routines", "steps", "rm"],
|
|
54022
|
+
summary: "Remove a step from a routine.",
|
|
54023
|
+
args: [
|
|
54024
|
+
{ name: "id", required: true, description: "Routine ULID." },
|
|
54025
|
+
{ name: "step", required: true, description: "The step's ULID or its number (1-based)." }
|
|
54026
|
+
],
|
|
54027
|
+
flags: [],
|
|
54028
|
+
scopes: [...routineOperations.get.scopes, ...routineOperations.update.scopes],
|
|
54029
|
+
returns: "RoutineView",
|
|
54030
|
+
notes: `${routeOf(routineOperations.get)}, then ${routeOf(routineOperations.update)} without it. A habit or task it linked is untouched; past runs keep it.`
|
|
54031
|
+
},
|
|
54032
|
+
run: async ({ args, requireClient }) => {
|
|
54033
|
+
const id = requireArg(args, 0, "routine id");
|
|
54034
|
+
const token = requireArg(args, 1, "step");
|
|
54035
|
+
const client = requireClient();
|
|
54036
|
+
const routine = await readRoutine(client, id);
|
|
54037
|
+
const index = stepIndex(routine.steps, token);
|
|
54038
|
+
const steps = routine.steps.filter((_, i) => i !== index).map((s, i) => stepInput(s, i));
|
|
54039
|
+
return { data: await putSteps(client, id, steps), render: renderRoutine };
|
|
54040
|
+
}
|
|
54041
|
+
},
|
|
54042
|
+
{
|
|
54043
|
+
spec: {
|
|
54044
|
+
path: ["routines", "steps", "move"],
|
|
54045
|
+
summary: "Move a routine's step to a new position.",
|
|
54046
|
+
args: [
|
|
54047
|
+
{ name: "id", required: true, description: "Routine ULID." },
|
|
54048
|
+
{ name: "step", required: true, description: "The step's ULID or its number (1-based)." },
|
|
54049
|
+
{ name: "position", required: true, description: "Where it goes, 1-based." }
|
|
54050
|
+
],
|
|
54051
|
+
flags: [],
|
|
54052
|
+
scopes: [...routineOperations.get.scopes, ...routineOperations.reorderSteps.scopes],
|
|
54053
|
+
returns: "RoutineView",
|
|
54054
|
+
notes: `${routeOf(routineOperations.get)}, then ${routeOf(routineOperations.reorderSteps)}. Runs already started keep their order.`
|
|
54055
|
+
},
|
|
54056
|
+
run: async ({ args, requireClient }) => {
|
|
54057
|
+
const id = requireArg(args, 0, "routine id");
|
|
54058
|
+
const token = requireArg(args, 1, "step");
|
|
54059
|
+
const to = requireArg(args, 2, "position");
|
|
54060
|
+
const client = requireClient();
|
|
54061
|
+
const routine = await readRoutine(client, id);
|
|
54062
|
+
const from = stepIndex(routine.steps, token);
|
|
54063
|
+
const ids = routine.steps.map((s) => s.id);
|
|
54064
|
+
const [moved] = ids.splice(from, 1);
|
|
54065
|
+
ids.splice(positionOf(to, routine.steps.length, "position"), 0, moved);
|
|
54066
|
+
const input = parseInput(reorderRoutineStepsInputSchema, { stepIds: ids });
|
|
54067
|
+
return {
|
|
54068
|
+
data: await client.patch(pathOf(routineOperations.reorderSteps, { id }), input),
|
|
54069
|
+
render: renderRoutine
|
|
54070
|
+
};
|
|
54071
|
+
}
|
|
54072
|
+
},
|
|
54073
|
+
{
|
|
54074
|
+
spec: {
|
|
54075
|
+
path: ["routines", "archive"],
|
|
54076
|
+
summary: "Archive a routine: into Trash, restorable.",
|
|
54077
|
+
args: [{ name: "id", required: true, description: "Routine ULID." }],
|
|
54078
|
+
flags: [],
|
|
54079
|
+
scopes: [...routineOperations.archive.scopes],
|
|
54080
|
+
returns: "{ ok, archived }",
|
|
54081
|
+
notes: `${routeOf(routineOperations.archive)}. The habits and tasks its steps link are untouched.`
|
|
54082
|
+
},
|
|
54083
|
+
run: async ({ args, flags, requireClient }) => {
|
|
54084
|
+
const id = requireArg(args, 0, "routine id");
|
|
54085
|
+
const client = requireClient();
|
|
54086
|
+
if (flagBool(flags, "dry-run")) {
|
|
54087
|
+
const before = await readRoutine(client, id);
|
|
54088
|
+
return {
|
|
54089
|
+
data: { dryRun: true, op: "archive", entity: "routines", id, before, softDelete: true },
|
|
54090
|
+
render: renderRecord
|
|
54091
|
+
};
|
|
54092
|
+
}
|
|
54093
|
+
await client.post(pathOf(routineOperations.archive, { id }), {});
|
|
54094
|
+
return { data: { ok: true, archived: id, softDelete: true }, render: renderRecord };
|
|
54095
|
+
}
|
|
54096
|
+
},
|
|
54097
|
+
{
|
|
54098
|
+
spec: {
|
|
54099
|
+
path: ["routines", "rm"],
|
|
54100
|
+
summary: "Delete a routine (into Trash); needs --allow-destructive.",
|
|
54101
|
+
args: [{ name: "id", required: true, description: "Routine ULID." }],
|
|
54102
|
+
flags: [],
|
|
54103
|
+
scopes: [...routineOperations.remove.scopes],
|
|
54104
|
+
returns: "{ ok, deleted }",
|
|
54105
|
+
notes: `${routeOf(routineOperations.remove)}. The same soft delete as archive, behind the destructive guardrail.`
|
|
54106
|
+
},
|
|
54107
|
+
run: async ({ args, flags, requireClient }) => {
|
|
54108
|
+
const id = requireArg(args, 0, "routine id");
|
|
54109
|
+
assertDestructiveAllowed(flags, `delete routines ${id}`);
|
|
54110
|
+
await requireClient().delete(pathOf(routineOperations.remove, { id }));
|
|
54111
|
+
return { data: { ok: true, deleted: id, softDelete: true }, render: renderRecord };
|
|
54112
|
+
}
|
|
54113
|
+
}
|
|
54114
|
+
];
|
|
53637
54115
|
var commands42 = [
|
|
54116
|
+
...routineCommands,
|
|
53638
54117
|
{
|
|
53639
54118
|
spec: {
|
|
53640
54119
|
path: ["routines", "today"],
|
|
@@ -55274,6 +55753,69 @@ function parseCapture2(input, now) {
|
|
|
55274
55753
|
}
|
|
55275
55754
|
|
|
55276
55755
|
// src/commands/tasks.ts
|
|
55756
|
+
var REPEAT_FLAGS = [
|
|
55757
|
+
{
|
|
55758
|
+
name: "repeat",
|
|
55759
|
+
type: "string",
|
|
55760
|
+
description: "Make it repeat: daily, weekly, monthly or yearly. On update it replaces the rule."
|
|
55761
|
+
},
|
|
55762
|
+
{
|
|
55763
|
+
name: "every",
|
|
55764
|
+
type: "string",
|
|
55765
|
+
description: "With --repeat: every N days/weeks/months (default 1)."
|
|
55766
|
+
},
|
|
55767
|
+
{
|
|
55768
|
+
name: "on",
|
|
55769
|
+
type: "string",
|
|
55770
|
+
description: "With --repeat weekly (or monthly): the weekdays, comma-separated: mo,th. Monthly also takes 1su (the 1st Sunday) or -1fr (the last Friday)."
|
|
55771
|
+
},
|
|
55772
|
+
{
|
|
55773
|
+
name: "day",
|
|
55774
|
+
type: "string",
|
|
55775
|
+
description: "With --repeat monthly: the day of the month, 1\u201331 or last (a shorter month uses its last day)."
|
|
55776
|
+
},
|
|
55777
|
+
{
|
|
55778
|
+
name: "after-done",
|
|
55779
|
+
type: "boolean",
|
|
55780
|
+
description: "With --repeat: the next one is due N after the last was done, not on the calendar (anchor from_completion)."
|
|
55781
|
+
}
|
|
55782
|
+
];
|
|
55783
|
+
var REPEAT_DETAIL = ["every", "on", "day", "after-done"];
|
|
55784
|
+
function repeatFromFlags(flags) {
|
|
55785
|
+
const repeat = flagString(flags, "repeat");
|
|
55786
|
+
if (flagBool(flags, "no-repeat")) {
|
|
55787
|
+
if (repeat !== undefined)
|
|
55788
|
+
throw usageError("--repeat and --no-repeat can't be combined");
|
|
55789
|
+
return null;
|
|
55790
|
+
}
|
|
55791
|
+
if (repeat === undefined) {
|
|
55792
|
+
const stray = REPEAT_DETAIL.find((f) => flags[f] !== undefined);
|
|
55793
|
+
if (stray)
|
|
55794
|
+
throw usageError(`--${stray} goes with --repeat daily|weekly|monthly`);
|
|
55795
|
+
return;
|
|
55796
|
+
}
|
|
55797
|
+
const out = { freq: repeat.trim().toLowerCase() };
|
|
55798
|
+
const every = flagString(flags, "every");
|
|
55799
|
+
if (every !== undefined) {
|
|
55800
|
+
if (!/^\d+$/.test(every.trim()))
|
|
55801
|
+
throw usageError("--every must be a whole number, e.g. --every 2");
|
|
55802
|
+
out.interval = Number(every);
|
|
55803
|
+
}
|
|
55804
|
+
const on = flagString(flags, "on");
|
|
55805
|
+
if (on !== undefined) {
|
|
55806
|
+
out.byDay = on.split(",").map((d) => d.trim().toUpperCase()).filter((d) => d.length > 0);
|
|
55807
|
+
}
|
|
55808
|
+
const day = flagString(flags, "day");
|
|
55809
|
+
if (day !== undefined) {
|
|
55810
|
+
const d = day.trim().toLowerCase();
|
|
55811
|
+
if (d !== "last" && !/^-?\d+$/.test(d))
|
|
55812
|
+
throw usageError("--day must be 1\u201331 or last");
|
|
55813
|
+
out.byMonthDay = d === "last" ? -1 : Number(d);
|
|
55814
|
+
}
|
|
55815
|
+
if (flagBool(flags, "after-done"))
|
|
55816
|
+
out.anchor = "from_completion";
|
|
55817
|
+
return out;
|
|
55818
|
+
}
|
|
55277
55819
|
var commands54 = [
|
|
55278
55820
|
{
|
|
55279
55821
|
spec: {
|
|
@@ -55335,17 +55877,16 @@ var commands54 = [
|
|
|
55335
55877
|
...scheduledAt ? { scheduledAt } : {},
|
|
55336
55878
|
...dueAt ? { dueAt } : {},
|
|
55337
55879
|
...scheduledAt || dueAt ? { allDay } : {},
|
|
55338
|
-
...duration ? { durationMinutes: duration } : {}
|
|
55880
|
+
...duration ? { durationMinutes: duration } : {},
|
|
55881
|
+
...parsed.recurrence ? {
|
|
55882
|
+
recurrence: {
|
|
55883
|
+
freq: parsed.recurrence.freq,
|
|
55884
|
+
interval: parsed.recurrence.interval,
|
|
55885
|
+
...parsed.recurrence.byDay ? { byDay: parsed.recurrence.byDay } : {}
|
|
55886
|
+
}
|
|
55887
|
+
} : {}
|
|
55339
55888
|
});
|
|
55340
|
-
const
|
|
55341
|
-
const task = await client.post(pathOf(taskOperations.create), input);
|
|
55342
|
-
if (parsed.recurrence) {
|
|
55343
|
-
const rule = parseInput(createRecurrenceRuleInputSchema, {
|
|
55344
|
-
templateId: id,
|
|
55345
|
-
...parsed.recurrence
|
|
55346
|
-
});
|
|
55347
|
-
await client.post("/recurrence/rules", rule);
|
|
55348
|
-
}
|
|
55889
|
+
const task = await requireClient().post(pathOf(taskOperations.create), input);
|
|
55349
55890
|
return { data: task, render: renderTask };
|
|
55350
55891
|
}
|
|
55351
55892
|
},
|
|
@@ -55633,6 +56174,101 @@ var commands54 = [
|
|
|
55633
56174
|
render: renderTask
|
|
55634
56175
|
};
|
|
55635
56176
|
}
|
|
56177
|
+
}),
|
|
56178
|
+
defineCommand({
|
|
56179
|
+
operation: taskOperations.create,
|
|
56180
|
+
path: ["tasks", "create"],
|
|
56181
|
+
summary: "Create a task; --repeat makes it repeat.",
|
|
56182
|
+
returns: "Task",
|
|
56183
|
+
args: [
|
|
56184
|
+
{
|
|
56185
|
+
name: "title",
|
|
56186
|
+
required: false,
|
|
56187
|
+
description: "The task's title (or pass it in --data)."
|
|
56188
|
+
}
|
|
56189
|
+
],
|
|
56190
|
+
flags: [
|
|
56191
|
+
{
|
|
56192
|
+
name: "data",
|
|
56193
|
+
type: "string",
|
|
56194
|
+
description: "JSON CreateTaskInput (or @file) for any other field."
|
|
56195
|
+
},
|
|
56196
|
+
...REPEAT_FLAGS
|
|
56197
|
+
],
|
|
56198
|
+
notes: ' \u2014 e.g. `do tasks create "Descale the kettle" --repeat monthly --after-done`, `do tasks create "Gym" --repeat weekly --on mo,th`, `do tasks create "Pay rent" --repeat monthly --day 1`. The id is minted when omitted.',
|
|
56199
|
+
run: async ({ args, flags, requireClient }) => {
|
|
56200
|
+
const title = args.join(" ").trim();
|
|
56201
|
+
const recurrence = repeatFromFlags(flags);
|
|
56202
|
+
if (recurrence === null)
|
|
56203
|
+
throw usageError("--no-repeat is for `tasks update`");
|
|
56204
|
+
const body = parseInput(createTaskInputSchema, {
|
|
56205
|
+
id: ulid3(),
|
|
56206
|
+
...readDataFlag(flags),
|
|
56207
|
+
...title ? { title } : {},
|
|
56208
|
+
...recurrence ? { recurrence } : {}
|
|
56209
|
+
});
|
|
56210
|
+
if (flagBool(flags, "dry-run")) {
|
|
56211
|
+
return {
|
|
56212
|
+
data: { dryRun: true, op: "create", entity: "tasks", after: body },
|
|
56213
|
+
render: renderRecord
|
|
56214
|
+
};
|
|
56215
|
+
}
|
|
56216
|
+
return {
|
|
56217
|
+
data: await requireClient().post(pathOf(taskOperations.create), body),
|
|
56218
|
+
render: renderTask
|
|
56219
|
+
};
|
|
56220
|
+
}
|
|
56221
|
+
}),
|
|
56222
|
+
defineCommand({
|
|
56223
|
+
operation: taskOperations.update,
|
|
56224
|
+
path: ["tasks", "update"],
|
|
56225
|
+
summary: "Change a task; --repeat sets its repeat rule, --no-repeat ends the series.",
|
|
56226
|
+
returns: "Task",
|
|
56227
|
+
args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
|
|
56228
|
+
flags: [
|
|
56229
|
+
{
|
|
56230
|
+
name: "data",
|
|
56231
|
+
type: "string",
|
|
56232
|
+
description: "JSON UpdateTaskInput (or @file): the fields to change."
|
|
56233
|
+
},
|
|
56234
|
+
...REPEAT_FLAGS,
|
|
56235
|
+
{
|
|
56236
|
+
name: "no-repeat",
|
|
56237
|
+
type: "boolean",
|
|
56238
|
+
description: "Stop the series: no more occurrences; the done history stays."
|
|
56239
|
+
}
|
|
56240
|
+
],
|
|
56241
|
+
notes: " \u2014 --repeat replaces the whole rule (pass every part you want kept). On a repeating task, `do task done` finishes the open occurrence and the task stays open for the next one.",
|
|
56242
|
+
run: async ({ args, flags, requireClient }) => {
|
|
56243
|
+
const id = requireArg(args, 0, "task id");
|
|
56244
|
+
const recurrence = repeatFromFlags(flags);
|
|
56245
|
+
const body = parseInput(updateTaskInputSchema, {
|
|
56246
|
+
...readDataFlag(flags),
|
|
56247
|
+
...recurrence !== undefined ? { recurrence } : {}
|
|
56248
|
+
});
|
|
56249
|
+
if (Object.keys(body).length === 0)
|
|
56250
|
+
throw usageError("nothing to change \u2014 pass --repeat, --no-repeat or --data");
|
|
56251
|
+
const client = requireClient();
|
|
56252
|
+
if (flagBool(flags, "dry-run")) {
|
|
56253
|
+
const before = await client.get(pathOf(taskOperations.get, { id }));
|
|
56254
|
+
return {
|
|
56255
|
+
data: {
|
|
56256
|
+
dryRun: true,
|
|
56257
|
+
op: "update",
|
|
56258
|
+
entity: "tasks",
|
|
56259
|
+
id,
|
|
56260
|
+
before,
|
|
56261
|
+
after: mergePreview(before, body),
|
|
56262
|
+
changes: body
|
|
56263
|
+
},
|
|
56264
|
+
render: renderRecord
|
|
56265
|
+
};
|
|
56266
|
+
}
|
|
56267
|
+
return {
|
|
56268
|
+
data: await client.patch(pathOf(taskOperations.update, { id }), body),
|
|
56269
|
+
render: renderTask
|
|
56270
|
+
};
|
|
56271
|
+
}
|
|
55636
56272
|
})
|
|
55637
56273
|
];
|
|
55638
56274
|
|
package/package.json
CHANGED