@zinn-dev/cli 0.7.0 → 0.9.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 +3 -0
- package/package.json +2 -2
- package/src/routes/relation-router.ts +61 -28
- package/src/routes/task-router.ts +2 -5
package/README.md
CHANGED
|
@@ -84,6 +84,9 @@ Tasks from separate projects can be related to each other. Tasks can also be rel
|
|
|
84
84
|
|
|
85
85
|
`task view` a task's existing relations.
|
|
86
86
|
|
|
87
|
+
Remove a relation with `zinn task relation delete MDR-1 MDR-2 --type dependency`, using its original direction for `dependency` and `duplicate`. For `related`, order does not matter.
|
|
88
|
+
Note that a deletion happens _without_ confirmation.
|
|
89
|
+
|
|
87
90
|
Use `task relation create --help` to discover types.
|
|
88
91
|
|
|
89
92
|
## Edit tasks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zinn-dev/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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.
|
|
45
|
+
"@zinn-dev/core": "0.9.0"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RouteDef } from "../types";
|
|
2
2
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
-
import {
|
|
4
|
+
import { relation, relationCreateSchema, task } from "@zinn-dev/core";
|
|
5
5
|
import { quit } from "../lib";
|
|
6
6
|
|
|
7
7
|
export function formatRelations(taskKey: string) {
|
|
@@ -38,46 +38,79 @@ export function formatRelations(taskKey: string) {
|
|
|
38
38
|
.join("\n");
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function parseRelationArgs(args: string[]) {
|
|
42
|
+
const { values, positionals, tokens } = parseArgs({
|
|
43
|
+
args,
|
|
44
|
+
options: { type: { type: "string" } },
|
|
45
|
+
allowPositionals: true,
|
|
46
|
+
strict: true,
|
|
47
|
+
tokens: true,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (positionals.length !== 2) {
|
|
51
|
+
quit("Exactly two task keys are required: source, target");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const optionCount = tokens.filter((token) => token.kind === "option").length;
|
|
55
|
+
|
|
56
|
+
if (optionCount > 1) {
|
|
57
|
+
// TODO: maybe a reusable option validator can be given expected option keys, and it can spit out unsupported options as well as help for what options are supported
|
|
58
|
+
quit('Option "--type" can only be specified once');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const typeResult = relationCreateSchema.shape.relation_type.safeParse(values.type);
|
|
62
|
+
|
|
63
|
+
if (!typeResult.success) {
|
|
64
|
+
quit("--type must be one of these: related, dependency, duplicate");
|
|
65
|
+
}
|
|
66
|
+
const sourceTaskKey = positionals[0]!;
|
|
67
|
+
const targetTaskKey = positionals[1]!;
|
|
68
|
+
|
|
69
|
+
return { sourceTaskKey, targetTaskKey, relation_type: typeResult.data };
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
export const relationRouter = {
|
|
42
|
-
|
|
73
|
+
delete: {
|
|
43
74
|
run: (args) => {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
options: { type: { type: "string" } },
|
|
47
|
-
allowPositionals: true,
|
|
48
|
-
strict: true,
|
|
49
|
-
tokens: true,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (positionals.length !== 2) {
|
|
53
|
-
quit("Provide exactly two task keys: source and target");
|
|
54
|
-
}
|
|
75
|
+
const props = parseRelationArgs(args);
|
|
76
|
+
relation.delete(props);
|
|
55
77
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
78
|
+
const source = task.getByKey(props.sourceTaskKey);
|
|
79
|
+
const target = task.getByKey(props.targetTaskKey);
|
|
80
|
+
console.info(`Deleted ${props.relation_type} relation from ${source.key} to ${target.key}`);
|
|
81
|
+
},
|
|
82
|
+
help: `Usage: zinn task relation delete <source-task-key> <target-task-key> --type <type>
|
|
59
83
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
const sourceTaskKey = positionals[0]!;
|
|
65
|
-
const targetTaskKey = positionals[1]!;
|
|
84
|
+
Remove only the specified relation. No confirmation is required.
|
|
85
|
+
|
|
86
|
+
Types: related, dependency, duplicate.
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
For related, either task order works.
|
|
89
|
+
For dependency and duplicate, use the creation direction:
|
|
90
|
+
source depends on target, or source is a duplicate of target.
|
|
91
|
+
|
|
92
|
+
Archived and cross-project tasks are supported.
|
|
93
|
+
A missing relation is an error.
|
|
94
|
+
|
|
95
|
+
Example: zinn task relation delete APP-1 APP-2 --type dependency`,
|
|
96
|
+
},
|
|
97
|
+
create: {
|
|
98
|
+
run: (args) => {
|
|
99
|
+
const props = parseRelationArgs(args);
|
|
100
|
+
const { sourceTaskKey, targetTaskKey } = props;
|
|
101
|
+
|
|
102
|
+
relation.create(props);
|
|
68
103
|
|
|
69
104
|
const source = task.getByKey(sourceTaskKey);
|
|
70
105
|
const target = task.getByKey(targetTaskKey);
|
|
71
106
|
|
|
72
|
-
const sourceKey = `${project.getById(source.project_id)!.key}-${source.number}`;
|
|
73
|
-
const targetKey = `${project.getById(target.project_id)!.key}-${target.number}`;
|
|
74
|
-
|
|
75
107
|
const label = {
|
|
76
108
|
related: "related to",
|
|
77
109
|
dependency: "depends on",
|
|
78
110
|
duplicate: "is a duplicate of",
|
|
79
|
-
}[
|
|
80
|
-
|
|
111
|
+
}[props.relation_type];
|
|
112
|
+
|
|
113
|
+
console.info(`${source.key} ${label} ${target.key}`);
|
|
81
114
|
},
|
|
82
115
|
help: `Usage: zinn task relation create <source-task-key> <target-task-key> --type <type>
|
|
83
116
|
|
|
@@ -10,9 +10,8 @@ function formatTask(
|
|
|
10
10
|
taskMatch: ReturnType<typeof task.getByKey>,
|
|
11
11
|
options: { showsStatus?: boolean } = {},
|
|
12
12
|
) {
|
|
13
|
-
const taskProject = project.getById(taskMatch.project_id)!;
|
|
14
13
|
const taskColumn = column.getById(taskMatch.column_id)!;
|
|
15
|
-
const taskKey =
|
|
14
|
+
const taskKey = taskMatch.key;
|
|
16
15
|
const status = options.showsStatus
|
|
17
16
|
? ` | ${taskMatch.archived_at == null ? "Active" : "Archived"}`
|
|
18
17
|
: "";
|
|
@@ -140,10 +139,8 @@ Example: zinn task create MDR "Refine the numbers" "Sort the numbers and meet th
|
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
const presentableTasks = tasks.map((t) => {
|
|
143
|
-
// TODO: A task's project and column is fetched in both `list` and `view`. Make a reusable fetcher?
|
|
144
|
-
const taskProject = project.getById(t.project_id)!;
|
|
145
142
|
const taskColumn = column.getById(t.column_id)!;
|
|
146
|
-
const taskKey =
|
|
143
|
+
const taskKey = t.key;
|
|
147
144
|
|
|
148
145
|
return {
|
|
149
146
|
id: taskKey,
|