@kaname-tasks/kaname 0.1.1 → 0.2.1
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 +56 -38
- 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,15 +18,60 @@ 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";
|
|
29
30
|
import pc3 from "picocolors";
|
|
30
31
|
|
|
32
|
+
// package.json
|
|
33
|
+
var package_default = {
|
|
34
|
+
name: "@kaname-tasks/kaname",
|
|
35
|
+
version: "0.2.1",
|
|
36
|
+
description: "Command-line client for Kaname",
|
|
37
|
+
type: "module",
|
|
38
|
+
bin: {
|
|
39
|
+
kaname: "dist/index.js",
|
|
40
|
+
"kaname-mcp": "dist/mcp.js"
|
|
41
|
+
},
|
|
42
|
+
files: [
|
|
43
|
+
"dist"
|
|
44
|
+
],
|
|
45
|
+
publishConfig: {
|
|
46
|
+
access: "public"
|
|
47
|
+
},
|
|
48
|
+
engines: {
|
|
49
|
+
node: ">=20"
|
|
50
|
+
},
|
|
51
|
+
scripts: {
|
|
52
|
+
build: "tsup",
|
|
53
|
+
dev: "tsx src/index.ts",
|
|
54
|
+
lint: "tsc --noEmit",
|
|
55
|
+
test: "vitest run",
|
|
56
|
+
"generate-types": "node scripts/generate-types.js"
|
|
57
|
+
},
|
|
58
|
+
dependencies: {
|
|
59
|
+
"@clack/prompts": "^0.11.0",
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
61
|
+
commander: "^14.0.0",
|
|
62
|
+
picocolors: "^1.1.1",
|
|
63
|
+
zod: "^4.4.3"
|
|
64
|
+
},
|
|
65
|
+
devDependencies: {
|
|
66
|
+
"@types/node": "^22.20.0",
|
|
67
|
+
"openapi-typescript": "^7.10.1",
|
|
68
|
+
tsup: "^8.3.5",
|
|
69
|
+
tsx: "^4.19.2",
|
|
70
|
+
typescript: "^5.7.2",
|
|
71
|
+
vitest: "^3.0.0"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
31
75
|
// src/commands/auth.ts
|
|
32
76
|
import crypto from "crypto";
|
|
33
77
|
import http from "http";
|
|
@@ -185,32 +229,6 @@ function registerAuthCommands(program2) {
|
|
|
185
229
|
|
|
186
230
|
// src/commands/todos.ts
|
|
187
231
|
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
232
|
async function buildEditPayload(opts) {
|
|
215
233
|
const payload = {};
|
|
216
234
|
if (opts.title !== void 0) payload.title = opts.title;
|
|
@@ -232,7 +250,7 @@ function registerTodoCommands(program2) {
|
|
|
232
250
|
const todo = await api.post("/todos", payload);
|
|
233
251
|
const globalOpts = program2.opts();
|
|
234
252
|
if (globalOpts.json) printJson(todo);
|
|
235
|
-
else console.log(pc2.green("Added:") + ` ${todo.title} ${pc2.dim(todo.id)}`);
|
|
253
|
+
else console.log(pc2.green("Added:") + ` ${todo.title} ${pc2.dim(shortId(todo.id))}`);
|
|
236
254
|
});
|
|
237
255
|
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
256
|
async (opts) => {
|
|
@@ -273,18 +291,18 @@ function registerTodoCommands(program2) {
|
|
|
273
291
|
}
|
|
274
292
|
);
|
|
275
293
|
program2.command("show <id>").description("Show a todo with its subtasks").action(async (idToken) => {
|
|
276
|
-
const todo = await api.get(`/todos/${resolveTodoId(idToken)}`);
|
|
294
|
+
const todo = await api.get(`/todos/${await resolveTodoId(idToken)}`);
|
|
277
295
|
printTodoDetail(todo, program2.opts());
|
|
278
296
|
});
|
|
279
297
|
program2.command("done <ids...>").description("Complete one or more todos").action(async (idTokens) => {
|
|
280
298
|
for (const token of idTokens) {
|
|
281
|
-
const todo = await api.put(`/todos/${resolveTodoId(token)}`, { completed: true });
|
|
299
|
+
const todo = await api.put(`/todos/${await resolveTodoId(token)}`, { completed: true });
|
|
282
300
|
console.log(`${pc2.green("\u2713")} ${todo.title}`);
|
|
283
301
|
}
|
|
284
302
|
});
|
|
285
303
|
program2.command("undone <ids...>").description("Reopen one or more completed todos").action(async (idTokens) => {
|
|
286
304
|
for (const token of idTokens) {
|
|
287
|
-
const todo = await api.put(`/todos/${resolveTodoId(token)}`, { completed: false });
|
|
305
|
+
const todo = await api.put(`/todos/${await resolveTodoId(token)}`, { completed: false });
|
|
288
306
|
console.log(`\u25CB ${todo.title}`);
|
|
289
307
|
}
|
|
290
308
|
});
|
|
@@ -293,11 +311,11 @@ function registerTodoCommands(program2) {
|
|
|
293
311
|
if (Object.keys(payload).length === 0) {
|
|
294
312
|
throw new Error("Nothing to change \u2014 pass at least one of --title/--notes/--when/--due/--list.");
|
|
295
313
|
}
|
|
296
|
-
const todo = await api.put(`/todos/${resolveTodoId(idToken)}`, payload);
|
|
314
|
+
const todo = await api.put(`/todos/${await resolveTodoId(idToken)}`, payload);
|
|
297
315
|
printTodoDetail(todo, program2.opts());
|
|
298
316
|
});
|
|
299
317
|
program2.command("rm <id>").description("Delete a todo").option("-f, --force", "skip confirmation").action(async (idToken, opts) => {
|
|
300
|
-
const id = resolveTodoId(idToken);
|
|
318
|
+
const id = await resolveTodoId(idToken);
|
|
301
319
|
const todo = await api.get(`/todos/${id}`);
|
|
302
320
|
if (!opts.force && !await confirmOrFail(`Delete "${todo.title}"?`)) return;
|
|
303
321
|
await api.del(`/todos/${id}`);
|
|
@@ -306,13 +324,13 @@ function registerTodoCommands(program2) {
|
|
|
306
324
|
const subtask = program2.command("subtask").description("Manage subtasks of a todo");
|
|
307
325
|
subtask.command("add <id> <title...>").description("Add a subtask").action(async (idToken, titleWords) => {
|
|
308
326
|
const created = await api.post(
|
|
309
|
-
`/todos/${resolveTodoId(idToken)}/subtasks`,
|
|
327
|
+
`/todos/${await resolveTodoId(idToken)}/subtasks`,
|
|
310
328
|
{ title: titleWords.join(" ") }
|
|
311
329
|
);
|
|
312
330
|
console.log(pc2.green("Added subtask:") + ` ${created.title}`);
|
|
313
331
|
});
|
|
314
332
|
const resolveSubtask = async (idToken, indexArg) => {
|
|
315
|
-
const todoId = resolveTodoId(idToken);
|
|
333
|
+
const todoId = await resolveTodoId(idToken);
|
|
316
334
|
const todo = await api.get(`/todos/${todoId}`);
|
|
317
335
|
const subtasks = todo.subtasks ?? [];
|
|
318
336
|
const index = Number(indexArg);
|
|
@@ -335,7 +353,7 @@ function registerTodoCommands(program2) {
|
|
|
335
353
|
|
|
336
354
|
// src/index.ts
|
|
337
355
|
var program = new Command();
|
|
338
|
-
program.name("kaname").description("Kaname from the command line").version(
|
|
356
|
+
program.name("kaname").description("Kaname from the command line").version(package_default.version).option("-j, --json", "output raw JSON (for scripting)").option("--plain", "plain tab-separated output (no color)");
|
|
339
357
|
registerAuthCommands(program);
|
|
340
358
|
registerTodoCommands(program);
|
|
341
359
|
registerListCommands(program);
|
package/dist/mcp.js
CHANGED