@todu/cli 0.1.1 → 0.2.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 (73) hide show
  1. package/dist/commands/config.js +3 -3
  2. package/dist/commands/config.js.map +1 -1
  3. package/dist/commands/daemon.d.ts +4 -0
  4. package/dist/commands/daemon.d.ts.map +1 -0
  5. package/dist/commands/daemon.js +675 -0
  6. package/dist/commands/daemon.js.map +1 -0
  7. package/dist/commands/habit.d.ts +2 -2
  8. package/dist/commands/habit.d.ts.map +1 -1
  9. package/dist/commands/habit.js +270 -315
  10. package/dist/commands/habit.js.map +1 -1
  11. package/dist/commands/label.d.ts +2 -2
  12. package/dist/commands/label.d.ts.map +1 -1
  13. package/dist/commands/label.js +76 -92
  14. package/dist/commands/label.js.map +1 -1
  15. package/dist/commands/note.d.ts +2 -2
  16. package/dist/commands/note.d.ts.map +1 -1
  17. package/dist/commands/note.js +97 -118
  18. package/dist/commands/note.js.map +1 -1
  19. package/dist/commands/plugin.d.ts +4 -0
  20. package/dist/commands/plugin.d.ts.map +1 -0
  21. package/dist/commands/plugin.js +527 -0
  22. package/dist/commands/plugin.js.map +1 -0
  23. package/dist/commands/project.d.ts +2 -2
  24. package/dist/commands/project.d.ts.map +1 -1
  25. package/dist/commands/project.js +99 -117
  26. package/dist/commands/project.js.map +1 -1
  27. package/dist/commands/recurring.d.ts +2 -2
  28. package/dist/commands/recurring.d.ts.map +1 -1
  29. package/dist/commands/recurring.js +250 -284
  30. package/dist/commands/recurring.js.map +1 -1
  31. package/dist/commands/sync.d.ts +2 -2
  32. package/dist/commands/sync.d.ts.map +1 -1
  33. package/dist/commands/sync.js +123 -12
  34. package/dist/commands/sync.js.map +1 -1
  35. package/dist/commands/task.d.ts +2 -2
  36. package/dist/commands/task.d.ts.map +1 -1
  37. package/dist/commands/task.js +181 -207
  38. package/dist/commands/task.js.map +1 -1
  39. package/dist/daemon-command-client.d.ts +11 -0
  40. package/dist/daemon-command-client.d.ts.map +1 -0
  41. package/dist/daemon-command-client.js +57 -0
  42. package/dist/daemon-command-client.js.map +1 -0
  43. package/dist/daemon-plugin-config.d.ts +8 -0
  44. package/dist/daemon-plugin-config.d.ts.map +1 -0
  45. package/dist/daemon-plugin-config.js +22 -0
  46. package/dist/daemon-plugin-config.js.map +1 -0
  47. package/dist/daemon-plugin-paths.d.ts +8 -0
  48. package/dist/daemon-plugin-paths.d.ts.map +1 -0
  49. package/dist/daemon-plugin-paths.js +28 -0
  50. package/dist/daemon-plugin-paths.js.map +1 -0
  51. package/dist/daemon-transport.d.ts +37 -0
  52. package/dist/daemon-transport.d.ts.map +1 -0
  53. package/dist/daemon-transport.js +422 -0
  54. package/dist/daemon-transport.js.map +1 -0
  55. package/dist/daemon-worker-assignment.d.ts +8 -0
  56. package/dist/daemon-worker-assignment.d.ts.map +1 -0
  57. package/dist/daemon-worker-assignment.js +22 -0
  58. package/dist/daemon-worker-assignment.js.map +1 -0
  59. package/dist/index.js +20 -23
  60. package/dist/index.js.map +1 -1
  61. package/dist/test-helpers/daemon-process.d.ts +5 -0
  62. package/dist/test-helpers/daemon-process.d.ts.map +1 -0
  63. package/dist/test-helpers/daemon-process.js +55 -0
  64. package/dist/test-helpers/daemon-process.js.map +1 -0
  65. package/dist/version.d.ts +2 -0
  66. package/dist/version.d.ts.map +1 -0
  67. package/dist/version.js +3 -0
  68. package/dist/version.js.map +1 -0
  69. package/dist/warnings.d.ts +7 -0
  70. package/dist/warnings.d.ts.map +1 -0
  71. package/dist/warnings.js +21 -0
  72. package/dist/warnings.js.map +1 -0
  73. package/package.json +5 -3
