@zinn-dev/cli 0.10.0 → 0.12.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
@@ -124,7 +124,7 @@ Use `task relation create --help` to discover types.
124
124
 
125
125
  ## Edit tasks
126
126
 
127
- Edit a task's title, description, or labels:
127
+ Edit a task's title, description, priority, or labels:
128
128
 
129
129
  ```sh
130
130
  zinn task edit MDR-1 --title "Meet the quarterly refinement quota"
@@ -196,3 +196,15 @@ zinn task move --help
196
196
  The short `-h` form works in the same positions.
197
197
 
198
198
  Running `zinn` without a command shows help
199
+
200
+ ## Task priority
201
+
202
+ Priority is optional: `low`, `medium`, `high`, or `urgent`. New tasks have no assigned priority by default.
203
+
204
+ ```sh
205
+ zinn task create MDR --title "Review the numbers" --priority high
206
+ zinn task edit MDR-1 --priority urgent
207
+ zinn task edit MDR-1 --priority none
208
+ ```
209
+
210
+ Use `none` to clear priority. Omitting `--priority` on edits preserves it. Assigned priorities appear in task list and view output; board ordering remains unchanged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zinn-dev/cli",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "A kanban workflow in the terminal.",
5
5
  "license": "MIT",
6
6
  "bugs": {
@@ -42,6 +42,6 @@
42
42
  "access": "public"
43
43
  },
44
44
  "dependencies": {
45
- "@zinn-dev/core": "0.10.0"
45
+ "@zinn-dev/core": "0.11.0"
46
46
  }
47
47
  }
@@ -0,0 +1 @@
1
+ export const TASK_DESCRIPTION_PREVIEW_LENGTH = 80;
@@ -5,7 +5,24 @@ import { task, project, column } from "@zinn-dev/core";
5
5
 
6
6
  import { quit, validatePositionalCount, validateUniqueOptions } from "../lib";
7
7
  import { formatRelations, relationRouter } from "./relation-router";
8
+ import { TASK_DESCRIPTION_PREVIEW_LENGTH } from "../constant";
8
9
 
10
+ function formatTaskDescriptionPreview(description: string | null) {
11
+ if (description == null) {
12
+ return "";
13
+ }
14
+
15
+ const normalizedDescription = description.replace(/\s+/g, " ").trim();
16
+
17
+ if (normalizedDescription.length <= TASK_DESCRIPTION_PREVIEW_LENGTH) {
18
+ return normalizedDescription;
19
+ }
20
+
21
+ return `${normalizedDescription.slice(0, TASK_DESCRIPTION_PREVIEW_LENGTH - 1).trimEnd()}…`;
22
+ }
23
+
24
+ // TODO: a CLI row formatter that accepts text columns and joins them with " | " would be useful
25
+ // Reuse it across task list/view and other command output in a separate change.
9
26
  function formatTask(
10
27
  taskMatch: ReturnType<typeof task.getByKey>,
11
28
  options: { showsStatus?: boolean } = {},
@@ -17,11 +34,12 @@ function formatTask(
17
34
  : "";
18
35
  const description = taskMatch.description == null ? "" : ` | ${taskMatch.description}`;
19
36
 
37
+ const priority = taskMatch.priority == null ? "" : ` | Priority: ${taskMatch.priority}`;
20
38
  const labels = task.label.getAll(taskKey);
21
39
  const labelText =
22
40
  labels.length === 0 ? "" : ` | Labels: ${labels.map((label) => label.name).join(", ")}`;
23
41
 
24
- return `${taskKey}${status} | ${taskColumn.name} | ${taskMatch.title}${description}${labelText}`;
42
+ return `${taskKey}${status} | ${taskColumn.name} | ${taskMatch.title}${description}${priority}${labelText}`;
25
43
  }
26
44
 
27
45
  export const taskRouter = {
@@ -32,6 +50,7 @@ export const taskRouter = {
32
50
  options: {
33
51
  title: { type: "string" },
34
52
  description: { type: "string" },
53
+ priority: { type: "string" },
35
54
  "add-label": { type: "string", multiple: true },
36
55
  "remove-label": { type: "string", multiple: true },
37
56
  },
@@ -53,12 +72,13 @@ export const taskRouter = {
53
72
  const hasEditFlags =
54
73
  values.title !== undefined ||
55
74
  values.description !== undefined ||
75
+ values.priority !== undefined ||
56
76
  values["add-label"] !== undefined ||
57
77
  values["remove-label"] !== undefined;
58
78
 
59
79
  if (!hasEditFlags) {
60
80
  quit(
61
- "Provide at least one edit flag: --title, --description, --add-label, or --remove-label",
81
+ "Provide at least one edit flag: --title, --description, --priority, --add-label, or --remove-label",
62
82
  );
63
83
  }
64
84
 
@@ -66,15 +86,17 @@ export const taskRouter = {
66
86
  taskKey,
67
87
  title: values.title,
68
88
  description: values.description,
89
+ priority: parsePriority(values.priority),
69
90
  addLabelNames: values["add-label"],
70
91
  removeLabelNames: values["remove-label"],
71
92
  });
72
93
 
73
94
  console.info(formatTask(editedTask));
74
95
  },
75
- help: `Usage: zinn task edit <task-key> [--title <text>] [--description <text>] [--add-label <name>] [--remove-label <name>]
96
+ help: `Usage: zinn task edit <task-key> [--title <text>] [--description <text>] [--priority <low|medium|high|urgent|none>] [--add-label <name>] [--remove-label <name>]
76
97
 
77
98
  Change the supplied fields and preserve everything else.
99
+ Use --priority none to clear priority.
78
100
  Provide at least one edit flag. Use --description "" for an empty description.
79
101
 
80
102
  Repeat --add-label or --remove-label for multiple existing labels.
@@ -95,6 +117,7 @@ Example: zinn task edit MDR-1 --title "Meet the quarterly refinement quota"`,
95
117
  options: {
96
118
  title: { type: "string" },
97
119
  description: { type: "string" },
120
+ priority: { type: "string" },
98
121
  label: { type: "string", multiple: true },
99
122
  },
