@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 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 1 # #-indexes refer to the last `kaname ls` output
65
- kaname done 1 2 # complete several at once
66
- kaname undone 2
67
- kaname edit 1 --when tomorrow --list none
68
- kaname rm 1 # asks for confirmation (or --force)
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 1 whole milk
71
- kaname subtask done 1 1
72
- kaname subtask rm 1 2
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
- } from "./chunk-AEAYCJI7.js";
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("0.1.0").option("-j, --json", "output raw JSON (for scripting)").option("--plain", "plain tab-separated output (no color)");
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
@@ -9,7 +9,7 @@ import {
9
9
  localToday,
10
10
  parseDateArg,
11
11
  resolveList
12
- } from "./chunk-AEAYCJI7.js";
12
+ } from "./chunk-FR675V3R.js";
13
13
 
14
14
  // src/mcp.ts
15
15
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaname-tasks/kaname",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Command-line client for Kaname",
5
5
  "type": "module",
6
6
  "bin": {