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/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 is 948 bytes whether the project holds ten
57
- tasks or a thousand — while the journal behind it grows from 5 KB to 528 KB. The
58
- cost of asking does not grow with the history that makes the answer worth having.
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 can never drift from reality.
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 sprint create "Sprint 14"
106
- kadence task add "Fix login" -d "Broken since 2.3" --type bug --priority high --estimate 3
107
- kadence task comment KAD-1 "Session cookie is fine — the redirect drops it."
108
- kadence task move KAD-1 done
109
- kadence sprint close
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 Sprint 14 9 tasks, 28 points
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 / filter q quit
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 because the journal has the timestamps, the cost comes out of it for free:**
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
- Sprint "Sprint 14" closed.
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
- Velocity: 23 of 28 points
136
- Actual: 37h — 1.6h per point
193
+ ### Also in the box
137
194
 
138
- Carried over (2):
139
- · KAD-12 Auth refactor
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
- Nobody fills in a form. Nobody can forget to update it. The number is derived
143
- from state changes the team already made.
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
- An MCP wrapper stays on the roadmap as an **optional package**: it costs about
174
- 700 tokens a session over the CLI path — [we measured it](docs/research/probe-c-agent-cost.md),
175
- and it is not the saving the industry benchmarks suggest — but it would be a
176
- second way to say the same thing, and it would not work for agents that have no
177
- MCP client at all.
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 803 KB in full, and
180
- a tenth of that with the fields an agent actually reads:
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
- Documents stay plain markdown — `kadence task doc KAD-1 docs/design.md` records
201
- only the link, which is the part git cannot express.
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 | 38 KB packed — 132 KB of kadence, plus 1.8 MB of blessed |
213
- | Startup | 80 ms |
214
- | 10,000 events | 28 ms cold, 7 ms warm |
215
- | Journal on disk | 1.9 MB |
216
- | One task, as an agent reads it | 948 bytes — the same at 10 tasks or 1,000 |
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. 548 tests, including an end-to-end run through the
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
- **On the roadmap, not shipped.** The optional MCP package, kept as a response to
236
- someone who cannot use the CLI rather than as an inevitability.
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, 948 bytes, constant.
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 reality. Two branches writing at once produce two
260
- different files, and git merges them without a conflict by construction.
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 # 548 tests
270
- npm run build # 35 KB bundle
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};