@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 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,10 +18,12 @@ 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";
@@ -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
@@ -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.0",
4
4
  "description": "Command-line client for Kaname",
5
5
  "type": "module",
6
6
  "bin": {