kadence 0.1.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 +56 -0
- package/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/chunks/board-7RVHIQJQ.js +12 -0
- package/dist/chunks/chunk-BUHXYOZ4.js +90 -0
- package/dist/chunks/ui-WFLRVGUH.js +7 -0
- package/dist/cli.js +135 -0
- package/package.json +58 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] — 2026-09-03
|
|
4
|
+
|
|
5
|
+
First release. Tasks, sprints and velocity as plain files inside a git
|
|
6
|
+
repository, with no server, account or network.
|
|
7
|
+
|
|
8
|
+
### Core
|
|
9
|
+
|
|
10
|
+
- Append-only event journal: one file per event, never rewritten. Two branches
|
|
11
|
+
editing the same task merge without a conflict — verified on real git
|
|
12
|
+
branches, not only in theory.
|
|
13
|
+
- State folded from the journal on every read, so the board cannot drift from
|
|
14
|
+
reality. A snapshot cache makes that cost 7 ms on 10,000 events.
|
|
15
|
+
- ULID identifiers, so event order is a property of the id rather than of how
|
|
16
|
+
far apart machine clocks have drifted.
|
|
17
|
+
|
|
18
|
+
### Tasks
|
|
19
|
+
|
|
20
|
+
- Title, description, type (task/bug/story/epic), priority, labels, assignee,
|
|
21
|
+
due date, estimate, comments, logged time.
|
|
22
|
+
- Subtasks and blocking dependencies, with cycle detection that **reports** a
|
|
23
|
+
loop instead of rejecting the later edit — rejecting it would make the state
|
|
24
|
+
depend on merge order.
|
|
25
|
+
- Search across titles, descriptions and comments; filters, sorting, and bulk
|
|
26
|
+
operations that apply all-or-nothing.
|
|
27
|
+
- Templates for repeated task shapes.
|
|
28
|
+
|
|
29
|
+
### Sprints
|
|
30
|
+
|
|
31
|
+
- Plan the next sprint while the current one runs.
|
|
32
|
+
- Velocity and hours-per-point derived from events, so the numbers cannot be
|
|
33
|
+
forgotten or faked.
|
|
34
|
+
- Burndown reconstructed from the journal for any day — including days before
|
|
35
|
+
the feature existed.
|
|
36
|
+
|
|
37
|
+
### Board
|
|
38
|
+
|
|
39
|
+
- `kadence board` — plain columns for pipes and scripts.
|
|
40
|
+
- `kadence ui` — interactive kanban: keyboard, mouse, drag between columns, and
|
|
41
|
+
every field editable in place. Loads lazily, so `kadence task add` never pays
|
|
42
|
+
for it.
|
|
43
|
+
- Custom columns per team; `done` cannot be removed because every analytic is
|
|
44
|
+
computed from it.
|
|
45
|
+
|
|
46
|
+
### For agents
|
|
47
|
+
|
|
48
|
+
- `--json` on every command with a stable `schema: "kadence/v1"`.
|
|
49
|
+
- stdout carries JSON only; warnings go to stderr.
|
|
50
|
+
- `init` writes a guide the agent finds on its own.
|
|
51
|
+
|
|
52
|
+
### Known limits
|
|
53
|
+
|
|
54
|
+
- The velocity bet is not yet validated with users. See README, Honest status.
|
|
55
|
+
- Terminal interaction is covered by manual testing; only the key router is
|
|
56
|
+
unit-tested.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FlowIt contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# kadence
|
|
2
|
+
|
|
3
|
+
**Your team plans sprints on gut feel. kadence counts what it actually delivers — from a journal that lives in your repository.**
|
|
4
|
+
|
|
5
|
+
Tasks, sprints and velocity as plain files inside your git repo. No server, no
|
|
6
|
+
account, no network. Works offline, and works for AI agents because the data is
|
|
7
|
+
just files they can read.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx kadence init
|
|
11
|
+
npx kadence task add "Fix login" -d "Broken since 2.3" --type bug --estimate 3
|
|
12
|
+
npx kadence ui # interactive board, or `kadence board` for a plain list
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> **v0.1.0.** The architecture is measured and covered by 383 tests. The product
|
|
16
|
+
> bet — that teams want sprint analytics in their repo — has not been validated
|
|
17
|
+
> with users yet. See [Honest status](#honest-status).
|
|
18
|
+
|
|
19
|
+
## Why another tracker
|
|
20
|
+
|
|
21
|
+
There are good file-based trackers already:
|
|
22
|
+
[git-bug](https://github.com/git-bug/git-bug),
|
|
23
|
+
[Backlog.md](https://github.com/MrLesk/Backlog.md),
|
|
24
|
+
[git-issues](https://steviee.github.io/git-issues/). kadence differs in two ways.
|
|
25
|
+
|
|
26
|
+
**1. It never conflicts on merge.** The others store a task as a *mutable* file,
|
|
27
|
+
so two branches touching one task collide. kadence stores an append-only journal
|
|
28
|
+
of events: one file per event, never rewritten.
|
|
29
|
+
|
|
30
|
+
We measured this rather than assumed it. Across **8,396 merge commits from 130
|
|
31
|
+
public repositories** using file-based trackers, conflicts in task files occur
|
|
32
|
+
in 15% of repositories — and **89% of them are `CONFLICT (content)`**, exactly
|
|
33
|
+
the type this design eliminates. Full data:
|
|
34
|
+
[probe-a-results.md](docs/research/probe-a-results.md).
|
|
35
|
+
|
|
36
|
+
**2. It computes velocity.** None of the three tracks sprints, velocity, or how
|
|
37
|
+
estimates compare with reality. kadence derives all of it from the journal, so
|
|
38
|
+
the numbers cannot be forgotten or faked — they are a product of the work.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Sprint "Sprint 12" closed.
|
|
42
|
+
|
|
43
|
+
Velocity: 10 of 10 points
|
|
44
|
+
Actual: 16h — 1.6h per point
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
That last number is the point of the whole tool: what a story point actually
|
|
48
|
+
costs your team.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
Requires Node 20 or newer, and a git repository.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx kadence init
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
No global install needed. `init` creates `.kadence/`, adds the derived cache to
|
|
59
|
+
`.gitignore`, and writes a short guide for AI agents. It does **not** commit
|
|
60
|
+
anything — that call is yours.
|
|
61
|
+
|
|
62
|
+
## Commands
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
kadence init set up kadence in this repository
|
|
66
|
+
kadence ui interactive kanban board
|
|
67
|
+
|
|
68
|
+
kadence task add "<title>" create a task
|
|
69
|
+
-d, --description <text> full description
|
|
70
|
+
--type task|bug|story|epic type; an epic is simply a parent task
|
|
71
|
+
--priority low|normal|high|urgent
|
|
72
|
+
-a, --assignee <who> assignee
|
|
73
|
+
--label <name> label; repeat for several
|
|
74
|
+
--due <date> deadline, YYYY-MM-DD
|
|
75
|
+
--parent <task> make it a subtask
|
|
76
|
+
--template <name> pre-fill from a saved template
|
|
77
|
+
--estimate <points> estimate, always last
|
|
78
|
+
kadence task list list tasks
|
|
79
|
+
--search <text> title, description and comments
|
|
80
|
+
--status|--type|--priority|--assignee|--label
|
|
81
|
+
--overdue --due-before <date>
|
|
82
|
+
--sort created|priority|due|estimate
|
|
83
|
+
--tree show parent/child structure
|
|
84
|
+
kadence task show KAD-1 full detail and history
|
|
85
|
+
kadence task edit KAD-1 opens $EDITOR; or pass field flags
|
|
86
|
+
kadence task move KAD-1 done change state
|
|
87
|
+
kadence task assign KAD-1 <who> assign; "none" unassigns
|
|
88
|
+
kadence task comment KAD-1 "text" comment
|
|
89
|
+
kadence task log KAD-1 2h log time; 90m, -30m to correct
|
|
90
|
+
kadence task parent KAD-2 KAD-1 nest under a parent
|
|
91
|
+
kadence task block KAD-2 KAD-1 KAD-2 waits for KAD-1
|
|
92
|
+
kadence task cancel KAD-1 keeps it in history
|
|
93
|
+
kadence task delete KAD-1 drops it from the board
|
|
94
|
+
|
|
95
|
+
kadence board plain board, one column per status
|
|
96
|
+
-a, --assignee me only your tasks
|
|
97
|
+
--sprint only the active sprint
|
|
98
|
+
kadence board config show or change the columns
|
|
99
|
+
--statuses "todo,doing,done" your own workflow
|
|
100
|
+
|
|
101
|
+
kadence sprint create "Sprint 1" first starts now, later ones are planned
|
|
102
|
+
kadence sprint add KAD-1 [--sprint "Sprint 2"]
|
|
103
|
+
kadence sprint start ["Sprint 2"] start the next planned sprint
|
|
104
|
+
kadence sprint close close and report velocity
|
|
105
|
+
kadence sprint status progress of the active sprint
|
|
106
|
+
kadence sprint burndown chart rebuilt from the journal
|
|
107
|
+
kadence sprint list every sprint
|
|
108
|
+
|
|
109
|
+
kadence template save bug --type bug --priority high
|
|
110
|
+
kadence template list | delete <name>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Most commands accept several tasks at once — `kadence task move KAD-1,KAD-2 done`
|
|
114
|
+
— and apply **all or nothing**: if one id does not exist, nothing changes.
|
|
115
|
+
|
|
116
|
+
Add `--json` to any command for a stable machine-readable shape.
|
|
117
|
+
|
|
118
|
+
## The interactive board
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
kadence ui
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Columns side by side, mouse and keyboard:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
←→ column ↑↓ task enter details [ ] shift a card
|
|
128
|
+
m status a assign c comment e edit in $EDITOR
|
|
129
|
+
p priority t log time n new d delete
|
|
130
|
+
s sprint menu (status, burndown, start, close) S add to sprint
|
|
131
|
+
/ filter ? help q quit
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Enter opens a card where every field is editable in place. Dragging a card with
|
|
135
|
+
the mouse moves it between columns. Each action runs the same command the CLI
|
|
136
|
+
does, so the board can never disagree with the terminal.
|
|
137
|
+
|
|
138
|
+
The board loads its UI layer lazily — `kadence task add` never pays for it.
|
|
139
|
+
|
|
140
|
+
## For AI agents
|
|
141
|
+
|
|
142
|
+
Tasks are files. An agent reads them directly, or through the CLI — no MCP
|
|
143
|
+
server, no token, no network:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
kadence board --json
|
|
147
|
+
kadence task list --json --status in_progress --sort priority
|
|
148
|
+
kadence task show KAD-1 --json
|
|
149
|
+
kadence sprint status --json
|
|
150
|
+
KADENCE_SOURCE=agent kadence task move KAD-1 in_progress
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Every `--json` response carries `schema: "kadence/v1"`. stdout holds JSON and
|
|
154
|
+
nothing else; warnings go to stderr. Exit codes: `0` success, `1` runtime error,
|
|
155
|
+
`2` bad arguments.
|
|
156
|
+
|
|
157
|
+
`init` writes `.kadence/README.md` and a section in `AGENTS.md` so your agent
|
|
158
|
+
finds this on its own.
|
|
159
|
+
|
|
160
|
+
## How it works
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
.kadence/
|
|
164
|
+
├── state.json derived cache — gitignored, safe to delete
|
|
165
|
+
└── events/
|
|
166
|
+
├── archive/ compacted history, one file per month
|
|
167
|
+
└── 2026-09/ recent events, one file each
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Every command appends one event. State is folded from the journal on read, so
|
|
171
|
+
the board can never drift from reality. Two branches writing at once produce two
|
|
172
|
+
different files, and git merges them without a conflict by construction.
|
|
173
|
+
|
|
174
|
+
Measured on 10,000 events:
|
|
175
|
+
|
|
176
|
+
| | |
|
|
177
|
+
|---|---|
|
|
178
|
+
| Cold start with compacted archive | 28 ms |
|
|
179
|
+
| Warm start (cache) | 7 ms |
|
|
180
|
+
| Journal on disk | 1.9 MB (39 MB without compaction) |
|
|
181
|
+
| Bundle | 30 KB, zero runtime deps in the fast path |
|
|
182
|
+
|
|
183
|
+
These are enforced by tests that fail on regression.
|
|
184
|
+
|
|
185
|
+
## Honest status
|
|
186
|
+
|
|
187
|
+
What is verified:
|
|
188
|
+
|
|
189
|
+
- The merge thesis, on real git branches: three people editing one task produce
|
|
190
|
+
zero conflicts, and every intent is preserved with its author.
|
|
191
|
+
- Performance and size guardrails, by tests that fail if they regress.
|
|
192
|
+
- The conflict problem exists in the wild — measured, not assumed.
|
|
193
|
+
|
|
194
|
+
What is not:
|
|
195
|
+
|
|
196
|
+
- **Whether teams want this.** The velocity bet rests on reasoning, not on user
|
|
197
|
+
interviews. That research is designed but not yet run
|
|
198
|
+
([interview script](docs/research/interview-script.md)).
|
|
199
|
+
- Conflicts are real but **rare** — roughly one merge in two hundred. That is
|
|
200
|
+
why the headline message is analytics, not conflict-freedom.
|
|
201
|
+
|
|
202
|
+
The full reasoning, including what would prove this product wrong, lives in
|
|
203
|
+
[docs/](docs/README.md).
|
|
204
|
+
|
|
205
|
+
## Development
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
npm install
|
|
209
|
+
npm test # 363 tests
|
|
210
|
+
npm run build # single 40 KB bundle
|
|
211
|
+
npm run typecheck
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The core has **zero runtime dependencies** — ULID and validation are
|
|
215
|
+
hand-rolled, because a general-purpose validator cost 15% of the startup budget
|
|
216
|
+
for a seven-field object ([ADR-003](docs/decisions/003-zero-runtime-deps-in-core.md)).
|
|
217
|
+
The CLI layer uses `cac` and nothing else.
|
|
218
|
+
|
|
219
|
+
Architecture decisions are in [docs/decisions/](docs/decisions/); each one
|
|
220
|
+
records what was measured and what would make us revisit it.
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import v from"blessed";var c={border:"gray",borderFocus:"cyan",headerFg:"white",headerBg:"blue",selectedFg:"black",selectedBg:"cyan",dim:"gray",hint:"gray",warn:"yellow",danger:"red"},oe={backlog:"gray",todo:"white",in_progress:"blue",doing:"blue",blocked:"red",in_review:"magenta",review:"magenta",done:"green",shipped:"green"};function W(s){return oe[s]??"white"}var le={urgent:"\u203C",high:"\u2191",normal:" ",low:"\u2193"},ae={urgent:"red",high:"yellow",normal:"white",low:"gray"},ue={bug:"\u2716",story:"\u25C6",epic:"\u2B22",task:"\xB7"};function E(s,i){return`{${i}-fg}${s}{/}`}function H(s,i){let d=E(le[s.priority],ae[s.priority]),g=E(ue[s.type]??"\xB7","gray"),a=E(s.label,"cyan"),u=[];if(s.blockedBy.length>0&&u.push(E("\u2298","red")),s.comments.length>0&&u.push(E("\u{1F4AC}","gray")),s.due!==null){let $=s.due<new Date().toISOString().slice(0,10);u.push(E($?"\u23F0":"\u{1F4C5}",$?"red":"gray"))}let f=[s.assignee!==null?E(`@${s.assignee.split("@")[0]}`,"gray"):"",s.estimate!==null?E(String(s.estimate),"yellow"):"",u.join("")].filter($=>$.length>0).join(" "),I=`${D(d)}${D(g)} ${D(a)} `,B=f.length>0?` ${D(f)}`:"",P=Math.max(4,i-I.length-B.length),R=[...s.title],x=R.length>P?`${R.slice(0,P-1).join("")}\u2026`:s.title;return`${d}${g} ${a} ${x}${f.length>0?` ${f}`:""}`}function Y(s,i){return i?`{cyan-bg}{black-fg}${D(s)}{/}`:` ${s}`}function z(s,i,d,g=12){let a=`${s.padEnd(g)} ${i}`;return d?`{cyan-bg}{black-fg}\u25B8 ${a}{/}`:` ${a}`}function D(s){return s.replace(/\{[^}]*\}/g,"")}var J=["\u2190\u2192\u2191\u2193 move","enter details","[ ] shift","m status","a assign","c comment","e edit","n new","s sprint","/ filter","? help","q quit (Ctrl-C forces)"].join(" ");function V(s){let i=[],d=!1;function g(){d=!0,setImmediate(()=>{d=!1})}return{dispatch(a){if(d)return;let u=i[i.length-1];u!==void 0?u(a):s(a)},push(a){i.push(a),g();let u=!1;return()=>{if(u)return;u=!0;let f=i.lastIndexOf(a);f!==-1&&i.splice(f,1),g()}},isDialogOpen:()=>i.length>0,depth:()=>i.length}}function X(s){if(s.ch==="")return!1;let i=s.ch.charCodeAt(0);return i<32||i===127}var de=["up","down","pageup","pagedown","home","end"],Q=["escape","q"];function _(s){return de.includes(s.name)}function Z(s){return Q.includes(s.name)||Q.includes(s.ch)}function M(s,i,d){return d===0?0:(s+i+d)%d}var ee="cancelled";function me(s){let i=v.screen({smartCSR:!0,title:"kadence",fullUnicode:!0,mouse:!0}),{state:d}=s.reload(),g="",a=[],u=0,f=V(t=>se(t));function I(t=0){try{i.destroy()}catch{}process.exit(t)}process.on("SIGINT",()=>I(0)),process.on("SIGTERM",()=>I(0)),i.program.on("keypress",(t,n)=>{let r={ch:t??"",name:n?.name??""};(r.name==="C-c"||r.ch==="")&&I(0),!(X(r)&&r.name!=="enter"&&r.name!=="escape")&&f.dispatch(r)});let B=v.box({parent:i,top:0,height:1,width:"100%",tags:!0,style:{fg:c.headerFg,bg:c.headerBg}}),P=v.box({parent:i,bottom:0,height:1,width:"100%",tags:!0,content:` ${J}`,style:{fg:c.hint}}),R=v.box({parent:i,bottom:1,height:1,width:"100%",tags:!0,style:{fg:c.warn}});function x(t,n=c.warn){R.setContent(` ${t}`),R.style.fg=n,i.render()}function $(){let t=d.statuses.filter(r=>r!==ee),n=d.orphanStatuses.filter(r=>r!==ee);return[...t,...n]}function te(t){let n=g.toLowerCase();return d.tasks.filter(r=>r.status===t&&(n===""||r.title.toLowerCase().includes(n)||(r.assignee??"").toLowerCase().includes(n)||r.labels.some(e=>e.toLowerCase().includes(n))))}function N(){for(let r of a)r.box.destroy();a=[];let t=$(),n=Math.max(Math.floor(100/Math.max(t.length,1)),12);t.forEach((r,e)=>{let o=v.box({parent:i,top:1,left:`${e*n}%`,width:`${n}%`,bottom:2,label:` ${r} `,border:{type:"line"},style:{border:{fg:c.border},label:{fg:W(r)}}}),h=v.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"}}});a.push({status:r,box:o,list:h,tasks:[],cursor:0})})}function p(t=!1){t&&(d=s.reload().state),a.length!==$().length&&N();let n=0,r=0;a.forEach((l,S)=>{l.tasks=te(l.status),n+=l.tasks.length,r+=l.tasks.reduce((C,T)=>C+(T.estimate??0),0);let K=l.box.width-4;l.cursor=Math.min(l.cursor,Math.max(0,l.tasks.length-1)),l.list.setItems(l.tasks.map((C,T)=>Y(H(C,K),S===u&&T===l.cursor)));let L=l.tasks.reduce((C,T)=>C+(T.estimate??0),0);l.box.setLabel(` ${l.status} (${l.tasks.length}${L>0?`, ${L}`:""}) `),l.box.style.border.fg=S===u?c.borderFocus:c.border});let e=d.sprints.find(l=>l.status==="active"),o=e===void 0?"no active sprint":e.name,h=g===""?"":` filter: "${g}"`;B.setContent(` kadence ${o} ${n} tasks, ${r} points${h}`),d.cycles.length>0&&x(`${d.cycles.length} dependency cycle(s) \u2014 see kadence task list`,c.danger),i.render()}function w(){let t=a[u];return t?.tasks[t.cursor]}function A(){a.forEach((t,n)=>{let r=t.box.width-4;t.list.setItems(t.tasks.map((e,o)=>Y(H(e,r),n===u&&o===t.cursor))),t.box.style.border.fg=n===u?c.borderFocus:c.border}),i.render()}function m(t){t!==null?x(t,c.warn):x(""),p(!0)}function k(t,n,r){let e=f.push(()=>{}),o=v.prompt({parent:i,top:"center",left:"center",width:"60%",height:7,border:{type:"line"},style:{border:{fg:c.borderFocus}},keys:!0,mouse:!0});o.input(t,n,(h,l)=>{o.destroy(),e(),i.render(),typeof l=="string"&&r(l.trim())})}function ne(t){let n=v.box({parent:i,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}),r=[{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 (${$().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=v.list({parent:n,top:0,left:1,right:1,height:r.length,keys:!1,mouse:!1,interactive:!1,tags:!0,style:{selected:{fg:c.selectedFg,bg:c.selectedBg}}}),o=0,h=v.box({parent:n,top:r.length+1,left:1,right:1,bottom:0,tags:!0,scrollable:!0});function l(){e.setItems(r.map((y,O)=>z(y.label,y.value(),O===o)));let b=[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(y=>` {gray-fg}${y.author}:{/} ${y.text}`),"","{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(y=>y!=="");h.setContent(b.join(`
|
|
7
|
+
`)),i.render()}l();let S=f.push(b=>T(b)),K=()=>{S(),n.destroy(),p(!0)};function L(){d=s.reload().state;let b=d.tasks.find(y=>y.id===t.id);if(b===void 0){K();return}Object.assign(t,b),l()}function C(){let b=G(()=>s.edit(t.id));b!==null&&x(b),L()}function T(b){let{ch:y,name:O}=b;if(O==="up"||y==="k")return o=M(o,-1,r.length),l();if(O==="down"||y==="j")return o=M(o,1,r.length),l();if(Z(b))return K();if(y==="e")return C();if(O==="enter"){let j=r[o];if(j===void 0)return;if(j.key==="description")return C();k(j.hint,j.value()==="\u2014"?"":j.value(),ie=>{let U=s.setField(t.id,j.key,ie);U!==null&&x(U),L()})}}i.render()}function re(){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","","{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{/}"],n=v.box({parent:i,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
|
+
`)}),r=f.push(e=>{_(e)||(r(),n.destroy(),p())});n.on("click",()=>{r(),n.destroy(),p()}),i.render()}function q(t,n,r){let e=r.length>0?`
|
|
9
|
+
|
|
10
|
+
{gray-fg}${r.map(l=>`${l.key} ${l.label}`).join(" ")} esc close{/}`:`
|
|
11
|
+
|
|
12
|
+
{gray-fg}press any key to close{/}`,o=v.box({parent:i,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:n+e}),h=f.push(l=>{if(_(l))return;let S=r.find(K=>K.key===l.ch);h(),o.destroy(),S!==void 0?S.run():p()});i.render()}function se(t){let{ch:n,name:r}=t;if(r==="left"||n==="h")return u=Math.max(0,u-1),p();if(r==="right"||n==="l")return u=Math.min(a.length-1,u+1),p();if(r==="up"||n==="k"){let e=a[u];e!==void 0&&(e.cursor=M(e.cursor,-1,e.tasks.length),A());return}if(r==="down"||n==="j"){let e=a[u];e!==void 0&&(e.cursor=M(e.cursor,1,e.tasks.length),A());return}if(r==="enter"){let e=w();e!==void 0&&ne(e);return}if(n==="["||n==="]"){let e=w();if(e===void 0)return;let o=n==="]"?1:-1,h=a[u+o];return h===void 0?void 0:(m(s.move(e.id,h.status)),u+=o,p())}if(n==="m"){let e=w();return e===void 0?void 0:k(`Move ${e.label} to (${$().join(", ")}):`,"",o=>m(s.move(e.id,o)))}if(n==="a"){let e=w();return e===void 0?void 0:k(`Assign ${e.label} to (or "none"):`,e.assignee??"",o=>m(s.assign(e.id,o)))}if(n==="c"){let e=w();return e===void 0?void 0:k(`Comment on ${e.label}:`,"",o=>m(s.comment(e.id,o)))}if(n==="t"){let e=w();return e===void 0?void 0:k(`Log time on ${e.label} (2h, 90m, -30m):`,"",o=>m(s.logTime(e.id,o)))}if(n==="p"){let e=w();return e===void 0?void 0:k(`Priority for ${e.label} (low, normal, high, urgent):`,e.priority,o=>m(s.setPriority(e.id,o)))}if(n==="n")return k("New task title:","",e=>m(s.create(e)));if(n==="d"){let e=w();return e===void 0?void 0:k(`Delete ${e.label}? type "yes":`,"",o=>{o.toLowerCase()==="yes"?m(s.remove(e.id)):x("Not deleted.")})}if(n==="e"){let e=w();return e===void 0?void 0:m(G(()=>s.edit(e.id)))}if(n==="S"){let e=w();return e===void 0?void 0:m(s.addToSprint(e.id))}if(n==="s")return q("Sprint",s.sprintStatus(),[{key:"b",label:"burndown",run:()=>q("Burndown",s.burndown(),[])},{key:"n",label:"start next sprint",run:()=>m(s.sprintStart())},{key:"x",label:"close sprint",run:()=>m(s.sprintClose())}]);if(n==="/")return k("Filter (title, assignee or label):",g,e=>{g=e==="*"?"":e,p()});if(r==="escape"&&g!=="")return g="",x("Filter cleared."),p();if(n==="r")return x("Reloaded."),p(!0);if(n==="?")return re();(n==="q"||r==="C-c")&&I(0)}function G(t){let n=i;n.leave();let r=t();return n.enter(),r}let F=null;a.forEach(()=>{}),i.on("mousedown",t=>{if(f.isDialogOpen())return;let n=a.findIndex(h=>{let l=h.box.left;return t.x>=l&&t.x<l+h.box.width});if(n===-1)return;u=n;let r=a[n],e=t.y-r.box.top-1,o=r.tasks[e];o!==void 0&&(r.cursor=e,F={task:o,from:n}),p()}),i.on("mouseup",t=>{if(f.isDialogOpen()||F===null)return;let n=a.findIndex(r=>{let e=r.box.left;return t.x>=e&&t.x<e+r.box.width});n!==-1&&n!==F.from&&(m(s.move(F.task.id,a[n].status)),u=n,p()),F=null}),N(),p()}export{me as runBoardUi};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var U=["backlog","todo","in_progress","blocked","in_review","done","cancelled"],Ot="done",Ie="cancelled",D=["task","bug","story","epic"],R=["low","normal","high","urgent"];function ne(e){let n=[...e].sort((l,p)=>l.id<p.id?-1:l.id>p.id?1:0),r=new Map,t=new Map,s=new Map,i=null,o=[],a=[];for(let l of n){if(l.type==="template.saved"){let p=typeof l.data?.name=="string"?l.data.name:null;p!==null&&s.set(p,{name:p,fields:l.data?.fields??{},author:l.actor});continue}if(l.type==="board.configured"){let p=l.data?.statuses;if(Array.isArray(p)){let m=p.filter(g=>typeof g=="string"&&g.length>0);m.length>0&&(i=m)}continue}if(l.type==="template.deleted"){let p=typeof l.data?.name=="string"?l.data.name:null;p!==null&&s.delete(p);continue}q(l,r,t,o,a)}let d=a.length>0,u=a;for(;u.length>0;){let l=[];for(let p of u)q(p,r,t,o,l);if(l.length===u.length){u=l;break}u=l}if(d)for(let l of r.values())l.history.sort((p,m)=>p.id<m.id?-1:1);let c=[...r.values()].sort((l,p)=>l.id<p.id?-1:1);c.forEach((l,p)=>{l.label=`KAD-${p+1}`});let f=new Set(c.map(l=>l.id));for(let l of c)l.parent!==null&&!f.has(l.parent)&&(l.parent=null),l.blockedBy=l.blockedBy.filter(p=>f.has(p));return{tasks:c,sprints:[...t.values()].sort((l,p)=>l.id<p.id?-1:1),statuses:i??[...U],orphanStatuses:[...new Set(c.map(l=>l.status).filter(l=>!(i??U).includes(l)))].sort(),templates:[...s.values()].sort((l,p)=>l.name.localeCompare(p.name)),cycles:_e(c),pending:u,rejected:o}}function q(e,n,r,t,s){let i=e.data??{};if(e.type==="task.created")return n.has(e.entity)||n.set(e.entity,{id:e.entity,label:"",title:typeof i.title=="string"?i.title:"(untitled)",description:x(i.description),type:Q(i.type),priority:ee(i.priority),status:"backlog",labels:te(i.labels),assignee:x(i.assignee),reporter:e.actor,sprint:null,parent:x(i.parent),blockedBy:[],due:x(i.due),comments:[],estimate:typeof i.estimate=="number"?i.estimate:null,loggedHours:0,createdAt:e.ts,updatedAt:e.ts,history:[]}),K(n.get(e.entity),e),!0;if(e.type==="sprint.created")return r.has(e.entity)||r.set(e.entity,{id:e.entity,name:typeof i.name=="string"?i.name:"(untitled)",description:x(i.description),startDate:x(i.startDate),endDate:x(i.endDate),status:"planned",closedBy:null,taskIds:[]}),!0;if(e.type.startsWith("sprint.")){let a=r.get(e.entity);if(a===void 0)return s.push(e),!1;if(e.type==="sprint.started")return a.status==="planned"&&(a.status="active"),!0;if(e.type==="sprint.updated")return a.status==="closed"?(t.push(e),!0):(typeof i.name=="string"&&(a.name=i.name),i.description!==void 0&&(a.description=x(i.description)),i.startDate!==void 0&&(a.startDate=x(i.startDate)),i.endDate!==void 0&&(a.endDate=x(i.endDate)),!0);if(e.type==="sprint.closed")return a.closedBy===null?(a.status="closed",a.closedBy=e.id):t.push(e),!0;if(e.type==="sprint.cancelled")return a.status==="planned"?a.status="cancelled":t.push(e),!0;if(e.type==="sprint.task_added"){if(a.status==="closed")return t.push(e),!0;let d=typeof i.task=="string"?i.task:null;if(d===null)return!0;let u=n.get(d);return u===void 0?(s.push(e),!1):(u.sprint=a.id,a.taskIds.includes(d)||a.taskIds.push(d),K(u,e),!0)}return!0}let o=n.get(e.entity);if(o===void 0)return s.push(e),!1;if(e.type==="task.deleted")return n.delete(e.entity),!0;switch(e.type){case"task.moved":{let a=i.to;typeof a=="string"&&a.length>0&&(o.status=a);break}case"task.cancelled":o.status=Ie;break;case"task.reopened":o.status="in_progress";break;case"task.assigned":o.assignee=x(i.assignee);break;case"task.updated":typeof i.title=="string"&&(o.title=i.title),i.description!==void 0&&(o.description=x(i.description)),i.type!==void 0&&(o.type=Q(i.type)),i.priority!==void 0&&(o.priority=ee(i.priority)),i.labels!==void 0&&(o.labels=te(i.labels)),i.due!==void 0&&(o.due=x(i.due)),typeof i.estimate=="number"&&(o.estimate=i.estimate);break;case"task.parent_set":o.parent=x(i.parent);break;case"task.blocked_by_added":{let a=x(i.blocker);a!==null&&!o.blockedBy.includes(a)&&o.blockedBy.push(a);break}case"task.blocked_by_removed":{let a=x(i.blocker);a!==null&&(o.blockedBy=o.blockedBy.filter(d=>d!==a));break}case"task.time_logged":{let a=i.hours;typeof a=="number"&&Number.isFinite(a)&&(o.loggedHours=Math.max(0,o.loggedHours+a));break}case"task.commented":{let a=x(i.text);a!==null&&o.comments.push({id:e.id,author:e.actor,ts:e.ts,text:a});break}default:break}return K(o,e),!0}function K(e,n){e.history.push({id:n.id,type:n.type,actor:n.actor,ts:n.ts,data:n.data??{}}),n.ts>e.updatedAt&&(e.updatedAt=n.ts)}function x(e){return typeof e=="string"&&e.length>0?e:null}function Q(e){return D.includes(e)?e:"task"}function ee(e){return R.includes(e)?e:"normal"}function te(e){return Array.isArray(e)?e.filter(n=>typeof n=="string"&&n.length>0):[]}function _e(e){let n=new Map(e.map(i=>[i.id,i])),r=[],t=new Set,s=(i,o)=>{let a=new Map,d=(u,c)=>{let f=a.get(u);if(f==="done")return;if(f==="visiting"){let p=c.indexOf(u),m=c.slice(p),g=`${i}:${[...m].sort().join(",")}`;t.has(g)||(t.add(g),r.push({kind:i,path:[...m,u]}));return}a.set(u,"visiting");let l=n.get(u);if(l!==void 0)for(let p of o(l))d(p,[...c,u]);a.set(u,"done")};for(let u of e)d(u.id,[])};return s("parent",i=>i.parent===null?[]:[i.parent]),s("blocking",i=>i.blockedBy),r}var re={urgent:0,high:1,normal:2,low:3};function se(e,n){return e!==null&&e.toLowerCase().includes(n)}function oe(e,n,r=new Date){let t=r.toISOString().slice(0,10),s=n.search?.toLowerCase();return e.filter(i=>{if(s!==void 0&&s.length>0){let o=i.comments.some(a=>a.text.toLowerCase().includes(s));if(!se(i.title,s)&&!se(i.description,s)&&!o)return!1}if(n.status!==void 0&&i.status!==n.status||n.type!==void 0&&i.type!==n.type||n.priority!==void 0&&i.priority!==n.priority)return!1;if(n.assignee!==void 0){let o=n.assignee.toLowerCase(),a=(i.assignee??"").toLowerCase();if(o==="none"?a!=="":a!==o)return!1}if(n.label!==void 0){let o=n.label.toLowerCase();if(!i.labels.some(a=>a.toLowerCase()===o))return!1}return!(n.sprint!==void 0&&i.sprint!==n.sprint||n.overdue===!0&&(i.due===null||i.due>=t)||n.dueBefore!==void 0&&(i.due===null||i.due>=n.dueBefore))})}function ae(e,n){let r=[...e];switch(n){case"priority":return r.sort((t,s)=>re[t.priority]-re[s.priority]);case"due":return r.sort((t,s)=>ie(t.due,s.due,(i,o)=>i<o?-1:i>o?1:0));case"estimate":return r.sort((t,s)=>ie(t.estimate,s.estimate,(i,o)=>o-i));default:return r.sort((t,s)=>t.id<s.id?-1:t.id>s.id?1:0)}}function ie(e,n,r){return e===null&&n===null?0:e===null?1:n===null?-1:r(e,n)}function de(e){let n=[];return e.search!==void 0&&n.push(`search="${e.search}"`),e.status!==void 0&&n.push(`status=${e.status}`),e.type!==void 0&&n.push(`type=${e.type}`),e.priority!==void 0&&n.push(`priority=${e.priority}`),e.assignee!==void 0&&n.push(`assignee=${e.assignee}`),e.label!==void 0&&n.push(`label=${e.label}`),e.overdue===!0&&n.push("overdue"),e.dueBefore!==void 0&&n.push(`due before ${e.dueBefore}`),n.length===0?`No tasks yet.
|
|
3
|
+
Create the first one:
|
|
4
|
+
kadence task add "title"`:`No tasks match ${n.join(" and ")}.
|
|
5
|
+
Try fewer filters:
|
|
6
|
+
kadence task list`}var H=["created","priority","due","estimate"];function ue(e){return H.includes(e)}import{execFileSync as Fe}from"node:child_process";function le(e,n){try{return Fe("git",n,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}}function ce(e){let n=le(e,["rev-parse","--show-toplevel"]);return n===null||n.length===0?null:n}function pe(e){let n=le(e,["config","user.email"]);return n===null||n.length===0?null:n}import{mkdirSync as He,readdirSync as Ye,readFileSync as We,renameSync as Ge,rmSync as Jt,writeFileSync as Ze}from"node:fs";import{join as N}from"node:path";var Me=["task.created","task.moved","task.assigned","task.commented","task.updated","task.cancelled","task.reopened","task.deleted","task.parent_set","task.blocked_by_added","task.blocked_by_removed","task.time_logged","template.saved","template.deleted","board.configured","sprint.created","sprint.started","sprint.updated","sprint.closed","sprint.cancelled","sprint.task_added"],Be=new Set(Me),fe=/^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/,Le=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})$/;function Je(e){return typeof e=="string"&&Be.has(e)}function Ue(e){if(typeof e!="object"||e===null)return["event"];let n=e,r=[];return(typeof n.id!="string"||!fe.test(n.id))&&r.push("id"),Je(n.type)||r.push("type"),(typeof n.entity!="string"||!fe.test(n.entity))&&r.push("entity"),(typeof n.actor!="string"||n.actor.length===0)&&r.push("actor"),(typeof n.ts!="string"||!Le.test(n.ts)||Number.isNaN(Date.parse(n.ts)))&&r.push("ts"),n.source!=="human"&&n.source!=="agent"&&r.push("source"),n.data!==void 0&&(typeof n.data!="object"||n.data===null)&&r.push("data"),r}function ge(e){let n=e.data?.description;if(typeof n!="string"||!n.includes(`
|
|
7
|
+
`))return JSON.stringify(e);let r={...e,data:{...e.data,description:n.split(`
|
|
8
|
+
`)}};return JSON.stringify(r,null,2)}function Y(e){let n;try{n=JSON.parse(e)}catch(t){return{event:null,error:`unreadable JSON: ${t.message}`,unknownType:!1}}n=Ke(n);let r=Ue(n);return r.length===1&&r[0]==="type"?{event:null,error:null,unknownType:!0}:r.length>0?{event:null,error:`invalid fields: ${r.join(", ")}`,unknownType:!1}:{event:n,error:null,unknownType:!1}}function Ke(e){if(typeof e!="object"||e===null)return e;let n=e,r=n.data;if(typeof r!="object"||r===null)return e;let t=r.description;return Array.isArray(t)?{...n,data:{...r,description:t.filter(s=>typeof s=="string").join(`
|
|
9
|
+
`)}}:e}var Ve=.2;function O(e){return N(e,".kadence")}function M(e){return N(O(e),"events")}function ze(e){return N(M(e),"archive")}function Xe(e){return e.slice(0,7)}function w(e,n){let r=N(M(e),Xe(n.ts));He(r,{recursive:!0});let t=N(r,`${n.id}.json`),s=N(r,`.${n.id}.tmp`);Ze(s,`${ge(n)}
|
|
10
|
+
`,"utf8"),Ge(s,t)}function I(e){let n=M(e),r=[],t=[],s=new Set,i=0,o=W(ze(e)),a=new Set(o);for(let u of W(n)){let c;try{c=We(u,"utf8")}catch{t.push(u);continue}if(a.has(u)){let l;try{l=JSON.parse(c)}catch{t.push(u);continue}if(!Array.isArray(l)){t.push(u);continue}for(let p of l){let m=Y(JSON.stringify(p));m.unknownType?i++:m.error!==null||m.event===null?t.push(u):s.has(m.event.id)||(s.add(m.event.id),r.push(m.event))}continue}let f=Y(c);if(f.unknownType){i++;continue}if(f.error!==null||f.event===null){t.push(u);continue}s.has(f.event.id)||(s.add(f.event.id),r.push(f.event))}r.sort((u,c)=>u.id<c.id?-1:u.id>c.id?1:0);let d=r.length+t.length;return{events:r,corrupted:t,unknownTypes:i,systemicCorruption:d>0&&t.length/d>Ve}}function W(e){let n;try{n=Ye(e,{withFileTypes:!0})}catch{return[]}let r=[];for(let t of n){let s=N(e,t.name);t.isDirectory()?r.push(...W(s)):t.name.endsWith(".json")&&r.push(s)}return r}import{randomFillSync as qe}from"node:crypto";var ke="0123456789ABCDEFGHJKMNPQRSTVWXYZ",Qe=10,et=16,me=2**48-1;function tt(e){let n="",r=e;for(let t=Qe-1;t>=0;t--)n=ke[r%32]+n,r=Math.floor(r/32);return n}function nt(e){let n="",r=0,t=0;for(let s of e){for(r=r<<8|s,t+=8;t>=5;)t-=5,n+=ke[r>>>t&31];r&=(1<<t)-1}return n.slice(0,et)}function rt(e){for(let n=e.length-1;n>=0;n--){let r=e[n];if(r<255){e[n]=r+1;return}e[n]=0}throw new Error("ULID: random space exhausted within a single millisecond")}function st(){let e=-1,n=new Uint8Array(10);return function(t=Date.now()){if(!Number.isInteger(t)||t<0||t>me)throw new RangeError(`ULID: time out of range 0..${me}: ${t}`);return t>e?(e=t,qe(n)):rt(n),tt(e)+nt(n)}}var $=st();import{readFileSync as it,writeFileSync as ot,renameSync as at,readdirSync as dt,mkdirSync as ut}from"node:fs";import{join as he}from"node:path";var ye="kadence-snapshot/1";function be(e){return he(O(e),"state.json")}function xe(e,n){let r=lt(M(e)),t=ct(e);if(t!==null&&t.version===ye&&t.lastEventId===r.lastEventId&&t.eventCount===r.count)return{state:t.state,fromCache:!0,incomingEvents:0};let s=I(e),i=ne(s.events),o=t===null||n===void 0?0:ft(s.events,t,n);return pt(e,{version:ye,lastEventId:r.lastEventId,eventCount:r.count,state:i}),{state:i,fromCache:!1,incomingEvents:o}}function lt(e){let n="",r=0,t=s=>{let i;try{i=dt(s,{withFileTypes:!0})}catch{return}for(let o of i)if(o.isDirectory())t(he(s,o.name));else if(o.name.endsWith(".json")){r++;let a=o.name.slice(0,-5);a>n&&(n=a)}};return t(e),{lastEventId:n,count:r}}function ct(e){try{let n=JSON.parse(it(be(e),"utf8"));if(typeof n!="object"||n===null)return null;let r=n;return typeof r.version!="string"||typeof r.lastEventId!="string"||typeof r.eventCount!="number"||typeof r.state!="object"?null:r}catch{return null}}function pt(e,n){try{ut(O(e),{recursive:!0});let r=be(e),t=`${r}.tmp`;ot(t,JSON.stringify(n),"utf8"),at(t,r)}catch{}}function ft(e,n,r){let t=0,s=0;for(let o of e)o.id<=n.lastEventId?t++:o.actor!==r&&s++;let i=Math.max(0,t-n.eventCount);return s+i}var S={dim:"\x1B[2m",bold:"\x1B[1m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",reset:"\x1B[0m"};function Z(e,n){return e.NO_COLOR!==void 0?!1:n}function E(e,n,r){return r?`${n}${e}${S.reset}`:e}var gt={done:S.green,in_progress:S.blue,blocked:S.yellow,cancelled:S.dim},mt=72;function Se(e,n=mt){let r=[...e];return r.length<=n?e:`${r.slice(0,n-1).join("")}\u2026`}function G(e){return[...e].length}function ve(e,n){let r=n-G(e);return r>0?e+" ".repeat(r):e}function $e(e,n){if(e.length===0)return`No tasks yet.
|
|
11
|
+
Create the first one:
|
|
12
|
+
kadence task add "title"`;let r=Math.max(...e.map(s=>G(s.label)),2),t=Math.max(...e.map(s=>G(s.status)),6);return e.map(s=>{let i=E(ve(s.label,r),S.bold,n),o=E(ve(s.status,t),gt[s.status]??S.dim,n),a=s.estimate===null?"":E(` (${s.estimate})`,S.dim,n);return`${i} ${o} ${Se(s.title)}${a}`}).join(`
|
|
13
|
+
`)}function we(e){return e<=0?null:`Merged ${e} ${e===1?"change":"changes"} from another branch, no conflicts.`}var kt={urgent:"!!",high:"!",normal:"",low:"v"},yt={bug:"BUG",story:"STORY",task:""};function Te(e,n){let r=[E(e.label,S.bold,n)],t=kt[e.priority];t!==""&&r.push(E(t,e.priority==="low"?S.dim:S.yellow,n));let s=yt[e.type]??"";s!==""&&r.push(E(s,S.dim,n)),r.push(Se(e.title));let i=[];return e.assignee!==null&&i.push(`@${e.assignee.split("@")[0]}`),e.estimate!==null&&i.push(`${e.estimate}`),i.length>0&&r.push(E(`(${i.join(", ")})`,S.dim,n)),` ${r.join(" ")}`}function qt(e,n){if(Object.values(e).reduce((s,i)=>s+i.length,0)===0)return`No tasks yet.
|
|
14
|
+
Create the first one:
|
|
15
|
+
kadence task add "title"`;let t=[];for(let[s,i]of Object.entries(e)){if(i.length===0)continue;let o=i.reduce((d,u)=>d+(u.estimate??0),0),a=o>0?` \u2014 ${o}`:"";t.push(E(`${s} (${i.length}${a})`,S.bold,n),...i.map(d=>Te(d,n)),"")}return t.join(`
|
|
16
|
+
`).trimEnd()}function ht(e,n=new Date){let r=Date.parse(`${e}T00:00:00.000Z`),t=Date.parse(`${n.toISOString().slice(0,10)}T00:00:00.000Z`);if(Number.isNaN(r))return"";let s=Math.round((r-t)/864e5);return s<0?` (overdue by ${-s} day${s===-1?"":"s"})`:s===0?" (today)":s===1?" (tomorrow)":s<=7?` (in ${s} days)`:""}function Ce(e,n){if(e.length===0)return`No tasks yet.
|
|
17
|
+
Create the first one:
|
|
18
|
+
kadence task add "title"`;let r=new Set(e.map(a=>a.id)),t=new Map;for(let a of e){let d=a.parent!==null&&r.has(a.parent)?a.parent:null,u=t.get(d)??[];u.push(a),t.set(d,u)}let s=[],i=new Set,o=(a,d)=>{if(!(d>10))for(let u of t.get(a)??[]){if(i.has(u.id))continue;i.add(u.id);let c=" ".repeat(d),f=u.blockedBy.length>0?E(" [blocked]",S.yellow,n):"";s.push(`${c}${Te(u,n).trimStart()}${f}`),o(u.id,d+1)}};return o(null,0),s.join(`
|
|
19
|
+
`)}function Ee(e){let n=[`${e.label} ${e.title}`,""];if(e.description!==null&&n.push(e.description,""),n.push(` Status: ${e.status}`),n.push(` Type: ${e.type}`),n.push(` Priority: ${e.priority}`),e.labels.length>0&&n.push(` Labels: ${e.labels.join(", ")}`),e.parent!==null&&n.push(` Parent: ${e.parent}`),e.blockedBy.length>0&&n.push(` Blocked by: ${e.blockedBy.length} task(s)`),n.push(` Assignee: ${e.assignee??"\u2014"}`),n.push(` Reporter: ${e.reporter}`),e.due!==null&&n.push(` Due: ${e.due}${ht(e.due)}`),n.push(` Estimate: ${e.estimate??"\u2014"}`),e.comments.length>0){n.push("",` Comments (${e.comments.length}):`);for(let r of e.comments){n.push(` ${r.author} \xB7 ${r.ts.slice(0,10)}`);for(let t of r.text.split(`
|
|
20
|
+
`))n.push(` ${t}`)}}if(e.history.length>0){n.push("",` History (${e.history.length}):`);for(let r of e.history){let t=r.type==="task.moved"?` \u2192 ${String(r.data.to)}`:"";n.push(` ${r.ts.slice(0,16).replace("T"," ")} ${r.actor} ${r.type}${t}`)}}return n.join(`
|
|
21
|
+
`)}import{existsSync as bt}from"node:fs";var un=U;function h(e,n){let r=ce(e);if(r===null)return{ok:!1,exitCode:1,message:`kadence lives inside a git repository, and there is none here.
|
|
22
|
+
Create one and try again:
|
|
23
|
+
git init`};if(!bt(O(r)))return{ok:!1,exitCode:1,message:`No .kadence/ found here.
|
|
24
|
+
Run:
|
|
25
|
+
npx kadence init`};let t=pe(r);if(t===null)return{ok:!1,exitCode:1,message:`Git does not know who you are, so there is no author for the event.
|
|
26
|
+
Run:
|
|
27
|
+
git config user.email you@example.com`};let s=n.KADENCE_SOURCE==="agent"?"agent":"human";return{root:r,actor:t,source:s}}function b(e){return"root"in e}function ln(e,n,r,t={}){let s=h(e,n);if(!b(s))return s;let i=r.trim();if(i.length===0)return{ok:!1,exitCode:2,message:"A task needs a title."};if(t.type!==void 0&&!D.includes(t.type))return{ok:!1,exitCode:2,message:`Unknown type "${t.type}".
|
|
28
|
+
Available: ${D.join(", ")}`};if(t.priority!==void 0&&!R.includes(t.priority))return{ok:!1,exitCode:2,message:`Unknown priority "${t.priority}".
|
|
29
|
+
Available: ${R.join(", ")}`};let o;if(t.parent!==void 0){let{state:c}=k(s.root,s.actor),f=T(c,t.parent);if(f===void 0)return{ok:!1,exitCode:1,message:`No parent task ${t.parent}.
|
|
30
|
+
kadence task list`};o=f.id}let a=$(),d={id:a,type:"task.created",entity:a,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{title:i,...t.description!==void 0?{description:t.description}:{},...t.type!==void 0?{type:t.type}:{},...t.priority!==void 0?{priority:t.priority}:{},...t.assignee!==void 0?{assignee:t.assignee}:{},...t.labels!==void 0&&t.labels.length>0?{labels:t.labels}:{},...t.due!==void 0?{due:t.due}:{},...o!==void 0?{parent:o}:{},...t.estimate!==void 0?{estimate:t.estimate}:{}}};w(s.root,d);let u=t.estimate===void 0?`
|
|
31
|
+
Without an estimate this task will not count towards velocity. Add --estimate.`:"";return{ok:!0,exitCode:0,message:`Created: ${i}${u}`,data:{schema:"kadence/v1",ok:!0,task:{id:a,title:i}}}}function k(e,n){let r=xe(e,n),t=I(e),s=[],i=we(r.incomingEvents);return i!==null&&s.push(i),t.systemicCorruption?s.push(`The journal is badly damaged: ${t.corrupted.length} of ${t.corrupted.length+t.events.length} events are unreadable. Try: git checkout .kadence/`):t.corrupted.length>0&&s.push(`Skipped ${t.corrupted.length} corrupted event(s).`),t.unknownTypes>0&&s.push(`Skipped ${t.unknownTypes} event(s) from a newer format. Update kadence.`),{state:r.state,warnings:s}}function T(e,n){let r=n.toUpperCase();return e.tasks.find(t=>t.id===r||t.label===r)}function De(e,n){return{ok:!1,exitCode:2,message:`Unknown status "${e}".
|
|
32
|
+
Available: ${n.join(", ")}
|
|
33
|
+
kadence board config --statuses "todo,doing,done"`}}function cn(e,n,r){let t=h(e,n);if(!b(t))return t;if(r.type!==void 0&&!D.includes(r.type))return{ok:!1,exitCode:2,message:`Unknown type "${r.type}".
|
|
34
|
+
Available: ${D.join(", ")}`};if(r.priority!==void 0&&!R.includes(r.priority))return{ok:!1,exitCode:2,message:`Unknown priority "${r.priority}".
|
|
35
|
+
Available: ${R.join(", ")}`};if(r.sort!==void 0&&!ue(r.sort))return{ok:!1,exitCode:2,message:`Unknown sort key "${r.sort}".
|
|
36
|
+
Available: ${H.join(", ")}`};let{state:s,warnings:i}=k(t.root,t.actor);if(r.status!==void 0&&!s.statuses.includes(r.status))return De(r.status,s.statuses);let{sort:o,tree:a,...d}=r,u=oe(s.tasks,d),c=o===void 0?u:ae(u,o);return c.length===0?{ok:!0,exitCode:0,warnings:i,message:de(d),data:{schema:"kadence/v1",ok:!0,tasks:[]}}:{ok:!0,exitCode:0,warnings:i,message:a===!0?Ce(c,Z(n,process.stdout.isTTY===!0)):$e(c,Z(n,process.stdout.isTTY===!0)),data:{schema:"kadence/v1",ok:!0,tasks:c.map(Re),cycles:s.cycles}}}function pn(e,n,r,t){let s=h(e,n);if(!b(s))return s;let{state:i,warnings:o}=k(s.root,s.actor);if(!i.statuses.includes(t))return De(t,i.statuses);let{tasks:a,error:d}=P(i,r);if(d!==null)return{ok:!1,exitCode:1,message:d};let u=[],c=[],f="";for(let p of a){if(p.status===t){c.push(p);continue}let m=p.status;w(s.root,{id:$(),type:"task.moved",entity:p.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{from:m,to:t}}),u.push(p),a.length===1&&i.statuses.indexOf(t)-i.statuses.indexOf(m)>1&&(f=`
|
|
37
|
+
Moved straight from ${m} to ${t}, skipping the stages between.`)}if(u.length===0)return{ok:!0,exitCode:0,warnings:o,message:v("already there",c,`is already ${t}.`)};let l=c.length>0?`
|
|
38
|
+
${c.length} already ${t}.`:"";return{ok:!0,exitCode:0,warnings:o,message:`${v("moved",u,`${u[0].status} \u2192 ${t}`)}${f}${l}`,data:{schema:"kadence/v1",ok:!0,moved:u.map(p=>({id:p.id,label:p.label,to:t}))}}}function Re(e){return{id:e.id,label:e.label,title:e.title,description:e.description,type:e.type,priority:e.priority,status:e.status,labels:e.labels,assignee:e.assignee,reporter:e.reporter,sprint:e.sprint,loggedHours:e.loggedHours,parent:e.parent,blockedBy:e.blockedBy,due:e.due,comments:e.comments,estimate:e.estimate,history:e.history}}function fn(e,n,r,t){let s=h(e,n);if(!b(s))return s;let{state:i,warnings:o}=k(s.root,s.actor),{tasks:a,error:d}=P(i,r);if(d!==null)return{ok:!1,exitCode:1,message:d};let u=t.trim().toLowerCase()==="none"?null:t.trim(),c=a.filter(f=>f.assignee!==u);if(c.length===0)return{ok:!0,exitCode:0,warnings:o,message:u===null?v("already unassigned",a,"is already unassigned."):v("already assigned",a,`is already assigned to ${u}.`)};for(let f of c)w(s.root,{id:$(),type:"task.assigned",entity:f.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{assignee:u}});return{ok:!0,exitCode:0,warnings:o,message:u===null?v("unassigned",c,"unassigned."):v("assigned",c,`\u2192 ${u}`),data:{schema:"kadence/v1",ok:!0,assigned:c.map(f=>({id:f.id,label:f.label,assignee:u}))}}}function gn(e,n,r){let t=h(e,n);if(!b(t))return t;let{state:s,warnings:i}=k(t.root,t.actor),o=T(s,r);return o===void 0?{ok:!1,exitCode:1,message:`No task ${r}.
|
|
39
|
+
kadence task list`}:{ok:!0,exitCode:0,warnings:i,message:Ee(o),data:{schema:"kadence/v1",ok:!0,task:Re(o)}}}function mn(e,n,r,t){let s=h(e,n);if(!b(s))return s;if(t.type!==void 0&&!D.includes(t.type))return{ok:!1,exitCode:2,message:`Unknown type "${t.type}".
|
|
40
|
+
Available: ${D.join(", ")}`};if(t.priority!==void 0&&!R.includes(t.priority))return{ok:!1,exitCode:2,message:`Unknown priority "${t.priority}".
|
|
41
|
+
Available: ${R.join(", ")}`};if(t.due!==void 0&&t.due!==""&&!xt(t.due))return{ok:!1,exitCode:2,message:`Due date must be YYYY-MM-DD, got "${t.due}".
|
|
42
|
+
kadence task edit KAD-1 --due 2026-09-30`};let{state:i,warnings:o}=k(s.root,s.actor),{tasks:a,error:d}=P(i,r);if(d!==null)return{ok:!1,exitCode:1,message:d};if(t.title!==void 0&&a.length>1)return{ok:!1,exitCode:2,message:"A title can only be set on one task at a time."};let u=[],c=new Set;for(let l of a){let p={},m=[];if(t.title!==void 0&&t.title!==l.title&&(p.title=t.title,m.push("title")),t.description!==void 0&&t.description!==(l.description??"")&&(p.description=t.description,m.push("description")),t.type!==void 0&&t.type!==l.type&&(p.type=t.type,m.push("type")),t.priority!==void 0&&t.priority!==l.priority&&(p.priority=t.priority,m.push("priority")),t.due!==void 0&&t.due!==(l.due??"")&&(p.due=t.due,m.push("due")),t.estimate!==void 0&&t.estimate!==l.estimate&&(p.estimate=t.estimate,m.push("estimate")),t.labels!==void 0&&t.labels.join(",")!==l.labels.join(",")&&(p.labels=t.labels,m.push("labels")),m.length!==0){w(s.root,{id:$(),type:"task.updated",entity:l.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:p}),u.push(l);for(let g of m)c.add(g)}}if(u.length===0)return{ok:!0,exitCode:0,warnings:o,message:v("unchanged",a,"nothing changed.")};let f=[...c].join(", ");return{ok:!0,exitCode:0,warnings:o,message:v("updated",u,`updated ${f}.`),data:{schema:"kadence/v1",ok:!0,updated:u.map(l=>({id:l.id,label:l.label})),changed:[...c]}}}function xt(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;let n=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(n.getTime())&&n.toISOString().slice(0,10)===e}function kn(e,n,r){let t=h(e,n);if(!b(t))return t;let{state:s,warnings:i}=k(t.root,t.actor),{tasks:o,error:a}=P(s,r);if(a!==null)return{ok:!1,exitCode:1,message:a};let d=o.filter(u=>u.status!=="cancelled");if(d.length===0)return{ok:!0,exitCode:0,warnings:i,message:v("already cancelled",o,"is already cancelled.")};for(let u of d)w(t.root,{id:$(),type:"task.cancelled",entity:u.id,actor:t.actor,ts:new Date().toISOString(),source:t.source,data:{}});return{ok:!0,exitCode:0,warnings:i,message:`${v("cancelled",d,"cancelled.")}
|
|
43
|
+
Cancelled work stays in history and does not count as missed.`,data:{schema:"kadence/v1",ok:!0,cancelled:d.map(u=>({id:u.id,label:u.label}))}}}function yn(e,n,r){let t=h(e,n);if(!b(t))return t;let{state:s,warnings:i}=k(t.root,t.actor),{tasks:o,error:a}=P(s,r);if(a!==null)return{ok:!1,exitCode:1,message:a};for(let u of o)w(t.root,{id:$(),type:"task.deleted",entity:u.id,actor:t.actor,ts:new Date().toISOString(),source:t.source,data:{title:u.title}});let d=o.length===1?`${o[0].label} deleted: "${o[0].title}".`:`${o.length} tasks deleted: ${o.map(u=>u.label).join(", ")}.`;return{ok:!0,exitCode:0,warnings:i,message:`${d}
|
|
44
|
+
The events stay in the journal \u2014 history is never rewritten.`,data:{schema:"kadence/v1",ok:!0,deleted:o.map(u=>({id:u.id,label:u.label}))}}}function hn(e,n,r,t){let s=h(e,n);if(!b(s))return s;let i=t.trim();if(i.length===0)return{ok:!1,exitCode:2,message:"A comment needs text."};let{state:o,warnings:a}=k(s.root,s.actor),d=T(o,r);return d===void 0?{ok:!1,exitCode:1,message:`No task ${r}.
|
|
45
|
+
kadence task list`}:(w(s.root,{id:$(),type:"task.commented",entity:d.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:i}}),{ok:!0,exitCode:0,warnings:a,message:`Comment added to ${d.label}.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label}}})}function P(e,n){let r=n.split(",").map(o=>o.trim()).filter(o=>o.length>0);if(r.length===0)return{tasks:[],error:"No task given."};let t=[],s=[];for(let o of r){let a=T(e,o);a===void 0?s.push(o):t.push(a)}return s.length>0?{tasks:[],error:`No task ${s.join(", ")} \u2014 nothing was changed.
|
|
46
|
+
All ids must exist before a bulk change runs.
|
|
47
|
+
kadence task list`}:{tasks:[...new Map(t.map(o=>[o.id,o])).values()],error:null}}function v(e,n,r){return n.length===1?`${n[0].label}: ${r}`:`${n.length} tasks ${e}: ${n.map(t=>t.label).join(", ")}`}function bn(e,n,r,t){let s=h(e,n);if(!b(s))return s;let{state:i,warnings:o}=k(s.root,s.actor),{tasks:a,error:d}=P(i,r);if(d!==null)return{ok:!1,exitCode:1,message:d};let u=t.trim().toLowerCase()==="none",c=null;if(!u){let p=T(i,t);if(p===void 0)return{ok:!1,exitCode:1,message:`No task ${t}.
|
|
48
|
+
kadence task list`};if(a.some(m=>m.id===p.id))return{ok:!1,exitCode:2,message:"A task cannot be its own parent."};c=p.id}let f=a.filter(p=>p.parent!==c);if(f.length===0)return{ok:!0,exitCode:0,warnings:o,message:v("unchanged",a,"already there.")};for(let p of f)w(s.root,{id:$(),type:"task.parent_set",entity:p.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{parent:c}});let l=u?"detached":T(i,t).label;return{ok:!0,exitCode:0,warnings:[...o,...Ne(s.root,s.actor)],message:u?v("detached",f,"detached from its parent."):v("moved",f,`\u2192 child of ${l}`),data:{schema:"kadence/v1",ok:!0,parent:c}}}function xn(e,n,r,t,s){let i=h(e,n);if(!b(i))return i;let{state:o,warnings:a}=k(i.root,i.actor),{tasks:d,error:u}=P(o,r);if(u!==null)return{ok:!1,exitCode:1,message:u};let c=T(o,t);if(c===void 0)return{ok:!1,exitCode:1,message:`No task ${t}.
|
|
49
|
+
kadence task list`};if(d.some(l=>l.id===c.id))return{ok:!1,exitCode:2,message:"A task cannot block itself."};let f=d.filter(l=>s?l.blockedBy.includes(c.id):!l.blockedBy.includes(c.id));if(f.length===0)return{ok:!0,exitCode:0,warnings:a,message:v("unchanged",d,s?"was not blocked by it.":"is already blocked by it.")};for(let l of f)w(i.root,{id:$(),type:s?"task.blocked_by_removed":"task.blocked_by_added",entity:l.id,actor:i.actor,ts:new Date().toISOString(),source:i.source,data:{blocker:c.id}});return{ok:!0,exitCode:0,warnings:[...a,...Ne(i.root,i.actor)],message:s?v("unblocked",f,`no longer blocked by ${c.label}.`):v("blocked",f,`blocked by ${c.label}.`),data:{schema:"kadence/v1",ok:!0,blocker:c.id}}}function Ne(e,n){let{state:r}=k(e,n),t=new Map(r.tasks.map(s=>[s.id,s.label]));return r.cycles.map(s=>{let i=s.path.map(o=>t.get(o)??o).join(" \u2192 ");return`Dependency cycle (${s.kind}): ${i}. Both edges were kept \u2014 resolve it when you can.`})}function vt(e){let n=/^(-?\d+(?:\.\d+)?)\s*(h|hours?|m|min|minutes?)?$/i.exec(e.trim());if(n===null)return null;let r=Number(n[1]);return!Number.isFinite(r)||r===0?null:(n[2]??"h").toLowerCase().startsWith("m")?r/60:r}function vn(e,n,r,t){let s=h(e,n);if(!b(s))return s;let i=vt(t);if(i===null)return{ok:!1,exitCode:2,message:`Cannot read "${t}" as a duration.
|
|
50
|
+
Use hours or minutes:
|
|
51
|
+
kadence task log KAD-1 2h
|
|
52
|
+
kadence task log KAD-1 90m
|
|
53
|
+
kadence task log KAD-1 -30m to correct a mistake`};let{state:o,warnings:a}=k(s.root,s.actor),d=T(o,r);if(d===void 0)return{ok:!1,exitCode:1,message:`No task ${r}.
|
|
54
|
+
kadence task list`};w(s.root,{id:$(),type:"task.time_logged",entity:d.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{hours:i}});let u=Math.max(0,d.loggedHours+i),c=d.estimate===null?`
|
|
55
|
+
No estimate on this task, so there is nothing to compare against.`:"";return{ok:!0,exitCode:0,warnings:a,message:`${d.label}: ${u.toFixed(1)}h logged in total.${c}`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label,loggedHours:u}}}}import{spawnSync as St}from"node:child_process";import{mkdtempSync as $t,readFileSync as wt,rmSync as Tt,writeFileSync as Ct}from"node:fs";import{tmpdir as Et}from"node:os";import{join as Pe}from"node:path";var B="#";function Dt(e){return e.GIT_EDITOR??e.VISUAL??e.EDITOR??"vi"}function En(e,n,r){let t=$t(Pe(Et(),"kadence-edit-")),s=Pe(t,"KADENCE_EDITMSG.md"),i=["",`${B} ${r}`,`${B} Lines starting with '${B}' are ignored.`,`${B} Save an empty file to abort.`].join(`
|
|
56
|
+
`);try{Ct(s,`${n}${i}
|
|
57
|
+
`,"utf8");let o=Dt(e),a=St(o,[s],{stdio:"inherit",shell:!0});if(a.error!==void 0||a.status!==null&&a.status!==0)return{text:null,error:`Editor "${o}" exited without saving.
|
|
58
|
+
Set one explicitly:
|
|
59
|
+
export EDITOR=nano`};let u=wt(s,"utf8").split(`
|
|
60
|
+
`).filter(c=>!c.startsWith(B)).join(`
|
|
61
|
+
`).trim();return{text:u.length===0?null:u,error:null}}catch(o){return{text:null,error:`Could not open an editor: ${o.message}`}}finally{Tt(t,{recursive:!0,force:!0})}}function Dn(e,n){return e.KADENCE_SOURCE==="agent"?!1:n}function V(e,n){let r=e.sprints.find(g=>g.id===n);if(r===void 0)return null;let t=e.tasks.filter(g=>g.sprint===n),s=t.filter(g=>g.status==="cancelled"),i=t.filter(g=>g.status!=="cancelled"),o=i.filter(g=>g.status==="done"),a=i.filter(g=>g.status!=="done"),d=L(i.map(g=>g.estimate??0)),u=L(o.map(g=>g.estimate??0)),c=o.filter(g=>g.estimate===null),f=o.map(Rt).filter(g=>g!==null),l=f.length>0?L(f):null,p=L(o.filter(g=>g.estimate!==null).map(g=>g.estimate)),m=l!==null&&p>0?l/p:null;return{id:r.id,name:r.name,status:r.status,committed:d,velocity:u,actualHours:l,hoursPerPoint:m,done:o,carriedOver:a,cancelled:s,unestimated:c,loggedHours:L(t.map(g=>g.loggedHours))}}function Rt(e){let n=e.history.find(s=>s.type==="task.moved"&&s.data.to==="in_progress"),r=[...e.history].reverse().find(s=>s.type==="task.moved"&&s.data.to==="done");if(n===void 0||r===void 0)return null;let t=Date.parse(r.ts)-Date.parse(n.ts);return t>0?t/36e5:null}function L(e){return e.reduce((n,r)=>n+r,0)}function J(e){return new Date(e).toISOString().slice(0,10)}function Nt(e,n){let r=[];for(let t=Date.parse(`${e}T00:00:00.000Z`);t<=Date.parse(`${n}T00:00:00.000Z`);t+=864e5)r.push(J(t));return r}function je(e,n,r,t=J(Date.now())){let s=e.tasks.filter(y=>y.sprint===r.id&&y.status!=="cancelled"),i=new Map(s.map(y=>[y.id,y.estimate??0])),o=[...i.values()].reduce((y,C)=>y+C,0),a=n.filter(y=>i.has(y.entity));if(a.length===0)return null;let d=r.startDate??J(a[0].ts),u=r.endDate??J(a.at(-1).ts),c=r.status==="closed"||u<t?u:t;if(c<d)return null;let f=new Set,l=new Map;for(let y of a){let C=J(y.ts);if(C<d)continue;let A=y.type==="task.moved"?y.data?.to:void 0,X=i.get(y.entity)??0;A==="done"&&!f.has(y.entity)?(f.add(y.entity),l.set(C,(l.get(C)??0)+X)):A!==void 0&&A!=="done"&&f.has(y.entity)&&(f.delete(y.entity),l.set(C,(l.get(C)??0)-X))}let p=Nt(d,c),m=Math.max(p.length-1,1),g=o;return{sprintName:r.name,committed:o,finalRemaining:(r.status==="closed",null),days:p.map((y,C)=>{let A=l.get(y)??0;return g-=A,{date:y,remaining:Math.max(0,g),ideal:Math.max(0,o-o/m*C),completed:A}})}}function Ae(e,n=40){if(e.committed===0)return`"${e.sprintName}": nothing to burn down \u2014 no estimated tasks.`;let r=[`"${e.sprintName}" \u2014 ${e.committed} points committed`,""];for(let s of e.days){let i=Math.round(s.remaining/e.committed*n),o=Math.round(s.ideal/e.committed*n),a=[];for(let c=0;c<n;c++)c===o&&c>=i?a.push("\u250A"):c<i?a.push(c===o?"\u2503":"\u2588"):a.push(" ");let d=s.remaining-s.ideal,u=Math.abs(d)<.5?"":d>0?` +${d.toFixed(0)}`:` ${d.toFixed(0)}`;r.push(`${s.date.slice(5)} ${a.join("")} ${String(s.remaining).padStart(3)}${u}`)}let t=e.days.at(-1);return t!==void 0&&(r.push(""),r.push(t.remaining===0?" All committed work is done.":` ${t.remaining} of ${e.committed} points remain.`+(t.remaining>t.ideal?" Behind the ideal line.":" On or ahead of the line."))),r.join(`
|
|
62
|
+
`)}function j(e){return e.sprints.find(n=>n.status==="active")}function Pt(e){return e.sprints.filter(n=>n.status==="planned")}function _(e,n){let r=n.trim().toLowerCase();return e.sprints.find(t=>t.name.toLowerCase()===r)}function F(e,n,r,t){w(e.root,{id:$(),type:n,entity:r,actor:e.actor,ts:new Date().toISOString(),source:e.source,data:t})}function Mn(e,n,r){let t=h(e,n);if(!b(t))return t;let s=r.trim();if(s.length===0)return{ok:!1,exitCode:2,message:"A sprint needs a name."};let{state:i,warnings:o}=k(t.root,t.actor);if(_(i,s)!==void 0)return{ok:!1,exitCode:2,message:`Sprint "${s}" already exists.`};let a=j(i)!==void 0,d=$();return F(t,"sprint.created",d,{name:s}),a||F(t,"sprint.started",d,{}),{ok:!0,exitCode:0,warnings:o,message:a?`Sprint "${s}" planned.
|
|
63
|
+
|
|
64
|
+
kadence sprint add KAD-1 --sprint "${s}"
|
|
65
|
+
kadence sprint start "${s}"`:`Sprint "${s}" started.
|
|
66
|
+
|
|
67
|
+
kadence sprint add KAD-1
|
|
68
|
+
kadence sprint status`,data:{schema:"kadence/v1",ok:!0,sprint:{id:d,name:s,status:a?"planned":"active"}}}}function Bn(e,n,r,t={}){let s=h(e,n);if(!b(s))return s;let{state:i,warnings:o}=k(s.root,s.actor),a=t.sprint===void 0?j(i):_(i,t.sprint);if(a===void 0)return t.sprint===void 0?{ok:!1,exitCode:1,message:`No active sprint.
|
|
69
|
+
kadence sprint create "Sprint 1"`}:{ok:!1,exitCode:1,message:`No sprint named "${t.sprint}".
|
|
70
|
+
kadence sprint list`};if(a.status==="closed")return{ok:!1,exitCode:1,message:`Sprint "${a.name}" is closed \u2014 its velocity is not rewritten.`};let d=T(i,r);if(d===void 0)return{ok:!1,exitCode:1,message:`No task ${r}.
|
|
71
|
+
kadence task list`};if(d.sprint===a.id)return{ok:!0,exitCode:0,warnings:o,message:`${d.label} is already in the sprint.`};F(s,"sprint.task_added",a.id,{task:d.id});let u=d.estimate===null?`
|
|
72
|
+
Without an estimate this task will not count towards velocity.`:"";return{ok:!0,exitCode:0,warnings:o,message:`${d.label} \u2192 "${a.name}"${u}`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label},sprint:a.id}}}function Ln(e,n){let r=h(e,n);if(!b(r))return r;let{state:t,warnings:s}=k(r.root,r.actor),i=j(t);if(i===void 0)return{ok:!1,exitCode:1,message:`No active sprint.
|
|
73
|
+
kadence sprint create "Sprint 1"`};let o=V(t,i.id);F(r,"sprint.closed",i.id,{velocity:o.velocity});let a=[...s];return o.unestimated.length>0&&a.push(`${o.unestimated.length} completed task(s) without an estimate were left out of velocity: `+o.unestimated.map(d=>d.label).join(", ")),{ok:!0,exitCode:0,warnings:a,message:jt(o),data:{schema:"kadence/v1",ok:!0,report:Oe(o)}}}function Jn(e,n){let r=h(e,n);if(!b(r))return r;let{state:t,warnings:s}=k(r.root,r.actor),i=j(t);if(i===void 0)return{ok:!0,exitCode:0,warnings:s,message:`No active sprint.
|
|
74
|
+
kadence sprint create "Sprint 1"`,data:{schema:"kadence/v1",ok:!0,sprint:null}};let o=V(t,i.id),a=[`"${o.name}" \u2014 ${o.velocity} of ${o.committed} points`,"",...o.done.map(d=>` \u2713 ${d.label} ${d.title}`),...o.carriedOver.map(d=>` \xB7 ${d.label} ${d.title} (${d.status})`)];return{ok:!0,exitCode:0,warnings:s,message:a.join(`
|
|
75
|
+
`),data:{schema:"kadence/v1",ok:!0,report:Oe(o)}}}function jt(e){let n=[`Sprint "${e.name}" closed.`,""];if(n.push(` Velocity: ${e.velocity} of ${e.committed} points`),e.hoursPerPoint!==null&&e.actualHours!==null&&e.actualHours>=1/60&&n.push(` Actual: ${z(e.actualHours)} \u2014 ${z(e.hoursPerPoint)} per point`),e.loggedHours>0&&n.push(` Logged: ${z(e.loggedHours)} entered by hand`),e.carriedOver.length>0){n.push("",` Carried over (${e.carriedOver.length}):`);for(let r of e.carriedOver)n.push(` \xB7 ${r.label} ${r.title}`)}return e.cancelled.length>0&&n.push("",` Cancelled (${e.cancelled.length}) \u2014 not counted towards velocity`),n.join(`
|
|
76
|
+
`)}function z(e){return e<1?`${Math.round(e*60)}m`:e<10?`${e.toFixed(1)}h`:`${Math.round(e)}h`}function Oe(e){return{id:e.id,name:e.name,status:e.status,velocity:e.velocity,committed:e.committed,actualHours:e.actualHours,hoursPerPoint:e.hoursPerPoint,done:e.done.map(n=>n.label),carriedOver:e.carriedOver.map(n=>n.label),cancelled:e.cancelled.map(n=>n.label),unestimated:e.unestimated.map(n=>n.label),loggedHours:e.loggedHours}}function Un(e,n,r){let t=h(e,n);if(!b(t))return t;let{state:s,warnings:i}=k(t.root,t.actor),o=j(s);if(o!==void 0)return{ok:!1,exitCode:1,message:`Sprint "${o.name}" is still active.
|
|
77
|
+
Close it so velocity can be computed:
|
|
78
|
+
kadence sprint close`};let a=Pt(s),d=r===void 0?a[0]:_(s,r);return d===void 0?{ok:!1,exitCode:1,message:r===void 0?`No planned sprints.
|
|
79
|
+
kadence sprint create "Sprint 2"`:`No sprint named "${r}".
|
|
80
|
+
kadence sprint list`}:d.status!=="planned"?{ok:!1,exitCode:1,message:`Sprint "${d.name}" is already ${d.status}.`}:(F(t,"sprint.started",d.id,{}),{ok:!0,exitCode:0,warnings:i,message:`Sprint "${d.name}" started.
|
|
81
|
+
|
|
82
|
+
kadence sprint status`,data:{schema:"kadence/v1",ok:!0,sprint:{id:d.id,name:d.name}}})}function Kn(e,n){let r=h(e,n);if(!b(r))return r;let{state:t,warnings:s}=k(r.root,r.actor);if(t.sprints.length===0)return{ok:!0,exitCode:0,warnings:s,message:`No sprints yet.
|
|
83
|
+
kadence sprint create "Sprint 1"`,data:{schema:"kadence/v1",ok:!0,sprints:[]}};let i=t.sprints.map(o=>{let a=t.tasks.filter(u=>u.sprint===o.id),d=a.reduce((u,c)=>u+(c.estimate??0),0);return{sprint:o,tasks:a,points:d}});return{ok:!0,exitCode:0,warnings:s,message:i.map(({sprint:o,tasks:a,points:d})=>{let u=o.status==="active"?"\u2192":" ",c=a.length===0?"":` ${a.length} tasks, ${d} points`;return`${u} ${o.name.padEnd(20)} ${o.status.padEnd(8)}${c}`}).join(`
|
|
84
|
+
`),data:{schema:"kadence/v1",ok:!0,sprints:i.map(({sprint:o,tasks:a,points:d})=>({id:o.id,name:o.name,status:o.status,taskIds:o.taskIds,taskCount:a.length,points:d}))}}}function At(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;let n=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(n.getTime())&&n.toISOString().slice(0,10)===e}function Hn(e,n,r,t){let s=h(e,n);if(!b(s))return s;for(let[l,p]of[["--start",t.startDate],["--end",t.endDate]])if(p!==void 0&&p!==""&&!At(p))return{ok:!1,exitCode:2,message:`${l} must be YYYY-MM-DD, got "${p}".
|
|
85
|
+
kadence sprint edit --start 2026-09-01`};let{state:i,warnings:o}=k(s.root,s.actor),a=r===void 0?j(i):_(i,r);if(a===void 0)return{ok:!1,exitCode:1,message:r===void 0?`No active sprint.
|
|
86
|
+
kadence sprint list`:`No sprint named "${r}".
|
|
87
|
+
kadence sprint list`};if(a.status==="closed")return{ok:!1,exitCode:1,message:`Sprint "${a.name}" is closed \u2014 its record is not rewritten.`};if(t.name!==void 0&&t.name!==a.name&&_(i,t.name)!==void 0)return{ok:!1,exitCode:2,message:`Sprint "${t.name}" already exists.`};let d=t.startDate??a.startDate??"",u=t.endDate??a.endDate??"";if(d!==""&&u!==""&&u<d)return{ok:!1,exitCode:2,message:`The end date (${u}) is before the start date (${d}).`};let c={},f=[];return t.name!==void 0&&t.name!==a.name&&(c.name=t.name,f.push("name")),t.description!==void 0&&t.description!==(a.description??"")&&(c.description=t.description,f.push("description")),t.startDate!==void 0&&t.startDate!==(a.startDate??"")&&(c.startDate=t.startDate,f.push("start")),t.endDate!==void 0&&t.endDate!==(a.endDate??"")&&(c.endDate=t.endDate,f.push("end")),f.length===0?{ok:!0,exitCode:0,warnings:o,message:`"${a.name}": nothing changed.`}:(F(s,"sprint.updated",a.id,c),{ok:!0,exitCode:0,warnings:o,message:`"${a.name}": updated ${f.join(", ")}.`,data:{schema:"kadence/v1",ok:!0,sprint:{id:a.id,changed:f}}})}function Yn(e,n,r){let t=h(e,n);if(!b(t))return t;let{state:s,warnings:i}=k(t.root,t.actor),o=r===void 0?j(s):_(s,r);if(o===void 0)return{ok:!1,exitCode:1,message:r===void 0?`No active sprint.
|
|
88
|
+
kadence sprint list`:`No sprint named "${r}".
|
|
89
|
+
kadence sprint list`};let a=je(s,I(t.root).events,o);return a===null?{ok:!0,exitCode:0,warnings:i,message:`"${o.name}" has no tasks yet.
|
|
90
|
+
kadence sprint add KAD-1`,data:{schema:"kadence/v1",ok:!0,burndown:null}}:{ok:!0,exitCode:0,warnings:i,message:Ae(a),data:{schema:"kadence/v1",ok:!0,burndown:a}}}export{ce as a,O as b,M as c,w as d,$ as e,Ot as f,Ie as g,D as h,R as i,Z as j,qt as k,H as l,un as m,h as n,b as o,ln as p,k as q,cn as r,pn as s,Re as t,fn as u,gn as v,mn as w,kn as x,yn as y,hn as z,bn as A,xn as B,vn as C,En as D,Dn as E,Mn as F,Bn as G,Ln as H,Jn as I,Un as J,Kn as K,Hn as L,Yn as M};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{C as x,D as C,E as h,G as y,H as E,I as A,J as N,M as F,n as b,o as T,p as c,q as l,s as d,u as f,w as u,y as S,z as k}from"./chunk-BUHXYOZ4.js";async function $(r,e){let a=b(r,e);if(!T(a))return a;if(process.stdout.isTTY!==!0)return{ok:!1,exitCode:1,message:`The interactive board needs a terminal.
|
|
3
|
+
For pipes and scripts use:
|
|
4
|
+
kadence board --json`};let g;try{g=await import("./board-7RVHIQJQ.js")}catch(t){return{ok:!1,exitCode:1,message:`The interactive board could not start: ${t.message}
|
|
5
|
+
The plain board always works:
|
|
6
|
+
kadence board`}}return g.runBoardUi({reload:()=>l(a.root,a.actor),move:(t,o)=>s(d(r,e,t,o)),assign:(t,o)=>s(f(r,e,t,o)),create:t=>s(c(r,e,t,{})),remove:t=>s(S(r,e,t)),edit:t=>{if(!h(e,!0))return"No editor available.";let{state:o}=l(a.root,a.actor),m=o.tasks.find(i=>i.id===t);if(m===void 0)return"Task not found.";let n=C(e,m.description??"",`Editing the description of ${m.label}.`);return n.error!==null?n.error:n.text===null?"Aborted \u2014 nothing changed.":s(u(r,e,t,{description:n.text}))},comment:(t,o)=>s(k(r,e,t,o)),setField:(t,o,m)=>{let n=m.trim();if(o==="status")return s(d(r,e,t,n));if(o==="assignee")return s(f(r,e,t,n.length===0?"none":n));if(o==="estimate"){let i=Number(n);return!Number.isFinite(i)||i<0?"Estimate must be a positive number.":s(u(r,e,t,{estimate:i}))}if(o==="labels"){let i=n.split(",").map(p=>p.trim()).filter(p=>p.length>0);return s(u(r,e,t,{labels:i}))}return s(u(r,e,t,{[o]:n}))},logTime:(t,o)=>s(x(r,e,t,o)),setPriority:(t,o)=>s(u(r,e,t,{priority:o})),addToSprint:t=>s(y(r,e,t,{})),sprintStatus:()=>A(r,e).message,burndown:()=>F(r,e,void 0).message,sprintStart:()=>s(N(r,e,void 0)),sprintClose:()=>s(E(r,e))}),{ok:!0,exitCode:0,message:""}}function s(r){return r.message.split(`
|
|
7
|
+
`)[0]??""}export{$ as runUi};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{A as se,B as ae,C as T,D as ie,E as re,F as oe,G as de,H as ce,I as le,J as ue,K as me,L as fe,M as pe,a as F,b as U,c as $,d as A,e as g,f as E,g as q,h as W,i as P,j as Y,k as G,l as V,m as S,n as k,o as b,p as J,q as y,r as H,s as z,t as Q,u as X,v as O,w as Z,x as ee,y as te,z as ne}from"./chunks/chunk-BUHXYOZ4.js";function K(n){return n==null?[]:Array.isArray(n)?n:[n]}function _e(n,t,s,e){var a,i=n[t],r=~e.string.indexOf(t)?s==null||s===!0?"":String(s):typeof s=="boolean"?s:~e.boolean.indexOf(t)?s==="false"?!1:s==="true"||(n._.push((a=+s,a*0===0?a:s)),!!s):(a=+s,a*0===0?a:s);n[t]=i==null?r:Array.isArray(i)?i.concat(r):[i,r]}function Be(n,t){n=n||[],t=t||{};var s,e,a,i,r,d={_:[]},o=0,l=0,u=0,m=n.length;let p=t.alias!==void 0,D=t.unknown!==void 0,M=t.default!==void 0;if(t.alias=t.alias||{},t.string=K(t.string),t.boolean=K(t.boolean),p)for(s in t.alias)for(e=t.alias[s]=K(t.alias[s]),o=0;o<e.length;o++)(t.alias[e[o]]=e.concat(s)).splice(o,1);for(o=t.boolean.length;o-- >0;)for(e=t.alias[t.boolean[o]]||[],l=e.length;l-- >0;)t.boolean.push(e[l]);for(o=t.string.length;o-- >0;)for(e=t.alias[t.string[o]]||[],l=e.length;l-- >0;)t.string.push(e[l]);if(M){for(s in t.default)if(i=typeof t.default[s],e=t.alias[s]=t.alias[s]||[],t[i]!==void 0)for(t[i].push(s),o=0;o<e.length;o++)t[i].push(e[o])}let L=D?Object.keys(t.alias):[];for(o=0;o<m;o++){if(a=n[o],a==="--"){d._=d._.concat(n.slice(++o));break}for(l=0;l<a.length&&a.charCodeAt(l)===45;l++);if(l===0)d._.push(a);else if(a.substring(l,l+3)==="no-"){if(i=a.substring(l+3),D&&!~L.indexOf(i))return t.unknown(a);d[i]=!1}else{for(u=l+1;u<a.length&&a.charCodeAt(u)!==61;u++);for(i=a.substring(l,u),r=a.substring(++u)||o+1===m||(""+n[o+1]).charCodeAt(0)===45||n[++o],e=l===2?[i]:i,u=0;u<e.length;u++){if(i=e[u],D&&!~L.indexOf(i))return t.unknown("-".repeat(l)+i);_e(d,i,u+1<e.length||r,t)}}}if(M)for(s in t.default)d[s]===void 0&&(d[s]=t.default[s]);if(p)for(s in d)for(e=t.alias[s]||[];e.length>0;)d[e.shift()]=d[s];return d}function ke(n){return n.replace(/[<[].+/,"").trim()}function Me(n){let t=/<([^>]+)>/g,s=/\[([^\]]+)\]/g,e=[],a=d=>{let o=!1,l=d[1];return l.startsWith("...")&&(l=l.slice(3),o=!0),{required:d[0].startsWith("<"),value:l,variadic:o}},i;for(;i=t.exec(n);)e.push(a(i));let r;for(;r=s.exec(n);)e.push(a(r));return e}function Le(n){let t={alias:{},boolean:[]};for(let[s,e]of n.entries())e.names.length>1&&(t.alias[e.names[0]]=e.names.slice(1)),e.isBoolean&&(e.negated&&n.some((a,i)=>i!==s&&a.names.some(r=>e.names.includes(r))&&typeof a.required=="boolean")||t.boolean.push(e.names[0]));return t}function he(n){return n.sort((t,s)=>t.length>s.length?-1:1)[0]}function ge(n,t){return n.length>=t?n:`${n}${" ".repeat(t-n.length)}`}function Fe(n){return n.replaceAll(/([a-z])-([a-z])/g,(t,s,e)=>s+e.toUpperCase())}function Ue(n,t,s){let e=n;for(let a=0;a<t.length;a++){let i=t[a];if(a===t.length-1){e[i]=s;return}if(e[i]==null){let r=+t[a+1]>-1;e[i]=r?[]:{}}e=e[i]}}function qe(n,t){for(let s of Object.keys(t)){let e=t[s];e.shouldTransform&&(n[s]=[n[s]].flat(),typeof e.transformFunction=="function"&&(n[s]=n[s].map(e.transformFunction)))}}function We(n){let t=/([^\\/]+)$/.exec(n);return t?t[1]:""}function be(n){return n.split(".").map((t,s)=>s===0?Fe(t):t).join(".")}var x=class extends Error{constructor(n){super(n),this.name="CACError",typeof Error.captureStackTrace!="function"&&(this.stack=new Error(n).stack)}},Pe=class{rawName;description;name;names;isBoolean;required;config;negated;constructor(n,t,s){this.rawName=n,this.description=t,this.config=Object.assign({},s),n=n.replaceAll(".*",""),this.negated=!1,this.names=ke(n).split(",").map(e=>{let a=e.trim().replace(/^-{1,2}/,"");return a.startsWith("no-")&&(this.negated=!0,a=a.replace(/^no-/,"")),be(a)}).sort((e,a)=>e.length>a.length?1:-1),this.name=this.names.at(-1),this.negated&&this.config.default==null&&(this.config.default=!0),n.includes("<")?this.required=!0:n.includes("[")?this.required=!1:this.isBoolean=!0}},j,w;if(typeof process<"u"){let n;typeof Deno<"u"&&typeof Deno.version?.deno=="string"?n="deno":typeof Bun<"u"&&typeof Bun.version=="string"?n="bun":n="node",w=`${process.platform}-${process.arch} ${n}-${process.version}`,j=process.argv}else typeof navigator>"u"?w="unknown":w=`${navigator.platform} ${navigator.userAgent}`;var ye=class{rawName;description;config;cli;options;aliasNames;name;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;constructor(n,t,s={},e){this.rawName=n,this.description=t,this.config=s,this.cli=e,this.options=[],this.aliasNames=[],this.name=ke(n),this.args=Me(n),this.examples=[]}usage(n){return this.usageText=n,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(n,t="-v, --version"){return this.versionNumber=n,this.option(t,"Display version number"),this}example(n){return this.examples.push(n),this}option(n,t,s){let e=new Pe(n,t,s);return this.options.push(e),this}alias(n){return this.aliasNames.push(n),this}action(n){return this.commandAction=n,this}isMatched(n){return this.name===n||this.aliasNames.includes(n)}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof ve}hasOption(n){return n=n.split(".")[0],this.options.find(t=>t.names.includes(n))}outputHelp(){let{name:n,commands:t}=this.cli,{versionNumber:s,options:e,helpCallback:a}=this.cli.globalCommand,i=[{body:`${n}${s?`/${s}`:""}`}];if(i.push({title:"Usage",body:` $ ${n} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&t.length>0){let d=he(t.map(o=>o.rawName));i.push({title:"Commands",body:t.map(o=>` ${ge(o.rawName,d.length)} ${o.description}`).join(`
|
|
3
|
+
`)},{title:"For more info, run any command with the `--help` flag",body:t.map(o=>` $ ${n}${o.name===""?"":` ${o.name}`} --help`).join(`
|
|
4
|
+
`)})}let r=this.isGlobalCommand?e:[...this.options,...e||[]];if(!this.isGlobalCommand&&!this.isDefaultCommand&&(r=r.filter(d=>d.name!=="version")),r.length>0){let d=he(r.map(o=>o.rawName));i.push({title:"Options",body:r.map(o=>` ${ge(o.rawName,d.length)} ${o.description} ${o.config.default===void 0?"":`(default: ${o.config.default})`}`).join(`
|
|
5
|
+
`)})}this.examples.length>0&&i.push({title:"Examples",body:this.examples.map(d=>typeof d=="function"?d(n):d).join(`
|
|
6
|
+
`)}),a&&(i=a(i)||i),console.info(i.map(d=>d.title?`${d.title}:
|
|
7
|
+
${d.body}`:d.body).join(`
|
|
8
|
+
|
|
9
|
+
`))}outputVersion(){let{name:n}=this.cli,{versionNumber:t}=this.cli.globalCommand;t&&console.info(`${n}/${t} ${w}`)}checkRequiredArgs(){let n=this.args.filter(t=>t.required).length;if(this.cli.args.length<n)throw new x(`missing required args for command \`${this.rawName}\``)}checkUnknownOptions(){let{options:n,globalCommand:t}=this.cli;if(!this.config.allowUnknownOptions){for(let s of Object.keys(n))if(s!=="--"&&!this.hasOption(s)&&!t.hasOption(s))throw new x(`Unknown option \`${s.length>1?`--${s}`:`-${s}`}\``)}}checkOptionValue(){let{options:n,globalCommand:t}=this.cli,s=[...t.options,...this.options];for(let e of s){let a=n[e.name.split(".")[0]];if(e.required){let i=s.some(r=>r.negated&&r.names.includes(e.name));if(a===!0||a===!1&&!i)throw new x(`option \`${e.rawName}\` value is missing`)}}}checkUnusedArgs(){let n=this.args.some(t=>t.variadic)?1/0:this.args.length;if(n<this.cli.args.length)throw new x(`Unused args: ${this.cli.args.slice(n).map(t=>`\`${t}\``).join(", ")}`)}},ve=class extends ye{constructor(n){super("@@global@@","",{},n)}},Ye=class extends EventTarget{name;commands;globalCommand;matchedCommand;matchedCommandName;rawArgs;args;options;showHelpOnExit;showVersionOnExit;constructor(n=""){super(),this.name=n,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new ve(this),this.globalCommand.usage("<command> [options]")}usage(n){return this.globalCommand.usage(n),this}command(n,t,s){let e=new ye(n,t||"",s,this);return e.globalCommand=this.globalCommand,this.commands.push(e),e}option(n,t,s){return this.globalCommand.option(n,t,s),this}help(n){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=n,this.showHelpOnExit=!0,this}version(n,t="-v, --version"){return this.globalCommand.version(n,t),this.showVersionOnExit=!0,this}example(n){return this.globalCommand.example(n),this}outputHelp(){this.matchedCommand?this.matchedCommand.outputHelp():this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:n,options:t},s,e){return this.args=n,this.options=t,s&&(this.matchedCommand=s),e&&(this.matchedCommandName=e),this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}parse(n,{run:t=!0}={}){if(!n){if(!j)throw new Error("No argv provided and runtime process argv is not available.");n=j}this.rawArgs=n,this.name||(this.name=n[1]?We(n[1]):"cli");let s=!0;for(let a of this.commands){let i=this.mri(n.slice(2),a),r=i.args[0];if(a.isMatched(r)){s=!1;let d={...i,args:i.args.slice(1)};this.setParsedInfo(d,a,r),this.dispatchEvent(new CustomEvent(`command:${r}`,{detail:a}))}}if(s){for(let a of this.commands)if(a.isDefaultCommand){s=!1;let i=this.mri(n.slice(2),a);this.setParsedInfo(i,a),this.dispatchEvent(new CustomEvent("command:!",{detail:a}))}}if(s){let a=this.mri(n.slice(2));this.setParsedInfo(a)}this.options.help&&this.showHelpOnExit&&(this.outputHelp(),t=!1,this.unsetMatchedCommand()),this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null&&(this.outputVersion(),t=!1,this.unsetMatchedCommand());let e={args:this.args,options:this.options};return t&&this.runMatchedCommand(),!this.matchedCommand&&this.args[0]&&this.dispatchEvent(new CustomEvent("command:*",{detail:this.args[0]})),e}mri(n,t){let s=[...this.globalCommand.options,...t?t.options:[]],e=Le(s),a=[],i=n.indexOf("--");i!==-1&&(a=n.slice(i+1),n=n.slice(0,i));let r=Be(n,e);r=Object.keys(r).reduce((m,p)=>({...m,[be(p)]:r[p]}),{_:[]});let d=r._,o={"--":a},l=t&&t.config.ignoreOptionDefaultValue?t.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,u=Object.create(null);for(let m of s){if(!l&&m.config.default!==void 0)for(let p of m.names)o[p]=m.config.default;Array.isArray(m.config.type)&&u[m.name]===void 0&&(u[m.name]=Object.create(null),u[m.name].shouldTransform=!0,u[m.name].transformFunction=m.config.type[0])}for(let m of Object.keys(r))m!=="_"&&(Ue(o,m.split("."),r[m]),qe(o,u));return{args:d,options:o}}runMatchedCommand(){let{args:n,options:t,matchedCommand:s}=this;if(!s||!s.commandAction)return;s.checkUnknownOptions(),s.checkOptionValue(),s.checkRequiredArgs(),s.checkUnusedArgs();let e=[];return s.args.forEach((a,i)=>{a.variadic?e.push(n.slice(i)):e.push(n[i])}),e.push(t),s.commandAction.apply(this,e)}},Ae=(n="")=>new Ye(n);import{existsSync as C,mkdirSync as Ge,readFileSync as $e,writeFileSync as I}from"node:fs";import{join as _}from"node:path";var xe=`# kadence \u2014 for AI agents
|
|
10
|
+
|
|
11
|
+
This project's tasks live here as plain files. Read them directly, or through
|
|
12
|
+
the CLI. No server required.
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
kadence board --json the whole board
|
|
17
|
+
kadence task list --json all tasks
|
|
18
|
+
kadence task show KAD-42 --json one task with its history
|
|
19
|
+
kadence task add "title" -d "..." --type bug --estimate 3
|
|
20
|
+
kadence task move KAD-42 in_progress
|
|
21
|
+
kadence task assign KAD-42 you@example.com
|
|
22
|
+
kadence sprint status --json current sprint progress
|
|
23
|
+
|
|
24
|
+
## JSON contract
|
|
25
|
+
|
|
26
|
+
Every \`--json\` response carries \`schema: "kadence/v1"\`. stdout holds JSON and
|
|
27
|
+
nothing else; warnings go to stderr. Exit codes: 0 success, 1 runtime error,
|
|
28
|
+
2 bad arguments.
|
|
29
|
+
|
|
30
|
+
## Working as an agent
|
|
31
|
+
|
|
32
|
+
Set \`KADENCE_SOURCE=agent\` so events record your authorship. Without it an
|
|
33
|
+
event is marked as human \u2014 we do not guess.
|
|
34
|
+
|
|
35
|
+
## What not to do
|
|
36
|
+
|
|
37
|
+
Do not hand-edit files under \`.kadence/events/\`: the journal is appended to,
|
|
38
|
+
never modified. To correct the state, add a new event through the CLI.
|
|
39
|
+
`,we="<!-- kadence:begin -->",R="<!-- kadence:end -->",N=`${we}
|
|
40
|
+
## Project tasks \u2014 kadence
|
|
41
|
+
|
|
42
|
+
Tasks live in \`.kadence/\` as plain files. Read them directly or via the CLI:
|
|
43
|
+
|
|
44
|
+
kadence board --json the whole board
|
|
45
|
+
kadence task list --json all tasks
|
|
46
|
+
kadence task move KAD-1 done change state
|
|
47
|
+
|
|
48
|
+
\`--json\` responses carry \`schema: "kadence/v1"\`; stdout is JSON only.
|
|
49
|
+
When acting as an agent, set \`KADENCE_SOURCE=agent\`.
|
|
50
|
+
|
|
51
|
+
Details: \`.kadence/README.md\`
|
|
52
|
+
${R}`;function Ce(n){if(n===null||n.trim().length===0)return`${N}
|
|
53
|
+
`;let t=n.indexOf(we),s=n.indexOf(R);if(t!==-1&&s!==-1&&s>t){let a=n.slice(0,t),i=n.slice(s+R.length);return`${a}${N}${i}`}let e=n.endsWith(`
|
|
54
|
+
`)?`
|
|
55
|
+
`:`
|
|
56
|
+
|
|
57
|
+
`;return`${n}${e}${N}
|
|
58
|
+
`}var De=".kadence/state.json";function Ee(n){let t=F(n);if(t===null)return{ok:!1,alreadyInitialized:!1,root:null,message:`kadence lives inside a git repository, and there is none here.
|
|
59
|
+
Create one and try again:
|
|
60
|
+
git init`};let s=C($(t));Ge($(t),{recursive:!0}),Ve(t);let e=_(U(t),"README.md");return C(e)||I(e,xe,"utf8"),Je(t),{ok:!0,alreadyInitialized:s,root:t,message:s?"kadence is already initialised.":`kadence is ready.
|
|
61
|
+
|
|
62
|
+
kadence task add "first task"
|
|
63
|
+
kadence board
|
|
64
|
+
|
|
65
|
+
Files were created but not committed \u2014 that call is yours.`}}function Ve(n){let t=_(n,".gitignore"),s="";if(C(t)&&(s=$e(t,"utf8")),s.includes(De))return;let e=s.length>0&&!s.endsWith(`
|
|
66
|
+
`)?`
|
|
67
|
+
`:"";I(t,`${s}${e}${De}
|
|
68
|
+
`,"utf8")}function Je(n){let t=_(n,"AGENTS.md"),s=C(t)?$e(t,"utf8"):null,e=Ce(s);e!==s&&I(t,e,"utf8")}var B="cancelled";function Se(n,t,s){let e=k(n,t);if(!b(e))return e;let{state:a,warnings:i}=y(e.root,e.actor),r=a.tasks.filter(u=>u.status!==B);if(s.assignee!==void 0){let u=s.assignee.toLowerCase(),m=u==="me"?e.actor.toLowerCase():u;r=r.filter(p=>(p.assignee??"").toLowerCase()===m)}if(s.sprint==="active"){let u=a.sprints.find(m=>m.status==="active"||m.status==="planned");r=u===void 0?[]:r.filter(m=>m.sprint===u.id)}let d={};for(let u of a.statuses)u!==B&&(d[u]=r.filter(m=>m.status===u));let o=a.orphanStatuses.filter(u=>u!==B);for(let u of o)d[u]=r.filter(m=>m.status===u);let l=o.length>0?[`Statuses not in the board configuration: ${o.join(", ")}.
|
|
69
|
+
Tasks there are still shown. Add the column or move them:
|
|
70
|
+
kadence board config --statuses "..."`]:[];return{ok:!0,exitCode:0,warnings:[...i,...l],message:G(d,Y(t,process.stdout.isTTY===!0)),data:{schema:"kadence/v1",ok:!0,columns:Object.fromEntries(Object.entries(d).map(([u,m])=>[u,m.map(Q)]))}}}function Oe(n,t,s){let e=k(n,t);if(!b(e))return e;let{state:a,warnings:i}=y(e.root,e.actor);if(s===void 0)return{ok:!0,exitCode:0,warnings:i,message:`Board columns: ${a.statuses.join(", ")}
|
|
71
|
+
|
|
72
|
+
Change them with:
|
|
73
|
+
kadence board config --statuses "todo,doing,review,done"`,data:{schema:"kadence/v1",ok:!0,statuses:a.statuses}};let r=s.split(",").map(l=>l.trim().toLowerCase().replace(/\s+/g,"_")).filter(l=>l.length>0);if(r.length===0)return{ok:!1,exitCode:2,message:"At least one status is required."};if(new Set(r).size!==r.length)return{ok:!1,exitCode:2,message:"The same status is listed twice."};if(!r.includes(E))return{ok:!1,exitCode:2,message:`The list must include "${E}" \u2014 velocity and burndown are computed from it.`};let d=a.tasks.filter(l=>!r.includes(l.status)&&l.status!==q);A(e.root,{id:g(),type:"board.configured",entity:g(),actor:e.actor,ts:new Date().toISOString(),source:e.source,data:{statuses:r}});let o=d.length>0?`
|
|
74
|
+
${d.length} task(s) remain in removed columns: ${[...new Set(d.map(l=>l.status))].join(", ")}. They are still listed.`:"";return{ok:!0,exitCode:0,warnings:i,message:`Board columns: ${r.join(", ")}${o}`,data:{schema:"kadence/v1",ok:!0,statuses:r}}}var He=["description","type","priority","estimate","labels","assignee"];function Te(n,t,s,e){let a=k(n,t);if(!b(a))return a;let i=s.trim();if(i.length===0)return{ok:!1,exitCode:2,message:"A template needs a name."};let r={};for(let d of He)e[d]!==void 0&&(r[d]=e[d]);return Object.keys(r).length===0?{ok:!1,exitCode:2,message:`A template needs at least one field.
|
|
75
|
+
kadence template save bug --type bug --priority high --label triage`}:(A(a.root,{id:g(),type:"template.saved",entity:g(),actor:a.actor,ts:new Date().toISOString(),source:a.source,data:{name:i,fields:r}}),{ok:!0,exitCode:0,message:`Template "${i}" saved: ${Object.keys(r).join(", ")}.
|
|
76
|
+
kadence task add "title" --template ${i}`,data:{schema:"kadence/v1",ok:!0,template:{name:i,fields:r}}})}function Ke(n,t){let s=k(n,t);if(!b(s))return s;let{state:e,warnings:a}=y(s.root,s.actor);return e.templates.length===0?{ok:!0,exitCode:0,warnings:a,message:`No templates yet.
|
|
77
|
+
kadence template save bug --type bug --priority high`,data:{schema:"kadence/v1",ok:!0,templates:[]}}:{ok:!0,exitCode:0,warnings:a,message:e.templates.map(i=>{let r=Object.entries(i.fields).map(([d,o])=>`${d}=${Array.isArray(o)?o.join("/"):String(o)}`).join(" ");return` ${i.name.padEnd(14)} ${r}`}).join(`
|
|
78
|
+
`),data:{schema:"kadence/v1",ok:!0,templates:e.templates}}}function je(n,t,s){let e=k(n,t);if(!b(e))return e;let{state:a,warnings:i}=y(e.root,e.actor);return a.templates.some(r=>r.name===s)?(A(e.root,{id:g(),type:"template.deleted",entity:g(),actor:e.actor,ts:new Date().toISOString(),source:e.source,data:{name:s}}),{ok:!0,exitCode:0,warnings:i,message:`Template "${s}" deleted.`}):{ok:!1,exitCode:1,message:`No template "${s}".
|
|
79
|
+
kadence template list`}}function Ne(n,t,s){let e=k(n,t);if(!b(e))return{error:e};let{state:a}=y(e.root,e.actor),i=a.templates.find(r=>r.name===s);if(i===void 0){let r=a.templates.map(d=>d.name).join(", ");return{error:{ok:!1,exitCode:1,message:`No template "${s}".`+(r.length>0?`
|
|
80
|
+
Available: ${r}`:`
|
|
81
|
+
kadence template save ...`)}}}return{fields:i.fields}}var h=Ae("kadence");function c(n,t){for(let s of n.warnings??[])process.stderr.write(`${s}
|
|
82
|
+
`);if(t){let s=n.data??{schema:"kadence/v1",ok:n.ok,...n.ok?{}:{error:{message:n.message}}};process.stdout.write(`${JSON.stringify(s)}
|
|
83
|
+
`)}else(n.ok?process.stdout:process.stderr).write(`${n.message}
|
|
84
|
+
`);process.exit(n.exitCode)}function Re(n,t,s,e){if(n!==void 0)return n;re(process.env,process.stdout.isTTY===!0)||c(f(`No terminal available for an editor.
|
|
85
|
+
Pass the text directly:
|
|
86
|
+
--description "..."`),e);let a=ie(process.env,t,s);return a.error!==null&&c({ok:!1,exitCode:1,message:a.error},e),a.text===null&&c({ok:!0,exitCode:0,message:"Aborted \u2014 nothing changed."},e),a.text}function f(n){return{ok:!1,exitCode:2,message:n}}h.command("init","Set up kadence in this repository").example(" kadence init").action(()=>{let n=Ee(process.cwd());c({ok:n.ok,message:n.message,exitCode:n.ok?0:1},!1)});h.command("task [action] [arg] [value]","Tasks: add | list | show | move | assign").option("--title <text>","New title (for edit)").option("-d, --description <text>","Full description; use quotes for multiple lines").option("--type <type>",`Type: ${W.join(" | ")}`).option("--priority <level>",`Priority: ${P.join(" | ")}`).option("-a, --assignee <who>","Assignee, e.g. dev@example.com").option("--label <name>","Label; repeat the flag for several").option("--estimate <points>","Estimate in points, a positive number").option("--due <date>","Due date, YYYY-MM-DD; empty string clears it").option("--status <status>",`Filter by status: ${S.join(" | ")}`).option("--search <text>","Search title, description and comments").option("--overdue","Only tasks past their due date").option("--due-before <date>","Only tasks due before YYYY-MM-DD").option("--sort <key>",`Sort by: ${V.join(" | ")}`).option("--tree","Show parent/child structure").option("--parent <task>",'Parent task, e.g. KAD-1 (use "none" to detach)').option("--template <name>","Pre-fill fields from a saved template").option("--json","Machine-readable output for agents").example(' kadence task add "Fix login" -d "Broken since 2.3" --type bug --priority high --estimate 3').example(" kadence task list --status in_progress --sort priority").example(" kadence task list --search cookie --overdue").example(" kadence task list --assignee me --label auth").example(" kadence task list --tree").example(" kadence task move KAD-1,KAD-2,KAD-3 done bulk: all or nothing").example(' kadence task add "Login form" --parent KAD-1 KAD-1 can be an epic').example(" kadence task parent KAD-2 KAD-1").example(" kadence task block KAD-2 KAD-1 KAD-2 waits for KAD-1").example(" kadence task unblock KAD-2 KAD-1").example(" kadence task log KAD-1 2h also 90m, or -30m to correct").example(' kadence task add "Crash on save" --template bug').example(" kadence task show KAD-1").example(" kadence task move KAD-1 done").example(" kadence task edit KAD-1 --priority urgent --due 2026-09-30").example(" kadence task edit KAD-1 opens $EDITOR for the description").example(' kadence task comment KAD-1 "Needs review"').example(' kadence task assign KAD-1 dev@example.com (use "none" to unassign)').example(" kadence task cancel KAD-1 keeps it in history").example(" kadence task delete KAD-1 drops it from the board").action((n,t,s,e)=>{let a=e.json===!0,i=process.cwd();switch(n===void 0&&c(f(`Which action?
|
|
87
|
+
kadence task add "Fix login"
|
|
88
|
+
kadence task list
|
|
89
|
+
kadence task show KAD-1
|
|
90
|
+
kadence task edit KAD-1
|
|
91
|
+
kadence task move KAD-1 done
|
|
92
|
+
kadence task assign KAD-1 dev@example.com
|
|
93
|
+
kadence task comment KAD-1 "text"
|
|
94
|
+
kadence task cancel KAD-1
|
|
95
|
+
kadence task delete KAD-1`),a),n){case"add":{t===void 0&&c(f(`A title is required:
|
|
96
|
+
kadence task add "Fix login"`),a);let r=e.estimate===void 0?void 0:Number(e.estimate);r!==void 0&&(!Number.isFinite(r)||r<0)&&c(f(`Estimate must be a positive number, got "${e.estimate}".
|
|
97
|
+
kadence task add "Fix login" --estimate 3`),a);let d={};if(e.template!==void 0){let l=Ne(i,process.env,e.template);"error"in l?c(l.error,a):d=l.fields}let o=e.label===void 0?void 0:Array.isArray(e.label)?e.label:[e.label];c(J(i,process.env,t,{...d,...e.description!==void 0?{description:e.description}:{},...e.type!==void 0?{type:e.type}:{},...e.priority!==void 0?{priority:e.priority}:{},...e.assignee!==void 0?{assignee:e.assignee}:{},...o!==void 0?{labels:o}:{},...e.due!==void 0?{due:e.due}:{},...e.parent!==void 0?{parent:e.parent}:{},...r!==void 0?{estimate:r}:{}}),a);break}case"log":(t===void 0||s===void 0)&&c(f(`A task and a duration are required:
|
|
98
|
+
kadence task log KAD-1 2h
|
|
99
|
+
kadence task log KAD-1 90m`),a),c(T(i,process.env,t,s),a);break;case"parent":(t===void 0||s===void 0)&&c(f(`A task and a parent are required:
|
|
100
|
+
kadence task parent KAD-2 KAD-1
|
|
101
|
+
kadence task parent KAD-2 none to detach`),a),c(se(i,process.env,t,s),a);break;case"block":case"unblock":(t===void 0||s===void 0)&&c(f(`A task and a blocker are required:
|
|
102
|
+
kadence task block KAD-2 KAD-1 KAD-2 waits for KAD-1
|
|
103
|
+
kadence task unblock KAD-2 KAD-1`),a),c(ae(i,process.env,t,s,n==="unblock"),a);break;case"edit":{t===void 0&&c(f(`Which task?
|
|
104
|
+
kadence task edit KAD-1 --priority high`),a);let r=e.estimate===void 0?void 0:Number(e.estimate);r!==void 0&&(!Number.isFinite(r)||r<0)&&c(f(`Estimate must be a positive number, got "${e.estimate}".`),a);let d=e.label===void 0?void 0:Array.isArray(e.label)?e.label:[e.label],o=e.title!==void 0||e.description!==void 0||e.type!==void 0||e.priority!==void 0||e.due!==void 0||r!==void 0||d!==void 0||s!==void 0,l=e.description;if(!o){let u=O(i,process.env,t);u.ok||c(u,a);let m=u.data.task??{description:null};l=Re(void 0,m.description??"",`Editing the description of ${t}.`,a)}c(Z(i,process.env,t,{...e.title!==void 0?{title:e.title}:s!==void 0?{title:s}:{},...l!==void 0?{description:l}:{},...e.type!==void 0?{type:e.type}:{},...e.priority!==void 0?{priority:e.priority}:{},...e.due!==void 0?{due:e.due}:{},...r!==void 0?{estimate:r}:{},...d!==void 0?{labels:d}:{}}),a);break}case"comment":{t===void 0&&c(f(`Which task?
|
|
105
|
+
kadence task comment KAD-1 "text"`),a);let r=Re(s,"",`Comment on ${t}.`,a);c(ne(i,process.env,t,r??""),a);break}case"cancel":t===void 0&&c(f(`Which task?
|
|
106
|
+
kadence task cancel KAD-1`),a),c(ee(i,process.env,t),a);break;case"delete":t===void 0&&c(f(`Which task?
|
|
107
|
+
kadence task delete KAD-1`),a),c(te(i,process.env,t),a);break;case"list":c(H(i,process.env,{...e.status!==void 0?{status:e.status}:{},...e.search!==void 0?{search:e.search}:{},...e.type!==void 0?{type:e.type}:{},...e.priority!==void 0?{priority:e.priority}:{},...e.assignee!==void 0?{assignee:e.assignee}:{},...e.label!==void 0?{label:Array.isArray(e.label)?e.label.at(-1):e.label}:{},...e.overdue===!0?{overdue:!0}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.sort!==void 0?{sort:e.sort}:{},...e.tree===!0?{tree:!0}:{}}),a);break;case"show":t===void 0&&c(f(`Which task?
|
|
108
|
+
kadence task show KAD-1`),a),c(O(i,process.env,t),a);break;case"move":(t===void 0||s===void 0)&&c(f(`A task and a target status are required:
|
|
109
|
+
kadence task move KAD-1 done
|
|
110
|
+
Statuses: ${S.join(", ")}`),a),c(z(i,process.env,t,s),a);break;case"assign":(t===void 0||s===void 0)&&c(f(`A task and an assignee are required:
|
|
111
|
+
kadence task assign KAD-1 dev@example.com
|
|
112
|
+
kadence task assign KAD-1 none to unassign`),a),c(X(i,process.env,t,s),a);break;default:c(f(`Unknown action "${n}".
|
|
113
|
+
Available: add, list, show, edit, move, assign, comment, log,
|
|
114
|
+
parent, block, unblock, cancel, delete
|
|
115
|
+
kadence task --help`),a)}});h.command("board [action]",'Kanban board in the terminal; "config" edits the columns').option("--statuses <list>",'Comma-separated columns, e.g. "todo,doing,done"').option("-a, --assignee <who>",`Only this person's tasks; "me" means you`).option("--sprint","Only tasks in the active sprint").option("--json","Machine-readable output for agents").example(" kadence board").example(" kadence board --assignee me --sprint").example(" kadence board config").example(' kadence board config --statuses "todo,doing,review,done"').action((n,t)=>{n==="config"&&c(Oe(process.cwd(),process.env,t.statuses),t.json===!0),n!==void 0&&c(f(`Unknown action "${n}".
|
|
116
|
+
Available: config
|
|
117
|
+
kadence board --help`),t.json===!0),c(Se(process.cwd(),process.env,{...t.assignee!==void 0?{assignee:t.assignee}:{},...t.sprint===!0?{sprint:"active"}:{}}),t.json===!0)});h.command("sprint [action] [name]","Sprints: create | add | edit | start | close | status | list | burndown").option("--sprint <name>","Which sprint to add to; defaults to the active one").option("--name <name>","New name (for edit)").option("-d, --description <text>","Sprint description (for edit)").option("--start <date>","Start date, YYYY-MM-DD").option("--end <date>","End date, YYYY-MM-DD").option("--json","Machine-readable output for agents").example(' kadence sprint create "Sprint 1" first one starts right away').example(' kadence sprint create "Sprint 2" later ones are planned').example(' kadence sprint add KAD-1 --sprint "Sprint 2"').example(" kadence sprint start starts the next planned sprint").example(" kadence sprint edit --start 2026-09-01 --end 2026-09-14").example(' kadence sprint edit "Sprint 2" --name "Sprint 2: auth"').example(" kadence sprint burndown chart from the journal, any day").example(" kadence sprint close closes the active one, reports velocity").action((n,t,s)=>{let e=s.json===!0,a=process.cwd();switch(n===void 0&&c(f(`Which action?
|
|
118
|
+
kadence sprint create "Sprint 1"
|
|
119
|
+
kadence sprint add KAD-1
|
|
120
|
+
kadence sprint edit --start 2026-09-01
|
|
121
|
+
kadence sprint start
|
|
122
|
+
kadence sprint close
|
|
123
|
+
kadence sprint status
|
|
124
|
+
kadence sprint list`),e),n){case"create":t===void 0&&c(f(`A sprint name is required:
|
|
125
|
+
kadence sprint create "Sprint 1"`),e),c(oe(a,process.env,t),e);break;case"add":t===void 0&&c(f(`Which task?
|
|
126
|
+
kadence sprint add KAD-1
|
|
127
|
+
kadence sprint add KAD-1 --sprint "Sprint 2"`),e),c(de(a,process.env,t,s.sprint===void 0?{}:{sprint:s.sprint}),e);break;case"edit":c(fe(a,process.env,t,{...s.name!==void 0?{name:s.name}:{},...s.description!==void 0?{description:s.description}:{},...s.start!==void 0?{startDate:s.start}:{},...s.end!==void 0?{endDate:s.end}:{}}),e);break;case"start":c(ue(a,process.env,t),e);break;case"close":c(ce(a,process.env),e);break;case"status":c(le(a,process.env),e);break;case"list":c(me(a,process.env),e);break;case"burndown":c(pe(a,process.env,t),e);break;default:c(f(`Unknown action "${n}".
|
|
128
|
+
Available: create, add, edit, start, close, status, list, burndown
|
|
129
|
+
kadence sprint --help`),e)}});h.command("template [action] [name]","Task templates: save | list | delete").option("-d, --description <text>","Default description").option("--type <type>","Default type").option("--priority <level>","Default priority").option("-a, --assignee <who>","Default assignee").option("--label <name>","Default label; repeat for several").option("--estimate <points>","Default estimate").option("--json","Machine-readable output for agents").example(" kadence template save bug --type bug --priority high --label triage").example(" kadence template list").action((n,t,s)=>{let e=s.json===!0,a=process.cwd();switch(n){case"save":{t===void 0&&c(f(`A template name is required:
|
|
130
|
+
kadence template save bug --type bug`),e);let i=s.label===void 0?void 0:Array.isArray(s.label)?s.label:[s.label];c(Te(a,process.env,t,{...s.description!==void 0?{description:s.description}:{},...s.type!==void 0?{type:s.type}:{},...s.priority!==void 0?{priority:s.priority}:{},...s.assignee!==void 0?{assignee:s.assignee}:{},...i!==void 0?{labels:i}:{},...s.estimate!==void 0?{estimate:Number(s.estimate)}:{}}),e);break}case"list":case void 0:c(Ke(a,process.env),e);break;case"delete":t===void 0&&c(f(`Which template?
|
|
131
|
+
kadence template delete bug`),e),c(je(a,process.env,t),e);break;default:c(f(`Unknown action "${n}".
|
|
132
|
+
Available: save, list, delete`),e)}});h.command("ui","Interactive kanban board").alias("board:ui").example(" kadence ui").action(async()=>{let{runUi:n}=await import("./chunks/ui-WFLRVGUH.js"),t=await n(process.cwd(),process.env);t.ok||c(t,!1)});h.help();h.version("0.1.0-dev");var v=process.argv;if(v[2]==="task"&&v[3]==="log"&&v[4]!==void 0&&v[5]!==void 0){let n=T(process.cwd(),process.env,v[4],v[5]),t=v.includes("--json");c(n,t)}var Ie=process.argv.findIndex((n,t)=>n.startsWith("-")&&/^-\d+(\.\d+)?$/.test(n)&&process.argv[t-1]==="--estimate");Ie!==-1&&(process.stderr.write(`Estimate must be a positive number, got "${process.argv[Ie]}".
|
|
133
|
+
kadence task add "Fix login" --estimate 3
|
|
134
|
+
`),process.exit(2));try{h.parse()}catch(n){process.stderr.write(`${n.message}
|
|
135
|
+
`),process.exit(2)}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kadence",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tasks, sprints and velocity as plain files inside your git repository",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"kadence": "dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"CHANGELOG.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"build": "node scripts/build.mjs",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@types/blessed": "^0.1.27",
|
|
28
|
+
"blessed": "^0.1.81",
|
|
29
|
+
"cac": "^7.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.0.0",
|
|
33
|
+
"esbuild": "^0.28.0",
|
|
34
|
+
"typescript": "^5.6.0",
|
|
35
|
+
"vitest": "^4.0.0"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"cli",
|
|
39
|
+
"git",
|
|
40
|
+
"task",
|
|
41
|
+
"sprint",
|
|
42
|
+
"velocity",
|
|
43
|
+
"kanban",
|
|
44
|
+
"tui",
|
|
45
|
+
"project-management",
|
|
46
|
+
"ai-agents",
|
|
47
|
+
"event-sourcing",
|
|
48
|
+
"offline"
|
|
49
|
+
],
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/bogutskiandriy/FlowIt.git"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/bogutskiandriy/FlowIt#readme",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/bogutskiandriy/FlowIt/issues"
|
|
57
|
+
}
|
|
58
|
+
}
|