@nijaru/tk 0.0.3 → 0.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nijaru/tk",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Minimal task tracker",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -13,11 +13,13 @@ import {
13
13
  green,
14
14
  red,
15
15
  yellow,
16
+ dim,
16
17
  } from "./lib/format";
17
18
  import { findRoot, setWorkingDir } from "./lib/root";
18
19
  import { parseId } from "./types";
19
20
  import type { Status, TaskWithMeta } from "./types";
20
21
  import { BASH_COMPLETION, ZSH_COMPLETION, FISH_COMPLETION } from "./lib/completions";
22
+ import { MAIN_HELP, COMMAND_HELP } from "./lib/help";
21
23
 
22
24
  const VALID_STATUSES: Status[] = ["open", "active", "done"];
23
25
  const PROJECT_PATTERN = /^[a-z][a-z0-9]*$/;
@@ -40,7 +42,7 @@ const TASK_MUTATION_OPTIONS = {
40
42
  function validateProject(name: string): void {
41
43
  if (!PROJECT_PATTERN.test(name)) {
42
44
  throw new Error(
43
- `Invalid project name: ${name}. Use lowercase letters and numbers, starting with a letter.`,
45
+ `Invalid project name: ${name}. Use lowercase letters and numbers, starting with a letter (e.g., 'api', 'web2').`,
44
46
  );
45
47
  }
46
48
  }
@@ -108,7 +110,7 @@ function resolveId(input: string | undefined, context: string): string {
108
110
  const matches = storage.findMatchingIds(input);
109
111
  if (matches.length > 1) {
110
112
  throw new Error(
111
- `Ambiguous ID '${input}' matches ${matches.length} tasks: ${matches.join(", ")}`,
113
+ `Ambiguous ID '${input}' matches ${matches.length} tasks: ${matches.join(", ")}. Use more characters to narrow it down.`,
112
114
  );
113
115
  }
114
116
 
@@ -116,7 +118,7 @@ function resolveId(input: string | undefined, context: string): string {
116
118
  // and let getTask handle the "not found" error consistently.
117
119
  if (parseId(input)) return input;
118
120
 
119
- throw new Error(`Task not found: ${input}`);
121
+ throw new Error(`Task not found: ${input}. Run 'tk ls' to see available tasks.`);
120
122
  }
121
123
 
122
124
  /**
@@ -126,7 +128,7 @@ function resolveId(input: string | undefined, context: string): string {
126
128
  function resolveTask(input: string | undefined, context: string): TaskWithMeta {
127
129
  const id = resolveId(input, context);
128
130
  const result = storage.getTask(id);
129
- if (!result) error(`Task not found: ${id}`);
131
+ if (!result) error(`Task not found: ${id}. Run 'tk ls' to see available tasks.`);
130
132
 
131
133
  outputCleanup(id, result.cleanup);
132
134
  return result.task;
@@ -195,258 +197,12 @@ function error(message: string): never {
195
197
  process.exit(1);
196
198
  }
197
199
 
198
- function showHelp() {
199
- console.log(`tk v${version} - Task tracker for AI agents
200
-
201
- USAGE:
202
- tk <command> [options]
203
-
204
- COMMANDS:
205
- init Initialize .tasks/ directory
206
- add Create task
207
- ls, list List tasks
208
- ready List ready tasks (active/open + unblocked)
209
- show Show task details
210
- start Start working (open -> active)
211
- done Complete task
212
- reopen Reopen task
213
- edit Edit task
214
- log Add log entry
215
- block Add blocker
216
- unblock Remove blocker
217
- rm, remove Delete task
218
- clean Remove old done tasks
219
- check Check for data issues
220
- config Show/set configuration
221
- completions Output shell completions
222
-
223
- GLOBAL OPTIONS:
224
- -C <dir> Run in different directory
225
- --json Output as JSON
226
- -h, --help Show help
227
- -V Show version
228
-
229
- Run 'tk <command> --help' for command-specific options.
230
- `);
200
+ function showHelp(): void {
201
+ console.log(MAIN_HELP);
231
202
  }
232
203
 
233
- function showCommandHelp(cmd: string) {
234
- const helps: Record<string, string> = {
235
- init: `tk init - Initialize .tasks/ directory
236
-
237
- USAGE:
238
- tk init [options]
239
-
240
- OPTIONS:
241
- -P, --project <name> Set default project name
242
- `,
243
- add: `tk add - Create a new task
244
-
245
- USAGE:
246
- tk add <title> [options]
247
-
248
- OPTIONS:
249
- -p, --priority <0-4> Priority (0=none, 1=urgent, 2=high, 3=medium, 4=low)
250
- -P, --project <name> Project prefix for ID
251
- -d, --description <text> Description
252
- -l, --labels <csv> Labels (comma-separated)
253
- -A, --assignees <csv> Assignees (comma-separated, @me for git user)
254
- --parent <id> Parent task ID
255
- --estimate <n> Estimate (user-defined units)
256
- --due <date> Due date (YYYY-MM-DD or +Nh/+Nd/+Nw/+Nm)
257
-
258
- EXAMPLES:
259
- tk add "Fix login bug" -p 1
260
- tk add "New feature" -P api -l bug,urgent
261
- tk add "Sprint task" --due +7d
262
- `,
263
- ls: `tk ls - List tasks
264
-
265
- USAGE:
266
- tk ls [options]
267
-
268
- OPTIONS:
269
- -s, --status <status> Filter by status (open, active, done)
270
- -p, --priority <0-4> Filter by priority
271
- -P, --project <name> Filter by project
272
- -l, --label <label> Filter by label
273
- --assignee <name> Filter by assignee
274
- --parent <id> Filter by parent
275
- --roots Show only root tasks (no parent)
276
- --overdue Show only overdue tasks
277
- -n, --limit <n> Limit results (default: 20)
278
- -a, --all Show all (no limit)
279
-
280
- EXAMPLES:
281
- tk ls -s open -p 1 # Urgent open tasks
282
- tk ls --overdue # Overdue tasks
283
- tk ls -P api --roots # Root tasks in api project
284
- `,
285
- list: `tk list - List tasks (alias for 'ls')
286
-
287
- Run 'tk ls --help' for options.
288
- `,
289
- ready: `tk ready - List ready tasks (active/open + unblocked)
290
-
291
- USAGE:
292
- tk ready
293
-
294
- Shows active or open tasks that are not blocked by any incomplete task.
295
- `,
296
- show: `tk show - Show task details
297
-
298
- USAGE:
299
- tk show <id>
300
-
301
- EXAMPLES:
302
- tk show tk-1
303
- tk show 1 # If unambiguous
304
- `,
305
- start: `tk start - Start working on a task
306
-
307
- USAGE:
308
- tk start <id>
309
-
310
- Changes status from open to active.
311
- `,
312
- done: `tk done - Complete a task
313
-
314
- USAGE:
315
- tk done <id>
316
-
317
- Changes status to done.
318
- `,
319
- reopen: `tk reopen - Reopen a task
320
-
321
- USAGE:
322
- tk reopen <id>
323
-
324
- Changes status back to open.
325
- `,
326
- edit: `tk edit - Edit a task
327
-
328
- USAGE:
329
- tk edit <id> [options]
330
-
331
- OPTIONS:
332
- -t, --title <text> New title
333
- -d, --description <text> New description
334
- -p, --priority <0-4> New priority
335
- -l, --labels <csv> Replace labels (use +tag/-tag to add/remove)
336
- -A, --assignees <csv> Replace assignees
337
- --parent <id> Set parent (use - to clear)
338
- --estimate <n> Set estimate (use - to clear)
339
- --due <date> Set due date (use - to clear)
340
-
341
- EXAMPLES:
342
- tk edit tk-1 -t "New title"
343
- tk edit tk-1 -l +urgent # Add label
344
- tk edit tk-1 --due - # Clear due date
345
- `,
346
- log: `tk log - Add a log entry to a task
347
-
348
- USAGE:
349
- tk log <id> "<message>"
350
-
351
- Message must be quoted.
352
-
353
- EXAMPLES:
354
- tk log tk-1 "Started implementation"
355
- tk log tk-1 "Blocked on API changes"
356
- `,
357
- block: `tk block - Add a blocker dependency
358
-
359
- USAGE:
360
- tk block <task> <blocker>
361
-
362
- The first task becomes blocked by the second.
363
-
364
- EXAMPLES:
365
- tk block tk-2 tk-1 # tk-2 is blocked by tk-1
366
- `,
367
- unblock: `tk unblock - Remove a blocker dependency
368
-
369
- USAGE:
370
- tk unblock <task> <blocker>
371
-
372
- EXAMPLES:
373
- tk unblock tk-2 tk-1
374
- `,
375
- rm: `tk rm - Delete a task
376
-
377
- USAGE:
378
- tk rm <id>
379
-
380
- EXAMPLES:
381
- tk rm tk-1
382
- `,
383
- remove: `tk remove - Delete a task (alias for 'rm')
384
-
385
- USAGE:
386
- tk remove <id>
387
- `,
388
- clean: `tk clean - Remove old completed tasks
389
-
390
- USAGE:
391
- tk clean [options]
392
-
393
- OPTIONS:
394
- --older-than <days> Age threshold in days (default: from config, 14)
395
- -f, --force Remove all done tasks (ignores age and disabled state)
396
-
397
- EXAMPLES:
398
- tk clean # Remove done tasks older than config.clean_after days
399
- tk clean --older-than 30 # Remove done tasks older than 30 days
400
- tk clean --force # Remove all done tasks regardless of age
401
- `,
402
- check: `tk check - Check for data issues
403
-
404
- USAGE:
405
- tk check
406
-
407
- Scans all tasks for issues. Auto-fixable issues (orphaned references, ID
408
- mismatches) are fixed automatically. Unfixable issues (corrupted JSON) are
409
- reported for manual intervention.
410
-
411
- Note: Auto-fixing also happens during normal task operations (show, done,
412
- edit, etc.) - this command is for bulk cleanup or diagnostics.
413
-
414
- EXAMPLES:
415
- tk check # Scan and fix all tasks
416
- `,
417
- config: `tk config - Show or set configuration
418
-
419
- USAGE:
420
- tk config Show all config
421
- tk config project Show default project
422
- tk config project <name> Set default project
423
- tk config project <new> --rename <old> Rename project (old-* → new-*)
424
- tk config alias List aliases
425
- tk config alias <name> <path> Add alias
426
- tk config alias --rm <name> Remove alias
427
-
428
- EXAMPLES:
429
- tk config project api
430
- tk config project lsmvec --rename cloudlsmvec
431
- tk config alias web src/web
432
- `,
433
- completions: `tk completions - Output shell completions
434
-
435
- USAGE:
436
- tk completions <shell>
437
-
438
- SHELLS:
439
- bash Bash completion script
440
- zsh Zsh completion script
441
- fish Fish completion script
442
-
443
- EXAMPLES:
444
- eval "$(tk completions bash)" # Add to ~/.bashrc
445
- eval "$(tk completions zsh)" # Add to ~/.zshrc
446
- `,
447
- };
448
-
449
- const help = helps[cmd];
204
+ function showCommandHelp(cmd: string): void {
205
+ const help = COMMAND_HELP[cmd];
450
206
  if (help) {
451
207
  console.log(help);
452
208
  } else {
@@ -498,7 +254,7 @@ function main() {
498
254
  if (info.exists) {
499
255
  output(
500
256
  { path: info.tasksDir, created: false },
501
- green(`Already initialized: ${info.tasksDir}`),
257
+ dim(`Already initialized: ${info.tasksDir}`),
502
258
  );
503
259
  } else {
504
260
  const path = storage.initTasks(values.project);
@@ -525,7 +281,8 @@ function main() {
525
281
  let parentId: string | undefined;
526
282
  if (values.parent) {
527
283
  const resolved = storage.resolveId(values.parent);
528
- if (!resolved) error(`Parent task not found: ${values.parent}`);
284
+ if (!resolved)
285
+ error(`Parent task not found: ${values.parent}. Run 'tk ls' to see available tasks.`);
529
286
  parentId = resolved;
530
287
  const parentResult = storage.validateParent(parentId);
531
288
  if (!parentResult.ok) error(parentResult.error!);
@@ -571,7 +328,8 @@ function main() {
571
328
  let parentFilter: string | undefined;
572
329
  if (values.parent) {
573
330
  const resolved = storage.resolveId(values.parent);
574
- if (!resolved) error(`Parent task not found: ${values.parent}`);
331
+ if (!resolved)
332
+ error(`Parent task not found: ${values.parent}. Run 'tk ls' to see available tasks.`);
575
333
  parentFilter = resolved;
576
334
  }
577
335
 
@@ -592,7 +350,10 @@ function main() {
592
350
 
593
351
  case "ready": {
594
352
  const list = storage.listReadyTasks();
595
- output(list, formatTaskList(list));
353
+ output(
354
+ list,
355
+ formatTaskList(list, undefined, "No ready tasks. All tasks are either done or blocked."),
356
+ );
596
357
  break;
597
358
  }
598
359
 
@@ -660,7 +421,8 @@ function main() {
660
421
  resolvedParent = null;
661
422
  } else if (values.parent) {
662
423
  const resolved = storage.resolveId(values.parent);
663
- if (!resolved) error(`Parent task not found: ${values.parent}`);
424
+ if (!resolved)
425
+ error(`Parent task not found: ${values.parent}. Run 'tk ls' to see available tasks.`);
664
426
  resolvedParent = resolved;
665
427
  const parentResult = storage.validateParent(resolvedParent, task.id);
666
428
  if (!parentResult.ok) error(parentResult.error!);
@@ -696,10 +458,13 @@ function main() {
696
458
  }
697
459
 
698
460
  case "block": {
699
- if (!args[0] || !args[1]) error("Usage: tk block <task> <blocker>");
461
+ if (!args[0] || !args[1])
462
+ error(
463
+ "Two IDs required: tk block <task> <blocker>. The first task becomes blocked by the second.",
464
+ );
700
465
  const taskId = resolveId(args[0], "block");
701
466
  const blockerId = resolveId(args[1], "block");
702
- if (taskId === blockerId) error("Task cannot block itself");
467
+ if (taskId === blockerId) error("Task cannot block itself.");
703
468
  const result = storage.addBlock(taskId, blockerId);
704
469
  if (!result.ok) error(result.error!);
705
470
  output(
@@ -710,7 +475,7 @@ function main() {
710
475
  }
711
476
 
712
477
  case "unblock": {
713
- if (!args[0] || !args[1]) error("Usage: tk unblock <task> <blocker>");
478
+ if (!args[0] || !args[1]) error("Two IDs required: tk unblock <task> <blocker>.");
714
479
  const taskId = resolveId(args[0], "unblock");
715
480
  const blockerId = resolveId(args[1], "unblock");
716
481
  const removed = storage.removeBlock(taskId, blockerId);
@@ -726,7 +491,7 @@ function main() {
726
491
  case "remove": {
727
492
  const id = resolveId(args[0], "rm");
728
493
  const deleted = storage.deleteTask(id);
729
- if (!deleted) error(`Task not found: ${id}`);
494
+ if (!deleted) error(`Task not found: ${id}. Run 'tk ls' to see available tasks.`);
730
495
  output({ id, deleted: true }, green(`Deleted: ${id}`));
731
496
  break;
732
497
  }
@@ -868,7 +633,7 @@ function main() {
868
633
  const alias = positionals[0];
869
634
  const path = positionals[1];
870
635
  if (!alias || !path || !alias.trim()) {
871
- error("Alias name and path are required");
636
+ error("Alias name and path required: tk config alias <name> <path>");
872
637
  }
873
638
  const updated = storage.setAlias(alias, path);
874
639
  output(updated, green(`Added alias: ${alias} → ${path}`));
@@ -876,7 +641,10 @@ function main() {
876
641
  // List aliases
877
642
  const aliases = config.aliases;
878
643
  if (Object.keys(aliases).length === 0) {
879
- output({ aliases: {} }, "No aliases defined.");
644
+ output(
645
+ { aliases: {} },
646
+ "No aliases defined. Add one with: tk config alias <name> <path>",
647
+ );
880
648
  } else {
881
649
  const lines = Object.entries(aliases)
882
650
  .map(([a, p]) => `${a} → ${p}`)
@@ -887,7 +655,7 @@ function main() {
887
655
  break;
888
656
  }
889
657
  default:
890
- error(`Unknown config command: ${subcommand}`);
658
+ error(`Unknown config command: ${subcommand}. Valid: project, alias.`);
891
659
  }
892
660
  break;
893
661
  }
@@ -905,7 +673,7 @@ function main() {
905
673
  console.log(FISH_COMPLETION);
906
674
  break;
907
675
  default:
908
- error("Shell required: tk completions <bash|zsh|fish>");
676
+ error("Shell required: tk completions <bash|zsh|fish>. Add to your shell's rc file.");
909
677
  }
910
678
  break;
911
679
  }
package/src/db/storage.ts CHANGED
@@ -158,7 +158,7 @@ export function renameProject(oldProject: string, newProject: string): RenameRes
158
158
  // Find tasks to rename
159
159
  const toRename = allTasks.filter((t) => t.project === oldProject);
160
160
  if (toRename.length === 0) {
161
- throw new Error(`No tasks found with project "${oldProject}"`);
161
+ throw new Error(`No tasks found with project "${oldProject}". Run 'tk ls' to see projects.`);
162
162
  }
163
163
 
164
164
  // Check for collisions
@@ -166,7 +166,7 @@ export function renameProject(oldProject: string, newProject: string): RenameRes
166
166
  for (const task of toRename) {
167
167
  const newId = `${newProject}-${task.ref}`;
168
168
  if (existingIds.has(newId)) {
169
- throw new Error(`Cannot rename: "${newId}" already exists`);
169
+ throw new Error(`Cannot rename: "${newId}" already exists. Choose a different project name.`);
170
170
  }
171
171
  }
172
172
 
@@ -92,13 +92,13 @@ describe("formatTaskRow", () => {
92
92
  expect(parts.length).toBe(4); // ID | PRIO | STATUS | TITLE
93
93
  });
94
94
 
95
- test("truncates long project names to 6 chars", () => {
95
+ test("truncates long project names with ellipsis", () => {
96
96
  const longProjectTask: TaskWithMeta = {
97
97
  ...task,
98
98
  id: "mylongproject-a1b2",
99
99
  };
100
100
  const result = formatTaskRow(longProjectTask, false);
101
- expect(result).toContain("mylong-a1b2");
101
+ expect(result).toContain("mylon…-a1b2");
102
102
  expect(result).not.toContain("mylongproject");
103
103
  });
104
104
 
@@ -130,8 +130,12 @@ describe("formatTaskRow", () => {
130
130
  });
131
131
 
132
132
  describe("formatTaskList", () => {
133
- test("returns message for empty list", () => {
134
- expect(formatTaskList([])).toBe("No tasks found.");
133
+ test("returns helpful message for empty list", () => {
134
+ expect(formatTaskList([])).toBe("No tasks found. Run 'tk add \"title\"' to create one.");
135
+ });
136
+
137
+ test("allows custom empty hint", () => {
138
+ expect(formatTaskList([], undefined, "Custom hint")).toBe("Custom hint");
135
139
  });
136
140
 
137
141
  test("includes header and divider", () => {
package/src/lib/format.ts CHANGED
@@ -43,22 +43,28 @@ export function dim(msg: string): string {
43
43
  return shouldUseColor() ? `${DIM}${msg}${RESET}` : msg;
44
44
  }
45
45
 
46
- function formatId(id: string): string {
47
- // Truncate project prefix to 6 chars, keep full 4-char ref
48
- // "myproject-a1b2" -> "myproj-a1b2"
46
+ function truncate(text: string, maxLen: number): string {
47
+ return text.length <= maxLen ? text : text.slice(0, maxLen - 1) + "…";
48
+ }
49
+
50
+ function formatId(id: string, maxLen = 11): string {
51
+ // Truncate project prefix to fit maxLen, keep full 4-char ref
52
+ // "myproject-a1b2" -> "mypr…-a1b2"
49
53
  const dash = id.lastIndexOf("-");
50
- if (dash === -1) return id.slice(0, 11);
54
+ if (dash === -1) return truncate(id, maxLen);
51
55
  const project = id.slice(0, dash);
52
56
  const ref = id.slice(dash + 1);
53
- const truncatedProject = project.length > 6 ? project.slice(0, 6) : project;
54
- return `${truncatedProject}-${ref}`;
57
+ const maxProjectLen = maxLen - ref.length - 1; // -1 for dash
58
+ return project.length > maxProjectLen
59
+ ? `${truncate(project, maxProjectLen)}-${ref}`
60
+ : `${project}-${ref}`;
55
61
  }
56
62
 
57
63
  export function formatTaskRow(task: TaskWithMeta, useColor?: boolean): string {
58
64
  const color = useColor ?? shouldUseColor();
59
65
  const id = formatId(task.id).padEnd(11);
60
66
  const priority = formatPriority(task.priority).padEnd(4);
61
- const title = task.title.slice(0, 50);
67
+ const title = truncate(task.title, 50);
62
68
  const isOverdue = task.is_overdue;
63
69
 
64
70
  let statusText: string = task.status;
@@ -78,8 +84,14 @@ export function formatTaskRow(task: TaskWithMeta, useColor?: boolean): string {
78
84
  return `${id} | ${priority} | ${status} | ${title}${overdueMarker}`;
79
85
  }
80
86
 
81
- export function formatTaskList(tasks: TaskWithMeta[], useColor?: boolean): string {
82
- if (tasks.length === 0) return "No tasks found.";
87
+ export function formatTaskList(
88
+ tasks: TaskWithMeta[],
89
+ useColor?: boolean,
90
+ emptyHint?: string,
91
+ ): string {
92
+ if (tasks.length === 0) {
93
+ return emptyHint ?? "No tasks found. Run 'tk add \"title\"' to create one.";
94
+ }
83
95
  const color = useColor ?? shouldUseColor();
84
96
 
85
97
  const header = "ID | PRIO | STATUS | TITLE";
@@ -0,0 +1,249 @@
1
+ import { version } from "../../package.json";
2
+
3
+ export const MAIN_HELP = `tk v${version} - Task tracker for AI agents
4
+
5
+ USAGE:
6
+ tk <command> [options]
7
+
8
+ COMMANDS:
9
+ init Initialize .tasks/ directory
10
+ add Create task
11
+ ls, list List tasks
12
+ ready List ready tasks (active/open + unblocked)
13
+ show Show task details
14
+ start Start working (open -> active)
15
+ done Complete task
16
+ reopen Reopen task
17
+ edit Edit task
18
+ log Add log entry
19
+ block Add blocker
20
+ unblock Remove blocker
21
+ rm, remove Delete task
22
+ clean Remove old done tasks
23
+ check Check for data issues
24
+ config Show/set configuration
25
+ completions Output shell completions
26
+
27
+ GLOBAL OPTIONS:
28
+ -C <dir> Run in different directory
29
+ --json Output as JSON
30
+ -h, --help Show help
31
+ -V Show version
32
+
33
+ Run 'tk <command> --help' for command-specific options.
34
+ `;
35
+
36
+ export const COMMAND_HELP: Record<string, string> = {
37
+ init: `tk init - Initialize .tasks/ directory
38
+
39
+ USAGE:
40
+ tk init [options]
41
+
42
+ OPTIONS:
43
+ -P, --project <name> Set default project name
44
+ `,
45
+ add: `tk add - Create a new task
46
+
47
+ USAGE:
48
+ tk add <title> [options]
49
+
50
+ OPTIONS:
51
+ -p, --priority <0-4> Priority (0=none, 1=urgent, 2=high, 3=medium, 4=low)
52
+ -P, --project <name> Project prefix for ID
53
+ -d, --description <text> Description
54
+ -l, --labels <csv> Labels (comma-separated)
55
+ -A, --assignees <csv> Assignees (comma-separated, @me for git user)
56
+ --parent <id> Parent task ID
57
+ --estimate <n> Estimate (user-defined units)
58
+ --due <date> Due date (YYYY-MM-DD or +Nh/+Nd/+Nw/+Nm)
59
+
60
+ EXAMPLES:
61
+ tk add "Fix login bug" -p 1
62
+ tk add "New feature" -P api -l bug,urgent
63
+ tk add "Sprint task" --due +7d
64
+ `,
65
+ ls: `tk ls - List tasks
66
+
67
+ USAGE:
68
+ tk ls [options]
69
+
70
+ OPTIONS:
71
+ -s, --status <status> Filter by status (open, active, done)
72
+ -p, --priority <0-4> Filter by priority
73
+ -P, --project <name> Filter by project
74
+ -l, --label <label> Filter by label
75
+ --assignee <name> Filter by assignee
76
+ --parent <id> Filter by parent
77
+ --roots Show only root tasks (no parent)
78
+ --overdue Show only overdue tasks
79
+ -n, --limit <n> Limit results (default: 20)
80
+ -a, --all Show all (no limit)
81
+
82
+ EXAMPLES:
83
+ tk ls -s open -p 1 # Urgent open tasks
84
+ tk ls --overdue # Overdue tasks
85
+ tk ls -P api --roots # Root tasks in api project
86
+ `,
87
+ list: `tk list - List tasks (alias for 'ls')
88
+
89
+ Run 'tk ls --help' for options.
90
+ `,
91
+ ready: `tk ready - List ready tasks (active/open + unblocked)
92
+
93
+ USAGE:
94
+ tk ready
95
+
96
+ Shows active or open tasks that are not blocked by any incomplete task.
97
+ `,
98
+ show: `tk show - Show task details
99
+
100
+ USAGE:
101
+ tk show <id>
102
+
103
+ EXAMPLES:
104
+ tk show tk-1
105
+ tk show 1 # If unambiguous
106
+ `,
107
+ start: `tk start - Start working on a task
108
+
109
+ USAGE:
110
+ tk start <id>
111
+
112
+ Changes status from open to active.
113
+ `,
114
+ done: `tk done - Complete a task
115
+
116
+ USAGE:
117
+ tk done <id>
118
+
119
+ Changes status to done.
120
+ `,
121
+ reopen: `tk reopen - Reopen a task
122
+
123
+ USAGE:
124
+ tk reopen <id>
125
+
126
+ Changes status back to open.
127
+ `,
128
+ edit: `tk edit - Edit a task
129
+
130
+ USAGE:
131
+ tk edit <id> [options]
132
+
133
+ OPTIONS:
134
+ -t, --title <text> New title
135
+ -d, --description <text> New description
136
+ -p, --priority <0-4> New priority
137
+ -l, --labels <csv> Replace labels (use +tag/-tag to add/remove)
138
+ -A, --assignees <csv> Replace assignees
139
+ --parent <id> Set parent (use - to clear)
140
+ --estimate <n> Set estimate (use - to clear)
141
+ --due <date> Set due date (use - to clear)
142
+
143
+ EXAMPLES:
144
+ tk edit tk-1 -t "New title"
145
+ tk edit tk-1 -l +urgent # Add label
146
+ tk edit tk-1 --due - # Clear due date
147
+ `,
148
+ log: `tk log - Add a log entry to a task
149
+
150
+ USAGE:
151
+ tk log <id> "<message>"
152
+
153
+ Message must be quoted.
154
+
155
+ EXAMPLES:
156
+ tk log tk-1 "Started implementation"
157
+ tk log tk-1 "Blocked on API changes"
158
+ `,
159
+ block: `tk block - Add a blocker dependency
160
+
161
+ USAGE:
162
+ tk block <task> <blocker>
163
+
164
+ The first task becomes blocked by the second.
165
+
166
+ EXAMPLES:
167
+ tk block tk-2 tk-1 # tk-2 is blocked by tk-1
168
+ `,
169
+ unblock: `tk unblock - Remove a blocker dependency
170
+
171
+ USAGE:
172
+ tk unblock <task> <blocker>
173
+
174
+ EXAMPLES:
175
+ tk unblock tk-2 tk-1
176
+ `,
177
+ rm: `tk rm - Delete a task
178
+
179
+ USAGE:
180
+ tk rm <id>
181
+
182
+ EXAMPLES:
183
+ tk rm tk-1
184
+ `,
185
+ remove: `tk remove - Delete a task (alias for 'rm')
186
+
187
+ USAGE:
188
+ tk remove <id>
189
+ `,
190
+ clean: `tk clean - Remove old completed tasks
191
+
192
+ USAGE:
193
+ tk clean [options]
194
+
195
+ OPTIONS:
196
+ --older-than <days> Age threshold in days (default: from config, 14)
197
+ -f, --force Remove all done tasks (ignores age and disabled state)
198
+
199
+ EXAMPLES:
200
+ tk clean # Remove done tasks older than config.clean_after days
201
+ tk clean --older-than 30 # Remove done tasks older than 30 days
202
+ tk clean --force # Remove all done tasks regardless of age
203
+ `,
204
+ check: `tk check - Check for data issues
205
+
206
+ USAGE:
207
+ tk check
208
+
209
+ Scans all tasks for issues. Auto-fixable issues (orphaned references, ID
210
+ mismatches) are fixed automatically. Unfixable issues (corrupted JSON) are
211
+ reported for manual intervention.
212
+
213
+ Note: Auto-fixing also happens during normal task operations (show, done,
214
+ edit, etc.) - this command is for bulk cleanup or diagnostics.
215
+
216
+ EXAMPLES:
217
+ tk check # Scan and fix all tasks
218
+ `,
219
+ config: `tk config - Show or set configuration
220
+
221
+ USAGE:
222
+ tk config Show all config
223
+ tk config project Show default project
224
+ tk config project <name> Set default project
225
+ tk config project <new> --rename <old> Rename project (old-* → new-*)
226
+ tk config alias List aliases
227
+ tk config alias <name> <path> Add alias
228
+ tk config alias --rm <name> Remove alias
229
+
230
+ EXAMPLES:
231
+ tk config project api
232
+ tk config project lsmvec --rename cloudlsmvec
233
+ tk config alias web src/web
234
+ `,
235
+ completions: `tk completions - Output shell completions
236
+
237
+ USAGE:
238
+ tk completions <shell>
239
+
240
+ SHELLS:
241
+ bash Bash completion script
242
+ zsh Zsh completion script
243
+ fish Fish completion script
244
+
245
+ EXAMPLES:
246
+ eval "$(tk completions bash)" # Add to ~/.bashrc
247
+ eval "$(tk completions zsh)" # Add to ~/.zshrc
248
+ `,
249
+ };