@basementuniverse/kanbn 2.1.0 → 2.5.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 +1 -0
- package/coverage/tmp/coverage-916017-1788028598821-0.json +1 -0
- package/coverage/tmp/{coverage-214293-1787777184569-0.json → coverage-916018-1788028597179-0.json} +1 -1
- package/coverage/tmp/coverage-916036-1788028598796-0.json +1 -0
- package/docs/actions.md +337 -0
- package/docs/advanced-configuration.md +32 -0
- package/docs/commands/add.txt +8 -1
- package/docs/commands/archive.txt +6 -0
- package/docs/commands/board.txt +4 -0
- package/docs/commands/burndown.txt +1 -0
- package/docs/commands/comment.txt +8 -1
- package/docs/commands/contributors.txt +58 -0
- package/docs/commands/edit.txt +13 -1
- package/docs/commands/find.txt +28 -1
- package/docs/commands/gantt.txt +1 -0
- package/docs/commands/help.txt +1 -0
- package/docs/commands/history.txt +1 -0
- package/docs/commands/move.txt +17 -0
- package/docs/commands/remove.txt +10 -0
- package/docs/commands/restore.txt +6 -0
- package/docs/commands/sort.txt +18 -0
- package/docs/commands/task.txt +4 -0
- package/docs/commands/validate.txt +14 -1
- package/docs/contributors.md +145 -0
- package/docs/filtering-and-sorting.md +60 -3
- package/docs/index-structure.md +119 -11
- package/docs/index.md +3 -1
- package/docs/multiple-boards.md +1 -0
- package/docs/task-structure.md +9 -2
- package/example/advanced/kanbn.yml +65 -0
- package/package.json +1 -1
- package/routes/add.json +5 -1
- package/routes/archive.json +6 -2
- package/routes/comment.json +5 -1
- package/routes/contributors.json +18 -0
- package/routes/edit.json +5 -1
- package/routes/move.json +4 -2
- package/routes/remove.json +6 -2
- package/routes/restore.json +6 -0
- package/routes/sort.json +5 -0
- package/src/actions.js +904 -0
- package/src/board.js +24 -1
- package/src/controller/add.js +21 -10
- package/src/controller/archive.js +1 -0
- package/src/controller/board.js +13 -4
- package/src/controller/burndown.js +5 -2
- package/src/controller/comment.js +5 -2
- package/src/controller/contributors.js +166 -0
- package/src/controller/edit.js +40 -14
- package/src/controller/find.js +75 -4
- package/src/controller/gantt.js +5 -2
- package/src/controller/history.js +5 -2
- package/src/controller/move.js +84 -10
- package/src/controller/remove.js +39 -2
- package/src/controller/restore.js +1 -0
- package/src/controller/sort.js +40 -0
- package/src/controller/task.js +12 -1
- package/src/controller/validate.js +141 -4
- package/src/git-user-name.js +5 -15
- package/src/git-user.js +55 -0
- package/src/main.d.ts +204 -4
- package/src/main.js +1412 -38
- package/src/parse-index.js +205 -17
- package/src/utility.js +135 -1
- package/coverage/tmp/coverage-214292-1787777191526-0.json +0 -1
package/docs/commands/sort.txt
CHANGED
|
@@ -98,6 +98,21 @@ Options:
|
|
|
98
98
|
{b}kanbn sort -w{b}
|
|
99
99
|
Sort tasks by workload.
|
|
100
100
|
|
|
101
|
+
{b}kanbn sort --overdue{b}
|
|
102
|
+
Sort tasks by whether they are overdue. Ascending puts overdue tasks last.
|
|
103
|
+
|
|
104
|
+
{b}kanbn sort --is-started{b}
|
|
105
|
+
Sort tasks by whether they have a started date in their metadata.
|
|
106
|
+
|
|
107
|
+
{b}kanbn sort --is-completed{b}
|
|
108
|
+
Sort tasks by whether they have a completed date in their metadata.
|
|
109
|
+
|
|
110
|
+
{b}kanbn sort --in-started-column{b}
|
|
111
|
+
Sort tasks by whether they are in one of the board's startedColumns.
|
|
112
|
+
|
|
113
|
+
{b}kanbn sort --in-completed-column{b}
|
|
114
|
+
Sort tasks by whether they are in one of the board's completedColumns.
|
|
115
|
+
|
|
101
116
|
{b}kanbn sort --<custom field name>{b}
|
|
102
117
|
Sort tasks by a custom field value.
|
|
103
118
|
|
|
@@ -108,6 +123,9 @@ Examples:
|
|
|
108
123
|
{b}kanbn sort "Todo" -z -n --count-tags{b}
|
|
109
124
|
Sort tasks in the "Todo" column first by name, then by the number of tags, all in descending order
|
|
110
125
|
|
|
126
|
+
{b}kanbn sort "Todo" --overdue -z -e -a{b}
|
|
127
|
+
Sort tasks in the "Todo" column so that overdue tasks come first, then by due date in ascending order
|
|
128
|
+
|
|
111
129
|
{b}kanbn sort "column" --board "board-slug"{b}
|
|
112
130
|
{b}kanbn sort "column" -b "board-slug"{b}
|
|
113
131
|
Target a board other than the main one. Falls back to the KANBN_BOARD environment variable and
|
package/docs/commands/task.txt
CHANGED
|
@@ -12,3 +12,7 @@ Options:
|
|
|
12
12
|
{b}kanbn task "task-id" -b "board-slug"{b}
|
|
13
13
|
Target a board other than the main one. Falls back to the KANBN_BOARD environment variable and
|
|
14
14
|
then to the defaultBoard option. See {b}kanbn boards{b} for the list of boards.
|
|
15
|
+
|
|
16
|
+
{b}kanbn task "Buy milk"{b}
|
|
17
|
+
A simple task - a line in a column that isn't a task link - has no task file to show, so this
|
|
18
|
+
reports which column it's in and how to promote it.
|
|
@@ -5,6 +5,11 @@ Validate kanbn index file and all task files, and report any formatting errors.
|
|
|
5
5
|
If the files are valid, this also reports tasks whose started/completed dates don't match the column
|
|
6
6
|
they're in - which usually means the task was moved by hand or by a merge, rather than through kanbn.
|
|
7
7
|
|
|
8
|
+
It also lists any line in a column that isn't a task link. Those lines are kept in the board file and
|
|
9
|
+
ignored by every other command, so this is information rather than a problem - except for a line that
|
|
10
|
+
looks like a task link with a typo in it, or one naming a task file that exists, both of which mean a
|
|
11
|
+
task isn't being tracked when it probably should be.
|
|
12
|
+
|
|
8
13
|
Options:
|
|
9
14
|
{b}kanbn validate --save{b}
|
|
10
15
|
Re-save the index and task files. This will ensure that all index column sorting settings are applied and that all tasks are formatted correctly.
|
|
@@ -20,7 +25,9 @@ Options:
|
|
|
20
25
|
|
|
21
26
|
{b}kanbn validate --json{b}
|
|
22
27
|
{b}kanbn validate -j{b}
|
|
23
|
-
Output validation errors in JSON format.
|
|
28
|
+
Output validation errors in JSON format. When there is nothing to report beyond date drift this
|
|
29
|
+
is the bare list of drift entries; when there are warnings as well - or the workspace has more
|
|
30
|
+
than one board - it is an object with {b}drift{b} and {b}warnings{b} keys.
|
|
24
31
|
|
|
25
32
|
{b}kanbn validate --board "board-slug"{b}
|
|
26
33
|
{b}kanbn validate -b "board-slug"{b}
|
|
@@ -36,3 +43,9 @@ workspace-scoped option in a board file, several boards stamping the same shared
|
|
|
36
43
|
of order, a task on no board at all, history naming a board that has been deleted, and markdown
|
|
37
44
|
files beside the boards that don't parse as one. These are warnings, not errors: each describes a
|
|
38
45
|
workspace that still works, just not the way its author probably meant it to.
|
|
46
|
+
|
|
47
|
+
Action rules are checked too. A rule that names an unknown event or verb, moves to a column that
|
|
48
|
+
doesn't exist, or writes a field Kanbn manages is an {b}error{b} - the same commands that would have
|
|
49
|
+
run it fail before writing anything. Rules that work but probably don't do what they look like -
|
|
50
|
+
two rules writing the same field on the same event, or {b}@me{b} where no user can be resolved - are
|
|
51
|
+
reported as warnings.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Contributors
|
|
2
|
+
|
|
3
|
+
`assigned` and comment `author` are free text, and they always will be. There is no authentication in
|
|
4
|
+
Kanbn and there is never going to be any, so it cannot and should not try to prove that a given string
|
|
5
|
+
corresponds to a given human — anyone who can edit `.kanbn/tasks/*.md` can write any name they like
|
|
6
|
+
into any field.
|
|
7
|
+
|
|
8
|
+
What Kanbn _can_ do is make the common case — "that's me" — a single keystroke instead of a retyped
|
|
9
|
+
name, and make the set of names a workspace uses discoverable rather than folkloric. That is what
|
|
10
|
+
`contributors` is for.
|
|
11
|
+
|
|
12
|
+
**Contributors are a convenience list, not an access control list.** Nothing is validated against
|
|
13
|
+
them, nothing is rejected, and nothing in an existing workspace changes when you add them.
|
|
14
|
+
|
|
15
|
+
## Declaring contributors
|
|
16
|
+
|
|
17
|
+
`contributors` is a [workspace-scoped option](index-structure.md#project-options): it goes in
|
|
18
|
+
`kanbn.yml` / `kanbn.json`, or in the main board's front matter when there is no configuration file,
|
|
19
|
+
and every board in the workspace inherits it. A secondary board that declares its own is reported by
|
|
20
|
+
`kanbn validate` and ignored — a name written into a shared task file has to mean the same thing on
|
|
21
|
+
every board that references that file.
|
|
22
|
+
|
|
23
|
+
The shorthand is a bare name, which is all most workspaces ever need:
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
contributors:
|
|
27
|
+
- gordon
|
|
28
|
+
- dave
|
|
29
|
+
- sam
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The long form is for a contributor who needs more than a name. Both forms can appear in the same list:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
contributors:
|
|
36
|
+
- name: gordon
|
|
37
|
+
displayName: Gordon Larrigan
|
|
38
|
+
email: gordonlarrigan@example.com
|
|
39
|
+
aliases:
|
|
40
|
+
- Gordon Larrigan
|
|
41
|
+
- gordonl
|
|
42
|
+
colour: '#7c5cff'
|
|
43
|
+
- dave
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Field | Required | Purpose |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| `name` | yes | The canonical value written into `assigned` and comment `author`. Everything else is lookup metadata |
|
|
49
|
+
| `displayName` | no | Shown in UIs; never stored in a task file. Defaults to `name` |
|
|
50
|
+
| `email` | no | Matched against `git config user.email` |
|
|
51
|
+
| `aliases` | no | Other spellings that mean this person |
|
|
52
|
+
| `colour` | no | Presentational only, for avatar chips. Kanbn stores and serves it, and never interprets it |
|
|
53
|
+
|
|
54
|
+
`aliases` is the field that does the actual work. Without it, `contributors` is a list of strings a UI
|
|
55
|
+
can put in a dropdown; with it, Kanbn can tell that the person at this machine is `gordon` even though
|
|
56
|
+
git says `Gordon Larrigan`.
|
|
57
|
+
|
|
58
|
+
## The current user
|
|
59
|
+
|
|
60
|
+
Kanbn resolves a single answer to "who is the current user?", used by the CLI, the library and the
|
|
61
|
+
VSCode extension. In order, first match wins:
|
|
62
|
+
|
|
63
|
+
1. the `KANBN_USER` environment variable, used **exactly as given**
|
|
64
|
+
2. `git config user.email` matched against a contributor's `email` → that contributor's `name`
|
|
65
|
+
3. `git config user.name` matched against a contributor's `name`, `displayName` or `aliases`,
|
|
66
|
+
ignoring case → that contributor's `name`
|
|
67
|
+
4. `git config user.name` as-is
|
|
68
|
+
5. nobody
|
|
69
|
+
|
|
70
|
+
Two things follow from that order:
|
|
71
|
+
|
|
72
|
+
- **It degrades to what Kanbn has always done.** With no contributors declared, steps 2 and 3 can't
|
|
73
|
+
match and step 4 returns exactly the git username Kanbn used before this feature existed. A
|
|
74
|
+
workspace that ignores contributors sees no change at all.
|
|
75
|
+
- **It canonicalises.** With contributors declared, a machine whose git username is `Gordon Larrigan`
|
|
76
|
+
writes `gordon` into the task file, because that is what the workspace has agreed to call him. That
|
|
77
|
+
is the typo mitigation, and it happens without anyone typing anything.
|
|
78
|
+
|
|
79
|
+
`KANBN_USER` is used verbatim rather than canonicalised: it is the explicit override, for a machine
|
|
80
|
+
whose git identity is wrong or missing, and an explicit value should be written exactly as given.
|
|
81
|
+
|
|
82
|
+
Ask Kanbn who it thinks you are with `kanbn contributors`.
|
|
83
|
+
|
|
84
|
+
_There is deliberately no `me` key in the config file. A `me` committed into a shared board file would
|
|
85
|
+
tell every collaborator that they are you. `KANBN_USER` plus git resolution covers the real cases._
|
|
86
|
+
|
|
87
|
+
## What uses it
|
|
88
|
+
|
|
89
|
+
| Where | Behaviour |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `kanbn add --assigned` (no value) | Assigns the task to the current user, canonicalised |
|
|
92
|
+
| `kanbn edit "task-id" --assigned` (no value) | The same |
|
|
93
|
+
| `kanbn comment "task-id"` | The comment author defaults to the current user, or to no author at all if there isn't one |
|
|
94
|
+
| `kanbn add -i` / `kanbn edit -i` | "Assigned to:" becomes an autocomplete over the contributor list when one is declared. Free text is still accepted |
|
|
95
|
+
| `kanbn find --assigned @me` | `@me` expands to the current user. Also `kanbn burndown`, `kanbn gantt` and `kanbn history` |
|
|
96
|
+
| Task history | Each history event records the current user as its `author`, and no `author` key at all when there is no resolvable user |
|
|
97
|
+
|
|
98
|
+
`@me` is only ever interpreted in a **filter** argument, never when setting a value and never inside a
|
|
99
|
+
task file. If the current user can't be resolved, `@me` is an error rather than a filter that quietly
|
|
100
|
+
matches everything.
|
|
101
|
+
|
|
102
|
+
## Adopting contributors in an existing workspace
|
|
103
|
+
|
|
104
|
+
`kanbn contributors --usage` shows how each contributor is actually used, and — the useful part —
|
|
105
|
+
every name used in a task that isn't a known contributor:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
$ kanbn contributors --usage
|
|
109
|
+
2 contributors:
|
|
110
|
+
|
|
111
|
+
gordon (you)
|
|
112
|
+
14 tasks, assigned 12, 4 comments
|
|
113
|
+
written as "Gordon Larrigan" in 2 tasks
|
|
114
|
+
|
|
115
|
+
dave
|
|
116
|
+
6 tasks, assigned 6, 1 comment
|
|
117
|
+
|
|
118
|
+
1 name is used in tasks but isn't a known contributor:
|
|
119
|
+
|
|
120
|
+
Gordon
|
|
121
|
+
12 tasks, assigned 12, 0 comments
|
|
122
|
+
task-1, task-4, task-9, ...
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
That is the list of aliases to add. It is read-only: nothing is rewritten, because renaming `Gordon`
|
|
126
|
+
to `gordon` across every task file is a bulk mutation that deserves its own command and its own dry
|
|
127
|
+
run.
|
|
128
|
+
|
|
129
|
+
`kanbn validate` reports the same thing more briefly, as a **warning** rather than an error — the
|
|
130
|
+
tasks it names are working exactly as they always have. Nothing is reported at all when a workspace
|
|
131
|
+
declares no contributors.
|
|
132
|
+
|
|
133
|
+
## Deliberate omissions
|
|
134
|
+
|
|
135
|
+
- **Validation.** `assigned` is never restricted to the contributor list. "If contributors is set,
|
|
136
|
+
only allow those values" turns an optional convenience into something that breaks a workspace
|
|
137
|
+
whenever someone adds a task on a machine with a stale config, or renames themselves, or receives a
|
|
138
|
+
task file from outside.
|
|
139
|
+
- **Multiple assignees.** `assigned` is a single string in the schema, the filters, the board template
|
|
140
|
+
and three reporting commands. One assignee per task.
|
|
141
|
+
- **Importing from `package.json`, `.mailmap` or `CODEOWNERS`.** npm contributors are publishers,
|
|
142
|
+
`.mailmap` is git plumbing, and `CODEOWNERS` is a permissions file — three different sets of
|
|
143
|
+
semantics, none of them this one.
|
|
144
|
+
- **Anything resembling accounts.** No permissions, no ownership, no invites. If `contributors` ever
|
|
145
|
+
grows those, it has become an auth system by accident.
|
|
@@ -41,7 +41,7 @@ filters:
|
|
|
41
41
|
| `due` | `--due`, `-e` | date | `due` metadata |
|
|
42
42
|
| `plannedStart` | `--plannedStart` | date | `plannedStart` metadata |
|
|
43
43
|
| `plannedFinish` | `--plannedFinish` | date | `plannedFinish` metadata |
|
|
44
|
-
| `assigned` | `--assigned` | string | `assigned` metadata (empty string if unset) |
|
|
44
|
+
| `assigned` | `--assigned` | string | `assigned` metadata (empty string if unset). `@me` on the command line expands to the [current user](contributors.md#the-current-user) |
|
|
45
45
|
| `workload` | `--workload` | number | The task's [calculated workload](index-structure.md#taskworkloadtags) |
|
|
46
46
|
| `progress` | `--progress` | number | The task's [calculated progress](task-structure.md#progress) |
|
|
47
47
|
| `sub-task` | `--sub-task`, `-s` | string | All sub-tasks, one per line, as `[ ] text` or `[x] text` |
|
|
@@ -52,6 +52,11 @@ filters:
|
|
|
52
52
|
| `count-relations` | `--count-relations` | number | Number of relations |
|
|
53
53
|
| `comment` | `--comment` | string | All comments, one per line, as `author text` |
|
|
54
54
|
| `count-comments` | `--count-comments` | number | Number of comments |
|
|
55
|
+
| `overdue` | `--overdue` | boolean | Whether the task is [overdue](#computed-values) |
|
|
56
|
+
| `is-started` | `--is-started` | boolean | Whether the task has a started date |
|
|
57
|
+
| `is-completed` | `--is-completed` | boolean | Whether the task has a completed date |
|
|
58
|
+
| `in-started-column` | `--in-started-column` | boolean | Whether the task is in one of the board's `startedColumns` |
|
|
59
|
+
| `in-completed-column` | `--in-completed-column` | boolean | Whether the task is in one of the board's `completedColumns` |
|
|
55
60
|
| *custom field name* | `--<custom field name>` | declared type | The custom field value in the task metadata |
|
|
56
61
|
|
|
57
62
|
Custom fields must be declared in the [`customFields`](index-structure.md#customfields) project option before they can be filtered on.
|
|
@@ -60,6 +65,8 @@ A task that doesn't have the metadata field being filtered on never matches a da
|
|
|
60
65
|
|
|
61
66
|
*Note: `hiddenColumns` only affects the board. `kanbn find` still returns tasks in hidden columns unless you filter them out with `column`.*
|
|
62
67
|
|
|
68
|
+
`@me` is substituted where the argument is parsed, so it works for `kanbn find --assigned @me` and for the simpler `--assigned` options on `kanbn burndown`, `kanbn gantt` and `kanbn history`. It is never interpreted when *setting* a value, and never inside a task file. If Kanbn can't work out who you are, `@me` is an error rather than a filter that quietly matches everything.
|
|
69
|
+
|
|
63
70
|
### String filters
|
|
64
71
|
|
|
65
72
|
String filter values are **case-insensitive regular expressions**, matched anywhere in the value (not anchored). Passing an array (or repeating a CLI option) joins the values with `|`, so an array means "match any of these":
|
|
@@ -119,13 +126,53 @@ kanbn find --workload 3 --workload 8 # between 3 and 8
|
|
|
119
126
|
|
|
120
127
|
### Boolean filters
|
|
121
128
|
|
|
122
|
-
Boolean filters
|
|
129
|
+
Boolean filters apply to the [computed values](#computed-values) and to custom fields with type `boolean`, and are matched by exact equality. On the command line, `--<name>` matches `true` and `--no-<name>` matches `false`:
|
|
123
130
|
|
|
124
131
|
```bash
|
|
125
132
|
kanbn find --needsDesign # tasks where needsDesign is true
|
|
126
133
|
kanbn find --no-needsDesign # tasks where needsDesign is false
|
|
134
|
+
kanbn find --overdue # overdue tasks
|
|
135
|
+
kanbn find --no-overdue # tasks that aren't overdue
|
|
127
136
|
```
|
|
128
137
|
|
|
138
|
+
Leaving the option off entirely is different from passing `--no-<name>`: it means "don't filter on this at all".
|
|
139
|
+
|
|
140
|
+
## Computed values
|
|
141
|
+
|
|
142
|
+
Most filter and sorter fields read a value straight off the task. These five are worked out from the task *and* the board it is being filtered on, which makes them useful for questions the raw metadata can't answer on its own — "what is overdue and nobody has picked up", "what is sitting in a done column without a completed date".
|
|
143
|
+
|
|
144
|
+
| Value | Meaning |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `overdue` | The task has a `due` date that has passed **and** hasn't been completed. A task with no due date is never overdue, and neither is a completed task, however late it was. |
|
|
147
|
+
| `is-started` | The task has a started date in its metadata. |
|
|
148
|
+
| `is-completed` | The task has a completed date in its metadata. |
|
|
149
|
+
| `in-started-column` | The task is currently in one of the board's [`startedColumns`](index-structure.md#startedcolumns). |
|
|
150
|
+
| `in-completed-column` | The task is currently in one of the board's [`completedColumns`](index-structure.md#completedcolumns). |
|
|
151
|
+
|
|
152
|
+
`is-started` and `is-completed` read the metadata fields the board is configured to use, so a board with a [`startedField`](index-structure.md#startedfield) or [`completedField`](index-structure.md#completedfield) override reports its own state rather than the built-in `started` / `completed` fields. A board that declares no `startedColumns` has no notion of work in progress, so nothing is in a started column; the same goes for `completedColumns`.
|
|
153
|
+
|
|
154
|
+
`is-completed` and `in-completed-column` normally agree, because moving a task into a completed column is what stamps the completed date (likewise `is-started` and `in-started-column`). They come apart when a task is edited directly, when a board overrides the state fields, or when a task is moved back out of a column - which is exactly when it's worth asking about them separately:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Tasks sitting in a completed column with no completed date
|
|
158
|
+
kanbn find --in-completed-column --no-is-completed
|
|
159
|
+
|
|
160
|
+
# Overdue work that nobody has picked up
|
|
161
|
+
kanbn find --overdue --no-in-started-column
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Under `kanbn find --all-boards` every board is filtered on its own, so a task matches `--in-started-column` if it is in a started column on **any** board in the workspace. Scope the question to one board with `--board`.
|
|
165
|
+
|
|
166
|
+
As with the other fields, the name written in YAML is not always the command line option, and the sorter form is camelCase where the filter form is kebab-case:
|
|
167
|
+
|
|
168
|
+
| Computed value | Filter field | Sorter field | CLI option |
|
|
169
|
+
| --- | --- | --- | --- |
|
|
170
|
+
| Overdue | `overdue` | `overdue` | `--overdue` |
|
|
171
|
+
| Started | `is-started` | `isStarted` | `--is-started` |
|
|
172
|
+
| Completed | `is-completed` | `isCompleted` | `--is-completed` |
|
|
173
|
+
| In a started column | `in-started-column` | `inStartedColumn` | `--in-started-column` |
|
|
174
|
+
| In a completed column | `in-completed-column` | `inCompletedColumn` | `--in-completed-column` |
|
|
175
|
+
|
|
129
176
|
## Sorters
|
|
130
177
|
|
|
131
178
|
A sorter list is an ordered array. Tasks are compared using the first sorter; ties are broken using the next one, and so on. Each sorter is an object:
|
|
@@ -145,7 +192,12 @@ sorters:
|
|
|
145
192
|
| `order` | no | `ascending` (default) or `descending` |
|
|
146
193
|
| `filter` | no | A regex used to transform values before comparing them (see below) |
|
|
147
194
|
|
|
148
|
-
Strings are compared with `localeCompare` (accent-sensitive, case-insensitive); dates and numbers are compared numerically.
|
|
195
|
+
Strings are compared with `localeCompare` (accent-sensitive, case-insensitive); dates and numbers are compared numerically. Booleans sort `false` before `true`, so `order: descending` on a [computed value](#computed-values) brings the matching tasks to the top:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Overdue tasks first, then everything else by due date
|
|
199
|
+
kanbn sort "Todo" --overdue -z --due -a
|
|
200
|
+
```
|
|
149
201
|
|
|
150
202
|
### Sort filters
|
|
151
203
|
|
|
@@ -188,6 +240,11 @@ kanbn sort "Todo" --name "Task (\d+)"
|
|
|
188
240
|
| `countRelations` | `--count-relations` |
|
|
189
241
|
| `comments` | `--comment` |
|
|
190
242
|
| `countComments` | `--count-comments` |
|
|
243
|
+
| `overdue` | `--overdue` |
|
|
244
|
+
| `isStarted` | `--is-started` |
|
|
245
|
+
| `isCompleted` | `--is-completed` |
|
|
246
|
+
| `inStartedColumn` | `--in-started-column` |
|
|
247
|
+
| `inCompletedColumn` | `--in-completed-column` |
|
|
191
248
|
| *custom field name* | `--<custom field name>` |
|
|
192
249
|
|
|
193
250
|
[View](views.md) column `sorters` accept all of the same fields, plus `column` (the index column the task is currently in), which is useful in a view whose columns don't map onto index columns.
|
package/docs/index-structure.md
CHANGED
|
@@ -29,7 +29,57 @@ The project description should appear below the title. The description can be of
|
|
|
29
29
|
|
|
30
30
|
Below the description there should be one or more level-2 headings. The 'Options' name is reserved for project options.
|
|
31
31
|
|
|
32
|
-
All level-2 headings (except for 'Options') will be treated as columns.
|
|
32
|
+
All level-2 headings (except for 'Options') will be treated as columns.
|
|
33
|
+
|
|
34
|
+
## Column contents
|
|
35
|
+
|
|
36
|
+
A column tracks a task when it contains a **markdown link** to that task's file:
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
## Todo
|
|
40
|
+
|
|
41
|
+
- [my-task](tasks/my-task.md)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
That link is the only thing kanbn reads as a task. Any other line in a column - a bare title, a checkbox item, a note, a paragraph - is left exactly as you wrote it and ignored by every command:
|
|
45
|
+
|
|
46
|
+
```markdown
|
|
47
|
+
## Todo
|
|
48
|
+
|
|
49
|
+
- [my-task](tasks/my-task.md)
|
|
50
|
+
- remember to check the staging deploy
|
|
51
|
+
- [ ] buy milk
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
These lines are **simple tasks**. They have no id, no metadata and no dates, they can't collide with a task of the same name, and they count towards nothing: not `kanbn status`, not workload, burndown, gantt or sprints. They are preserved when kanbn saves the board file, and stay in the column they were written in - though a line isn't anchored to the task above it, so sorting a column can move tasks around it.
|
|
55
|
+
|
|
56
|
+
They are shown on the board in the default layout, dimmed, at the end of their column. Views leave them out: a view filters and sorts tasks, and a simple task has no fields to filter or sort on. They are left out of `kanbn board --json` for the same reason.
|
|
57
|
+
|
|
58
|
+
### Working with simple tasks
|
|
59
|
+
|
|
60
|
+
Four commands accept a simple task, addressed by its title - exactly, ignoring case, or slugified, so `"Buy milk"` and `buy-milk` both work. **A real task always wins**: kanbn only looks for a simple task when no task has that id, so a line whose title matches an existing task can only be edited by hand.
|
|
61
|
+
|
|
62
|
+
| Command | Effect |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `kanbn move "Buy milk" -c Done` | Moves the line to another column. `-b` moves it to another board |
|
|
65
|
+
| `kanbn edit "Buy milk" ...` | **Promotes** it to a real task file, then edits it as normal |
|
|
66
|
+
| `kanbn remove "Buy milk"` | Deletes the line. There is no file to delete and nothing to archive |
|
|
67
|
+
| `kanbn task "Buy milk"` | Says which column it's in, and how to promote it |
|
|
68
|
+
|
|
69
|
+
Promotion is one-way. The new task gets a file, an id, a `created` date as of the promotion and a `created` history event naming the column the line was in. There is no way back: a task carries metadata, comments and history that a line can't hold.
|
|
70
|
+
|
|
71
|
+
Two lines with the same title on one board are two different lines, so a command naming one of them reports the ambiguity rather than guessing. Two lines with the same title on *different* boards are unrelated - a simple task belongs to the board file it's written in, and nothing else.
|
|
72
|
+
|
|
73
|
+
Anything that isn't a single line of a list - a paragraph, a quote, a code block - is preserved just the same, but isn't a simple task: it isn't shown on the board, and no command will move, promote or remove it.
|
|
74
|
+
|
|
75
|
+
`kanbn validate` lists them, so they can't hide. It reports two cases more loudly, because both mean a task silently isn't being tracked:
|
|
76
|
+
|
|
77
|
+
- a line that looks like a task link with a typo in it, e.g. a missing closing bracket;
|
|
78
|
+
- a line naming a task file that exists but isn't written as a link, e.g. `- my-task`.
|
|
79
|
+
|
|
80
|
+
_Note: before version 2.2.0 a bare task id in a column (`- my-task`) was treated as a task reference. It no longer is - only links are. Run `kanbn validate` to find any, and write them as links._
|
|
81
|
+
|
|
82
|
+
If a column references a task file that doesn't exist, kanbn skips that task and carries on rather than failing - this happens routinely when a board file and a task file arrive in different commits. `kanbn board` says how many tasks were skipped, and `kanbn validate` names them.
|
|
33
83
|
|
|
34
84
|
## Project options
|
|
35
85
|
|
|
@@ -37,7 +87,7 @@ The 'Options' heading should be followed by a code block containing YAML. The 'y
|
|
|
37
87
|
|
|
38
88
|
Project options will be merged with YAML front-matter if any is present.
|
|
39
89
|
|
|
40
|
-
|
|
90
|
+
_Note: when the index is saved, all project options will be placed into YAML front-matter. The 'Options' heading is still supported for backwards-compatibility._
|
|
41
91
|
|
|
42
92
|
```yaml
|
|
43
93
|
hiddenColumns:
|
|
@@ -89,6 +139,23 @@ customFields:
|
|
|
89
139
|
type: 'date'
|
|
90
140
|
{customFieldName}Columns:
|
|
91
141
|
- {Column name}
|
|
142
|
+
contributors:
|
|
143
|
+
- gordon
|
|
144
|
+
- name: dave
|
|
145
|
+
displayName: Dave Smith
|
|
146
|
+
email: dave@example.com
|
|
147
|
+
aliases:
|
|
148
|
+
- Dave Smith
|
|
149
|
+
colour: '#7c5cff'
|
|
150
|
+
actions:
|
|
151
|
+
- name: start-work
|
|
152
|
+
on: task.moved
|
|
153
|
+
when:
|
|
154
|
+
toColumn: In Progress
|
|
155
|
+
then:
|
|
156
|
+
- assign: '@me'
|
|
157
|
+
- addTag: active
|
|
158
|
+
actionsFile: ""
|
|
92
159
|
```
|
|
93
160
|
|
|
94
161
|
### `hiddenColumns`
|
|
@@ -105,7 +172,7 @@ A list of column names. When a task is created in or dragged into one of these c
|
|
|
105
172
|
|
|
106
173
|
A list of column names. When a task is created in or dragged into one of these columns, the task's `completed` date will be set to the current time, unless the task already has a `completed` date.
|
|
107
174
|
|
|
108
|
-
Note that these columns control when the date is
|
|
175
|
+
Note that these columns control when the date is _written_. They don't decide whether a task counts as complete - that comes from the date itself. See [Task state](#task-state) below.
|
|
109
176
|
|
|
110
177
|
### `startedField`
|
|
111
178
|
|
|
@@ -205,13 +272,13 @@ progress
|
|
|
205
272
|
{...customFields}
|
|
206
273
|
```
|
|
207
274
|
|
|
208
|
-
|
|
275
|
+
_Note: referencing a variable that isn't in the list above (or a custom field that isn't declared in `customFields`) will print "Unable to build task template" instead of rendering the board._
|
|
209
276
|
|
|
210
277
|
The default task template is `^+^_${overdue ? '^R' : ''}${name}^: ${created ? ('\\n^-^/' + created) : ''}`.
|
|
211
278
|
|
|
212
279
|
This string can contain markup sequences. See [terminal-kit](https://github.com/cronvel/terminal-kit/blob/21607fb51749853dd9193c6aaf205b14c63b2768/doc/markup.md#markup) for markup reference.
|
|
213
280
|
|
|
214
|
-
|
|
281
|
+
_Note: custom fields can also be interpolated into the task template, but only if they are defined in `customFields` (see below)._
|
|
215
282
|
|
|
216
283
|
### `dateFormat`
|
|
217
284
|
|
|
@@ -234,6 +301,7 @@ An array of custom metadata fields, where each field should have `name` and `typ
|
|
|
234
301
|
Run `kanbn add --help`, `kanbn edit --help`, `kanbn find --help` and `kanbn sort --help` for more information.
|
|
235
302
|
|
|
236
303
|
Valid types are:
|
|
304
|
+
|
|
237
305
|
- `boolean`
|
|
238
306
|
- `date`
|
|
239
307
|
- `number`
|
|
@@ -253,8 +321,8 @@ If the custom field has `updateDate` set to `always`, the field will be updated
|
|
|
253
321
|
|
|
254
322
|
Here's an example of how this could be used:
|
|
255
323
|
|
|
256
|
-
|
|
257
|
-
|
|
324
|
+
- Assume we have added a column called `Testing`
|
|
325
|
+
- In `index.md` (or `kanbn.json` / `kanbn.yml` if using a separate configuration file), we have:
|
|
258
326
|
```
|
|
259
327
|
customFields:
|
|
260
328
|
- name: testedAt
|
|
@@ -263,16 +331,56 @@ Here's an example of how this could be used:
|
|
|
263
331
|
testedAtColumns:
|
|
264
332
|
- Testing
|
|
265
333
|
```
|
|
266
|
-
|
|
334
|
+
- When a task is moved into the `Testing` column and the task doesn't already have a `testedAt` value in its metadata, this value will be automatically populated with the current date/time.
|
|
335
|
+
|
|
336
|
+
### `contributors`
|
|
337
|
+
|
|
338
|
+
An optional list of the people who work on this workspace. Each entry is either a bare name or an object with a required `name` and optional `displayName`, `email`, `aliases` and `colour`.
|
|
339
|
+
|
|
340
|
+
`contributors` is **workspace-scoped**: like `customFields`, it belongs in `kanbn.json` / `kanbn.yml`, or in the main board's front matter when there is no configuration file, and every board inherits it. A secondary board that declares its own is reported by `kanbn validate` and ignored.
|
|
341
|
+
|
|
342
|
+
Contributors are **advisory**. `assigned` and comment `author` remain free text and are never validated against the list, never rejected and never rewritten. What the list buys you is a canonical spelling for each person, an autocomplete in the interactive prompts, `@me` in filters, and `kanbn contributors --usage` to find the spellings already in use.
|
|
343
|
+
|
|
344
|
+
See [Contributors](contributors.md) for the full description, and `kanbn contributors --help`.
|
|
345
|
+
|
|
346
|
+
### `actions`
|
|
347
|
+
|
|
348
|
+
A list of rules that fire when a task is created, moved, updated, commented on, archived, restored, deleted, started or completed. Each rule names an event (`on`), an optional condition (`when`), an optional set of related tasks to act on (`for`), and the verbs to apply (`then`).
|
|
349
|
+
|
|
350
|
+
```yaml
|
|
351
|
+
actions:
|
|
352
|
+
- name: start-work
|
|
353
|
+
on: task.moved
|
|
354
|
+
when:
|
|
355
|
+
toColumn: In Progress
|
|
356
|
+
then:
|
|
357
|
+
- assign: '@me'
|
|
358
|
+
- addTag: active
|
|
359
|
+
- removeTag: ready
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`actions` is board-scoped: in a configuration file it applies to every board, and in a board's front matter it applies to that board alone. A board that declares its own replaces the inherited list rather than adding to it.
|
|
363
|
+
|
|
364
|
+
Rules only fire during commands that change something, and nothing a rule writes ever fires another rule. See [Actions](actions.md) for the events, the verbs, the substitutions and the failure semantics.
|
|
365
|
+
|
|
366
|
+
### `actionsFile`
|
|
367
|
+
|
|
368
|
+
A path, relative to the `.kanbn` folder, of a file holding the same list of rules that `actions` would have held. For rule sets that have outgrown a board's front matter.
|
|
369
|
+
|
|
370
|
+
```yaml
|
|
371
|
+
actionsFile: actions/design.yml
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Setting both `actions` and `actionsFile` on the same board is an error, and so is naming a file that doesn't exist or doesn't parse.
|
|
267
375
|
|
|
268
376
|
## Task state
|
|
269
377
|
|
|
270
378
|
Whether a task has started or been completed is decided by its metadata dates, and nothing else:
|
|
271
379
|
|
|
272
|
-
|
|
273
|
-
|
|
380
|
+
- a task has started if its `started` field is set (or whichever field `startedField` names);
|
|
381
|
+
- a task is complete if its `completed` field is set (or whichever field `completedField` names).
|
|
274
382
|
|
|
275
|
-
The column a task sits in doesn't make it started or complete. Columns listed in `startedColumns` and `completedColumns` cause those dates to be
|
|
383
|
+
The column a task sits in doesn't make it started or complete. Columns listed in `startedColumns` and `completedColumns` cause those dates to be _written_ when a task is moved through the CLI or the VSCode extension, but the dates are what everything else reads: `kanbn status`, `kanbn find`, progress, burndown charts and gantt charts.
|
|
276
384
|
|
|
277
385
|
This matters when a task is moved **outside** of kanbn - by editing the index file by hand, or through a git merge. No date is written in that case, so the task keeps whatever state its metadata already described. `kanbn validate` reports tasks whose dates and columns disagree, and `kanbn validate --fix` fills in the missing dates, taking them from the task's history where possible.
|
|
278
386
|
|
package/docs/index.md
CHANGED
|
@@ -10,6 +10,8 @@ To get started quickly, check out the [Quick Start](quick-start.md) guide.
|
|
|
10
10
|
- [Index Structure](index-structure.md) — the board file and all project options
|
|
11
11
|
- [Task Structure](task-structure.md) — task files, metadata, sub-tasks, relations, comments and history
|
|
12
12
|
- [Multiple Boards](multiple-boards.md) — several boards over one shared pool of tasks
|
|
13
|
+
- [Contributors](contributors.md) — an optional list of who works on a workspace, and who "you" are
|
|
14
|
+
- [Actions](actions.md) — declarative rules that fire when a task is created, moved, updated or finished
|
|
13
15
|
- [Views](views.md) — custom board layouts, columns and lanes
|
|
14
16
|
- [Filtering and Sorting](filtering-and-sorting.md) — the filter and sorter model shared by `find`, `sort` and views
|
|
15
17
|
- [Advanced Configuration](advanced-configuration.md)
|
|
@@ -24,7 +26,7 @@ The [`example`](../example) directory contains ready-to-run workspaces:
|
|
|
24
26
|
| [`example/basic`](../example/basic) | A typical board: columns, tags, workload, sprints |
|
|
25
27
|
| [`example/views`](../example/views) | Views, lanes, filters and sorters |
|
|
26
28
|
| [`example/boards`](../example/boards) | Multiple boards over one shared pool of tasks, with per-board state fields |
|
|
27
|
-
| [`example/advanced`](../example/advanced) | A `kanbn.yml` config file, custom fields, column sorting and a custom task template |
|
|
29
|
+
| [`example/advanced`](../example/advanced) | A `kanbn.yml` config file, custom fields, contributors, column sorting and a custom task template |
|
|
28
30
|
|
|
29
31
|
Run any Kanbn command from inside one of those directories to try it out.
|
|
30
32
|
|
package/docs/multiple-boards.md
CHANGED
|
@@ -166,6 +166,7 @@ Some options describe the workspace rather than a single board:
|
|
|
166
166
|
| `hiddenColumns`, `startedColumns`, `completedColumns`, `startedField`, `completedField`, `columnSorting`, `views`, `taskTemplate`, `verbose` | Board |
|
|
167
167
|
| `sprints` | Workspace, overridable per board |
|
|
168
168
|
| `customFields` | Workspace — task files are shared, so a field defined on one board must parse on all |
|
|
169
|
+
| `contributors` | Workspace — `assigned: gordon` has to mean the same person on every board that references the task |
|
|
169
170
|
| `defaultTaskWorkload`, `taskWorkloadTags` | Workspace — workload is a property of the task, not the board |
|
|
170
171
|
| `dateFormat` | Workspace only — dates should read identically everywhere |
|
|
171
172
|
| `mainFolder`, `indexFile`, `taskFolder`, `archiveFolder`, `defaultBoard`, `boards` | Workspace only — layout, meaningless in a board file |
|
package/docs/task-structure.md
CHANGED
|
@@ -100,7 +100,11 @@ An array of tags to apply to this task.
|
|
|
100
100
|
|
|
101
101
|
### `assigned`
|
|
102
102
|
|
|
103
|
-
The name of the user this task is assigned to.
|
|
103
|
+
The name of the user this task is assigned to. This is free text: any string is a valid assignee, and Kanbn never rejects or rewrites one.
|
|
104
|
+
|
|
105
|
+
A workspace can declare a list of [contributors](contributors.md), which gives each person a canonical spelling, autocompletes this field in the interactive prompts and lets `kanbn find --assigned @me` work. Contributors are advisory — `assigned` stays free text either way, and a name that isn't in the list is still perfectly valid.
|
|
106
|
+
|
|
107
|
+
A task has one assignee. `assigned` is a single string in the schema, the filters, the board template and the reporting commands, and there is no multiple-assignee form.
|
|
104
108
|
|
|
105
109
|
### `progress`
|
|
106
110
|
|
|
@@ -150,6 +154,8 @@ The `depends-on` relation type is reserved for gantt scheduling. It means the cu
|
|
|
150
154
|
|
|
151
155
|
An array of comments, where each comment has an `author` and `date` property, and some text.
|
|
152
156
|
|
|
157
|
+
`author` is free text, exactly like [`assigned`](#assigned), and it is optional — a comment added on a machine with no resolvable user has an empty author rather than failing to save. When the workspace declares [contributors](contributors.md), the default author is the canonical name for the current user.
|
|
158
|
+
|
|
153
159
|
## History
|
|
154
160
|
|
|
155
161
|
An optional array of structured lifecycle events used for richer timeline reporting (including burndown).
|
|
@@ -168,7 +174,8 @@ Notes:
|
|
|
168
174
|
|
|
169
175
|
- History entries are stored in the reserved `## History` section as list items.
|
|
170
176
|
- `date` should be an ISO timestamp.
|
|
171
|
-
-
|
|
177
|
+
- Every event carries an optional `author`, naming the [current user](contributors.md#the-current-user) at the time it happened. The key is omitted entirely when there is no resolvable user, so a machine with no git identity writes exactly the history it always has.
|
|
178
|
+
- Any other additional field is preserved if present.
|
|
172
179
|
- An event type this version of Kanbn doesn't recognise is preserved and skipped rather than rejected, so a task file written by a newer version stays readable.
|
|
173
180
|
|
|
174
181
|
### Board attribution
|
|
@@ -53,6 +53,71 @@ columnSorting:
|
|
|
53
53
|
|
|
54
54
|
dateFormat: yyyy-mm-dd
|
|
55
55
|
|
|
56
|
+
# The people who work on this workspace. A contributor can be a bare name or an
|
|
57
|
+
# object; `name` is the value written into a task's `assigned` field and a
|
|
58
|
+
# comment's `author`, and `aliases` catch the other spellings that mean the same
|
|
59
|
+
# person. The list is advisory - `assigned` stays free text and is never
|
|
60
|
+
# validated against it. Run `kanbn contributors --usage` to see which spellings
|
|
61
|
+
# are already in use. See docs/contributors.md.
|
|
62
|
+
contributors:
|
|
63
|
+
- name: Ana
|
|
64
|
+
email: ana@example.com
|
|
65
|
+
aliases:
|
|
66
|
+
- ana
|
|
67
|
+
- Ana Ruiz
|
|
68
|
+
- name: Ben
|
|
69
|
+
email: ben@example.com
|
|
70
|
+
aliases:
|
|
71
|
+
- ben
|
|
72
|
+
|
|
73
|
+
# Rules that fire when a task changes. Actions only run during commands that
|
|
74
|
+
# change something - never during `board`, `find` or `burndown` - and nothing a
|
|
75
|
+
# rule writes can fire another rule. `--no-actions` on any of those commands, or
|
|
76
|
+
# KANBN_NO_ACTIONS=1, steps around a rule that is misbehaving.
|
|
77
|
+
# See docs/actions.md.
|
|
78
|
+
actions:
|
|
79
|
+
# `task.started` is a transition rather than an operation: it fires when the
|
|
80
|
+
# task wasn't started before and is now, whatever caused it. It reads
|
|
81
|
+
# startedColumns above, so adding another started column doesn't break it.
|
|
82
|
+
- name: start-work
|
|
83
|
+
on: task.started
|
|
84
|
+
then:
|
|
85
|
+
- assign: '@me'
|
|
86
|
+
- addTag: active
|
|
87
|
+
- removeTag: ready
|
|
88
|
+
|
|
89
|
+
- name: close-out
|
|
90
|
+
on: task.completed
|
|
91
|
+
then:
|
|
92
|
+
- removeTag: active
|
|
93
|
+
- setProgress: 1
|
|
94
|
+
- set: { closedBy: '@me' }
|
|
95
|
+
|
|
96
|
+
# `reviewedAt` above is stamped by the column linkage; the rule runs after it,
|
|
97
|
+
# so it can see and override anything that stamping wrote.
|
|
98
|
+
- name: flag-review
|
|
99
|
+
on: task.moved
|
|
100
|
+
when:
|
|
101
|
+
toColumn: Review
|
|
102
|
+
then:
|
|
103
|
+
- addTag: needs-review
|
|
104
|
+
- comment: 'Review requested by @me'
|
|
105
|
+
|
|
106
|
+
# `for` points the verbs at related tasks instead of this one. Children carry
|
|
107
|
+
# the `child-of` relation, so `incoming` finds them from the epic.
|
|
108
|
+
- name: epic-blocks-children
|
|
109
|
+
on: task.moved
|
|
110
|
+
when:
|
|
111
|
+
tag: epic
|
|
112
|
+
toColumn: Blocked
|
|
113
|
+
for:
|
|
114
|
+
related: child-of
|
|
115
|
+
direction: incoming
|
|
116
|
+
where:
|
|
117
|
+
is-completed: false
|
|
118
|
+
then:
|
|
119
|
+
- addTag: blocked-by-epic
|
|
120
|
+
|
|
56
121
|
# Custom fields (team, storyPoints, ...) can be interpolated here because they
|
|
57
122
|
# are declared in customFields above. See docs/index-structure.md for the full
|
|
58
123
|
# list of available variables.
|