kadence 0.3.2 → 0.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/CHANGELOG.md +515 -0
- package/README.md +158 -49
- package/dist/chunks/board-6UNW5ISX.js +12 -0
- package/dist/chunks/chunk-CDGGOWGY.js +10 -0
- package/dist/chunks/chunk-FCPGSN7D.js +240 -0
- package/dist/chunks/ui-Q53SLKMA.js +7 -0
- package/dist/cli.js +332 -60
- package/package.json +10 -8
- package/scripts/reference.mjs +120 -0
- package/dist/chunks/board-7RVHIQJQ.js +0 -12
- package/dist/chunks/chunk-EPFIY2MT.js +0 -199
- package/dist/chunks/ui-HHJ5TWYZ.js +0 -7
package/README.md
CHANGED
|
@@ -53,10 +53,11 @@ That is the whole state of a piece of work, in one call, with no server to ask
|
|
|
53
53
|
and no context to rebuild. A human reads it in `kadence task show`. An AI agent
|
|
54
54
|
reads the same thing as JSON.
|
|
55
55
|
|
|
56
|
-
**And it stays one call.** That answer
|
|
57
|
-
tasks or a thousand — while the journal behind it grows from 5 KB to
|
|
58
|
-
cost of asking does not grow with the history that makes the answer
|
|
59
|
-
[Measured](docs/research/probe-c-agent-cost.md)
|
|
56
|
+
**And it stays one call.** That answer stays under a kilobyte whether the project
|
|
57
|
+
holds ten tasks or a thousand — while the journal behind it grows from 5 KB to
|
|
58
|
+
528 KB. The cost of asking does not grow with the history that makes the answer
|
|
59
|
+
worth having. [Measured](docs/research/probe-c-agent-cost.md) at 948 bytes in
|
|
60
|
+
0.2; 982 bytes at 0.4, after claims and acceptance criteria joined every record.
|
|
60
61
|
|
|
61
62
|
## Why events and not files
|
|
62
63
|
|
|
@@ -76,7 +77,15 @@ journal is append-only, one file per event.
|
|
|
76
77
|
keeps every step, so «how did we get here» has an answer.
|
|
77
78
|
|
|
78
79
|
State is still there when you want it — it is folded from the journal on read,
|
|
79
|
-
which is why the board
|
|
80
|
+
which is why the board cannot drift from the journal. No one maintains a
|
|
81
|
+
column by hand, so no column can be stale in the way a task file can.
|
|
82
|
+
|
|
83
|
+
**What that does not buy you.** A journal records what was written to it. If a
|
|
84
|
+
piece of work stops being touched, the board stays perfectly accurate and
|
|
85
|
+
perfectly uninformative about it — the same way `git log` is honest about a
|
|
86
|
+
branch nobody pushed to. kadence removes the drift between what is shown and
|
|
87
|
+
what was recorded. It cannot remove the drift between what was recorded and
|
|
88
|
+
what is actually happening; that still takes someone looking.
|
|
80
89
|
|
|
81
90
|
---
|
|
82
91
|
|
|
@@ -99,48 +108,112 @@ Full data: [probe-a-results.md](docs/research/probe-a-results.md).
|
|
|
99
108
|
|
|
100
109
|
## In practice
|
|
101
110
|
|
|
111
|
+
**The first ten minutes, for the person who owns `CLAUDE.md`:**
|
|
112
|
+
|
|
102
113
|
```bash
|
|
103
|
-
kadence init
|
|
114
|
+
kadence init --hooks # .kadence/, a short section in AGENTS.md and CLAUDE.md,
|
|
115
|
+
# and a Claude Code hook that runs `kadence prime` at session start
|
|
116
|
+
kadence task add "Fix login" --type bug --priority high
|
|
117
|
+
kadence decision add "Keep sessions in Redis" \
|
|
118
|
+
--why "Revocation must be instant" --rejected "JWT: cannot revoke before expiry"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Open a new agent session. The hook runs `kadence prime`, and the agent starts
|
|
122
|
+
knowing what is open, what is ready and **DEC-1 with its reason** — ask it how
|
|
123
|
+
sessions should be stored and it answers from the journal, not from a guess.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git add .kadence AGENTS.md CLAUDE.md .claude/settings.json .gitignore
|
|
127
|
+
git commit -m "Keep the team's work next to the code"
|
|
128
|
+
```
|
|
104
129
|
|
|
105
|
-
kadence
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
130
|
+
kadence never commits for you. Once that commit is pushed, the journal is the
|
|
131
|
+
team's, not yours.
|
|
132
|
+
|
|
133
|
+
### Adding a teammate
|
|
134
|
+
|
|
135
|
+
1. **They install it:** `npm install -g kadence` (Node 20 or newer). Nothing is
|
|
136
|
+
configured per person.
|
|
137
|
+
2. **They pull.** Their agent reads the same section in `CLAUDE.md` or `AGENTS.md`
|
|
138
|
+
and, through the hook, runs `kadence prime` in its first session. On a machine
|
|
139
|
+
where kadence is not installed yet, the hook prints one line asking for the
|
|
140
|
+
install instead of failing.
|
|
141
|
+
3. **One identity per person.** Authorship is `git config user.email`. The same
|
|
142
|
+
person on two machines with two addresses reads as two people — set the same
|
|
143
|
+
address everywhere.
|
|
144
|
+
4. **Agents say so.** Put `KADENCE_SOURCE=agent` in the agent's environment; its
|
|
145
|
+
writes then carry `[agent]` next to the same email, so a person and their agent
|
|
146
|
+
stay distinguishable.
|
|
147
|
+
|
|
148
|
+
What you should see afterwards: `kadence task show KAD-1` lists their comment,
|
|
149
|
+
note or move with their address. That second author is the moment the journal
|
|
150
|
+
starts doing its job.
|
|
151
|
+
|
|
152
|
+
**When several people — or several agents — work the same board:**
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
kadence ready # open, unblocked, nobody else's
|
|
156
|
+
kadence task claim # take the top of that list, in one step
|
|
157
|
+
kadence note "Redirect drops the cookie, not the session" --task KAD-1
|
|
158
|
+
kadence task release KAD-1
|
|
110
159
|
```
|
|
111
160
|
|
|
161
|
+
There is no lock, and the message says so. Two machines can each claim before
|
|
162
|
+
either pushes; the merge keeps both claims and the task reads `contested` with
|
|
163
|
+
both names. Refusing the second one would make the owner depend on which branch
|
|
164
|
+
merged first, and that is the property the whole design rests on.
|
|
165
|
+
|
|
112
166
|
**The board, when you want to look at it:**
|
|
113
167
|
|
|
114
168
|
```
|
|
115
169
|
$ kadence ui
|
|
116
170
|
|
|
117
|
-
kadence
|
|
171
|
+
kadence 6 open · 2 ready · 1 decision in force
|
|
118
172
|
+- backlog (2) -------++- in_progress (1) --++- in_review (1) ----++- done (3) ---------+
|
|
119
173
|
| ^# KAD-1 Auth epic || . KAD-4 Tokens @dev||!! KAD-7 Crash [] || v KAD-2 Export |
|
|
120
174
|
| * KAD-3 Login form || || || v KAD-5 Docs |
|
|
121
175
|
+---------------------++--------------------++--------------------++--------------------+
|
|
122
|
-
arrows move enter details m status a assign e edit s sprint
|
|
176
|
+
arrows move enter details m status a assign e edit s sprint R ready b branch q quit
|
|
123
177
|
```
|
|
124
178
|
|
|
125
179
|
Keyboard, mouse, drag between columns, every field editable in place. It calls
|
|
126
180
|
the same commands the CLI does, so the two can never disagree.
|
|
127
181
|
|
|
128
|
-
**And
|
|
182
|
+
**And what the branch you are on is actually about:**
|
|
129
183
|
|
|
184
|
+
```bash
|
|
185
|
+
kadence task list --branch
|
|
130
186
|
```
|
|
131
|
-
$ kadence sprint close
|
|
132
187
|
|
|
133
|
-
|
|
188
|
+
Nothing is stored for that: which tasks belong to a branch lives in git's
|
|
189
|
+
history and is read when you ask. It narrows the answer between three and
|
|
190
|
+
twenty times, [measured](docs/research/branch-context-2026-09.md) on real board
|
|
191
|
+
sizes.
|
|
134
192
|
|
|
135
|
-
|
|
136
|
-
Actual: 37h — 1.6h per point
|
|
193
|
+
### Also in the box
|
|
137
194
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
195
|
+
Sprints (`sprint create/close`, with points and hours derived from moves),
|
|
196
|
+
milestones, acceptance criteria and a definition of done, templates, custom
|
|
197
|
+
columns, reports folded from the same journal (`report flow`, `cfd`,
|
|
198
|
+
`attention`; burndown, velocity and workload are in the repository and not yet
|
|
199
|
+
on npm), a self-contained HTML or Markdown export, `compact` for long journals,
|
|
200
|
+
and shell completion. None of it is required, and none of it is the point —
|
|
201
|
+
it is what the journal happens to know. Commands and caveats:
|
|
202
|
+
[docs/reports.md](docs/reports.md), and `--help` on each command.
|
|
203
|
+
|
|
204
|
+
### Removing kadence
|
|
205
|
+
|
|
206
|
+
Everything `init` touched, so you can undo it by hand:
|
|
207
|
+
|
|
208
|
+
| What | Where |
|
|
209
|
+
|---|---|
|
|
210
|
+
| The journal | `.kadence/` — yours to keep or delete; kadence never deletes it |
|
|
211
|
+
| The agent section | between `<!-- kadence:begin -->` and `<!-- kadence:end -->` in `AGENTS.md` and `CLAUDE.md` |
|
|
212
|
+
| The hook (only with `--hooks`) | the `SessionStart` entry that runs `kadence prime` in `.claude/settings.json` |
|
|
213
|
+
| The cache entry | the `.kadence/state.json` line in `.gitignore` |
|
|
141
214
|
|
|
142
|
-
|
|
143
|
-
|
|
215
|
+
Then `npm uninstall -g kadence`. Nothing lives outside the repository and your
|
|
216
|
+
global `node_modules`.
|
|
144
217
|
|
|
145
218
|
---
|
|
146
219
|
|
|
@@ -149,6 +222,18 @@ from state changes the team already made.
|
|
|
149
222
|
Files first. Every command speaks `--json`, every response carries
|
|
150
223
|
`schema: "kadence/v1"`, stdout is JSON and nothing else, warnings go to stderr.
|
|
151
224
|
|
|
225
|
+
Start a session with one command:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
kadence prime # active sprint, your work, what is ready, decisions in force, recent notes
|
|
229
|
+
kadence ready --json # seven fields per task, not the whole record
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`prime` is held to forty lines and three kilobytes by a test, because it is paid
|
|
233
|
+
for on every turn that follows. `kadence init --hooks` will add it as a
|
|
234
|
+
`SessionStart` hook — only with the flag, because `.claude/settings.json` is
|
|
235
|
+
yours.
|
|
236
|
+
|
|
152
237
|
```bash
|
|
153
238
|
kadence board --json
|
|
154
239
|
kadence task show KAD-1 --json # full history and comments
|
|
@@ -170,16 +255,19 @@ A failure carries `error.code` and, where the valid set is knowable, `allowed`
|
|
|
170
255
|
which matters most for statuses, because they are configured per project and no
|
|
171
256
|
documentation can tell an agent what yours are.
|
|
172
257
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
MCP client at
|
|
258
|
+
There is no MCP wrapper, and one gets built only as an **optional package**, when
|
|
259
|
+
someone who cannot run a CLI asks for it: it costs about 700 tokens a session
|
|
260
|
+
over the CLI path — [we measured it](docs/research/probe-c-agent-cost.md), and it
|
|
261
|
+
is not the saving the industry benchmarks suggest — it would be a second way to
|
|
262
|
+
say the same thing, and it would not work for agents that have no MCP client at
|
|
263
|
+
all.
|
|
178
264
|
|
|
179
|
-
Ask for only what you need — a board of a thousand tasks is
|
|
180
|
-
|
|
265
|
+
Ask for only what you need — a board of a thousand tasks is 855 KB in full,
|
|
266
|
+
275 KB with `--summary`, and 104 KB with the three fields an agent actually
|
|
267
|
+
reads:
|
|
181
268
|
|
|
182
269
|
```bash
|
|
270
|
+
kadence board --json --summary
|
|
183
271
|
kadence board --json --fields label,status,assignee
|
|
184
272
|
```
|
|
185
273
|
|
|
@@ -197,8 +285,16 @@ one; that is how a reversed decision keeps looking authoritative.
|
|
|
197
285
|
`decision list` returns what is still in force, `--all` adds the history, and
|
|
198
286
|
`task show --json` carries the decisions made about that task.
|
|
199
287
|
|
|
200
|
-
|
|
201
|
-
|
|
288
|
+
Something learned that was never a choice is a note, not a decision — no `--why`,
|
|
289
|
+
no number, and `prime` shows the latest:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
kadence note "The staging clock runs 40 s behind" --task KAD-1
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Documents stay plain markdown — `kadence task doc add KAD-1 docs/design.md`
|
|
296
|
+
records only the link, which is the part git cannot express. If the file does
|
|
297
|
+
not exist it is created from a template; an existing one is never overwritten.
|
|
202
298
|
|
|
203
299
|
Bulk works everywhere and is all or nothing: `kadence task move KAD-1,KAD-2 done`
|
|
204
300
|
either moves both or changes nothing. A typo does not leave half a board.
|
|
@@ -209,11 +305,11 @@ either moves both or changes nothing. A typo does not leave half a board.
|
|
|
209
305
|
|
|
210
306
|
| | |
|
|
211
307
|
|---|---|
|
|
212
|
-
| Install |
|
|
213
|
-
| Startup |
|
|
214
|
-
| 10,000 events |
|
|
215
|
-
| Journal on disk |
|
|
216
|
-
| One task, as an agent reads it |
|
|
308
|
+
| Install | 76 KB packed — 233 KB of kadence, plus 1.8 MB of blessed |
|
|
309
|
+
| Startup | 65 ms |
|
|
310
|
+
| 10,000 events | 21 ms cold with a compacted archive, 12 ms warm — 199 ms cold if every event is still a separate file |
|
|
311
|
+
| Journal on disk | under 5 MB |
|
|
312
|
+
| One task, as an agent reads it | 982 bytes, or 304 with `--summary` — the same however long the task has been worked on |
|
|
217
313
|
|
|
218
314
|
These are tests. They fail the build on regression, which is why they are still
|
|
219
315
|
true.
|
|
@@ -224,24 +320,35 @@ true.
|
|
|
224
320
|
|
|
225
321
|
**Verified.** The merge thesis, on real git branches. Performance and size, by
|
|
226
322
|
tests that fail if they regress. That the conflict problem exists in the wild —
|
|
227
|
-
measured, not assumed.
|
|
228
|
-
installed binary.
|
|
323
|
+
measured, not assumed. 998 tests in the repository today, including an
|
|
324
|
+
end-to-end run through the installed binary.
|
|
229
325
|
|
|
230
326
|
**Not verified.** That teams and their AI agents actually lose enough context to want
|
|
231
327
|
this. The bet rests on reasoning and on the industry naming the problem out
|
|
232
|
-
loud — not on our own users. That research is
|
|
233
|
-
[designed](docs/research/interview-script.md) and not yet run
|
|
328
|
+
loud — not on our own users. That research, Probe B, is
|
|
329
|
+
[designed](docs/research/interview-script.md) and not yet run: as of
|
|
330
|
+
2026-09-16, [zero conversations](docs/research/probe-b-results.md) and no
|
|
331
|
+
external users. [The strategy](docs/product/strategy.md) says what happens next
|
|
332
|
+
and on which dates.
|
|
333
|
+
|
|
334
|
+
**Not built, on purpose.** An MCP package — only if someone who cannot run a CLI
|
|
335
|
+
asks for it, not as an inevitability.
|
|
234
336
|
|
|
235
|
-
**
|
|
236
|
-
|
|
337
|
+
**In the repository, not yet released.** `report burndown`, `report velocity`,
|
|
338
|
+
`report workload`, `report --list` and `report <name> --html`. `npm install`
|
|
339
|
+
gives you 0.4.1 without them.
|
|
237
340
|
|
|
238
341
|
`kadence context <task>` was dropped: we measured what `task show --json` already
|
|
239
|
-
returns and it is the whole history of one piece of work,
|
|
342
|
+
returns and it is the whole history of one piece of work, under a kilobyte,
|
|
343
|
+
constant.
|
|
240
344
|
The only thing left to add was a different format, and nobody has asked for one.
|
|
241
345
|
|
|
242
346
|
**Known limits.** Conflicts are real but rare: roughly one merge in two hundred.
|
|
243
347
|
Terminal interaction is covered by manual testing; only the key router is
|
|
244
|
-
unit-tested.
|
|
348
|
+
unit-tested. Deleting several tasks by `KAD-N` in one loop removes the wrong
|
|
349
|
+
ones, because labels are derived and renumber as earlier tasks go — delete by
|
|
350
|
+
ULID, or one at a time. There is no `task ac remove`, so a board-wide Definition
|
|
351
|
+
of Done lands on every new task unless it is added with `--no-dod`.
|
|
245
352
|
|
|
246
353
|
---
|
|
247
354
|
|
|
@@ -256,8 +363,9 @@ unit-tested.
|
|
|
256
363
|
```
|
|
257
364
|
|
|
258
365
|
Every command appends one event. State is folded from the journal on read, so
|
|
259
|
-
the board cannot drift from
|
|
260
|
-
different files, and git merges them
|
|
366
|
+
the board cannot drift from the journal — nothing is maintained by hand. Two
|
|
367
|
+
branches writing at once produce two different files, and git merges them
|
|
368
|
+
without a conflict by construction.
|
|
261
369
|
|
|
262
370
|
Design decisions, each recording what was measured and what would make us
|
|
263
371
|
revisit it: [docs/decisions/](docs/decisions/).
|
|
@@ -266,8 +374,9 @@ revisit it: [docs/decisions/](docs/decisions/).
|
|
|
266
374
|
|
|
267
375
|
```bash
|
|
268
376
|
npm install
|
|
269
|
-
npm test
|
|
270
|
-
npm run
|
|
377
|
+
npm test # 998 tests; builds dist/cli.js first
|
|
378
|
+
npm run typecheck
|
|
379
|
+
npm run build # one 111 KB bundle, blessed kept external (82 KB at 0.4.1)
|
|
271
380
|
```
|
|
272
381
|
|
|
273
382
|
`CLAUDE.md` documents the invariants, the boundaries, and the decisions that
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{p as G}from"./chunk-CDGGOWGY.js";import S from"blessed";var c={border:"gray",borderFocus:"cyan",headerFg:"white",headerBg:"blue",selectedFg:"black",selectedBg:"cyan",dim:"gray",hint:"gray",warn:"yellow",danger:"red"},he={backlog:"gray",todo:"white",in_progress:"blue",doing:"blue",blocked:"red",in_review:"magenta",review:"magenta",done:"green",shipped:"green"};function ne(n){return he[n]??"white"}var pe={urgent:"\u203C",high:"\u2191",normal:" ",low:"\u2193"},me={urgent:"red",high:"yellow",normal:"white",low:"gray"},ye={bug:"\u2716",story:"\u25C6",epic:"\u2B22",task:"\xB7"};function k(n,s){return`{${s}-fg}${n}{/}`}function W(n,s){let d=k(pe[n.priority],me[n.priority]),p=k(ye[n.type]??"\xB7","gray"),h=k(n.label,"cyan"),g=[];n.blockedBy.length>0&&g.push(k("\u2298","red")),n.contestedBy.length>0?g.push(k("\u2691","magenta")):n.claimedBy!==null&&g.push(k("\u2713","green"));let E=n.criteria.filter(O=>!O.checked).length;if(E>0?g.push(k(`${n.criteria.length-E}/${n.criteria.length}`,"yellow")):n.criteria.length>0&&g.push(k("\u2611","green")),n.comments.length>0&&g.push(k("\u{1F4AC}","gray")),n.due!==null){let O=n.due<new Date().toISOString().slice(0,10);g.push(k(O?"\u23F0":"\u{1F4C5}",O?"red":"gray"))}let C=[n.assignee!==null?k(`@${n.assignee.split("@")[0]}`,"gray"):"",n.estimate!==null?k(String(n.estimate),"yellow"):"",g.join("")].filter(O=>O.length>0).join(" "),m=`${Y(d)}${Y(p)} ${Y(h)} `,b=C.length>0?` ${Y(C)}`:"",T=Math.max(4,s-m.length-b.length),F=[...n.title],q=F.length>T?`${F.slice(0,T-1).join("")}\u2026`:n.title;return`${d}${p} ${h} ${q}${C.length>0?` ${C}`:""}`}function z(n,s){return s?`{cyan-bg}{black-fg}${Y(n)}{/}`:` ${n}`}function J(n,s,d,p=12){let h=`${n.padEnd(p)} ${s}`;return d?`{cyan-bg}{black-fg}\u25B8 ${h}{/}`:` ${h}`}function Y(n){return n.replace(/\{[^}]*\}/g,"")}var re=["\u2190\u2192\u2191\u2193 move","enter details","[ ] shift","m status","a assign","c comment","e edit","n new","s sprint","/ filter","R ready","b branch","M milestone","C claim","? help","q quit (Ctrl-C forces)"].join(" ");function se(n){let s=[],d=!1;function p(){d=!0,setImmediate(()=>{d=!1})}return{dispatch(h){if(d)return;let g=s[s.length-1];g!==void 0?g(h):n(h)},push(h){s.push(h),p();let g=!1;return()=>{if(g)return;g=!0;let E=s.lastIndexOf(h);E!==-1&&s.splice(E,1),p()}},isDialogOpen:()=>s.length>0,depth:()=>s.length}}function oe(n){if(n.ch==="")return!1;let s=n.ch.charCodeAt(0);return s<32||s===127}var be=["up","down","pageup","pagedown","home","end"],ie=["escape","q"];function Q(n){return be.includes(n.name)}function le(n){return ie.includes(n.name)||ie.includes(n.ch)}function _(n,s,d){return d===0?0:(n+s+d)%d}var ae="cancelled",we="done";function ve(n){let s=[`${n.open} open`,`${n.ready} ready`,`${n.decisions} ${n.decisions===1?"decision":"decisions"} in force`,...n.points===null?[]:[`${n.points} points`]];return`kadence ${n.sprint===null?"":`${n.sprint} `}${s.join(" \xB7 ")}${n.filterNote}`}function Ie(n){let s=S.screen({smartCSR:!0,title:"kadence",fullUnicode:!0,mouse:!0}),{state:d}=n.reload(),p=n.branchEventIds(),h="",g=!1,E=!1,C="",m=[],b=0,T=se(t=>ge(t));function F(t=0){try{s.destroy()}catch{}process.exit(t)}process.on("SIGINT",()=>F(0)),process.on("SIGTERM",()=>F(0)),s.program.on("keypress",(t,r)=>{let i={ch:t??"",name:r?.name??""};(i.name==="C-c"||i.ch==="")&&F(0),!(oe(i)&&i.name!=="enter"&&i.name!=="escape")&&T.dispatch(i)});let q=S.box({parent:s,top:0,height:1,width:"100%",tags:!0,style:{fg:c.headerFg,bg:c.headerBg}}),O=S.box({parent:s,bottom:0,height:1,width:"100%",tags:!0,content:` ${re}`,style:{fg:c.hint}}),V=S.box({parent:s,bottom:1,height:1,width:"100%",tags:!0,style:{fg:c.warn}});function w(t,r=c.warn){V.setContent(` ${t}`),V.style.fg=r,s.render()}function A(){let t=d.statuses.filter(i=>i!==ae),r=d.orphanStatuses.filter(i=>i!==ae);return[...t,...r]}function de(t){let r=h.toLowerCase(),i=g?new Set(G(d.tasks,{viewer:n.actor,statuses:d.statuses,started:d.started}).map(a=>a.id)):null,e=E&&!("reason"in p)?p:null,o=e===null?null:a=>e.ids.has(a.id)||a.history.some(l=>e.ids.has(l.id)),u=C===""?null:d.milestones.find(a=>a.label===C)?.id??null;return d.tasks.filter(a=>a.status===t&&(i===null||i.has(a.id))&&(o===null||o(a))&&(u===null||a.milestone===u)&&(r===""||a.title.toLowerCase().includes(r)||(a.assignee??"").toLowerCase().includes(r)||a.labels.some(l=>l.toLowerCase().includes(r))))}function X(){for(let i of m)i.box.destroy();m=[];let t=A(),r=Math.max(Math.floor(100/Math.max(t.length,1)),12);t.forEach((i,e)=>{let o=S.box({parent:s,top:1,left:`${e*r}%`,width:`${r}%`,bottom:2,label:` ${i} `,border:{type:"line"},style:{border:{fg:c.border},label:{fg:ne(i)}}}),u=S.list({parent:o,top:0,left:0,right:0,bottom:0,keys:!1,mouse:!1,interactive:!1,tags:!0,scrollable:!0,style:{selected:{fg:c.selectedFg,bg:c.selectedBg},item:{fg:"white"}}});m.push({status:i,box:o,list:u,tasks:[],cursor:0})})}function y(t=!1){t&&(d=n.reload().state,p=n.branchEventIds()),m.length!==A().length&&X();let r=0,i=0,e=!1;m.forEach((l,N)=>{l.tasks=de(l.status),l.status!==we&&(r+=l.tasks.length,i+=l.tasks.reduce((R,M)=>R+(M.estimate??0),0)),l.tasks.some(R=>R.estimate!==null)&&(e=!0);let U=l.box.width-4;l.cursor=Math.min(l.cursor,Math.max(0,l.tasks.length-1)),l.list.setItems(l.tasks.map((R,M)=>z(W(R,U),N===b&&M===l.cursor)));let L=l.tasks.reduce((R,M)=>R+(M.estimate??0),0);l.box.setLabel(` ${l.status} (${l.tasks.length}${L>0?`, ${L}`:""}) `),l.box.style.border.fg=N===b?c.borderFocus:c.border});let o=d.sprints.find(l=>l.status==="active"),u=(h===""?"":` filter: "${h}"`)+(g?" ready only":"")+(E&&!("reason"in p)?` branch: ${p.name}`:"")+(C===""?"":` milestone: ${C}`),a=G(d.tasks,{viewer:n.actor,statuses:d.statuses,started:d.started}).length;q.setContent(` ${ve({sprint:o===void 0?null:o.name,open:r,ready:a,decisions:d.decisions.filter(l=>l.supersededBy===null).length,points:e?i:null,filterNote:u})}`),d.cycles.length>0&&w(`${d.cycles.length} dependency cycle(s) \u2014 see kadence task list`,c.danger),s.render()}function I(){let t=m[b];return t?.tasks[t.cursor]}function Z(){m.forEach((t,r)=>{let i=t.box.width-4;t.list.setItems(t.tasks.map((e,o)=>z(W(e,i),r===b&&o===t.cursor))),t.box.style.border.fg=r===b?c.borderFocus:c.border}),s.render()}function x(t){t!==null?w(t,c.warn):w(""),y(!0)}function K(t,r,i){let e=T.push(()=>{}),o=S.prompt({parent:s,top:"center",left:"center",width:"60%",height:7,border:{type:"line"},style:{border:{fg:c.borderFocus}},keys:!0,mouse:!0});o.input(t,r,(u,a)=>{o.destroy(),e(),s.render(),typeof a=="string"&&i(a.trim())})}function ue(t){let r=S.box({parent:s,top:"center",left:"center",width:"75%",height:"80%",border:{type:"line"},label:` ${t.label} `,style:{border:{fg:c.borderFocus}},tags:!0,keys:!0,mouse:!0}),i=[{key:"title",label:"title",value:()=>t.title,hint:"Title:"},{key:"description",label:"description",value:()=>(t.description??"\u2014").split(`
|
|
3
|
+
`)[0]??"\u2014",hint:"Description (one line here, e for $EDITOR):"},{key:"status",label:"status",value:()=>t.status,hint:`Status (${A().join(", ")}):`},{key:"type",label:"type",value:()=>t.type,hint:"Type (task, bug, story, epic):"},{key:"priority",label:"priority",value:()=>t.priority,hint:"Priority (low, normal, high, urgent):"},{key:"assignee",label:"assignee",value:()=>t.assignee??"\u2014",hint:'Assignee (or "none"):'},{key:"estimate",label:"estimate",value:()=>t.estimate===null?"\u2014":String(t.estimate),hint:"Estimate in points:"},{key:"due",label:"due",value:()=>t.due??"\u2014",hint:"Due date YYYY-MM-DD (empty clears):"},{key:"labels",label:"labels",value:()=>t.labels.length>0?t.labels.join(", "):"\u2014",hint:"Labels, comma separated:"}],e=()=>[...i.map((v,f)=>({kind:"field",index:f})),...t.criteria.map(v=>({kind:"criterion",n:v.n}))],o=S.list({parent:r,top:0,left:1,right:1,height:i.length+t.criteria.length,keys:!1,mouse:!1,interactive:!1,tags:!0,style:{selected:{fg:c.selectedFg,bg:c.selectedBg}}}),u=0,a=S.box({parent:r,top:i.length+t.criteria.length+1,left:1,right:1,bottom:0,tags:!0,scrollable:!0});function l(){o.setItems(e().map((f,B)=>f.kind==="field"?J(i[f.index].label,i[f.index].value(),B===u):J(` ${f.n}.`,`[${t.criteria.find($=>$.n===f.n)?.checked===!0?"x":" "}] ${t.criteria.find($=>$.n===f.n)?.text??""}`,B===u)));let v=[t.blockedBy.length>0?`{red-fg}blocked by ${t.blockedBy.length} task(s){/}`:"",t.parent!==null?"{gray-fg}has a parent{/}":"",t.loggedHours>0?`{gray-fg}logged ${t.loggedHours.toFixed(1)}h{/}`:"","",t.description!==null&&t.description.includes(`
|
|
4
|
+
`)?`{gray-fg}${t.description.split(`
|
|
5
|
+
`).slice(1).join(`
|
|
6
|
+
`)}{/}`:"",t.comments.length>0?`{cyan-fg}comments (${t.comments.length}){/}`:"",...t.comments.map(f=>` {gray-fg}${f.author}:{/} ${f.text}`),"",t.criteria.length>0?"{gray-fg}space toggles the criterion under the cursor{/}":"","{gray-fg}\u2191\u2193 field enter edit e description in $EDITOR esc close{/}","{gray-fg}description opens the editor, so it can hold paragraphs{/}"].filter(f=>f!=="");a.setContent(v.join(`
|
|
7
|
+
`)),s.render()}l();let N=T.push(v=>M(v)),U=()=>{N(),r.destroy(),y(!0)};function L(){d=n.reload().state;let v=d.tasks.find(f=>f.id===t.id);if(v===void 0){U();return}Object.assign(t,v),l()}function R(){let v=te(()=>n.edit(t.id));v!==null&&w(v),L()}function M(v){let{ch:f,name:B}=v;if(B==="up"||f==="k")return u=_(u,-1,e().length),l();if(B==="down"||f==="j")return u=_(u,1,e().length),l();if(f===" "||B==="space"){let $=e()[u];if($===void 0||$.kind!=="criterion")return;let j=t.criteria.find(H=>H.n===$.n);if(j===void 0)return;let D=n.toggleCriterion(t.id,$.n,j.checked);return D!==null&&w(D),L()}if(le(v))return U();if(f==="e")return R();if(B==="enter"){let $=e()[u];if($!==void 0&&$.kind==="criterion"){let D=t.criteria.find(fe=>fe.n===$.n);if(D===void 0)return;let H=n.toggleCriterion(t.id,$.n,D.checked);return H!==null&&w(H),L()}let j=i[u];if(j===void 0)return;if(j.key==="description")return R();K(j.hint,j.value()==="\u2014"?"":j.value(),D=>{let H=n.setField(t.id,j.key,D);H!==null&&w(H),L()})}}s.render()}function ce(){let t=["{cyan-fg}Navigation{/}"," \u2190 \u2192 h l move between columns"," \u2191 \u2193 k j move between tasks"," enter task details"," / filter escape clears it"," r reload from the journal"," R show only what can be started now (ready)"," b show only what this branch introduced"," M show only one milestone (empty clears it)"," C claim the selected task, or release it if it is yours","","{cyan-fg}Task actions{/}"," [ ] move one column left or right"," m move to a named status"," a assign c comment"," e edit description in $EDITOR"," p priority t log time"," n new task d delete","","{cyan-fg}Sprint{/}"," s sprint menu: status, start, close, burndown"," S add the selected task to the active sprint","","{cyan-fg}Mouse{/}"," click select a card"," drag move a card to another column","","{gray-fg}press any key to close{/}"],r=S.box({parent:s,top:"center",left:"center",width:60,height:Math.min(t.length+2,30),border:{type:"line"},label:" Keys ",style:{border:{fg:c.borderFocus}},tags:!0,scrollable:!0,keys:!0,mouse:!0,content:t.join(`
|
|
8
|
+
`)}),i=T.push(e=>{Q(e)||(i(),r.destroy(),y())});r.on("click",()=>{i(),r.destroy(),y()}),s.render()}function ee(t,r,i){let e=i.length>0?`
|
|
9
|
+
|
|
10
|
+
{gray-fg}${i.map(a=>`${a.key} ${a.label}`).join(" ")} esc close{/}`:`
|
|
11
|
+
|
|
12
|
+
{gray-fg}press any key to close{/}`,o=S.box({parent:s,top:"center",left:"center",width:"80%",height:"70%",border:{type:"line"},label:` ${t} `,style:{border:{fg:c.borderFocus}},tags:!0,scrollable:!0,mouse:!0,content:r+e}),u=T.push(a=>{if(Q(a))return;let l=i.find(N=>N.key===a.ch);u(),o.destroy(),l!==void 0?l.run():y()});s.render()}function ge(t){let{ch:r,name:i}=t;if(i==="left"||r==="h")return b=Math.max(0,b-1),y();if(i==="right"||r==="l")return b=Math.min(m.length-1,b+1),y();if(i==="up"||r==="k"){let e=m[b];e!==void 0&&(e.cursor=_(e.cursor,-1,e.tasks.length),Z());return}if(i==="down"||r==="j"){let e=m[b];e!==void 0&&(e.cursor=_(e.cursor,1,e.tasks.length),Z());return}if(i==="enter"){let e=I();e!==void 0&&ue(e);return}if(r==="["||r==="]"){let e=I();if(e===void 0)return;let o=r==="]"?1:-1,u=m[b+o];return u===void 0?void 0:(x(n.move(e.id,u.status)),b+=o,y())}if(r==="m"){let e=I();return e===void 0?void 0:K(`Move ${e.label} to (${A().join(", ")}):`,"",o=>x(n.move(e.id,o)))}if(r==="a"){let e=I();return e===void 0?void 0:K(`Assign ${e.label} to (or "none"):`,e.assignee??"",o=>x(n.assign(e.id,o)))}if(r==="c"){let e=I();return e===void 0?void 0:K(`Comment on ${e.label}:`,"",o=>x(n.comment(e.id,o)))}if(r==="t"){let e=I();return e===void 0?void 0:K(`Log time on ${e.label} (2h, 90m, -30m):`,"",o=>x(n.logTime(e.id,o)))}if(r==="p"){let e=I();return e===void 0?void 0:K(`Priority for ${e.label} (low, normal, high, urgent):`,e.priority,o=>x(n.setPriority(e.id,o)))}if(r==="n")return K("New task title:","",e=>x(n.create(e)));if(r==="d"){let e=I();return e===void 0?void 0:K(`Delete ${e.label}? type "yes":`,"",o=>{o.toLowerCase()==="yes"?x(n.remove(e.id)):w("Not deleted.")})}if(r==="e"){let e=I();return e===void 0?void 0:x(te(()=>n.edit(e.id)))}if(r==="S"){let e=I();return e===void 0?void 0:x(n.addToSprint(e.id))}if(r==="s")return ee("Sprint",n.sprintStatus(),[{key:"b",label:"burndown",run:()=>ee("Burndown",n.burndown(),[])},{key:"n",label:"start next sprint",run:()=>x(n.sprintStart())},{key:"x",label:"close sprint",run:()=>x(n.sprintClose())}]);if(r==="/")return K("Filter (title, assignee or label):",h,e=>{h=e==="*"?"":e,y()});if(i==="escape"&&h!=="")return h="",w("Filter cleared."),y();if(r==="r")return w("Reloaded."),y(!0);if(r==="R")return g=!g,w(g?"Showing only what can be started now.":"Showing every task."),y();if(r==="b")return"reason"in p?w(p.reason==="detached"?"HEAD is detached, so there is no branch to filter by.":`There is no branch "${p.base}" to compare against.`,c.warn):(E=!E,w(E?`Showing only what ${p.name} introduced since ${p.base}.`:"Showing every task."),y());if(r==="M"){if(d.milestones.length===0)return w("No milestones yet. Create one with kadence milestone create.",c.warn);let e=d.milestones.map(o=>`${o.label} ${o.name}`).join(", ");return K(`Milestone (${e}; empty for all):`,C,o=>{let u=o.trim();if(u==="")return C="",w("Showing every task."),y();let a=d.milestones.find(l=>l.label.toUpperCase()===u.toUpperCase()||l.name.toLowerCase()===u.toLowerCase());if(a===void 0)return w(`No milestone "${u}".`,c.warn);C=a.label,w(`Showing ${a.label} ${a.name}.`),y()})}if(r==="C"){let e=I();return e===void 0?void 0:x(e.claimedBy===null?n.claim(e.id):n.release(e.id))}if(r==="?")return ce();(r==="q"||i==="C-c")&&F(0)}function te(t){let r=s;r.leave();let i=t();return r.enter(),i}let P=null;m.forEach(()=>{}),s.on("mousedown",t=>{if(T.isDialogOpen())return;let r=m.findIndex(u=>{let a=u.box.left;return t.x>=a&&t.x<a+u.box.width});if(r===-1)return;b=r;let i=m[r],e=t.y-i.box.top-1,o=i.tasks[e];o!==void 0&&(i.cursor=e,P={task:o,from:r}),y()}),s.on("mouseup",t=>{if(T.isDialogOpen()||P===null)return;let r=m.findIndex(i=>{let e=i.box.left;return t.x>=e&&t.x<e+i.box.width});r!==-1&&r!==P.from&&(x(n.move(P.task.id,m[r].status)),b=r,y()),P=null}),X(),y()}export{ve as boardHeader,Ie as runBoardUi};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var I=["backlog","todo","in_progress","blocked","in_review","done","cancelled"],S="done",A="cancelled",P="in_progress",O="backlog",nt=["task","bug","story","epic"],st=["low","normal","high","urgent"];function $(t,e){for(let n=t.length-1;n>=0;n--)if(t[n].id<e)return t[n].started;return P}function pt(t){let e=[...t].sort((r,d)=>r.id<d.id?-1:r.id>d.id?1:0),n=new Map,u=new Map,a=new Map,o=new Map,p=new Map,s=[],l=null,i=[],f={started:P,changes:[]},y=[],g=[];for(let r of e)r.type==="task.created"?Y(n,r):r.type==="sprint.created"?Z(u,r):r.type==="milestone.created"&&q(a,r);for(let r of e){if(r.type==="decision.recorded"){let d=r.data??{},w=T=>typeof d[T]=="string"&&d[T].length>0?d[T]:null,c=w("title"),h=w("why");c!==null&&h!==null&&p.set(r.id,{id:r.id,label:"",title:c,why:h,rejected:w("rejected"),task:w("task"),docs:Array.isArray(d.docs)?d.docs.filter(T=>typeof T=="string"):[],supersedes:w("supersedes"),supersededBy:null,at:r.ts,by:r.actor,source:r.source});continue}if(r.type==="note.recorded"){let d=b(r.data?.text);d!==null&&s.push({id:r.id,text:d,task:b(r.data?.task),at:r.ts,by:r.actor,source:r.source});continue}if(r.type==="template.saved"){let d=typeof r.data?.name=="string"?r.data.name:null;d!==null&&o.set(d,{name:d,fields:r.data?.fields??{},author:r.actor});continue}if(r.type==="board.configured"){let d=r.data?.statuses;if(Array.isArray(d)){let h=d.filter(T=>typeof T=="string"&&T.length>0);h.length>0&&(l=h)}let w=r.data?.dod;Array.isArray(w)&&(i=w.filter(h=>typeof h=="string"&&h.length>0));let c=b(r.data?.started);c!==null&&(f.started=c,f.changes.push({id:r.id,started:c}));continue}if(r.type==="template.deleted"){let d=typeof r.data?.name=="string"?r.data.name:null;d!==null&&o.delete(d);continue}rt(r,n,u,a,y,g,f)}let k=g,m=[...n.values()].sort((r,d)=>r.id<d.id?-1:1);m.forEach((r,d)=>{r.label=`KAD-${d+1}`,ot(r),r.criteria.sort((w,c)=>w.id<c.id?-1:1),r.criteria.forEach((w,c)=>{w.n=c+1}),it(r)});let x=new Set(m.map(r=>r.id));for(let r of m)r.parent!==null&&!x.has(r.parent)&&(r.parent=null),r.blockedBy=r.blockedBy.filter(d=>x.has(d));let v=[...p.values()].sort((r,d)=>r.id<d.id?-1:1);v.forEach((r,d)=>{r.label=`DEC-${d+1}`});for(let r of v){if(r.supersedes===null)continue;let d=p.get(r.supersedes);d!==void 0&&(d.supersededBy=r.id)}let M=[...a.values()].sort((r,d)=>r.id<d.id?-1:1);M.forEach((r,d)=>{r.label=`MS-${d+1}`});let B=new Map;for(let r of m){if(r.milestone===null||!a.has(r.milestone))continue;let d=B.get(r.milestone)??[];d.push(r.id),B.set(r.milestone,d)}for(let r of M){r.taskIds=B.get(r.id)??[];let d=r.taskIds.map(w=>n.get(w)).filter(w=>w!==void 0);r.totalTasks=d.length,r.doneTasks=d.filter(w=>w.status===S).length,r.totalPoints=d.reduce((w,c)=>w+(c.estimate??0),0),r.donePoints=d.filter(w=>w.status===S).reduce((w,c)=>w+(c.estimate??0),0)}for(let r of v)r.task!==null&&!x.has(r.task)&&(r.task=null);for(let r of s)r.task!==null&&!x.has(r.task)&&(r.task=null);return{tasks:m,decisions:v,milestones:M,notes:s,sprints:[...u.values()].sort((r,d)=>r.id<d.id?-1:1),statuses:l??[...I],dod:i,started:f.started,startedChanges:[...f.changes],orphanStatuses:[...new Set(m.map(r=>r.status).filter(r=>!(l??I).includes(r)))].sort(),templates:[...o.values()].sort((r,d)=>r.name.localeCompare(d.name)),cycles:at(m),pending:k,rejected:y}}function Y(t,e){if(t.has(e.entity))return;let n=e.data??{};t.set(e.entity,{id:e.entity,label:"",title:typeof n.title=="string"?n.title:"(untitled)",description:b(n.description),type:z(n.type),priority:G(n.priority),status:O,labels:J(n.labels),assignee:b(n.assignee),reporter:e.actor,sprint:null,milestone:null,parent:b(n.parent),blockedBy:[],due:b(n.due),claimedBy:null,claimedAt:null,claimedEventId:null,contestedBy:[],criteria:[],comments:[],docs:[],estimate:typeof n.estimate=="number"?n.estimate:null,loggedHours:0,createdAt:e.ts,updatedAt:e.ts,history:[]})}function Z(t,e){if(t.has(e.entity))return;let n=e.data??{};t.set(e.entity,{id:e.entity,name:typeof n.name=="string"?n.name:"(untitled)",description:b(n.description),startDate:b(n.startDate),endDate:b(n.endDate),status:"planned",closedBy:null,taskIds:[]})}function q(t,e){if(t.has(e.entity))return;let n=e.data??{};t.set(e.entity,{id:e.entity,label:"",name:typeof n.name=="string"?n.name:"(untitled)",due:b(n.due),status:"open",closedBy:null,taskIds:[],donePoints:0,totalPoints:0,doneTasks:0,totalTasks:0})}function rt(t,e,n,u,a,o,p){let s=t.data??{};if(t.type==="milestone.created")return q(u,t),!0;if(t.type==="milestone.task_added"){let i=b(s.task);if(i===null)return!0;let f=e.get(i);return f===void 0?(o.push(t),!1):(f.milestone=t.entity,!0)}if(t.type==="milestone.closed"){let i=u.get(t.entity);return i===void 0?(o.push(t),!1):(i.closedBy===null?(i.status="closed",i.closedBy=t.id):a.push(t),!0)}if(t.type==="task.created")return Y(e,t),W(e.get(t.entity),t),!0;if(t.type==="sprint.created")return Z(n,t),!0;if(t.type.startsWith("sprint.")){let i=n.get(t.entity);if(i===void 0)return o.push(t),!1;if(t.type==="sprint.started")return i.status==="planned"&&(i.status="active"),!0;if(t.type==="sprint.updated")return i.status==="closed"?(a.push(t),!0):(typeof s.name=="string"&&(i.name=s.name),s.description!==void 0&&(i.description=b(s.description)),s.startDate!==void 0&&(i.startDate=b(s.startDate)),s.endDate!==void 0&&(i.endDate=b(s.endDate)),!0);if(t.type==="sprint.closed")return i.closedBy===null?(i.status="closed",i.closedBy=t.id):a.push(t),!0;if(t.type==="sprint.cancelled")return i.status==="planned"?i.status="cancelled":a.push(t),!0;if(t.type==="sprint.task_added"){if(i.status==="closed")return a.push(t),!0;let f=typeof s.task=="string"?s.task:null;if(f===null)return!0;let y=e.get(f);return y===void 0?(o.push(t),!1):(y.sprint=i.id,i.taskIds.includes(f)||i.taskIds.push(f),W(y,t),!0)}return!0}let l=e.get(t.entity);if(l===void 0)return o.push(t),!1;if(t.type==="task.deleted")return e.delete(t.entity),!0;switch(t.type){case"task.moved":{let i=s.to;typeof i=="string"&&i.length>0&&(l.status=i);break}case"task.cancelled":l.status=A;break;case"task.reopened":l.status=$(p.changes,t.id);break;case"task.assigned":l.assignee=b(s.assignee);break;case"task.updated":typeof s.title=="string"&&(l.title=s.title),s.description!==void 0&&(l.description=b(s.description)),s.type!==void 0&&(l.type=z(s.type)),s.priority!==void 0&&(l.priority=G(s.priority)),s.labels!==void 0&&(l.labels=J(s.labels)),s.due!==void 0&&(l.due=b(s.due)),typeof s.estimate=="number"&&(l.estimate=s.estimate);break;case"task.parent_set":l.parent=b(s.parent);break;case"task.blocked_by_added":{let i=b(s.blocker);i!==null&&!l.blockedBy.includes(i)&&l.blockedBy.push(i);break}case"task.blocked_by_removed":{let i=b(s.blocker);i!==null&&(l.blockedBy=l.blockedBy.filter(f=>f!==i));break}case"task.label_added":{let i=b(s.label);i!==null&&!l.labels.includes(i)&&l.labels.push(i);break}case"task.label_removed":{let i=b(s.label);i!==null&&(l.labels=l.labels.filter(f=>f!==i));break}case"task.criterion_added":{let i=b(s.text);i!==null&&l.criteria.push({id:t.id,n:0,text:i,checked:!1,checkedBy:null});break}case"task.criterion_checked":case"task.criterion_unchecked":break;case"task.doc_linked":{let i=b(s.path);i!==null&&!l.docs.includes(i)&&l.docs.push(i);break}case"task.time_logged":{let i=s.hours;typeof i=="number"&&Number.isFinite(i)&&(l.loggedHours=Math.max(0,l.loggedHours+i));break}case"task.commented":{let i=b(s.text);i!==null&&l.comments.push({id:t.id,author:t.actor,ts:t.ts,text:i,source:t.source});break}default:break}return W(l,t),!0}function it(t){let e=new Map(t.criteria.map(n=>[n.id,n]));for(let n of t.criteria)n.checked=!1,n.checkedBy=null;for(let n of t.history){if(n.type!=="task.criterion_checked"&&n.type!=="task.criterion_unchecked")continue;let u=b(n.data.criterion),a=u===null?void 0:e.get(u);if(a===void 0)continue;let o=n.type==="task.criterion_checked";a.checked=o,a.checkedBy=o?n.actor:null}}function ot(t){let e=null,n=null,u=null,a=[],o=()=>{e=null,n=null,u=null,a=[]};for(let p of t.history)if(p.type==="task.claimed"){let s=b(p.data.by)??p.actor;e===null?(e=s,n=p.ts,u=p.id):s!==e&&!a.includes(s)&&a.push(s)}else if(p.type==="task.released"){let s=b(p.data.by)??p.actor;e===s?o():a=a.filter(l=>l!==s)}else(p.type==="task.cancelled"||p.type==="task.moved"&&p.data.to===S)&&o();t.claimedBy=e,t.claimedAt=n,t.claimedEventId=u,t.contestedBy=a}function W(t,e){t.history.push({id:e.id,type:e.type,actor:e.actor,ts:e.ts,source:e.source,data:e.data??{}}),e.ts>t.updatedAt&&(t.updatedAt=e.ts)}function b(t){return typeof t=="string"&&t.length>0?t:null}function z(t){return nt.includes(t)?t:"task"}function G(t){return st.includes(t)?t:"normal"}function J(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"&&e.length>0):[]}function at(t){let e=new Map(t.map(o=>[o.id,o])),n=[],u=new Set,a=(o,p)=>{let s=new Map,l=(i,f)=>{let y=s.get(i);if(y==="done")return;if(y==="visiting"){let k=f.indexOf(i),m=f.slice(k),x=`${o}:${[...m].sort().join(",")}`;u.has(x)||(u.add(x),n.push({kind:o,path:[...m,i]}));return}s.set(i,"visiting");let g=e.get(i);if(g!==void 0)for(let k of p(g))l(k,[...f,i]);s.set(i,"done")};for(let i of t)l(i.id,[])};return a("parent",o=>o.parent===null?[]:[o.parent]),a("blocking",o=>o.blockedBy),n}var D=864e5;function C(t){return new Date(t).toISOString().slice(0,10)}function E(t){return Date.parse(`${t}T00:00:00.000Z`)}function lt(t){let e=new Date(E(t)),n=(e.getUTCDay()+6)%7;return C(e.getTime()-n*D)}function V(t,e){let n=C(t.getTime());return{from:C(E(n)-(e-1)*D),to:n,days:e}}function Q(t,e){let n=t.slice(0,10);return n>=e.from&&n<=e.to}function F(t,e){if(t.length===0)return 0;let n=Math.max(1,Math.ceil(e*t.length));return t[n-1]}var _=t=>Math.round(t*10)/10;function K(t){if(t.length===0)return null;let e=[...t].sort((n,u)=>n-u);return{shown:{n:e.length,p50:_(F(e,.5)),p85:_(F(e,.85)),p95:_(F(e,.95))},rawP85:F(e,.85)}}function N(t,e){let n=t.indexOf(e);return n===-1?null:new Set(t.slice(0,n))}function H(t,e,n=[],u=[]){let a=N(n,e),o=s=>s===S||s===A?!1:a===null?s===e:s===e||!a.has(s);return t.history.find(s=>s.type==="task.reopened"?o($(u,s.id)):s.type==="task.moved"&&typeof s.data.to=="string"&&o(s.data.to))?.ts??null}function dt(t,e){if(e.status===S||e.status===A)return!1;let n=N(t.statuses,t.started);return n===null?e.status===t.started:n.has(e.status)?!1:H(e,t.started,t.statuses,t.startedChanges)!==null}function R(t){if(t.status!==S)return null;for(let e=t.history.length-1;e>=0;e--){let n=t.history[e];if(n.type==="task.moved"&&n.data.to===S)return n.ts}return null}function ut(t){if(t.status!==A)return null;for(let e=t.history.length-1;e>=0;e--){let n=t.history[e];if(n.type==="task.cancelled"||n.type==="task.moved"&&n.data.to===A)return n.ts}return null}function L(t,e){return Math.max(0,(Date.parse(e)-Date.parse(t))/D)}function ct(t,e,n,u){let a=R(t)??ut(t),o=a===null?u:Date.parse(a),p=[],s=new Map;for(let k of t.history){let m=typeof k.data.blocker=="string"?k.data.blocker:null;if(m!==null){if(k.type==="task.blocked_by_added")s.has(m)||s.set(m,Date.parse(k.ts));else if(k.type==="task.blocked_by_removed"){let x=s.get(m);x!==void 0&&(p.push([x,Math.min(Date.parse(k.ts),o)]),s.delete(m))}}}for(let[k,m]of s){let x=Math.min(e.get(k)??u,o);p.push([m,Math.max(m,x)])}let l=E(n.from),i=E(n.to)+D,f=p.map(([k,m])=>[Math.max(k,l),Math.min(m,i)]).filter(([k,m])=>m>k).sort((k,m)=>k[0]-m[0]),y=0,g=null;for(let[k,m]of f)g===null||k>g[1]?(g!==null&&(y+=g[1]-g[0]),g=[k,m]):m>g[1]&&(g[1]=m);return g!==null&&(y+=g[1]-g[0]),y/D}function mt(t,e=new Date,n=30){let u=V(e,n),a=e.getTime(),o=[],p=t.tasks.filter(c=>c.status!==A),s=p.filter(c=>{let h=R(c);return h!==null&&Q(h,u)}),l=[],i=[],f=[];for(let c of s){let h=R(c),T=H(c,t.started,t.statuses,t.startedChanges);i.push(L(c.createdAt,h)),T!==null&&(l.push(L(T,h)),f.push(L(c.createdAt,T)))}let y=K(l),g=y?.shown??null,k=p.filter(c=>dt(t,c)),m=k.map(c=>{let h=L(H(c,t.started,t.statuses,t.startedChanges),e.toISOString());return{label:c.label,title:c.title,status:c.status,ageDays:_(h),overP85:y!==null&&h>y.rawP85}}).sort((c,h)=>h.ageDays-c.ageDays),x=new Map,v=c=>{let h=lt(c),T=x.get(h);return T===void 0&&(T={week:h,finished:0,created:0},x.set(h,T)),T};for(let c=E(u.from);c<=E(u.to);c+=D)v(C(c));for(let c of s)v(R(c).slice(0,10)).finished+=1;for(let c of p)Q(c.createdAt,u)&&(v(c.createdAt.slice(0,10)).created+=1);let M=[...x.values()].sort((c,h)=>c.week<h.week?-1:1),B=new Map;for(let c of t.tasks){let h=R(c);h!==null&&B.set(c.id,Date.parse(h))}let r=0,d=0;for(let c of p){let h=ct(c,B,u,a);h>0&&(r+=1,d+=h)}t.tasks.length===0?o.push("No tasks yet."):(k.length===0&&s.length===0&&o.push(`Nothing has crossed "${t.started}" yet. If work starts in another column, say so: kadence board config --started <status>`),s.length===0?o.push(`Nothing finished between ${u.from} and ${u.to}; cycle time needs finished work.`):l.length===0&&o.push(`${s.length} item(s) finished without ever entering "${t.started}", so there is no cycle time to measure.`),t.statuses.includes(t.started)||o.push(`"${t.started}" is not one of the board's columns; set one with kadence board config --started.`));let w=g===null?null:`${g.n<10?`From ${g.n} finished items (a small sample): `:""}85% of finished items took ${g.p85} calendar days or less.`;return{window:u,started:t.started,unit:"calendar days",wip:k.length,finished:s.length,perWeek:M,cycleTime:g,leadTime:K(i)?.shown??null,responseTime:K(f)?.shown??null,sle:w,aging:m,blocked:{tasks:r,days:_(d)},notes:o}}function ht(t,e=new Date,n=30){let u=V(e,n),a=[...t.statuses];for(let l of t.tasks)a.includes(l.status)||a.push(l.status);let o=O;!a.includes(o)&&t.tasks.length>0&&a.unshift(o);let p=t.tasks.map(l=>{let i=[];for(let f of l.history)f.type==="task.moved"&&typeof f.data.to=="string"?i.push({at:Date.parse(f.ts),status:f.data.to}):f.type==="task.cancelled"?i.push({at:Date.parse(f.ts),status:A}):f.type==="task.reopened"&&i.push({at:Date.parse(f.ts),status:$(t.startedChanges,f.id)});return{createdAt:Date.parse(l.createdAt),changes:i,next:0,status:o}}),s=[];for(let l=E(u.from);l<=E(u.to);l+=D){let i=l+D,f={};for(let y of a)f[y]=0;for(let y of p){for(;y.next<y.changes.length&&y.changes[y.next].at<i;)y.status=y.changes[y.next].status,y.next+=1;y.createdAt>=i||(f[y.status]=(f[y.status]??0)+1)}s.push({date:C(l),counts:f})}return{window:u,statuses:a,days:s}}var j={urgent:0,high:1,normal:2,low:3};function X(t,e){return t!==null&&t.toLowerCase().includes(e)}function Tt(t,e,n=new Date){let u=n.toISOString().slice(0,10),a=e.search?.toLowerCase();return t.filter(o=>{if(a!==void 0&&a.length>0){let p=o.comments.some(s=>s.text.toLowerCase().includes(a));if(!X(o.title,a)&&!X(o.description,a)&&!p)return!1}if(e.status!==void 0&&o.status!==e.status||e.type!==void 0&&o.type!==e.type||e.priority!==void 0&&o.priority!==e.priority)return!1;if(e.assignee!==void 0){let p=e.assignee.toLowerCase(),s=(o.assignee??"").toLowerCase();if(p==="none"?s!=="":s!==p)return!1}if(e.label!==void 0){let p=e.label.toLowerCase();if(!o.labels.some(s=>s.toLowerCase()===p))return!1}return!(e.sprint!==void 0&&o.sprint!==e.sprint||e.overdue===!0&&(o.due===null||o.due>=u)||e.dueBefore!==void 0&&(o.due===null||o.due>=e.dueBefore))})}function xt(t,e){let n=[...t];switch(e){case"priority":return n.sort((u,a)=>j[u.priority]-j[a.priority]);case"due":return n.sort((u,a)=>tt(u.due,a.due,(o,p)=>o<p?-1:o>p?1:0));case"estimate":return n.sort((u,a)=>tt(u.estimate,a.estimate,(o,p)=>p-o));default:return n.sort((u,a)=>u.id<a.id?-1:u.id>a.id?1:0)}}function tt(t,e,n){return t===null&&e===null?0:t===null?1:e===null?-1:n(t,e)}function St(t){let e=[];return t.search!==void 0&&e.push(`search="${t.search}"`),t.status!==void 0&&e.push(`status=${t.status}`),t.type!==void 0&&e.push(`type=${t.type}`),t.priority!==void 0&&e.push(`priority=${t.priority}`),t.assignee!==void 0&&e.push(`assignee=${t.assignee}`),t.label!==void 0&&e.push(`label=${t.label}`),t.overdue===!0&&e.push("overdue"),t.dueBefore!==void 0&&e.push(`due before ${t.dueBefore}`),e.length===0?`No tasks yet.
|
|
3
|
+
Create the first one:
|
|
4
|
+
kadence task add "title"`:`No tasks match ${e.join(" and ")}.
|
|
5
|
+
Try fewer filters:
|
|
6
|
+
kadence task list`}function et(t){return N(t.statuses??I,t.started??P)}function U(t){return t.status===S||t.status===A}function At(t,e={}){let n=e.viewer??null,u=new Set(t.filter(U).map(s=>s.id)),a=e.assignee==="me"?n:e.assignee??null,o=et(e);return t.filter(s=>{if(U(s)||o!==null&&!o.has(s.status)||s.blockedBy.some(l=>!u.has(l))||s.claimedBy!==null&&s.claimedBy!==n&&s.contestedBy.length===0)return!1;if(a!==null){if(a==="none")return s.assignee===null;if(s.assignee!==a)return!1}return!0}).sort((s,l)=>j[s.priority]-j[l.priority]||(s.id<l.id?-1:1))}function vt(t,e,n,u={}){if(t.length===0)return`No tasks yet.
|
|
7
|
+
Create the first one:
|
|
8
|
+
kadence task add "title"`;let a=t.filter(g=>!U(g));if(a.length===0)return"Nothing ready: every task is done or cancelled.";let o=new Set(t.filter(U).map(g=>g.id)),p=et(u),s=p===null?0:a.filter(g=>!p.has(g.status)).length,l=a.filter(g=>g.blockedBy.some(k=>!o.has(k))).length,i=a.filter(g=>g.claimedBy!==null&&g.claimedBy!==e&&g.contestedBy.length===0).length,f=[];return s>0&&f.push(`${s} already started`),l>0&&f.push(`${l} blocked`),i>0&&f.push(`${i} claimed by someone else`),n!==void 0&&f.push(`filtered to assignee=${n}`),`Nothing ready${f.length>0?` (${f.join(", ")})`:""}.
|
|
9
|
+
See the whole board:
|
|
10
|
+
kadence task list`}var ft=["created","priority","due","estimate"];function Dt(t){return ft.includes(t)}export{I as a,S as b,A as c,nt as d,st as e,pt as f,H as g,dt as h,R as i,mt as j,ht as k,Tt as l,xt as m,St as n,U as o,At as p,vt as q,ft as r,Dt as s};
|