@kaname-tasks/kaname 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/README.md +19 -8
- package/dist/{chunk-AEAYCJI7.js → chunk-FR675V3R.js} +48 -3
- package/dist/index.js +12 -37
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,15 +61,15 @@ kaname ls --all
|
|
|
61
61
|
kaname ls --list groceries
|
|
62
62
|
kaname ls --smart errands
|
|
63
63
|
|
|
64
|
-
kaname show
|
|
65
|
-
kaname done
|
|
66
|
-
kaname undone
|
|
67
|
-
kaname edit
|
|
68
|
-
kaname rm
|
|
64
|
+
kaname show 4f2a1c # short ids are shown by `kaname ls` and never change
|
|
65
|
+
kaname done 4f2a1c 9bd003 # complete several at once
|
|
66
|
+
kaname undone 9bd003
|
|
67
|
+
kaname edit 4f2a1c --when tomorrow --list none
|
|
68
|
+
kaname rm 4f2a1c # asks for confirmation (or --force)
|
|
69
69
|
|
|
70
|
-
kaname subtask add
|
|
71
|
-
kaname subtask done
|
|
72
|
-
kaname subtask rm
|
|
70
|
+
kaname subtask add 4f2a1c whole milk
|
|
71
|
+
kaname subtask done 4f2a1c 1
|
|
72
|
+
kaname subtask rm 4f2a1c 2
|
|
73
73
|
|
|
74
74
|
kaname list ls
|
|
75
75
|
kaname list add groceries --color '#22c55e'
|
|
@@ -83,6 +83,17 @@ kaname logout
|
|
|
83
83
|
Dates accept `today`, `tomorrow`, weekday names (`fri`), offsets (`+3d`), ISO
|
|
84
84
|
(`2026-07-10`), and `none` to clear a date in `edit`.
|
|
85
85
|
|
|
86
|
+
Any command taking a todo accepts three ways to name one:
|
|
87
|
+
|
|
88
|
+
| Form | Example | Notes |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| short id | `4f2a1c` | Shown by `ls`; the leading chars of the todo's uuid, so it never changes |
|
|
91
|
+
| `#`-index | `#2` or `2` | Position in the **last** `ls` output — convenient, but shifts whenever you re-list |
|
|
92
|
+
| full uuid | `4f2a1c…` | What `--json` and `--plain` print |
|
|
93
|
+
|
|
94
|
+
Prefer the short id: an index means whatever the most recent `ls` in *any* shell
|
|
95
|
+
put in that row, so it goes stale the moment you list something else.
|
|
96
|
+
|
|
86
97
|
Every read command takes `--json` (raw API response, for `jq`/scripts) and
|
|
87
98
|
`--plain` (tab-separated, no color). Exit codes: `1` for errors, `2` for
|
|
88
99
|
"not logged in / session expired".
|
|
@@ -199,6 +199,50 @@ async function confirmOrFail(message) {
|
|
|
199
199
|
|
|
200
200
|
// src/util/output.ts
|
|
201
201
|
import pc from "picocolors";
|
|
202
|
+
|
|
203
|
+
// src/util/ids.ts
|
|
204
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
205
|
+
var SHORT_ID_LEN = 6;
|
|
206
|
+
function shortId(id) {
|
|
207
|
+
return id.slice(0, SHORT_ID_LEN);
|
|
208
|
+
}
|
|
209
|
+
function matchPrefix(ids, prefix) {
|
|
210
|
+
const lower = prefix.toLowerCase();
|
|
211
|
+
return ids.filter((id) => id.toLowerCase().startsWith(lower));
|
|
212
|
+
}
|
|
213
|
+
function ambiguous(token, count) {
|
|
214
|
+
return new Error(`Ambiguous id "${token}" (${count} matches) \u2014 use more characters.`);
|
|
215
|
+
}
|
|
216
|
+
async function resolveTodoId(token) {
|
|
217
|
+
const indexMatch = token.match(/^#?(\d{1,4})$/);
|
|
218
|
+
if (indexMatch) {
|
|
219
|
+
const ids = loadLastView();
|
|
220
|
+
const index = Number(indexMatch[1]);
|
|
221
|
+
if (index < 1 || index > ids.length) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
ids.length === 0 ? `No cached listing \u2014 run \`kaname ls\` first, or pass a short id.` : `#${index} is out of range (last listing had ${ids.length} items).`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
return ids[index - 1];
|
|
227
|
+
}
|
|
228
|
+
if (UUID_RE.test(token)) return token;
|
|
229
|
+
if (/^[0-9a-f]{4,}$/i.test(token)) {
|
|
230
|
+
const cached = matchPrefix(loadLastView(), token);
|
|
231
|
+
if (cached.length === 1) return cached[0];
|
|
232
|
+
if (cached.length > 1) throw ambiguous(token, cached.length);
|
|
233
|
+
const todos = await api.get("/todos?all=true");
|
|
234
|
+
const matches = matchPrefix(
|
|
235
|
+
todos.map((t) => t.id),
|
|
236
|
+
token
|
|
237
|
+
);
|
|
238
|
+
if (matches.length === 1) return matches[0];
|
|
239
|
+
if (matches.length > 1) throw ambiguous(token, matches.length);
|
|
240
|
+
throw new Error(`No todo has id "${token}".`);
|
|
241
|
+
}
|
|
242
|
+
throw new Error(`"${token}" is not a #index, short id, or UUID.`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/util/output.ts
|
|
202
246
|
function printJson(data) {
|
|
203
247
|
console.log(JSON.stringify(data, null, 2));
|
|
204
248
|
}
|
|
@@ -242,7 +286,7 @@ function printTodos(todos, opts, listNames) {
|
|
|
242
286
|
const index = pc.dim(`#${i + 1}`.padStart(indexWidth + 1));
|
|
243
287
|
const mark = t.completed ? pc.green("\u2713") : "\u25CB";
|
|
244
288
|
const title = t.completed ? pc.dim(t.title) : t.title;
|
|
245
|
-
const parts = [index, mark, title];
|
|
289
|
+
const parts = [index, pc.dim(shortId(t.id)), mark, title];
|
|
246
290
|
const when = colorDate(dateLabel(t.when_date, t.when_time), t.when_date, t.completed);
|
|
247
291
|
if (when) parts.push(when);
|
|
248
292
|
if (t.due_date) {
|
|
@@ -262,7 +306,7 @@ function printTodoDetail(todo, opts) {
|
|
|
262
306
|
return;
|
|
263
307
|
}
|
|
264
308
|
console.log(`${todo.completed ? pc.green("\u2713") : "\u25CB"} ${pc.bold(todo.title)}`);
|
|
265
|
-
console.log(pc.dim(` id: ${todo.id}`));
|
|
309
|
+
console.log(pc.dim(` id: ${shortId(todo.id)} (${todo.id})`));
|
|
266
310
|
if (todo.description) console.log(` ${todo.description}`);
|
|
267
311
|
if (todo.when_date) console.log(` when: ${colorDate(dateLabel(todo.when_date, todo.when_time), todo.when_date, todo.completed)}`);
|
|
268
312
|
if (todo.due_date) console.log(` due: ${colorDate(dateLabel(todo.due_date, todo.due_time), todo.due_date, todo.completed)}`);
|
|
@@ -371,13 +415,14 @@ export {
|
|
|
371
415
|
saveCredentials,
|
|
372
416
|
clearCredentials,
|
|
373
417
|
saveLastView,
|
|
374
|
-
loadLastView,
|
|
375
418
|
ApiError,
|
|
376
419
|
AuthError,
|
|
377
420
|
api,
|
|
378
421
|
confirmOrFail,
|
|
379
422
|
localToday,
|
|
380
423
|
parseDateArg,
|
|
424
|
+
shortId,
|
|
425
|
+
resolveTodoId,
|
|
381
426
|
printJson,
|
|
382
427
|
printTodos,
|
|
383
428
|
printTodoDetail,
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
fetchLists,
|
|
12
12
|
listNameMap,
|
|
13
13
|
loadConfig,
|
|
14
|
-
loadLastView,
|
|
15
14
|
localToday,
|
|
16
15
|
parseDateArg,
|
|
17
16
|
printJson,
|
|
@@ -19,10 +18,12 @@ import {
|
|
|
19
18
|
printTodos,
|
|
20
19
|
registerListCommands,
|
|
21
20
|
resolveList,
|
|
21
|
+
resolveTodoId,
|
|
22
22
|
saveConfig,
|
|
23
23
|
saveCredentials,
|
|
24
|
-
saveLastView
|
|
25
|
-
|
|
24
|
+
saveLastView,
|
|
25
|
+
shortId
|
|
26
|
+
} from "./chunk-FR675V3R.js";
|
|
26
27
|
|
|
27
28
|
// src/index.ts
|
|
28
29
|
import { Command } from "commander";
|
|
@@ -185,32 +186,6 @@ function registerAuthCommands(program2) {
|
|
|
185
186
|
|
|
186
187
|
// src/commands/todos.ts
|
|
187
188
|
import pc2 from "picocolors";
|
|
188
|
-
|
|
189
|
-
// src/util/ids.ts
|
|
190
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
191
|
-
function resolveTodoId(token) {
|
|
192
|
-
const indexMatch = token.match(/^#?(\d{1,4})$/);
|
|
193
|
-
if (indexMatch) {
|
|
194
|
-
const ids = loadLastView();
|
|
195
|
-
const index = Number(indexMatch[1]);
|
|
196
|
-
if (index < 1 || index > ids.length) {
|
|
197
|
-
throw new Error(
|
|
198
|
-
ids.length === 0 ? `No cached listing \u2014 run \`kaname ls\` first, or pass a UUID.` : `#${index} is out of range (last listing had ${ids.length} items).`
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
return ids[index - 1];
|
|
202
|
-
}
|
|
203
|
-
if (UUID_RE.test(token)) return token;
|
|
204
|
-
if (/^[0-9a-f]{4,}$/i.test(token)) {
|
|
205
|
-
const matches = loadLastView().filter((id) => id.toLowerCase().startsWith(token.toLowerCase()));
|
|
206
|
-
if (matches.length === 1) return matches[0];
|
|
207
|
-
if (matches.length > 1) throw new Error(`Ambiguous id prefix "${token}" (${matches.length} matches).`);
|
|
208
|
-
throw new Error(`No todo in the last listing matches "${token}" \u2014 run \`kaname ls\` or pass a full UUID.`);
|
|
209
|
-
}
|
|
210
|
-
throw new Error(`"${token}" is not a #index, UUID, or UUID prefix.`);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// src/commands/todos.ts
|
|
214
189
|
async function buildEditPayload(opts) {
|
|
215
190
|
const payload = {};
|
|
216
191
|
if (opts.title !== void 0) payload.title = opts.title;
|
|
@@ -232,7 +207,7 @@ function registerTodoCommands(program2) {
|
|
|
232
207
|
const todo = await api.post("/todos", payload);
|
|
233
208
|
const globalOpts = program2.opts();
|
|
234
209
|
if (globalOpts.json) printJson(todo);
|
|
235
|
-
else console.log(pc2.green("Added:") + ` ${todo.title} ${pc2.dim(todo.id)}`);
|
|
210
|
+
else console.log(pc2.green("Added:") + ` ${todo.title} ${pc2.dim(shortId(todo.id))}`);
|
|
236
211
|
});
|
|
237
212
|
program2.command("ls").description("List todos (default: today view)").option("-t, --today", "todos scheduled for today (default)").option("-i, --inbox", "todos with no list").option("-a, --all", "all todos").option("-l, --list <list>", "todos in a list").option("-s, --smart <name>", "todos in a smart list").option("--status <status>", "filter by completion: open, done, or all", "all").action(
|
|
238
213
|
async (opts) => {
|
|
@@ -273,18 +248,18 @@ function registerTodoCommands(program2) {
|
|
|
273
248
|
}
|
|
274
249
|
);
|
|
275
250
|
program2.command("show <id>").description("Show a todo with its subtasks").action(async (idToken) => {
|
|
276
|
-
const todo = await api.get(`/todos/${resolveTodoId(idToken)}`);
|
|
251
|
+
const todo = await api.get(`/todos/${await resolveTodoId(idToken)}`);
|
|
277
252
|
printTodoDetail(todo, program2.opts());
|
|
278
253
|
});
|
|
279
254
|
program2.command("done <ids...>").description("Complete one or more todos").action(async (idTokens) => {
|
|
280
255
|
for (const token of idTokens) {
|
|
281
|
-
const todo = await api.put(`/todos/${resolveTodoId(token)}`, { completed: true });
|
|
256
|
+
const todo = await api.put(`/todos/${await resolveTodoId(token)}`, { completed: true });
|
|
282
257
|
console.log(`${pc2.green("\u2713")} ${todo.title}`);
|
|
283
258
|
}
|
|
284
259
|
});
|
|
285
260
|
program2.command("undone <ids...>").description("Reopen one or more completed todos").action(async (idTokens) => {
|
|
286
261
|
for (const token of idTokens) {
|
|
287
|
-
const todo = await api.put(`/todos/${resolveTodoId(token)}`, { completed: false });
|
|
262
|
+
const todo = await api.put(`/todos/${await resolveTodoId(token)}`, { completed: false });
|
|
288
263
|
console.log(`\u25CB ${todo.title}`);
|
|
289
264
|
}
|
|
290
265
|
});
|
|
@@ -293,11 +268,11 @@ function registerTodoCommands(program2) {
|
|
|
293
268
|
if (Object.keys(payload).length === 0) {
|
|
294
269
|
throw new Error("Nothing to change \u2014 pass at least one of --title/--notes/--when/--due/--list.");
|
|
295
270
|
}
|
|
296
|
-
const todo = await api.put(`/todos/${resolveTodoId(idToken)}`, payload);
|
|
271
|
+
const todo = await api.put(`/todos/${await resolveTodoId(idToken)}`, payload);
|
|
297
272
|
printTodoDetail(todo, program2.opts());
|
|
298
273
|
});
|
|
299
274
|
program2.command("rm <id>").description("Delete a todo").option("-f, --force", "skip confirmation").action(async (idToken, opts) => {
|
|
300
|
-
const id = resolveTodoId(idToken);
|
|
275
|
+
const id = await resolveTodoId(idToken);
|
|
301
276
|
const todo = await api.get(`/todos/${id}`);
|
|
302
277
|
if (!opts.force && !await confirmOrFail(`Delete "${todo.title}"?`)) return;
|
|
303
278
|
await api.del(`/todos/${id}`);
|
|
@@ -306,13 +281,13 @@ function registerTodoCommands(program2) {
|
|
|
306
281
|
const subtask = program2.command("subtask").description("Manage subtasks of a todo");
|
|
307
282
|
subtask.command("add <id> <title...>").description("Add a subtask").action(async (idToken, titleWords) => {
|
|
308
283
|
const created = await api.post(
|
|
309
|
-
`/todos/${resolveTodoId(idToken)}/subtasks`,
|
|
284
|
+
`/todos/${await resolveTodoId(idToken)}/subtasks`,
|
|
310
285
|
{ title: titleWords.join(" ") }
|
|
311
286
|
);
|
|
312
287
|
console.log(pc2.green("Added subtask:") + ` ${created.title}`);
|
|
313
288
|
});
|
|
314
289
|
const resolveSubtask = async (idToken, indexArg) => {
|
|
315
|
-
const todoId = resolveTodoId(idToken);
|
|
290
|
+
const todoId = await resolveTodoId(idToken);
|
|
316
291
|
const todo = await api.get(`/todos/${todoId}`);
|
|
317
292
|
const subtasks = todo.subtasks ?? [];
|
|
318
293
|
const index = Number(indexArg);
|
package/dist/mcp.js
CHANGED