@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.
- package/dist/commands/config.js +3 -3
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/daemon.d.ts +4 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +675 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/habit.d.ts +2 -2
- package/dist/commands/habit.d.ts.map +1 -1
- package/dist/commands/habit.js +270 -315
- package/dist/commands/habit.js.map +1 -1
- package/dist/commands/label.d.ts +2 -2
- package/dist/commands/label.d.ts.map +1 -1
- package/dist/commands/label.js +76 -92
- package/dist/commands/label.js.map +1 -1
- package/dist/commands/note.d.ts +2 -2
- package/dist/commands/note.d.ts.map +1 -1
- package/dist/commands/note.js +97 -118
- package/dist/commands/note.js.map +1 -1
- package/dist/commands/plugin.d.ts +4 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +527 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/project.d.ts +2 -2
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +99 -117
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/recurring.d.ts +2 -2
- package/dist/commands/recurring.d.ts.map +1 -1
- package/dist/commands/recurring.js +250 -284
- package/dist/commands/recurring.js.map +1 -1
- package/dist/commands/sync.d.ts +2 -2
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +123 -12
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/task.d.ts +2 -2
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +181 -207
- package/dist/commands/task.js.map +1 -1
- package/dist/daemon-command-client.d.ts +11 -0
- package/dist/daemon-command-client.d.ts.map +1 -0
- package/dist/daemon-command-client.js +57 -0
- package/dist/daemon-command-client.js.map +1 -0
- package/dist/daemon-plugin-config.d.ts +8 -0
- package/dist/daemon-plugin-config.d.ts.map +1 -0
- package/dist/daemon-plugin-config.js +22 -0
- package/dist/daemon-plugin-config.js.map +1 -0
- package/dist/daemon-plugin-paths.d.ts +8 -0
- package/dist/daemon-plugin-paths.d.ts.map +1 -0
- package/dist/daemon-plugin-paths.js +28 -0
- package/dist/daemon-plugin-paths.js.map +1 -0
- package/dist/daemon-transport.d.ts +37 -0
- package/dist/daemon-transport.d.ts.map +1 -0
- package/dist/daemon-transport.js +422 -0
- package/dist/daemon-transport.js.map +1 -0
- package/dist/daemon-worker-assignment.d.ts +8 -0
- package/dist/daemon-worker-assignment.d.ts.map +1 -0
- package/dist/daemon-worker-assignment.js +22 -0
- package/dist/daemon-worker-assignment.js.map +1 -0
- package/dist/index.js +20 -23
- package/dist/index.js.map +1 -1
- package/dist/test-helpers/daemon-process.d.ts +5 -0
- package/dist/test-helpers/daemon-process.d.ts.map +1 -0
- package/dist/test-helpers/daemon-process.js +55 -0
- package/dist/test-helpers/daemon-process.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/warnings.d.ts +7 -0
- package/dist/warnings.d.ts.map +1 -0
- package/dist/warnings.js +21 -0
- package/dist/warnings.js.map +1 -0
- package/package.json +5 -3
package/dist/commands/habit.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describeSchedule } from "@todu/engine";
|
|
2
|
-
import {
|
|
2
|
+
import { formatDaemonCommandError } from "../daemon-command-client.js";
|
|
3
|
+
import { formatJSON, formatTable } from "../format.js";
|
|
3
4
|
const HABIT_COLUMNS = [
|
|
4
5
|
{ key: "id", label: "ID" },
|
|
5
6
|
{ key: "title", label: "Title" },
|
|
@@ -35,24 +36,28 @@ function habitDetail(h) {
|
|
|
35
36
|
lines.push(`Updated: ${h.updatedAt}`);
|
|
36
37
|
return lines.join("\n");
|
|
37
38
|
}
|
|
38
|
-
async function resolveHabit(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
async function resolveHabit(invokeDaemon, ref) {
|
|
40
|
+
const byId = await invokeDaemon("habit.get", { id: ref });
|
|
41
|
+
if (byId.ok) {
|
|
42
|
+
return { ok: true, value: byId.value };
|
|
43
|
+
}
|
|
44
|
+
if (byId.error.code !== "NOT_FOUND") {
|
|
45
|
+
return { ok: false, message: formatDaemonCommandError(byId.error) };
|
|
46
|
+
}
|
|
47
|
+
const listResult = await invokeDaemon("habit.list", {});
|
|
48
|
+
if (!listResult.ok) {
|
|
49
|
+
return { ok: false, message: formatDaemonCommandError(listResult.error) };
|
|
50
|
+
}
|
|
47
51
|
const byName = listResult.value.filter((h) => h.title.toLowerCase() === ref.toLowerCase());
|
|
48
|
-
if (byName.length === 1)
|
|
52
|
+
if (byName.length === 1) {
|
|
49
53
|
return { ok: true, value: byName[0] };
|
|
54
|
+
}
|
|
50
55
|
if (byName.length > 1) {
|
|
51
56
|
return { ok: false, message: `Multiple habits match "${ref}". Use the habit ID.` };
|
|
52
57
|
}
|
|
53
58
|
return { ok: false, message: `Habit not found: ${ref}` };
|
|
54
59
|
}
|
|
55
|
-
export function registerHabitCommands(program,
|
|
60
|
+
export function registerHabitCommands(program, invokeDaemon) {
|
|
56
61
|
const habit = program.command("habit").description("Manage habits");
|
|
57
62
|
habit
|
|
58
63
|
.command("create")
|
|
@@ -64,32 +69,28 @@ export function registerHabitCommands(program, getTodu) {
|
|
|
64
69
|
.option("--end-date <date>", "end date (YYYY-MM-DD)")
|
|
65
70
|
.option("--description <text>", "habit description")
|
|
66
71
|
.action(async (opts) => {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
const result = await todu.habit.create({
|
|
72
|
+
const result = await invokeDaemon("habit.create", {
|
|
73
|
+
input: {
|
|
70
74
|
title: opts.title,
|
|
71
75
|
schedule: opts.schedule,
|
|
72
76
|
timezone: opts.timezone,
|
|
73
77
|
startDate: opts.startDate,
|
|
74
78
|
endDate: opts.endDate,
|
|
75
79
|
description: opts.description,
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const format = program.opts().format;
|
|
83
|
-
if (format === "json") {
|
|
84
|
-
console.log(formatJSON(result.value));
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
console.log(`Created habit: ${result.value.id}`);
|
|
88
|
-
console.log(habitDetail(result.value));
|
|
89
|
-
}
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
console.error(formatDaemonCommandError(result.error));
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
90
86
|
}
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
const format = program.opts().format;
|
|
88
|
+
if (format === "json") {
|
|
89
|
+
console.log(formatJSON(result.value));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(`Created habit: ${result.value.id}`);
|
|
93
|
+
console.log(habitDetail(result.value));
|
|
93
94
|
}
|
|
94
95
|
});
|
|
95
96
|
habit
|
|
@@ -98,80 +99,70 @@ export function registerHabitCommands(program, getTodu) {
|
|
|
98
99
|
.option("--active", "show only active habits")
|
|
99
100
|
.option("--paused", "show only paused habits")
|
|
100
101
|
.action(async (opts) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.log(formatJSON(result.value));
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
// Enhance with streak info
|
|
120
|
-
const rows = [];
|
|
121
|
-
for (const h of result.value) {
|
|
122
|
-
const row = habitToRow(h);
|
|
123
|
-
const streak = await todu.habit.streak(h.id);
|
|
124
|
-
if (streak.ok) {
|
|
125
|
-
row.streak = streak.value.current > 0 ? `🔥 ${streak.value.current}` : "0";
|
|
126
|
-
row.today = streak.value.completedToday ? "✅" : "—";
|
|
127
|
-
}
|
|
128
|
-
rows.push(row);
|
|
129
|
-
}
|
|
130
|
-
const columns = [
|
|
131
|
-
...HABIT_COLUMNS,
|
|
132
|
-
{ key: "streak", label: "Streak" },
|
|
133
|
-
{ key: "today", label: "Today" },
|
|
134
|
-
];
|
|
135
|
-
console.log(formatTable(rows, columns));
|
|
102
|
+
const filter = {};
|
|
103
|
+
if (opts.active)
|
|
104
|
+
filter.paused = false;
|
|
105
|
+
if (opts.paused)
|
|
106
|
+
filter.paused = true;
|
|
107
|
+
const result = await invokeDaemon("habit.list", { filter });
|
|
108
|
+
if (!result.ok) {
|
|
109
|
+
console.error(formatDaemonCommandError(result.error));
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const format = program.opts().format;
|
|
114
|
+
if (format === "json") {
|
|
115
|
+
console.log(formatJSON(result.value));
|
|
116
|
+
return;
|
|
136
117
|
}
|
|
137
|
-
|
|
138
|
-
|
|
118
|
+
const rows = [];
|
|
119
|
+
for (const h of result.value) {
|
|
120
|
+
const row = habitToRow(h);
|
|
121
|
+
const streak = await invokeDaemon("habit.streak", { id: h.id });
|
|
122
|
+
if (streak.ok) {
|
|
123
|
+
row.streak = streak.value.current > 0 ? `🔥 ${streak.value.current}` : "0";
|
|
124
|
+
row.today = streak.value.completedToday ? "✅" : "—";
|
|
125
|
+
}
|
|
126
|
+
rows.push(row);
|
|
139
127
|
}
|
|
128
|
+
const columns = [
|
|
129
|
+
...HABIT_COLUMNS,
|
|
130
|
+
{ key: "streak", label: "Streak" },
|
|
131
|
+
{ key: "today", label: "Today" },
|
|
132
|
+
];
|
|
133
|
+
console.log(formatTable(rows, columns));
|
|
140
134
|
});
|
|
141
135
|
habit
|
|
142
136
|
.command("show <id>")
|
|
143
137
|
.description("Show habit details with streak info")
|
|
144
138
|
.action(async (ref) => {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
process.exitCode = 1;
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const format = program.opts().format;
|
|
154
|
-
if (format === "json") {
|
|
155
|
-
// Include streak in JSON output
|
|
156
|
-
const streak = await todu.habit.streak(resolved.value.id);
|
|
157
|
-
const data = { ...resolved.value };
|
|
158
|
-
if (streak.ok)
|
|
159
|
-
data.streak = streak.value;
|
|
160
|
-
console.log(formatJSON(data));
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
console.log(habitDetail(resolved.value));
|
|
164
|
-
const streak = await todu.habit.streak(resolved.value.id);
|
|
165
|
-
if (streak.ok) {
|
|
166
|
-
console.log("");
|
|
167
|
-
console.log(`Streak: ${streak.value.current > 0 ? `🔥 ${streak.value.current} days` : "0 days"}`);
|
|
168
|
-
console.log(`Longest: ${streak.value.longest} days`);
|
|
169
|
-
console.log(`Today: ${streak.value.completedToday ? "✅ Done" : "— Not done"}`);
|
|
170
|
-
console.log(`Total: ${streak.value.totalCheckins} check-ins`);
|
|
171
|
-
}
|
|
139
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
140
|
+
if (!resolved.ok) {
|
|
141
|
+
console.error(resolved.message);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
172
144
|
}
|
|
173
|
-
|
|
174
|
-
|
|
145
|
+
const format = program.opts().format;
|
|
146
|
+
if (format === "json") {
|
|
147
|
+
const streak = await invokeDaemon("habit.streak", {
|
|
148
|
+
id: resolved.value.id,
|
|
149
|
+
});
|
|
150
|
+
const data = { ...resolved.value };
|
|
151
|
+
if (streak.ok)
|
|
152
|
+
data.streak = streak.value;
|
|
153
|
+
console.log(formatJSON(data));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.log(habitDetail(resolved.value));
|
|
157
|
+
const streak = await invokeDaemon("habit.streak", {
|
|
158
|
+
id: resolved.value.id,
|
|
159
|
+
});
|
|
160
|
+
if (streak.ok) {
|
|
161
|
+
console.log("");
|
|
162
|
+
console.log(`Streak: ${streak.value.current > 0 ? `🔥 ${streak.value.current} days` : "0 days"}`);
|
|
163
|
+
console.log(`Longest: ${streak.value.longest} days`);
|
|
164
|
+
console.log(`Today: ${streak.value.completedToday ? "✅ Done" : "— Not done"}`);
|
|
165
|
+
console.log(`Total: ${streak.value.totalCheckins} check-ins`);
|
|
175
166
|
}
|
|
176
167
|
});
|
|
177
168
|
habit
|
|
@@ -183,229 +174,196 @@ export function registerHabitCommands(program, getTodu) {
|
|
|
183
174
|
.option("--description <text>", "update description")
|
|
184
175
|
.option("--end-date <date>", "update end date")
|
|
185
176
|
.action(async (ref, opts) => {
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
else {
|
|
216
|
-
console.log(`Updated habit: ${result.value.id}`);
|
|
217
|
-
}
|
|
177
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
178
|
+
if (!resolved.ok) {
|
|
179
|
+
console.error(resolved.message);
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const input = {};
|
|
184
|
+
if (opts.title)
|
|
185
|
+
input.title = opts.title;
|
|
186
|
+
if (opts.schedule)
|
|
187
|
+
input.schedule = opts.schedule;
|
|
188
|
+
if (opts.timezone)
|
|
189
|
+
input.timezone = opts.timezone;
|
|
190
|
+
if (opts.description)
|
|
191
|
+
input.description = opts.description;
|
|
192
|
+
if (opts.endDate)
|
|
193
|
+
input.endDate = opts.endDate;
|
|
194
|
+
const result = await invokeDaemon("habit.update", {
|
|
195
|
+
id: resolved.value.id,
|
|
196
|
+
input,
|
|
197
|
+
});
|
|
198
|
+
if (!result.ok) {
|
|
199
|
+
console.error(formatDaemonCommandError(result.error));
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const format = program.opts().format;
|
|
204
|
+
if (format === "json") {
|
|
205
|
+
console.log(formatJSON(result.value));
|
|
218
206
|
}
|
|
219
|
-
|
|
220
|
-
|
|
207
|
+
else {
|
|
208
|
+
console.log(`Updated habit: ${result.value.id}`);
|
|
221
209
|
}
|
|
222
210
|
});
|
|
223
211
|
habit
|
|
224
212
|
.command("delete <id>")
|
|
225
213
|
.description("Delete a habit")
|
|
226
214
|
.action(async (ref) => {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
process.exitCode = 1;
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
const format = program.opts().format;
|
|
242
|
-
if (format === "json") {
|
|
243
|
-
console.log(formatJSON({ deleted: resolved.value.id }));
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
console.log(`Deleted habit: ${resolved.value.id}`);
|
|
247
|
-
}
|
|
215
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
216
|
+
if (!resolved.ok) {
|
|
217
|
+
console.error(resolved.message);
|
|
218
|
+
process.exitCode = 1;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const result = await invokeDaemon("habit.delete", { id: resolved.value.id });
|
|
222
|
+
if (!result.ok) {
|
|
223
|
+
console.error(formatDaemonCommandError(result.error));
|
|
224
|
+
process.exitCode = 1;
|
|
225
|
+
return;
|
|
248
226
|
}
|
|
249
|
-
|
|
250
|
-
|
|
227
|
+
const format = program.opts().format;
|
|
228
|
+
if (format === "json") {
|
|
229
|
+
console.log(formatJSON({ deleted: resolved.value.id }));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
console.log(`Deleted habit: ${resolved.value.id}`);
|
|
251
233
|
}
|
|
252
234
|
});
|
|
253
235
|
habit
|
|
254
236
|
.command("pause <id>")
|
|
255
237
|
.description("Pause a habit")
|
|
256
238
|
.action(async (ref) => {
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (format === "json") {
|
|
273
|
-
console.log(formatJSON(result.value));
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
console.log(`Paused habit: ${resolved.value.id}`);
|
|
277
|
-
}
|
|
239
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
240
|
+
if (!resolved.ok) {
|
|
241
|
+
console.error(resolved.message);
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const result = await invokeDaemon("habit.pause", { id: resolved.value.id });
|
|
246
|
+
if (!result.ok) {
|
|
247
|
+
console.error(formatDaemonCommandError(result.error));
|
|
248
|
+
process.exitCode = 1;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const format = program.opts().format;
|
|
252
|
+
if (format === "json") {
|
|
253
|
+
console.log(formatJSON(result.value));
|
|
278
254
|
}
|
|
279
|
-
|
|
280
|
-
|
|
255
|
+
else {
|
|
256
|
+
console.log(`Paused habit: ${resolved.value.id}`);
|
|
281
257
|
}
|
|
282
258
|
});
|
|
283
259
|
habit
|
|
284
260
|
.command("resume <id>")
|
|
285
261
|
.description("Resume a habit")
|
|
286
262
|
.action(async (ref) => {
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
process.exitCode = 1;
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
const format = program.opts().format;
|
|
302
|
-
if (format === "json") {
|
|
303
|
-
console.log(formatJSON(result.value));
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
console.log(`Resumed habit: ${resolved.value.id}`);
|
|
307
|
-
}
|
|
263
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
264
|
+
if (!resolved.ok) {
|
|
265
|
+
console.error(resolved.message);
|
|
266
|
+
process.exitCode = 1;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const result = await invokeDaemon("habit.resume", { id: resolved.value.id });
|
|
270
|
+
if (!result.ok) {
|
|
271
|
+
console.error(formatDaemonCommandError(result.error));
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
return;
|
|
308
274
|
}
|
|
309
|
-
|
|
310
|
-
|
|
275
|
+
const format = program.opts().format;
|
|
276
|
+
if (format === "json") {
|
|
277
|
+
console.log(formatJSON(result.value));
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
console.log(`Resumed habit: ${resolved.value.id}`);
|
|
311
281
|
}
|
|
312
282
|
});
|
|
313
283
|
habit
|
|
314
284
|
.command("check <id>")
|
|
315
285
|
.description("Check in for today")
|
|
316
286
|
.action(async (ref) => {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
process.exitCode = 1;
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
const result = await todu.habit.check(resolved.value.id);
|
|
326
|
-
if (!result.ok) {
|
|
327
|
-
console.error(formatError(result.error));
|
|
328
|
-
process.exitCode = 1;
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
const format = program.opts().format;
|
|
332
|
-
if (format === "json") {
|
|
333
|
-
console.log(formatJSON(result.value));
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
console.log(`✅ ${resolved.value.title} — checked in for ${result.value.date}`);
|
|
337
|
-
const streak = await todu.habit.streak(resolved.value.id);
|
|
338
|
-
if (streak.ok && streak.value.current > 0) {
|
|
339
|
-
console.log(`🔥 ${streak.value.current} day streak!`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
287
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
288
|
+
if (!resolved.ok) {
|
|
289
|
+
console.error(resolved.message);
|
|
290
|
+
process.exitCode = 1;
|
|
291
|
+
return;
|
|
342
292
|
}
|
|
343
|
-
|
|
344
|
-
|
|
293
|
+
const result = await invokeDaemon("habit.check", {
|
|
294
|
+
id: resolved.value.id,
|
|
295
|
+
});
|
|
296
|
+
if (!result.ok) {
|
|
297
|
+
console.error(formatDaemonCommandError(result.error));
|
|
298
|
+
process.exitCode = 1;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const format = program.opts().format;
|
|
302
|
+
if (format === "json") {
|
|
303
|
+
console.log(formatJSON(result.value));
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
console.log(`✅ ${resolved.value.title} — checked in for ${result.value.date}`);
|
|
307
|
+
const streak = await invokeDaemon("habit.streak", {
|
|
308
|
+
id: resolved.value.id,
|
|
309
|
+
});
|
|
310
|
+
if (streak.ok && streak.value.current > 0) {
|
|
311
|
+
console.log(`🔥 ${streak.value.current} day streak!`);
|
|
312
|
+
}
|
|
345
313
|
}
|
|
346
314
|
});
|
|
347
315
|
habit
|
|
348
316
|
.command("uncheck <id>")
|
|
349
317
|
.description("Remove today's check-in")
|
|
350
318
|
.action(async (ref) => {
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
process.exitCode = 1;
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
const format = program.opts().format;
|
|
366
|
-
if (format === "json") {
|
|
367
|
-
console.log(formatJSON({ unchecked: resolved.value.id }));
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
console.log(`Unchecked: ${resolved.value.title}`);
|
|
371
|
-
}
|
|
319
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
320
|
+
if (!resolved.ok) {
|
|
321
|
+
console.error(resolved.message);
|
|
322
|
+
process.exitCode = 1;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const result = await invokeDaemon("habit.uncheck", { id: resolved.value.id });
|
|
326
|
+
if (!result.ok) {
|
|
327
|
+
console.error(formatDaemonCommandError(result.error));
|
|
328
|
+
process.exitCode = 1;
|
|
329
|
+
return;
|
|
372
330
|
}
|
|
373
|
-
|
|
374
|
-
|
|
331
|
+
const format = program.opts().format;
|
|
332
|
+
if (format === "json") {
|
|
333
|
+
console.log(formatJSON({ unchecked: resolved.value.id }));
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
console.log(`Unchecked: ${resolved.value.title}`);
|
|
375
337
|
}
|
|
376
338
|
});
|
|
377
339
|
habit
|
|
378
340
|
.command("streak <id>")
|
|
379
341
|
.description("Show streak info")
|
|
380
342
|
.action(async (ref) => {
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
const format = program.opts().format;
|
|
396
|
-
if (format === "json") {
|
|
397
|
-
console.log(formatJSON(result.value));
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
console.log(`${resolved.value.title}`);
|
|
401
|
-
console.log(`Current: ${result.value.current > 0 ? `🔥 ${result.value.current} days` : "0 days"}`);
|
|
402
|
-
console.log(`Longest: ${result.value.longest} days`);
|
|
403
|
-
console.log(`Today: ${result.value.completedToday ? "✅ Done" : "— Not done"}`);
|
|
404
|
-
console.log(`Total: ${result.value.totalCheckins} check-ins`);
|
|
405
|
-
}
|
|
343
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
344
|
+
if (!resolved.ok) {
|
|
345
|
+
console.error(resolved.message);
|
|
346
|
+
process.exitCode = 1;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const result = await invokeDaemon("habit.streak", {
|
|
350
|
+
id: resolved.value.id,
|
|
351
|
+
});
|
|
352
|
+
if (!result.ok) {
|
|
353
|
+
console.error(formatDaemonCommandError(result.error));
|
|
354
|
+
process.exitCode = 1;
|
|
355
|
+
return;
|
|
406
356
|
}
|
|
407
|
-
|
|
408
|
-
|
|
357
|
+
const format = program.opts().format;
|
|
358
|
+
if (format === "json") {
|
|
359
|
+
console.log(formatJSON(result.value));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
console.log(`${resolved.value.title}`);
|
|
363
|
+
console.log(`Current: ${result.value.current > 0 ? `🔥 ${result.value.current} days` : "0 days"}`);
|
|
364
|
+
console.log(`Longest: ${result.value.longest} days`);
|
|
365
|
+
console.log(`Today: ${result.value.completedToday ? "✅ Done" : "— Not done"}`);
|
|
366
|
+
console.log(`Total: ${result.value.totalCheckins} check-ins`);
|
|
409
367
|
}
|
|
410
368
|
});
|
|
411
369
|
habit
|
|
@@ -413,45 +371,42 @@ export function registerHabitCommands(program, getTodu) {
|
|
|
413
371
|
.description("Show check-in history")
|
|
414
372
|
.option("--days <days>", "number of days to look back", "30")
|
|
415
373
|
.action(async (ref, opts) => {
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
console.log(`${resolved.value.title} — last ${days} days`);
|
|
441
|
-
console.log("");
|
|
442
|
-
const columns = [
|
|
443
|
-
{ key: "date", label: "Date" },
|
|
444
|
-
{ key: "status", label: "Status" },
|
|
445
|
-
];
|
|
446
|
-
const rows = result.value.map((entry) => ({
|
|
447
|
-
date: entry.date,
|
|
448
|
-
status: entry.completed ? "✅" : "—",
|
|
449
|
-
}));
|
|
450
|
-
console.log(formatTable(rows, columns));
|
|
451
|
-
}
|
|
452
|
-
finally {
|
|
453
|
-
await todu.close();
|
|
374
|
+
const resolved = await resolveHabit(invokeDaemon, ref);
|
|
375
|
+
if (!resolved.ok) {
|
|
376
|
+
console.error(resolved.message);
|
|
377
|
+
process.exitCode = 1;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const days = Number.parseInt(opts.days, 10);
|
|
381
|
+
const result = await invokeDaemon("habit.history", {
|
|
382
|
+
id: resolved.value.id,
|
|
383
|
+
days,
|
|
384
|
+
});
|
|
385
|
+
if (!result.ok) {
|
|
386
|
+
console.error(formatDaemonCommandError(result.error));
|
|
387
|
+
process.exitCode = 1;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const format = program.opts().format;
|
|
391
|
+
if (format === "json") {
|
|
392
|
+
console.log(formatJSON(result.value));
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (result.value.length === 0) {
|
|
396
|
+
console.log("No history.");
|
|
397
|
+
return;
|
|
454
398
|
}
|
|
399
|
+
console.log(`${resolved.value.title} — last ${days} days`);
|
|
400
|
+
console.log("");
|
|
401
|
+
const columns = [
|
|
402
|
+
{ key: "date", label: "Date" },
|
|
403
|
+
{ key: "status", label: "Status" },
|
|
404
|
+
];
|
|
405
|
+
const rows = result.value.map((entry) => ({
|
|
406
|
+
date: entry.date,
|
|
407
|
+
status: entry.completed ? "✅" : "—",
|
|
408
|
+
}));
|
|
409
|
+
console.log(formatTable(rows, columns));
|
|
455
410
|
});
|
|
456
411
|
}
|
|
457
412
|
//# sourceMappingURL=habit.js.map
|