100
123
  allowPositionals: true,
@@ -105,7 +128,11 @@ Example: zinn task edit MDR-1 --title "Meet the quarterly refinement quota"`,
105
128
  if (projectKey == null) {
106
129
  quit("A task needs to belong to a project");
107
130
  }
108
- validatePositionalCount(positionals, 1, "Only one project key can be specified; use --title and --description");
131
+ validatePositionalCount(
132
+ positionals,
133
+ 1,
134
+ "Only one project key can be specified; use --title and --description",
135
+ );
109
136
  if (values.title === undefined) {
110
137
  quit("A task needs at least a title: use --title");
111
138
  }
@@ -118,13 +145,15 @@ Example: zinn task edit MDR-1 --title "Meet the quarterly refinement quota"`,
118
145
  project_id: projectMatch.id,
119
146
  title: values.title,
120
147
  description: values.description ?? null,
148
+ priority: parsePriority(values.priority),
121
149
  labelNames: values.label,
122
150
  });
123
151
  console.info(formatTask(createdTask));
124
152
  },
125
- help: `Usage: zinn task create <project-key> --title <text> [--description <text>] [--label <name>]
153
+ help: `Usage: zinn task create <project-key> --title <text> [--description <text>] [--priority <low|medium|high|urgent|none>] [--label <name>]
126
154
 
127
155
  Create a task in the project's first column.
156
+ Priority defaults to none (unassigned).
128
157
  Repeat --label to attach multiple existing labels. Unknown labels are errors.
129
158
  Titles cannot be blank. Use --title="--example" for text beginning with a dash.
130
159
 
@@ -163,6 +192,7 @@ Example: zinn task create MDR --title "Refine the numbers" --description "Meet t
163
192
  status: t.archived_at == null ? "Active" : "Archived",
164
193
  title: t.title,
165
194
  description: t.description,
195
+ priority: t.priority,
166
196
  column: taskColumn.name,
167
197
  };
168
198
  });
@@ -181,8 +211,10 @@ Example: zinn task create MDR --title "Refine the numbers" --description "Meet t
181
211
  .map((t) => {
182
212
  // TODO: console output formatting for standardizied output
183
213
  const status = showsAll ? ` | ${t.status.padEnd(longestStatusLength)}` : "";
184
- const description = t.description == null ? "" : ` | ${t.description}`;
185
- return `${t.id.padEnd(longestIdLength)}${status} | ${t.column} | ${t.title}${description}`;
214
+ const descriptionPreview = formatTaskDescriptionPreview(t.description);
215
+ const description = t.description == null ? "" : ` | ${descriptionPreview}`;
216
+ const priority = t.priority == null ? "" : ` | Priority: ${t.priority}`;
217
+ return `${t.id.padEnd(longestIdLength)}${status} | ${t.column} | ${t.title}${description}${priority}`;
186
218
  })
187
219
  .join("\n"),
188
220
  );
@@ -212,7 +244,7 @@ Example: zinn task list MDR --all`,
212
244
  },
213
245
  help: `Usage: zinn task view <task-key>
214
246
 
215
- Show a task with its current column, description, labels, and relations.
247
+ Show a task with its current column, description, priority, labels, and relations.
216
248
 
217
249
  Example: zinn task view MDR-1`,
218
250
  },
@@ -388,3 +420,19 @@ function parseTaskLabelArgs(args: string[]) {
388
420
  }
389
421
  return { taskKey: positionals[0]!, labelNames: positionals.slice(1) };
390
422
  }
423
+
424
+ function parsePriority(value: string | undefined) {
425
+ switch (value) {
426
+ case undefined:
427
+ return undefined;
428
+ case "none":
429
+ return null;
430
+ case "low":
431
+ case "medium":
432
+ case "high":
433
+ case "urgent":
434
+ return value;
435
+ default:
436
+ quit("Priority must be low, medium, high, urgent, or none");
437
+ }
438
+ }