@bdsqqq/lnr-cli 1.6.0 → 2.0.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/package.json +2 -3
- package/src/bench-lnr-overhead.ts +160 -0
- package/src/e2e-mutations.test.ts +378 -0
- package/src/e2e-readonly.test.ts +103 -0
- package/src/generated/doc.ts +270 -0
- package/src/generated/issue.ts +807 -0
- package/src/generated/label.ts +273 -0
- package/src/generated/project.ts +596 -0
- package/src/generated/template.ts +157 -0
- package/src/hand-crafted/issue.ts +27 -0
- package/src/lib/adapters/doc.ts +14 -0
- package/src/lib/adapters/index.ts +4 -0
- package/src/lib/adapters/issue.ts +32 -0
- package/src/lib/adapters/label.ts +20 -0
- package/src/lib/adapters/project.ts +23 -0
- package/src/lib/arktype-config.ts +18 -0
- package/src/lib/command-introspection.ts +97 -0
- package/src/lib/dispatch-effects.test.ts +297 -0
- package/src/lib/error.ts +37 -1
- package/src/lib/operation-spec.test.ts +317 -0
- package/src/lib/operation-spec.ts +11 -0
- package/src/lib/operation-specs.ts +21 -0
- package/src/lib/output.test.ts +3 -1
- package/src/lib/output.ts +1 -296
- package/src/lib/renderers/comments.ts +300 -0
- package/src/lib/renderers/detail.ts +61 -0
- package/src/lib/renderers/index.ts +2 -0
- package/src/router/agent-sessions.ts +253 -0
- package/src/router/auth.ts +6 -5
- package/src/router/config.ts +7 -6
- package/src/router/contract.test.ts +364 -0
- package/src/router/cycles.ts +372 -95
- package/src/router/git-automation-states.ts +355 -0
- package/src/router/git-automation-target-branches.ts +309 -0
- package/src/router/index.ts +26 -8
- package/src/router/initiatives.ts +260 -0
- package/src/router/me.ts +8 -7
- package/src/router/notifications.ts +176 -0
- package/src/router/roadmaps.ts +172 -0
- package/src/router/search.ts +7 -6
- package/src/router/teams.ts +82 -24
- package/src/router/users.ts +126 -0
- package/src/router/views.ts +399 -0
- package/src/router/docs.ts +0 -153
- package/src/router/issues.ts +0 -606
- package/src/router/labels.ts +0 -192
- package/src/router/projects.ts +0 -220
package/src/router/cycles.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../lib/arktype-config";
|
|
2
|
+
import { type } from "arktype";
|
|
2
3
|
import {
|
|
3
4
|
getClient,
|
|
4
5
|
listCycles,
|
|
6
|
+
getCycle,
|
|
5
7
|
getCurrentCycle,
|
|
6
8
|
getCycleIssues,
|
|
9
|
+
createCycle,
|
|
10
|
+
updateCycle,
|
|
11
|
+
deleteCycle,
|
|
12
|
+
findTeamByKeyOrName,
|
|
13
|
+
type Cycle,
|
|
7
14
|
} from "@bdsqqq/lnr-core";
|
|
15
|
+
import type { OperationSpec } from "../lib/operation-spec";
|
|
8
16
|
import { router, procedure } from "./trpc";
|
|
9
|
-
import { exitWithError, handleApiError } from "../lib/error";
|
|
17
|
+
import { exitWithError, handleApiError, EXIT_CODES } from "../lib/error";
|
|
10
18
|
import {
|
|
11
19
|
outputJson,
|
|
12
20
|
outputQuiet,
|
|
@@ -14,23 +22,350 @@ import {
|
|
|
14
22
|
getOutputFormat,
|
|
15
23
|
formatDate,
|
|
16
24
|
truncate,
|
|
25
|
+
type OutputOptions,
|
|
26
|
+
type TableColumn,
|
|
17
27
|
} from "../lib/output";
|
|
18
28
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
export const listCyclesInput = type({
|
|
30
|
+
team: type("string").describe("team key"),
|
|
31
|
+
"json?": type("boolean").describe("output as json"),
|
|
32
|
+
"quiet?": type("boolean").describe("output ids only"),
|
|
33
|
+
"verbose?": type("boolean").describe("show all columns"),
|
|
23
34
|
});
|
|
24
35
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
export const cycleInput = type({
|
|
37
|
+
nameOrNumber: type("string").configure({ positional: true }).describe("cycle name, number, or 'new'"),
|
|
38
|
+
team: type("string").describe("team key"),
|
|
39
|
+
"name?": type("string").describe("cycle name"),
|
|
40
|
+
"description?": type("string").describe("cycle description"),
|
|
41
|
+
"startsAt?": type("string").describe("start date (ISO format)"),
|
|
42
|
+
"endsAt?": type("string").describe("end date (ISO format)"),
|
|
43
|
+
"current?": type("boolean").describe("show current active cycle"),
|
|
44
|
+
"issues?": type("boolean").describe("list issues in cycle"),
|
|
45
|
+
"delete?": type("boolean").describe("archive the cycle"),
|
|
46
|
+
"json?": type("boolean").describe("output as json"),
|
|
47
|
+
"quiet?": type("boolean").describe("output ids only"),
|
|
48
|
+
"verbose?": type("boolean").describe("show all columns"),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
type CycleInput = typeof cycleInput.infer;
|
|
52
|
+
|
|
53
|
+
const cycleColumns: TableColumn<Cycle>[] = [
|
|
54
|
+
{ header: "#", value: (c) => String(c.number), width: 4 },
|
|
55
|
+
{ header: "NAME", value: (c) => c.name ?? `Cycle ${c.number}`, width: 20 },
|
|
56
|
+
{ header: "START", value: (c) => formatDate(c.startsAt), width: 12 },
|
|
57
|
+
{ header: "END", value: (c) => formatDate(c.endsAt), width: 12 },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const verboseCycleColumns: TableColumn<Cycle>[] = [
|
|
61
|
+
...cycleColumns,
|
|
62
|
+
{
|
|
63
|
+
header: "PROGRESS",
|
|
64
|
+
value: (c) => (c.progress != null ? `${Math.round(c.progress * 100)}%` : "-"),
|
|
65
|
+
width: 10,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
header: "DESCRIPTION",
|
|
69
|
+
value: (c) => truncate(c.description ?? "-", 30),
|
|
70
|
+
width: 30,
|
|
71
|
+
},
|
|
72
|
+
{ header: "ID", value: (c) => c.id, width: 36 },
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
export const cycleOperations = ["create", "read", "update", "delete", "current"] as const;
|
|
76
|
+
type Operation = (typeof cycleOperations)[number];
|
|
77
|
+
|
|
78
|
+
export const cycleMutationFlags: readonly (keyof CycleInput)[] = [
|
|
79
|
+
"name", "description", "startsAt", "endsAt"
|
|
80
|
+
] as const;
|
|
81
|
+
|
|
82
|
+
export function inferOperation(input: CycleInput): Operation {
|
|
83
|
+
if (input.current) return "current";
|
|
84
|
+
if (input.nameOrNumber === "new") return "create";
|
|
85
|
+
if (input.delete) return "delete";
|
|
86
|
+
|
|
87
|
+
for (const flag of cycleMutationFlags) {
|
|
88
|
+
if (input[flag] !== undefined) return "update";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return "read";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const cycleOperationSpec: OperationSpec<CycleInput, Operation> = {
|
|
95
|
+
command: "cycle",
|
|
96
|
+
operations: cycleOperations,
|
|
97
|
+
mutationFlags: cycleMutationFlags,
|
|
98
|
+
inferOperation,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
async function handleListCycles(
|
|
102
|
+
input: typeof listCyclesInput.infer
|
|
103
|
+
): Promise<void> {
|
|
104
|
+
try {
|
|
105
|
+
const client = getClient();
|
|
106
|
+
const cycles = await listCycles(client, input.team);
|
|
107
|
+
|
|
108
|
+
const outputOpts: OutputOptions = {
|
|
109
|
+
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
110
|
+
verbose: input.verbose,
|
|
111
|
+
};
|
|
112
|
+
const format = getOutputFormat(outputOpts);
|
|
113
|
+
|
|
114
|
+
if (format === "json") {
|
|
115
|
+
outputJson(cycles);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (format === "quiet") {
|
|
120
|
+
outputQuiet(cycles.map((c) => c.id));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const columns = input.verbose ? verboseCycleColumns : cycleColumns;
|
|
125
|
+
outputTable(cycles, columns, outputOpts);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
handleApiError(error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function handleShowCycle(
|
|
132
|
+
nameOrNumber: string,
|
|
133
|
+
input: CycleInput
|
|
134
|
+
): Promise<void> {
|
|
135
|
+
try {
|
|
136
|
+
const client = getClient();
|
|
137
|
+
|
|
138
|
+
const outputOpts: OutputOptions = {
|
|
139
|
+
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
140
|
+
verbose: input.verbose,
|
|
141
|
+
};
|
|
142
|
+
const format = getOutputFormat(outputOpts);
|
|
143
|
+
|
|
144
|
+
const cycle = await getCycle(client, input.team, nameOrNumber);
|
|
145
|
+
|
|
146
|
+
if (!cycle) {
|
|
147
|
+
exitWithError(
|
|
148
|
+
`cycle "${nameOrNumber}" not found`,
|
|
149
|
+
`try: lnr cycles --team ${input.team}`,
|
|
150
|
+
EXIT_CODES.NOT_FOUND
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (input.issues) {
|
|
155
|
+
const issues = await getCycleIssues(client, input.team);
|
|
156
|
+
|
|
157
|
+
if (format === "json") {
|
|
158
|
+
outputJson(issues);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (format === "quiet") {
|
|
163
|
+
outputQuiet(issues.map((i) => i.identifier));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
outputTable(
|
|
168
|
+
issues,
|
|
169
|
+
[
|
|
170
|
+
{ header: "ID", value: (i) => i.identifier, width: 10 },
|
|
171
|
+
{ header: "TITLE", value: (i) => truncate(i.title, 50), width: 50 },
|
|
172
|
+
],
|
|
173
|
+
outputOpts
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (format === "json") {
|
|
179
|
+
outputJson(cycle);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (format === "quiet") {
|
|
184
|
+
console.log(cycle.id);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(`cycle ${cycle.number}: ${cycle.name ?? `Cycle ${cycle.number}`}`);
|
|
189
|
+
console.log(` start: ${formatDate(cycle.startsAt)}`);
|
|
190
|
+
console.log(` end: ${formatDate(cycle.endsAt)}`);
|
|
191
|
+
if (cycle.progress != null) {
|
|
192
|
+
console.log(` progress: ${Math.round(cycle.progress * 100)}%`);
|
|
193
|
+
}
|
|
194
|
+
if (cycle.description) {
|
|
195
|
+
console.log(` description: ${cycle.description}`);
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
handleApiError(error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function handleCurrentCycle(input: CycleInput): Promise<void> {
|
|
203
|
+
try {
|
|
204
|
+
const client = getClient();
|
|
205
|
+
|
|
206
|
+
const outputOpts: OutputOptions = {
|
|
207
|
+
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
208
|
+
verbose: input.verbose,
|
|
209
|
+
};
|
|
210
|
+
const format = getOutputFormat(outputOpts);
|
|
28
211
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
212
|
+
const cycle = await getCurrentCycle(client, input.team);
|
|
213
|
+
|
|
214
|
+
if (!cycle) {
|
|
215
|
+
exitWithError("no active cycle", `team "${input.team}" has no current cycle`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (input.issues) {
|
|
219
|
+
const issues = await getCycleIssues(client, input.team);
|
|
220
|
+
|
|
221
|
+
if (format === "json") {
|
|
222
|
+
outputJson(issues);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (format === "quiet") {
|
|
227
|
+
outputQuiet(issues.map((i) => i.identifier));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
outputTable(
|
|
232
|
+
issues,
|
|
233
|
+
[
|
|
234
|
+
{ header: "ID", value: (i) => i.identifier, width: 10 },
|
|
235
|
+
{ header: "TITLE", value: (i) => truncate(i.title, 50), width: 50 },
|
|
236
|
+
],
|
|
237
|
+
outputOpts
|
|
238
|
+
);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (format === "json") {
|
|
243
|
+
outputJson(cycle);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (format === "quiet") {
|
|
248
|
+
console.log(cycle.id);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
console.log(`cycle ${cycle.number}: ${cycle.name ?? `Cycle ${cycle.number}`}`);
|
|
253
|
+
console.log(` start: ${formatDate(cycle.startsAt)}`);
|
|
254
|
+
console.log(` end: ${formatDate(cycle.endsAt)}`);
|
|
255
|
+
if (cycle.progress != null) {
|
|
256
|
+
console.log(` progress: ${Math.round(cycle.progress * 100)}%`);
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
handleApiError(error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function handleCreateCycle(input: CycleInput): Promise<void> {
|
|
264
|
+
if (!input.startsAt || !input.endsAt) {
|
|
265
|
+
exitWithError(
|
|
266
|
+
"--starts-at and --ends-at are required",
|
|
267
|
+
'usage: lnr cycle new --team ENG --starts-at "2026-02-01" --ends-at "2026-02-14"'
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const client = getClient();
|
|
273
|
+
|
|
274
|
+
const team = await findTeamByKeyOrName(client, input.team);
|
|
275
|
+
if (!team) {
|
|
276
|
+
exitWithError(`team "${input.team}" not found`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const cycle = await createCycle(client, {
|
|
280
|
+
teamId: team.id,
|
|
281
|
+
name: input.name,
|
|
282
|
+
description: input.description,
|
|
283
|
+
startsAt: input.startsAt,
|
|
284
|
+
endsAt: input.endsAt,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (cycle) {
|
|
288
|
+
console.log(
|
|
289
|
+
`created cycle ${cycle.number}: ${cycle.name ?? `Cycle ${cycle.number}`}`
|
|
290
|
+
);
|
|
291
|
+
} else {
|
|
292
|
+
exitWithError("failed to create cycle");
|
|
293
|
+
}
|
|
294
|
+
} catch (error) {
|
|
295
|
+
handleApiError(error);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function handleUpdateCycle(
|
|
300
|
+
nameOrNumber: string,
|
|
301
|
+
input: CycleInput
|
|
302
|
+
): Promise<void> {
|
|
303
|
+
try {
|
|
304
|
+
const client = getClient();
|
|
305
|
+
|
|
306
|
+
const cycle = await getCycle(client, input.team, nameOrNumber);
|
|
307
|
+
|
|
308
|
+
if (!cycle) {
|
|
309
|
+
exitWithError(
|
|
310
|
+
`cycle "${nameOrNumber}" not found`,
|
|
311
|
+
`try: lnr cycles --team ${input.team}`,
|
|
312
|
+
EXIT_CODES.NOT_FOUND
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const success = await updateCycle(client, cycle.id, {
|
|
317
|
+
name: input.name,
|
|
318
|
+
description: input.description,
|
|
319
|
+
startsAt: input.startsAt,
|
|
320
|
+
endsAt: input.endsAt,
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (!success) {
|
|
324
|
+
exitWithError(
|
|
325
|
+
`failed to update cycle "${nameOrNumber}"`,
|
|
326
|
+
undefined,
|
|
327
|
+
EXIT_CODES.NOT_FOUND
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
console.log(`updated cycle: ${cycle.name ?? `Cycle ${cycle.number}`}`);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
handleApiError(error);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function handleDeleteCycle(
|
|
338
|
+
nameOrNumber: string,
|
|
339
|
+
input: CycleInput
|
|
340
|
+
): Promise<void> {
|
|
341
|
+
try {
|
|
342
|
+
const client = getClient();
|
|
343
|
+
|
|
344
|
+
const cycle = await getCycle(client, input.team, nameOrNumber);
|
|
345
|
+
|
|
346
|
+
if (!cycle) {
|
|
347
|
+
exitWithError(
|
|
348
|
+
`cycle "${nameOrNumber}" not found`,
|
|
349
|
+
`try: lnr cycles --team ${input.team}`,
|
|
350
|
+
EXIT_CODES.NOT_FOUND
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const success = await deleteCycle(client, cycle.id);
|
|
355
|
+
|
|
356
|
+
if (!success) {
|
|
357
|
+
exitWithError(
|
|
358
|
+
`failed to archive cycle "${nameOrNumber}"`,
|
|
359
|
+
undefined,
|
|
360
|
+
EXIT_CODES.NOT_FOUND
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
console.log(`archived cycle: ${cycle.name ?? `Cycle ${cycle.number}`}`);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
handleApiError(error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
34
369
|
|
|
35
370
|
export const cyclesRouter = router({
|
|
36
371
|
cycles: procedure
|
|
@@ -38,94 +373,36 @@ export const cyclesRouter = router({
|
|
|
38
373
|
aliases: { command: ["c"] },
|
|
39
374
|
description: "list cycles for a team",
|
|
40
375
|
})
|
|
41
|
-
.input(
|
|
376
|
+
.input(listCyclesInput)
|
|
42
377
|
.query(async ({ input }) => {
|
|
43
|
-
|
|
44
|
-
const client = getClient();
|
|
45
|
-
const cycles = await listCycles(client, input.team);
|
|
46
|
-
|
|
47
|
-
if (cycles.length === 0) {
|
|
48
|
-
exitWithError(`team "${input.team}" not found`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const format = input.json ? "json" : input.quiet ? "quiet" : getOutputFormat(input);
|
|
52
|
-
|
|
53
|
-
if (format === "json") {
|
|
54
|
-
outputJson(cycles);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (format === "quiet") {
|
|
59
|
-
outputQuiet(cycles.map((c) => c.id));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
outputTable(cycles, [
|
|
64
|
-
{ header: "#", value: (c) => String(c.number), width: 4 },
|
|
65
|
-
{ header: "NAME", value: (c) => c.name ?? `Cycle ${c.number}`, width: 20 },
|
|
66
|
-
{ header: "START", value: (c) => formatDate(c.startsAt), width: 12 },
|
|
67
|
-
{ header: "END", value: (c) => formatDate(c.endsAt), width: 12 },
|
|
68
|
-
], input);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
handleApiError(error);
|
|
71
|
-
}
|
|
378
|
+
await handleListCycles(input);
|
|
72
379
|
}),
|
|
73
380
|
|
|
74
381
|
cycle: procedure
|
|
75
382
|
.meta({
|
|
76
|
-
description: "show cycle
|
|
383
|
+
description: "show, create, update, or archive a cycle",
|
|
77
384
|
})
|
|
78
385
|
.input(cycleInput)
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (format === "quiet") {
|
|
103
|
-
outputQuiet(issues.map((i) => i.identifier));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
outputTable(issues, [
|
|
108
|
-
{ header: "ID", value: (i) => i.identifier, width: 10 },
|
|
109
|
-
{ header: "TITLE", value: (i) => truncate(i.title, 50), width: 50 },
|
|
110
|
-
], input);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (format === "json") {
|
|
115
|
-
outputJson(cycle);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (format === "quiet") {
|
|
120
|
-
console.log(cycle.id);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
console.log(`cycle ${cycle.number}: ${cycle.name ?? `Cycle ${cycle.number}`}`);
|
|
125
|
-
console.log(` start: ${formatDate(cycle.startsAt)}`);
|
|
126
|
-
console.log(` end: ${formatDate(cycle.endsAt)}`);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
handleApiError(error);
|
|
386
|
+
.mutation(async ({ input }) => {
|
|
387
|
+
const operation = inferOperation(input);
|
|
388
|
+
|
|
389
|
+
switch (operation) {
|
|
390
|
+
case "current":
|
|
391
|
+
await handleCurrentCycle(input);
|
|
392
|
+
break;
|
|
393
|
+
case "create":
|
|
394
|
+
await handleCreateCycle(input);
|
|
395
|
+
break;
|
|
396
|
+
case "delete":
|
|
397
|
+
await handleDeleteCycle(input.nameOrNumber, input);
|
|
398
|
+
break;
|
|
399
|
+
case "update":
|
|
400
|
+
await handleUpdateCycle(input.nameOrNumber, input);
|
|
401
|
+
break;
|
|
402
|
+
case "read":
|
|
403
|
+
default:
|
|
404
|
+
await handleShowCycle(input.nameOrNumber, input);
|
|
405
|
+
break;
|
|
129
406
|
}
|
|
130
407
|
}),
|
|
131
408
|
});
|