@@ -1,6 +1,6 @@
1
- import { createProjectId, createRecurringId } from "@todu/core";
2
1
  import { describeSchedule } from "@todu/engine";
3
- import { colorPriority, formatError, formatJSON, formatTable } from "../format.js";
2
+ import { formatDaemonCommandError } from "../daemon-command-client.js";
3
+ import { colorPriority, formatJSON, formatTable } from "../format.js";
4
4
  const TEMPLATE_COLUMNS = [
5
5
  { key: "id", label: "ID" },
6
6
  { key: "title", label: "Title" },
@@ -46,48 +46,57 @@ function templateDetail(t, projectName) {
46
46
  lines.push(`Updated: ${t.updatedAt}`);
47
47
  return lines.join("\n");
48
48
  }
49
- async function resolveTemplate(todu, ref) {
50
- // Try as ID first
51
- const getResult = await todu.recurring.get(ref);
52
- if (getResult.ok)
53
- return { ok: true, value: getResult.value };
54
- // Try by name
55
- const listResult = await todu.recurring.list();
56
- if (!listResult.ok)
57
- return { ok: false, message: formatError(listResult.error) };
49
+ async function resolveTemplate(invokeDaemon, ref) {
50
+ const byId = await invokeDaemon("recurring.get", { id: ref });
51
+ if (byId.ok) {
52
+ return { ok: true, value: byId.value };
53
+ }
54
+ if (byId.error.code !== "NOT_FOUND") {
55
+ return { ok: false, message: formatDaemonCommandError(byId.error) };
56
+ }
57
+ const listResult = await invokeDaemon("recurring.list", {});
58
+ if (!listResult.ok) {
59
+ return { ok: false, message: formatDaemonCommandError(listResult.error) };
60
+ }
58
61
  const byName = listResult.value.filter((t) => t.title.toLowerCase() === ref.toLowerCase());
59
- if (byName.length === 1)
62
+ if (byName.length === 1) {
60
63
  return { ok: true, value: byName[0] };
64
+ }
61
65
  if (byName.length > 1) {
62
66
  return { ok: false, message: `Multiple templates match "${ref}". Use the template ID.` };
63
67
  }
64
68
  return { ok: false, message: `Recurring template not found: ${ref}` };
65
69
  }
66
- async function getProjectName(todu, projectId) {
67
- const result = await todu.project.list();
68
- if (!result.ok)
70
+ async function getProjectName(invokeDaemon, projectId) {
71
+ const result = await invokeDaemon("project.list", {});
72
+ if (!result.ok) {
69
73
  return projectId;
74
+ }
70
75
  const project = result.value.find((p) => p.id === projectId);
71
76
  return project?.name ?? projectId;
72
77
  }
73
- async function resolveProjectId(todu, ref) {
74
- const result = await todu.project.list();
75
- if (!result.ok)
76
- return { ok: false, message: formatError(result.error) };
77
- // Try by ID
78
- const byId = result.value.find((p) => p.id === ref);
79
- if (byId)
80
- return { ok: true, value: byId.id };
81
- // Try by name
78
+ async function resolveProjectId(invokeDaemon, ref) {
79
+ const byId = await invokeDaemon("project.get", { id: ref });
80
+ if (byId.ok) {
81
+ return { ok: true, value: byId.value.id };
82
+ }
83
+ if (byId.error.code !== "NOT_FOUND") {
84
+ return { ok: false, message: formatDaemonCommandError(byId.error) };
85
+ }
86
+ const result = await invokeDaemon("project.list", {});
87
+ if (!result.ok) {
88
+ return { ok: false, message: formatDaemonCommandError(result.error) };
89
+ }
82
90
  const byName = result.value.filter((p) => p.name.toLowerCase() === ref.toLowerCase());
83
- if (byName.length === 1)
91
+ if (byName.length === 1) {
84
92
  return { ok: true, value: byName[0].id };
93
+ }
85
94
  if (byName.length > 1) {
86
95
  return { ok: false, message: `Multiple projects match "${ref}". Use the project ID.` };
87
96
  }
88
97
  return { ok: false, message: `Project not found: ${ref}` };
89
98
  }
90
- export function registerRecurringCommands(program, getTodu) {
99
+ export function registerRecurringCommands(program, invokeDaemon) {
91
100
  const recurring = program.command("recurring").description("Manage recurring task templates");
92
101
  recurring
93
102
  .command("create")
@@ -102,42 +111,38 @@ export function registerRecurringCommands(program, getTodu) {
102
111
  .option("--description <text>", "template description")
103
112
  .option("--label <labels...>", "labels to apply to generated tasks")
104
113
  .action(async (opts) => {
105
- const todu = await getTodu();
106
- try {
107
- const projectRes = await resolveProjectId(todu, opts.project);
108
- if (!projectRes.ok) {
109
- console.error(projectRes.message);
110
- process.exitCode = 1;
111
- return;
112
- }
113
- const result = await todu.recurring.create({
114
+ const projectRes = await resolveProjectId(invokeDaemon, opts.project);
115
+ if (!projectRes.ok) {
116
+ console.error(projectRes.message);
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ const result = await invokeDaemon("recurring.create", {
121
+ input: {
114
122
  title: opts.title,
115
123
  schedule: opts.schedule,
116
124
  timezone: opts.timezone,
117
125
  startDate: opts.startDate,
118
- projectId: createProjectId(projectRes.value),
126
+ projectId: projectRes.value,
119
127
  description: opts.description,
120
128
  priority: opts.priority,
121
129
  endDate: opts.endDate,
122
130
  labels: opts.label,
123
- });
124
- if (!result.ok) {
125
- console.error(formatError(result.error));
126
- process.exitCode = 1;
127
- return;
128
- }
129
- const format = program.opts().format;
130
- if (format === "json") {
131
- console.log(formatJSON(result.value));
132
- }
133
- else {
134
- const projectName = await getProjectName(todu, result.value.projectId);
135
- console.log(`Created recurring template: ${result.value.id}`);
136
- console.log(templateDetail(result.value, projectName));
137
- }
131
+ },
132
+ });
133
+ if (!result.ok) {
134
+ console.error(formatDaemonCommandError(result.error));
135
+ process.exitCode = 1;
136
+ return;
137
+ }
138
+ const format = program.opts().format;
139
+ if (format === "json") {
140
+ console.log(formatJSON(result.value));
138
141
  }
139
- finally {
140
- await todu.close();
142
+ else {
143
+ const projectName = await getProjectName(invokeDaemon, result.value.projectId);
144
+ console.log(`Created recurring template: ${result.value.id}`);
145
+ console.log(templateDetail(result.value, projectName));
141
146
  }
142
147
  });
143
148
  recurring
@@ -147,74 +152,61 @@ export function registerRecurringCommands(program, getTodu) {
147
152
  .option("--paused", "show only paused templates")
148
153
  .option("--project <project>", "filter by project")
149
154
  .action(async (opts) => {
150
- const todu = await getTodu();
151
- try {
152
- let projectId;
153
- if (opts.project) {
154
- const res = await resolveProjectId(todu, opts.project);
155
- if (!res.ok) {
156
- console.error(res.message);
157
- process.exitCode = 1;
158
- return;
159
- }
160
- projectId = res.value;
161
- }
162
- const filter = {};
163
- if (opts.active)
164
- filter.paused = false;
165
- if (opts.paused)
166
- filter.paused = true;
167
- if (projectId)
168
- filter.projectId = createProjectId(projectId);
169
- const result = await todu.recurring.list(filter);
170
- if (!result.ok) {
171
- console.error(formatError(result.error));
155
+ let projectId;
156
+ if (opts.project) {
157
+ const res = await resolveProjectId(invokeDaemon, opts.project);
158
+ if (!res.ok) {
159
+ console.error(res.message);
172
160
  process.exitCode = 1;
173
161
  return;
174
162
  }
175
- const format = program.opts().format;
176
- if (format === "json") {
177
- console.log(formatJSON(result.value));
178
- return;
179
- }
180
- // Get project names for display
181
- const projects = await todu.project.list();
182
- const projectNames = {};
183
- if (projects.ok) {
184
- for (const p of projects.value) {
185
- projectNames[p.id] = p.name;
186
- }
187
- }
188
- const rows = result.value.map((t) => templateToRow(t, projectNames[t.projectId]));
189
- console.log(formatTable(rows, TEMPLATE_COLUMNS));
163
+ projectId = res.value;
164
+ }
165
+ const filter = {};
166
+ if (opts.active)
167
+ filter.paused = false;
168
+ if (opts.paused)
169
+ filter.paused = true;
170
+ if (projectId)
171
+ filter.projectId = projectId;
172
+ const result = await invokeDaemon("recurring.list", { filter });
173
+ if (!result.ok) {
174
+ console.error(formatDaemonCommandError(result.error));
175
+ process.exitCode = 1;
176
+ return;
177
+ }
178
+ const format = program.opts().format;
179
+ if (format === "json") {
180
+ console.log(formatJSON(result.value));
181
+ return;
190
182
  }
191
- finally {
192
- await todu.close();
183
+ const projects = await invokeDaemon("project.list", {});
184
+ const projectNames = {};
185
+ if (projects.ok) {
186
+ for (const p of projects.value) {
187
+ projectNames[p.id] = p.name;
188
+ }
193
189
  }
190
+ const rows = result.value.map((t) => templateToRow(t, projectNames[t.projectId]));
191
+ console.log(formatTable(rows, TEMPLATE_COLUMNS));
194
192
  });
195
193
  recurring
196
194
  .command("show <id>")
197
195
  .description("Show recurring template details")
198
196
  .action(async (ref) => {
199
- const todu = await getTodu();
200
- try {
201
- const resolved = await resolveTemplate(todu, ref);
202
- if (!resolved.ok) {
203
- console.error(resolved.message);
204
- process.exitCode = 1;
205
- return;
206
- }
207
- const format = program.opts().format;
208
- if (format === "json") {
209
- console.log(formatJSON(resolved.value));
210
- return;
211
- }
212
- const projectName = await getProjectName(todu, resolved.value.projectId);
213
- console.log(templateDetail(resolved.value, projectName));
197
+ const resolved = await resolveTemplate(invokeDaemon, ref);
198
+ if (!resolved.ok) {
199
+ console.error(resolved.message);
200
+ process.exitCode = 1;
201
+ return;
214
202
  }
215
- finally {
216
- await todu.close();
203
+ const format = program.opts().format;
204
+ if (format === "json") {
205
+ console.log(formatJSON(resolved.value));
206
+ return;
217
207
  }
208
+ const projectName = await getProjectName(invokeDaemon, resolved.value.projectId);
209
+ console.log(templateDetail(resolved.value, projectName));
218
210
  });
219
211
  recurring
220
212
  .command("update <id>")
@@ -228,144 +220,127 @@ export function registerRecurringCommands(program, getTodu) {
228
220
  .option("--label <labels...>", "replace labels")
229
221
  .option("--project <project>", "move to different project")
230
222
  .action(async (ref, opts) => {
231
- const todu = await getTodu();
232
- try {
233
- const resolved = await resolveTemplate(todu, ref);
234
- if (!resolved.ok) {
235
- console.error(resolved.message);
236
- process.exitCode = 1;
237
- return;
238
- }
239
- const input = {};
240
- if (opts.title)
241
- input.title = opts.title;
242
- if (opts.schedule)
243
- input.schedule = opts.schedule;
244
- if (opts.timezone)
245
- input.timezone = opts.timezone;
246
- if (opts.priority)
247
- input.priority = opts.priority;
248
- if (opts.description)
249
- input.description = opts.description;
250
- if (opts.endDate)
251
- input.endDate = opts.endDate;
252
- if (opts.label)
253
- input.labels = opts.label;
254
- if (opts.project) {
255
- const projectRes = await resolveProjectId(todu, opts.project);
256
- if (!projectRes.ok) {
257
- console.error(projectRes.message);
258
- process.exitCode = 1;
259
- return;
260
- }
261
- input.projectId = createProjectId(projectRes.value);
262
- }
263
- const result = await todu.recurring.update(resolved.value.id, input);
264
- if (!result.ok) {
265
- console.error(formatError(result.error));
223
+ const resolved = await resolveTemplate(invokeDaemon, ref);
224
+ if (!resolved.ok) {
225
+ console.error(resolved.message);
226
+ process.exitCode = 1;
227
+ return;
228
+ }
229
+ const input = {};
230
+ if (opts.title)
231
+ input.title = opts.title;
232
+ if (opts.schedule)
233
+ input.schedule = opts.schedule;
234
+ if (opts.timezone)
235
+ input.timezone = opts.timezone;
236
+ if (opts.priority)
237
+ input.priority = opts.priority;
238
+ if (opts.description)
239
+ input.description = opts.description;
240
+ if (opts.endDate)
241
+ input.endDate = opts.endDate;
242
+ if (opts.label)
243
+ input.labels = opts.label;
244
+ if (opts.project) {
245
+ const projectRes = await resolveProjectId(invokeDaemon, opts.project);
246
+ if (!projectRes.ok) {
247
+ console.error(projectRes.message);
266
248
  process.exitCode = 1;
267
249
  return;
268
250
  }
269
- const format = program.opts().format;
270
- if (format === "json") {
271
- console.log(formatJSON(result.value));
272
- }
273
- else {
274
- console.log(`Updated recurring template: ${result.value.id}`);
275
- }
251
+ input.projectId = projectRes.value;
276
252
  }
277
- finally {
278
- await todu.close();
253
+ const result = await invokeDaemon("recurring.update", {
254
+ id: resolved.value.id,
255
+ input,
256
+ });
257
+ if (!result.ok) {
258
+ console.error(formatDaemonCommandError(result.error));
259
+ process.exitCode = 1;
260
+ return;
261
+ }
262
+ const format = program.opts().format;
263
+ if (format === "json") {
264
+ console.log(formatJSON(result.value));
265
+ }
266
+ else {
267
+ console.log(`Updated recurring template: ${result.value.id}`);
279
268
  }
280
269
  });
281
270
  recurring
282
271
  .command("delete <id>")
283
272
  .description("Delete a recurring template")
284
273
  .action(async (ref) => {
285
- const todu = await getTodu();
286
- try {
287
- const resolved = await resolveTemplate(todu, ref);
288
- if (!resolved.ok) {
289
- console.error(resolved.message);
290
- process.exitCode = 1;
291
- return;
292
- }
293
- const result = await todu.recurring.delete(resolved.value.id);
294
- if (!result.ok) {
295
- console.error(formatError(result.error));
296
- process.exitCode = 1;
297
- return;
298
- }
299
- const format = program.opts().format;
300
- if (format === "json") {
301
- console.log(formatJSON({ deleted: resolved.value.id }));
302
- }
303
- else {
304
- console.log(`Deleted recurring template: ${resolved.value.id}`);
305
- }
274
+ const resolved = await resolveTemplate(invokeDaemon, ref);
275
+ if (!resolved.ok) {
276
+ console.error(resolved.message);
277
+ process.exitCode = 1;
278
+ return;
279
+ }
280
+ const result = await invokeDaemon("recurring.delete", { id: resolved.value.id });
281
+ if (!result.ok) {
282
+ console.error(formatDaemonCommandError(result.error));
283
+ process.exitCode = 1;
284
+ return;
285
+ }
286
+ const format = program.opts().format;
287
+ if (format === "json") {
288
+ console.log(formatJSON({ deleted: resolved.value.id }));
306
289
  }
307
- finally {
308
- await todu.close();
290
+ else {
291
+ console.log(`Deleted recurring template: ${resolved.value.id}`);
309
292
  }
310
293
  });
311
294
  recurring
312
295
  .command("pause <id>")
313
296
  .description("Pause a recurring template")
314
297
  .action(async (ref) => {
315
- const todu = await getTodu();
316
- try {
317
- const resolved = await resolveTemplate(todu, ref);
318
- if (!resolved.ok) {
319
- console.error(resolved.message);
320
- process.exitCode = 1;
321
- return;
322
- }
323
- const result = await todu.recurring.pause(resolved.value.id);
324
- if (!result.ok) {
325
- console.error(formatError(result.error));
326
- process.exitCode = 1;
327
- return;
328
- }
329
- const format = program.opts().format;
330
- if (format === "json") {
331
- console.log(formatJSON(result.value));
332
- }
333
- else {
334
- console.log(`Paused recurring template: ${resolved.value.id}`);
335
- }
298
+ const resolved = await resolveTemplate(invokeDaemon, ref);
299
+ if (!resolved.ok) {
300
+ console.error(resolved.message);
301
+ process.exitCode = 1;
302
+ return;
303
+ }
304
+ const result = await invokeDaemon("recurring.pause", {
305
+ id: resolved.value.id,
306
+ });
307
+ if (!result.ok) {
308
+ console.error(formatDaemonCommandError(result.error));
309
+ process.exitCode = 1;
310
+ return;
336
311
  }
337
- finally {
338
- await todu.close();
312
+ const format = program.opts().format;
313
+ if (format === "json") {
314
+ console.log(formatJSON(result.value));
315
+ }
316
+ else {
317
+ console.log(`Paused recurring template: ${resolved.value.id}`);
339
318
  }
340
319
  });
341
320
  recurring
342
321
  .command("resume <id>")
343
322
  .description("Resume a recurring template")
344
323
  .action(async (ref) => {
345
- const todu = await getTodu();
346
- try {
347
- const resolved = await resolveTemplate(todu, ref);
348
- if (!resolved.ok) {
349
- console.error(resolved.message);
350
- process.exitCode = 1;
351
- return;
352
- }
353
- const result = await todu.recurring.resume(resolved.value.id);
354
- if (!result.ok) {
355
- console.error(formatError(result.error));
356
- process.exitCode = 1;
357
- return;
358
- }
359
- const format = program.opts().format;
360
- if (format === "json") {
361
- console.log(formatJSON(result.value));
362
- }
363
- else {
364
- console.log(`Resumed recurring template: ${resolved.value.id}`);
365
- }
324
+ const resolved = await resolveTemplate(invokeDaemon, ref);
325
+ if (!resolved.ok) {
326
+ console.error(resolved.message);
327
+ process.exitCode = 1;
328
+ return;
366
329
  }
367
- finally {
368
- await todu.close();
330
+ const result = await invokeDaemon("recurring.resume", {
331
+ id: resolved.value.id,
332
+ });
333
+ if (!result.ok) {
334
+ console.error(formatDaemonCommandError(result.error));
335
+ process.exitCode = 1;
336
+ return;
337
+ }
338
+ const format = program.opts().format;
339
+ if (format === "json") {
340
+ console.log(formatJSON(result.value));
341
+ }
342
+ else {
343
+ console.log(`Resumed recurring template: ${resolved.value.id}`);
369
344
  }
370
345
  });
371
346
  recurring
@@ -374,75 +349,66 @@ export function registerRecurringCommands(program, getTodu) {
374
349
  .option("--days <days>", "number of days to look ahead", "14")
375
350
  .option("--template <id>", "filter by template ID")
376
351
  .action(async (opts) => {
377
- const todu = await getTodu();
378
- try {
379
- const options = {
380
- days: Number.parseInt(opts.days, 10),
381
- };
382
- if (opts.template) {
383
- options.templateId = createRecurringId(opts.template);
384
- }
385
- const result = await todu.recurring.upcoming(options);
386
- if (!result.ok) {
387
- console.error(formatError(result.error));
388
- process.exitCode = 1;
389
- return;
390
- }
391
- const format = program.opts().format;
392
- if (format === "json") {
393
- console.log(formatJSON(result.value));
394
- return;
395
- }
396
- if (result.value.length === 0) {
397
- console.log("No upcoming occurrences.");
398
- return;
399
- }
400
- const upcomingColumns = [
401
- { key: "date", label: "Date" },
402
- { key: "title", label: "Title" },
403
- { key: "priority", label: "Priority", colorize: colorPriority },
404
- { key: "schedule", label: "Schedule" },
405
- ];
406
- const rows = result.value.map((o) => ({
407
- date: o.date,
408
- title: o.title,
409
- priority: o.priority,
410
- schedule: o.schedule,
411
- }));
412
- console.log(formatTable(rows, upcomingColumns));
413
- }
414
- finally {
415
- await todu.close();
352
+ const options = {
353
+ days: Number.parseInt(opts.days, 10),
354
+ };
355
+ if (opts.template) {
356
+ options.templateId = opts.template;
357
+ }
358
+ const result = await invokeDaemon("recurring.upcoming", { options });
359
+ if (!result.ok) {
360
+ console.error(formatDaemonCommandError(result.error));
361
+ process.exitCode = 1;
362
+ return;
363
+ }
364
+ const format = program.opts().format;
365
+ if (format === "json") {
366
+ console.log(formatJSON(result.value));
367
+ return;
416
368
  }
369
+ if (result.value.length === 0) {
370
+ console.log("No upcoming occurrences.");
371
+ return;
372
+ }
373
+ const upcomingColumns = [
374
+ { key: "date", label: "Date" },
375
+ { key: "title", label: "Title" },
376
+ { key: "priority", label: "Priority", colorize: colorPriority },
377
+ { key: "schedule", label: "Schedule" },
378
+ ];
379
+ const rows = result.value.map((o) => ({
380
+ date: o.date,
381
+ title: o.title,
382
+ priority: o.priority,
383
+ schedule: o.schedule,
384
+ }));
385
+ console.log(formatTable(rows, upcomingColumns));
417
386
  });
418
387
  recurring
419
388
  .command("generate <id> <date>")
420
389
  .description("Create a task for a specific future date (early materialization)")
421
390
  .action(async (ref, date) => {
422
- const todu = await getTodu();
423
- try {
424
- const resolved = await resolveTemplate(todu, ref);
425
- if (!resolved.ok) {
426
- console.error(resolved.message);
427
- process.exitCode = 1;
428
- return;
429
- }
430
- const result = await todu.recurring.generate(resolved.value.id, date);
431
- if (!result.ok) {
432
- console.error(formatError(result.error));
433
- process.exitCode = 1;
434
- return;
435
- }
436
- const format = program.opts().format;
437
- if (format === "json") {
438
- console.log(formatJSON(result.value));
439
- }
440
- else {
441
- console.log(`Generated task: ${result.value.id} (${result.value.title} on ${date})`);
442
- }
391
+ const resolved = await resolveTemplate(invokeDaemon, ref);
392
+ if (!resolved.ok) {
393
+ console.error(resolved.message);
394
+ process.exitCode = 1;
395
+ return;
396
+ }
397
+ const result = await invokeDaemon("recurring.generate", {
398
+ templateId: resolved.value.id,
399
+ date,
400
+ });
401
+ if (!result.ok) {
402
+ console.error(formatDaemonCommandError(result.error));
403
+ process.exitCode = 1;
404
+ return;
405
+ }
406
+ const format = program.opts().format;
407
+ if (format === "json") {
408
+ console.log(formatJSON(result.value));
443
409
  }
444
- finally {
445
- await todu.close();
410
+ else {
411
+ console.log(`Generated task: ${result.value.id} (${result.value.title} on ${date})`);
446
412
  }
447
413
  });
448
414
  }