kadence 0.6.0 → 0.8.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 +183 -11
- package/README.md +30 -26
- package/dist/chunks/{board-5SQP5RX6.js → board-2HUYLTND.js} +1 -1
- package/dist/chunks/chunk-E7WLBKNZ.js +286 -0
- package/dist/chunks/chunk-IP426V3B.js +16 -0
- package/dist/chunks/{ui-OTWFK76P.js → ui-PATAOGVA.js} +2 -2
- package/dist/cli.js +139 -129
- package/package.json +1 -1
- package/dist/chunks/chunk-CR5YON2J.js +0 -10
- package/dist/chunks/chunk-J7QEHKDW.js +0 -248
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as ce,b as ee,c as ue,d as N,e as R,f as me,i as he,k as fe,n as pe,o as ge,p as ke,s as ye,t as we,u as te,v as be}from"./chunk-IP426V3B.js";import{execFileSync as ct}from"node:child_process";import{basename as ve}from"node:path";function O(t,r){try{return ct("git",r,{cwd:t,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}}function $e(t){let r=O(t,["rev-parse","--show-toplevel"]);return r===null||r.length===0?null:r}function Se(t){let r=O(t,["rev-parse","--path-format=absolute","--git-dir","--git-common-dir"]);if(r===null)return null;let[n,e]=r.split(`
|
|
3
|
+
`);return n===void 0||e===void 0||n===e?null:ve(n)}function xe(t){let r=O(t,["config","user.email"]);return r===null||r.length===0?null:r}function je(t){let r=O(t,["rev-parse","--abbrev-ref","HEAD"]);return r===null||r.length===0||r==="HEAD"?null:r}function Te(t){if(ut(t,"main"))return"main";let r=O(t,["config","init.defaultBranch"]);return r!==null&&r.length>0?r:"main"}function ut(t,r){return O(t,["rev-parse","--verify","--quiet",`${r}^{commit}`])!==null}function Ce(t,r,n){let e=O(t,["log",`${r}..${n}`,"--name-only","--pretty=format:","--",".kadence/events"]);if(e===null)return null;let s=new Set;for(let o of e.split(`
|
|
4
|
+
`)){let i=o.trim();i.endsWith(".json")&&s.add(ve(i,".json"))}return s}function Ee(t){return`<!-- generated by kadence ${t} \u2014 refresh with \`kadence init\` -->`}function Tn(t){return`${Ee(t)}
|
|
5
|
+
# kadence \u2014 for AI agents
|
|
6
|
+
|
|
7
|
+
This project's tasks live here as plain files. Read them directly, or through
|
|
8
|
+
the CLI. No server required.
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
kadence prime start here \u2014 the live state, in 40 lines
|
|
13
|
+
kadence search "\u2026" --json why, where, how \u2014 before grep or a list
|
|
14
|
+
kadence ready --json what can be started right now
|
|
15
|
+
kadence task claim KAD-42 take it; no argument takes the top of ready
|
|
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 note "text" --task KAD-42 something learned; a choice is a decision
|
|
23
|
+
kadence decision list --json why the current choices were made
|
|
24
|
+
kadence decision add "..." --why "..." a choice, with its reason
|
|
25
|
+
|
|
26
|
+
## Looking something up
|
|
27
|
+
|
|
28
|
+
When the question is why, where or how \u2014 a reason, a rule, a number somebody
|
|
29
|
+
wrote down \u2014 run \`kadence search "\u2026" --json\` before grep or a list. It reads
|
|
30
|
+
tasks, decisions, notes and documents together and returns up to five records,
|
|
31
|
+
each with a quoted passage, its ULID and a \`coverage\` figure; grep over
|
|
32
|
+
\`.kadence/events/\` returns whole events, a document at a time. In a 20-question
|
|
33
|
+
A/B on kadence's own journal, search took half the context and half the time and
|
|
34
|
+
got more answers right.
|
|
35
|
+
|
|
36
|
+
An empty result says so. Rephrase with the words the answer would use and search
|
|
37
|
+
again before falling back to grep.
|
|
38
|
+
|
|
39
|
+
In Claude Code, \`kadence init\` sets up the first search for you: each prompt
|
|
40
|
+
is looked up on the way in, and up to three records arrive next to it. Nothing
|
|
41
|
+
arrives when the journal has no words for the question.
|
|
42
|
+
|
|
43
|
+
## What a lookup costs
|
|
44
|
+
|
|
45
|
+
Cheapest first. Stop at the first one that answers. Medians over 75 questions
|
|
46
|
+
asked of kadence's own journal, in tokens of context:
|
|
47
|
+
|
|
48
|
+
| Way | Tokens | When |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| The prompt hook's lines | ~90 | already there after \`init\`, unless \`--no-hooks\` |
|
|
51
|
+
| \`decision show DEC-1 --json\` | ~300 | you know the record |
|
|
52
|
+
| \`search "\u2026"\` / \`search "\u2026" --json\` | ~320 / ~400 | why, where, how |
|
|
53
|
+
| \`task show KAD-1 --json\` | ~1,000 | one task with its history |
|
|
54
|
+
| \`doc show DOC-1 --json\` | ~3,600 | the whole document, every section |
|
|
55
|
+
| \`board --json --summary\` | ~4,000 | the state of the work |
|
|
56
|
+
| \`decision list --json\` / \`note list --json\` | ~8,500 / ~9,200 | everything of one kind |
|
|
57
|
+
| grep over \`.kadence/events/\`, three files read | ~11,000 (p90 ~21,000) | whole events, a document at a time |
|
|
58
|
+
| \`task list --json\` | ~16,000 | every task; \`--search "\u2026"\` narrows it with the same matching as search |
|
|
59
|
+
| \`board --json\` | ~51,000 | every task in full |
|
|
60
|
+
|
|
61
|
+
## JSON contract
|
|
62
|
+
|
|
63
|
+
Every \`--json\` response carries \`schema: "kadence/v1"\`. stdout holds JSON and
|
|
64
|
+
nothing else; warnings go to stderr. Exit codes: 0 success, 1 runtime error,
|
|
65
|
+
2 bad arguments.
|
|
66
|
+
|
|
67
|
+
The contract itself is machine-readable \u2014 read it instead of guessing:
|
|
68
|
+
|
|
69
|
+
kadence schema --json
|
|
70
|
+
|
|
71
|
+
It lists every command, the fields you can rely on, and every error code. It
|
|
72
|
+
works outside a repository, so you can read it before \`init\`.
|
|
73
|
+
|
|
74
|
+
## When something fails
|
|
75
|
+
|
|
76
|
+
A failed \`--json\` call carries \`error.code\` \u2014 branch on that, not on the
|
|
77
|
+
sentence. Where the valid set is knowable it also carries \`allowed\`:
|
|
78
|
+
|
|
79
|
+
{ "ok": false,
|
|
80
|
+
"error": { "code": "unknown_status", "received": "shipped",
|
|
81
|
+
"allowed": ["backlog", "in_progress", "done"],
|
|
82
|
+
"hint": "kadence board statuses --json" } }
|
|
83
|
+
|
|
84
|
+
Statuses are configured per project, so \`allowed\` is the only reliable source \u2014
|
|
85
|
+
never assume the defaults. Retrying an identical command never helps: there is no
|
|
86
|
+
network and no lock, so the same input always fails the same way.
|
|
87
|
+
|
|
88
|
+
## Working as an agent
|
|
89
|
+
|
|
90
|
+
Set \`KADENCE_SOURCE=agent\` so events record your authorship. Without it an
|
|
91
|
+
event is marked as human \u2014 we do not guess.
|
|
92
|
+
|
|
93
|
+
## Adding a teammate
|
|
94
|
+
|
|
95
|
+
Nothing to invite and no account to create \u2014 the journal travels with git.
|
|
96
|
+
|
|
97
|
+
1. They install the CLI: \`npm install -g kadence\`.
|
|
98
|
+
2. They \`git pull\`; \`.kadence/\`, \`AGENTS.md\` and \`CLAUDE.md\` come with it.
|
|
99
|
+
3. One \`git config user.email\` per person, the same on all their machines \u2014
|
|
100
|
+
that address is who they are in the journal.
|
|
101
|
+
4. Their agent runs with \`KADENCE_SOURCE=agent\` in its environment.
|
|
102
|
+
|
|
103
|
+
Afterwards \`kadence task show KAD-42\` shows the lead who did what, and whether
|
|
104
|
+
a person or their agent did it.
|
|
105
|
+
|
|
106
|
+
## Why to ask rather than read the files
|
|
107
|
+
|
|
108
|
+
You cannot find a task by its label in the journal. \`KAD-3\` is derived while
|
|
109
|
+
folding and is never stored in an event, so \`grep -r "KAD-3" .kadence/events/\`
|
|
110
|
+
returns nothing. Folding the journal yourself is the only alternative to asking.
|
|
111
|
+
|
|
112
|
+
Asking is also cheaper, and stays cheap: \`task show --json\` is about 950 bytes
|
|
113
|
+
whether the project holds ten tasks or a thousand, while the journal behind it
|
|
114
|
+
grows past 500 KB.
|
|
115
|
+
|
|
116
|
+
\`board --json\` is the exception: it returns every task in full, which on a large
|
|
117
|
+
project is a lot of description you did not ask for. Narrow it:
|
|
118
|
+
|
|
119
|
+
kadence board --json --fields label,status,assignee
|
|
120
|
+
kadence task list --json --fields id,label,status
|
|
121
|
+
|
|
122
|
+
\`kadence schema --json\` lists every selectable field.
|
|
123
|
+
|
|
124
|
+
## Recording why
|
|
125
|
+
|
|
126
|
+
The journal holds what happened. \`decision\` holds why, which the code and
|
|
127
|
+
\`git log\` never carry:
|
|
128
|
+
|
|
129
|
+
kadence decision add "Use ULIDs" --why "Clocks disagree between machines" \\
|
|
130
|
+
--rejected "Auto-increment: collides across branches" --task KAD-1
|
|
131
|
+
|
|
132
|
+
\`--why\` is required: without it the record is a changelog line. \`--rejected\` is
|
|
133
|
+
what stops the next agent proposing the thing that was already turned down.
|
|
134
|
+
|
|
135
|
+
When a decision stops being true, do not edit it \u2014 supersede it:
|
|
136
|
+
|
|
137
|
+
kadence decision add "Use signed tokens" --why "..." --supersedes DEC-1
|
|
138
|
+
|
|
139
|
+
One event writes both directions. \`decision list\` then returns only what is
|
|
140
|
+
still in force; pass \`--all\` when you need the history too. **Read the default,
|
|
141
|
+
not \`--all\`** \u2014 a reversed reason quoted as current is worse than no memory.
|
|
142
|
+
|
|
143
|
+
## Documents
|
|
144
|
+
|
|
145
|
+
\`kadence task doc KAD-1 docs/design.md\` links a document to a task, and
|
|
146
|
+
\`task show --json\` returns the links. kadence does not store the document: it
|
|
147
|
+
stays plain markdown in the repository. The link is the part git cannot express.
|
|
148
|
+
|
|
149
|
+
## What not to do
|
|
150
|
+
|
|
151
|
+
Do not hand-edit files under \`.kadence/events/\`: the journal is appended to,
|
|
152
|
+
never modified. To correct the state, add a new event through the CLI.
|
|
153
|
+
`}var _e="<!-- kadence:begin -->",ne="<!-- kadence:end -->";function mt(t){return`${_e}
|
|
154
|
+
${Ee(t)}
|
|
155
|
+
## Project tasks \u2014 kadence
|
|
156
|
+
|
|
157
|
+
Tasks, notes, decisions and documentation live in \`.kadence/\` as plain files, shared through git.
|
|
158
|
+
|
|
159
|
+
kadence prime start here: sprint, your work, what is ready
|
|
160
|
+
kadence search "\u2026" --json why, where, how: ~400 tokens; grep costs ~11k
|
|
161
|
+
kadence decision show DEC-1 --json one record: ~300 (task show ~1k, doc show ~4k)
|
|
162
|
+
kadence board --json --summary the board's state: ~4k
|
|
163
|
+
kadence decision list --json every decision in force: ~8k
|
|
164
|
+
kadence task list --json every task: ~16k; narrow with --search "\u2026"
|
|
165
|
+
kadence schema --json the contract: commands, fields, error codes
|
|
166
|
+
|
|
167
|
+
kadence task claim take the top of ready before starting
|
|
168
|
+
kadence task move KAD-1 done change state
|
|
169
|
+
kadence note "\u2026" --task KAD-1 something learned, for the next session
|
|
170
|
+
kadence decision add "\u2026" --why "\u2026" a choice between approaches, and its reason
|
|
171
|
+
|
|
172
|
+
\`--json\` responses carry \`schema: "kadence/v1"\`; stdout is JSON only. A failure
|
|
173
|
+
carries \`error.code\` and, where knowable, \`allowed\`. As an agent, set \`KADENCE_SOURCE=agent\`.
|
|
174
|
+
If \`kadence\` is not on PATH, ask the human to run \`npm install -g kadence\` (\`npx kadence prime\` needs network \u2014 their call).
|
|
175
|
+
|
|
176
|
+
Details: \`.kadence/README.md\`
|
|
177
|
+
${ne}`}function Cn(t,r){let n=mt(r);if(t===null||t.trim().length===0)return`${n}
|
|
178
|
+
`;let e=t.indexOf(_e),s=t.indexOf(ne);if(e!==-1&&s!==-1&&s>e){let i=t.slice(0,e),d=t.slice(s+ne.length);return`${i}${n}${d}`}let o=t.endsWith(`
|
|
179
|
+
`)?`
|
|
180
|
+
`:`
|
|
181
|
+
|
|
182
|
+
`;return`${t}${o}${n}
|
|
183
|
+
`}var F=["id","label","title","description","type","priority","status","labels","assignee","reporter","sprint","milestone","loggedHours","parent","blockedBy","due","claimedBy","contestedBy","criteria","openCriteria","comments","estimate","history"],De=100,Ae=["not_a_repository","not_initialised","no_git_identity","task_not_found","sprint_not_found","decision_not_found","unknown_status","unknown_type","unknown_priority","unknown_field","invalid_argument","template_not_found","conflicting_state","nothing_ready","milestone_not_found","doc_not_found"],ht={not_a_repository:"There is no git repository here. kadence stores its journal inside one.",not_initialised:"The repository has no .kadence/ folder yet. Run `kadence init`.",no_git_identity:"Git has no user.email, so an event would have no author.",task_not_found:"No task carries that ULID or KAD-N label. Nothing was changed.",sprint_not_found:"No sprint carries that name or id.",decision_not_found:"No decision carries that ULID or DEC-N label.",unknown_status:"The status is not one of this project\u2019s columns. They are configurable, so read `allowed`.",unknown_type:"The task type is not one of the four kadence defines.",unknown_priority:"The priority is not one of the four kadence defines.",unknown_field:"A name given to --fields is not a task field. Read `allowed`.",invalid_argument:"An argument was missing or malformed; the command did nothing.",template_not_found:"No template carries that name.",conflicting_state:"The arguments were understood, but the current state does not allow it \u2014 a closed sprint, or one already started.",milestone_not_found:"No milestone carries that name, MS-N label or ULID.",doc_not_found:"No document carries that ULID or DOC-N label.",nothing_ready:"Nothing can be started right now: the board is empty, or every open task is blocked or claimed by someone else. The message says which."};function En(t){return{version:"kadence/v1",tool:{name:"kadence",version:t},stability:"Additive only: fields and error codes may be added, never renamed or removed within kadence/v1.",exitCodes:{0:"success",1:"runtime error \u2014 the command understood you and could not comply",2:"bad arguments \u2014 nothing was attempted"},output:{stdout:"With --json, JSON and nothing else.",stderr:"Warnings and human messages, never part of the contract."},env:{KADENCE_SOURCE:'Set to "agent" so events record agent authorship. Without it an event counts as human \u2014 we do not guess.',KADENCE_ACTOR:'Who holds a claim, when that is not your git email: one of several agents a person runs, e.g. "you@example.com#agent-2". Without it, an agent (KADENCE_SOURCE=agent) in a linked git worktree claims as "email#<worktree>". Events are still authored by the git email.',NO_COLOR:"Any value disables colour."},errors:Ae.map(r=>({code:r,meaning:ht[r]})),shapes:{task:{required:["id","label","title","status","type","priority","estimate","assignee","blockedBy","loggedHours","claimedBy","contestedBy","labels"],selectable:[...F],notes:{id:"ULID. The identity of the task; KAD-N is a label derived while folding and is never stored.",history:"Present in `task show` only \u2014 the events that produced this state. Each entry carries `source`.",comments:'Each comment carries `source`: "human" or "agent", from KADENCE_SOURCE at the time of writing. `actor` is the git identity, which a person and their agent share \u2014 `source` is the only field that separates them.',required:"What a full response carries. With --fields you get exactly what you asked for and nothing else.",paging:"A list command returns one page. <name>Total is how many matched, never how many came back, and <name>Offset is where the page starts \u2014 together they say whether there is more and where to ask for it. The default page is 100; --limit 0 returns everything and --offset N walks. An agent that ignores the total will answer as though the page were the board."}},board:{required:["schema","ok","columns","tasksTotal","tasksOffset"],notes:{columns:"With --summary each task carries only the state fields, never `history` or `comments`. The response without the flag is unchanged \u2014 the contract only ever gains."}},search:{required:["schema","ok","query","hits","hitsTotal"],notes:{hits:"Each: {kind, id, label, title, score, span, lines, at, by}, ordered by score. Never more than --limit and often fewer: a passage that shares only a common word with the question is dropped rather than returned, because a model handed something that merely looks relevant fabricates more than one handed nothing.",id:"ULID of the record the passage came from \u2014 what to pass to task show, decision show or doc show. `label` is KAD-N, DEC-N or DOC-N, derived while folding, and is for a reader to read (I7). A note has no label and comes back null.",span:"{text, start, end} over the record\u2019s own quotable text, with text.length === end - start. It is a quotation, not a summary: an agent citing it is citing the journal.",lines:"For a document: the lines of the section the passage came from, so `doc show` can be read at the right place. Null for every other kind.",coverage:"How much of the question this record holds, 0 to 1, weighted by how rare each word is \u2014 not a ranking and not a probability. Measured over a golden set: where the first hit is right this is 1.00 at the median, where it is wrong 0.62. Below 0.7 the human output says the passage answers part of the question, and an agent should treat it as somewhere to look rather than as the answer.",hitsTotal:"How many passages came back. Zero is an ordinary answer and means the journal has nothing on the question \u2014 not that the command failed."}},ready:{required:["label","title","priority","estimate","labels","claimedBy","contestedBy"],notes:{contestedBy:"Always an array. Non-empty means two people claimed the task and it is in this list on purpose \u2014 it needs a person, not a filter.",labels:"Always an array, empty when there are none. Free-form: kadence attaches no meaning to any label. A team that marks blast radius \u2014 impact-critical and the like \u2014 decides from this how carefully to work; the mapping from a label to how an agent behaves is the team\u2019s, never ours.",priority:"Urgency, from a fixed vocabulary: low, normal, high, urgent. Not blast radius \u2014 that is a label, and the two are different questions."}},milestone:{required:["label","name","due","status","doneTasks","totalTasks","donePoints","totalPoints"],notes:{label:"MS-N, derived from ULID order during the fold, exactly like KAD-N. It can change when a branch merges.",donePoints:"Progress is measured in points, the same unit as velocity and burndown."}},criterion:{required:["n","text","checked","checkedBy"],notes:{n:"A position in the folded list, assigned like KAD-N and never stored. It can change when a branch merges.",checkedBy:"Who checked it, or null. Cleared when it is unchecked."}},branch:{required:["name","base"],notes:{name:"Present on a `task list --branch` response only. A task belongs to the branch when it was created there or any event about it arrived there; nothing is stored, the answer comes from git at read time."}},note:{required:["id","text","task","at","by","source"],notes:{task:"The KAD-N label, or null. A note is not a decision: there is no `why` and no number."}},flow:{required:["schema","ok","report","window","started","unit","wip","finished","perWeek","cycleTime","leadTime","responseTime","sle","aging","blocked","notes"],notes:{cycleTime:"null, or {n, p50, p85, p95} in calendar days. Percentiles only \u2014 there is no mean, on purpose.",started:"The column work counts as started at. A cycle time quoted without it is a number about the default, not the team.",window:"UTC dates, inclusive at both ends. `--since 1` is the current UTC day."}},cfd:{required:["schema","ok","report","window","statuses","days"],notes:{days:"One row per day of the window, each with a count per column at the end of that UTC day. A move backwards subtracts."}},attention:{required:["schema","ok","report","threshold","started","asOf","rows","notes"],notes:{rows:"One row per task, however many signals it carries. `signals[].kind` is one of stalled, unowned, stale_claim, dead_blocker.",threshold:"Days of silence before work counts as stalled, and the age a claim must reach. From `--since`; the default is 7, not the 30 a window report uses.",asOf:"The UTC day the question was asked. This report is the one answer that changes without the journal changing."}},prime:{required:["sprint","mine","mineTotal","documentation","documentationTotal","ready","attention","attentionTotal","decisions","notes","commands"],notes:{mine:"Capped; `mineTotal` is the real count. Each item: {label, title, status, claimedBy, contestedBy}. A task whose `contestedBy` is non-empty comes first; if `claimedBy` is not you, you lost the claim \u2014 talk to the holder before starting. Every free-text field in this payload carries at most 2000 characters and ends with \u201C\u2026\u201D when it was longer; nothing a person writes comes near that.",documentation:"Documents linked to any work you hold, at most three: {label, title, task, bytes}; `documentationTotal` is the real count. No bodies \u2014 `kadence doc show DOC-N` returns one. Always an array.",decisions:"The five in force, newest first: {label, title}. Titles only \u2014 `kadence decision list --json` carries the reasons.",notes:"The last five, newest first: {text, bytes, by, task}. `bytes` is the note\u2019s full length; `text` is capped and ends with \u201C\u2026\u201D when it was longer, so a note too large for a preamble is still known about \u2014 `kadence note list --json` returns it whole.",ready:"A count, not a list. `kadence ready --json` returns the list.",attention:"Work in flight that nobody is moving, at most three, `attentionTotal` is the real count. Empty when there is nothing \u2014 and then the human output says nothing at all. `kadence report attention --json` returns the rest."}},decision:{required:["id","label","title","why","rejected","docs","supersedes","supersededBy","source"],notes:{why:"The reason. Required when recording \u2014 a decision without one is a changelog line.",supersededBy:"Derived while folding from a later decision carrying `supersedes`. Both directions come from one write, so they cannot fall out of step.",docs:"Repository-relative paths. kadence does not store the documents, only the link.",source:'"human" or "agent", from KADENCE_SOURCE at the time of writing. Never guessed: without the variable an event counts as human.',listing:"`decision list` hides superseded decisions unless --all is passed: a reversed reason presented as current is worse than no memory."}},document:{required:["id","label","title","bytes","revisions","tasks","updatedAt","updatedBy","source","conflicted"],notes:{label:"DOC-N, derived from ULID order during the fold, exactly like KAD-N. It can change when a branch merges.",body:"Present in `doc show` only. `doc list` and `task show` carry the size in `bytes` so a caller can decide whether to read it.",conflicts:"Present in `doc show` only, always an array. Non-empty means two revisions were written on top of the same one; `body` shows the later, each entry here carries another. `doc edit` settles it.",tasks:"KAD-N labels of the tasks this document explains.",documentation:"`task show` carries `documentation[]` \u2014 {id, label, title, bytes, updatedAt, conflicted} for each document linked to the task, never bodies."}},error:{required:["code","message"],optional:["received","allowed","hint"],notes:{allowed:"Present when the valid set is knowable \u2014 statuses are project-configurable.",retryable:"Deliberately absent: no network, no lock, so a retry always fails identically."}}},commands:[{name:"schema",summary:"This contract. Works outside a repository."},{name:"init",summary:"Create .kadence/ and the agent instruction files."},{name:"board",summary:"The whole board, grouped by column. Every task in full \u2014 pass --summary for the state without history and comments, which is what makes a large board big.",flags:["--json","--fields","--summary"],json:!0},{name:"prime",summary:"The session preamble: active sprint, your claimed work, how many tasks are ready, decisions in force, recent notes. Capped at 40 lines.",flags:["--json"],json:!0},{name:"ready",summary:"What can be started now: not yet started, unblocked, not claimed by anyone else. Priority first, then age. Work already in the started column or past it is left out \u2014 it has begun, so it cannot be started.",flags:["--json","--assignee","--limit"],json:!0},{name:"search",summary:"One question over everything written down: tasks with their criteria and comments, decisions with their reason and the alternative they rejected, notes, and documents cut at their headings. Lexical, not semantic \u2014 it finds the words that are there, which on a vocabulary of ULIDs and KAD-N is the stronger method rather than the weaker one. Five passages by default, each carrying the ULID of the record and a quoted span. An empty result means the journal has no answer, which is a result and not a failure.",flags:["--json","--kind","--limit","--all"],json:!0},{name:"task claim",summary:'Take a task, or the top of `ready` with no argument. No lock: a second claim is recorded and reported as contested. `claim` in the response is "claimed", "already_yours" or "contested", read from the journal after the write.',args:["ref"],json:!0},{name:"task release",summary:"Give a claimed task back.",args:["ref"],json:!0},{name:"task list",summary:"All tasks, filterable. `--branch` narrows to the work the current branch introduced, measured against `--base` (main by default).",flags:["--json","--fields","--branch","--base"],json:!0},{name:"task show",summary:"One task with its comments and full history.",args:["ref"],json:!0},{name:"task add",summary:"Create a task.",args:["title"],json:!0},{name:"task move",summary:"Change status. Accepts several refs, all or nothing.",args:["refs","status"],json:!0},{name:"task assign",summary:"Assign or unassign.",args:["refs","who"],json:!0},{name:"task comment",summary:"Append a comment.",args:["ref","text"],json:!0},{name:"task edit",summary:"Change fields. Labels move as deltas: --add-label and --remove-label change one and leave the rest, --label makes the set exactly what you pass. Prefer the first two \u2014 a whole set passed from two branches is two people\u2019s intent, and only the difference is recorded (ADR-013).",args:["refs"],flags:["--title","--description","--type","--priority","--due","--estimate","--label","--add-label","--remove-label","--json"],json:!0},{name:"task log",summary:"Log hours.",args:["refs","hours"],json:!0},{name:"task block",summary:"Record a blocker.",args:["ref","blocker"],json:!0},{name:"task ac",summary:'Acceptance criteria: `add <ref> "text"`, `check <ref> <n>`, `uncheck <ref> <n>`, `list <ref>`. The number is a position in the folded list, never stored. Moving to done with unchecked criteria warns and proceeds.',args:["action","ref","value"],json:!0},{name:"note",summary:"Record something learned that was never a choice. For a choice with a rejected alternative, use `decision add`.",args:["text"],flags:["--task","--json"],json:!0},{name:"note list",summary:"Notes, newest first.",flags:["--task","--limit","--json"],json:!0},{name:"decision add",summary:"Record why something was chosen, and what was turned down.",args:["title"],flags:["--why","--rejected","--task","--supersedes","--doc"],json:!0},{name:"decision list",summary:"Current decisions. Superseded ones need --all.",flags:["--all","--task"],json:!0},{name:"decision show",summary:"One decision in full.",args:["ref"],json:!0},{name:"task doc",summary:"Link a document to a task; `task doc add <ref> <path>` creates it from a small template and links it in one call. The file stays plain markdown in the repo and is never overwritten.",args:["ref","path"],json:!0},{name:"doc add",summary:"Write documentation into the journal: the current account of a module, contract or process. Not a file and not a note \u2014 it is revised, and readers get the latest. Text from --body, --file, or --stdin. Past 16 KiB it is written with a warning.",args:["title"],flags:["--body","--stdin","--file","--task","--json"],json:!0},{name:"doc edit",summary:"Write a new revision; earlier ones stay in the journal. A revision names what it was written on top of; two written on top of the same one are a conflict, shown by `doc show`. An edit with new text settles it \u2014 without text it is refused with conflicting_state, so no version is dropped unread. One text source: --body, --file or --stdin.",args:["ref"],flags:["--body","--stdin","--file","--title","--json"],json:!0},{name:"doc show",summary:"One document with its body.",args:["ref"],json:!0},{name:"doc list",summary:"Documentation without bodies: titles, sizes, the tasks each explains.",flags:["--task","--json"],json:!0},{name:"doc link",summary:"Attach a document to a task, so it arrives with `task show`.",args:["ref","task"],json:!0},{name:"report flow",summary:"WIP, throughput per week, cycle / lead / response time as p50 p85 p95 in calendar days, aging work against p85, created vs resolved, blocked days. Names the window and the started boundary it used. `--html` writes the same numbers as one self-contained page with charts, and answers with the path rather than the report.",flags:["--since","--html","--file","--json"],json:!0},{name:"report cfd",summary:"Cumulative flow: tasks per column at the end of each day in the window. `--html` writes it as a stacked area chart in one self-contained page.",flags:["--since","--html","--file","--json"],json:!0},{name:"report attention",summary:"Work past the started boundary that nobody is moving: stalled, unowned, held by a stale claim, or blocked by something already done. `--since` is days of silence, default 7. `--html` writes it as one self-contained page.",flags:["--since","--html","--file","--json"],json:!0},{name:"report burndown",summary:"One sprint against an even burn, day by day. The active sprint by default; `--sprint <name>` for a closed one. The same fold as `sprint burndown`, under the verb the catalogue lives at.",flags:["--sprint","--html","--file","--json"],json:!0},{name:"report velocity",summary:"Points committed against points finished, per closed sprint, newest last. Reported as a range \u2014 low, median, high \u2014 not as an average: the spread between sprints is the forecast, and a series shorter than four sprints says so.",flags:["--html","--file","--json"],json:!0},{name:"report workload",summary:"Open tasks, open points, work in progress and blocked work per owner, with a row for unassigned work. No hours and no capacity: the journal has neither.",flags:["--html","--file","--json"],json:!0},{name:"compact",summary:"Fold months older than --keep-months into one archive file each. Same events, faster cold start; an archived month is one file, so compact on one branch and merge before compacting on another.",flags:["--keep-months","--dry-run","--json"],json:!0},{name:"stats",summary:"Counts by status and assignee, open blockers, contested claims, and the velocity of the last three closed sprints.",flags:["--json"],json:!0},{name:"milestone list",summary:"Milestones with progress in points. Grouping by outcome, where an epic groups by structure and a sprint by time. Closed ones need --all.",flags:["--all","--json"],json:!0},{name:"milestone create",summary:"Create one. A task carries at most one milestone, as a field beside `sprint`.",args:["name"],flags:["--due","--json"],json:!0},{name:"milestone add",summary:"Put a task in a milestone; it leaves the one it was in.",args:["ref"],flags:["--milestone","--json"],json:!0},{name:"milestone close",summary:"Close it. A statement about the calendar, not about the work: open tasks stay open and are named.",args:["ref"],json:!0},{name:"board export",summary:"Write a snapshot: `--html` for one self-contained file that opens from disk, `--md` for a README or a pull request. No server and no network; nothing outlives the command.",flags:["--html","--md","--readme","--file","--json"],json:!0},{name:"board config",summary:"Read or set the columns, the started boundary and the definition of done. `--started` names the column cycle time and actual hours count from; `--dod` criteria are copied into each new task, never referenced.",flags:["--statuses","--started","--dod","--json"],json:!0},{name:"sprint status",summary:"Current sprint progress.",json:!0},{name:"sprint create",summary:"Start a sprint.",args:["name"],json:!0},{name:"sprint close",summary:"Close it and report velocity.",json:!0},{name:"sprint add",summary:"Put a task in a sprint: the active one, or `--sprint <name>`.",args:["ref"],flags:["--sprint","--json"],json:!0},{name:"sprint edit",summary:"Rename a sprint or change its description and dates. The active sprint when no name is given.",args:["name"],flags:["--name","--description","--start","--end","--json"],json:!0},{name:"sprint start",summary:"Start a planned sprint; with no name, the oldest planned one. Fails with conflicting_state while another is active.",args:["name"],json:!0},{name:"sprint list",summary:"Every sprint with its status.",json:!0},{name:"sprint burndown",summary:"One sprint against an even burn, folded from the journal. The active sprint when no name is given; `burndown` is null when there is none.",args:["name"],json:!0},{name:"task parent",summary:"Set a task\u2019s parent; `none` detaches it.",args:["ref","parent"],json:!0},{name:"task unblock",summary:"Remove a recorded blocker.",args:["ref","blocker"],json:!0},{name:"task cancel",summary:"Cancel a task. It stays in history and does not count as missed work. Accepts several refs.",args:["refs"],json:!0},{name:"task delete",summary:"Drop a task from the board. The journal is append-only: this writes a `task.deleted` event and the earlier events stay. Accepts several refs.",args:["refs"],json:!0},{name:"template save",summary:"Save default fields under a name, for `task add --template <name>`.",args:["name"],flags:["--description","--type","--priority","--assignee","--label","--estimate","--json"],json:!0},{name:"template list",summary:"Saved templates and their fields. Also the bare `template`.",json:!0},{name:"template delete",summary:"Remove a template.",args:["name"],json:!0},{name:"completion",summary:"Print the shell completion script for `--shell`, or the shell in $SHELL.",flags:["--shell","--json"],json:!0},{name:"completion install",summary:"Write the completion script where the shell looks. Refuses to overwrite a file kadence did not write unless --force.",flags:["--shell","--force","--json"],json:!0},{name:"ui",summary:"Interactive kanban board for a person at a terminal. No --json: an agent reads `board --json` instead."},{name:"hook",summary:"Run by Claude Code hooks, not by hand. `hook prompt` reads a UserPromptSubmit payload on stdin, searches the journal for the prompt and prints up to three records to add to the context, or nothing. Always exits 0: exit 2 would erase the prompt. Installed by `init` unless `--no-hooks`; a record is shown once per session.",args:["event"]}]}}import{isAbsolute as Ht,relative as Yt,dirname as Gt,resolve as Vt}from"node:path";import{existsSync as bt,mkdirSync as Oe,readdirSync as se,readFileSync as Z,renameSync as Pe,rmSync as vt,writeFileSync as Be}from"node:fs";import{join as D}from"node:path";var ft=["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.label_added","task.label_removed","task.time_logged","task.doc_linked","task.claimed","task.released","task.criterion_added","task.criterion_checked","task.criterion_unchecked","template.saved","template.deleted","board.configured","sprint.created","sprint.started","sprint.updated","sprint.closed","sprint.cancelled","sprint.task_added","milestone.created","milestone.task_added","milestone.closed","decision.recorded","note.recorded","doc.written","doc.linked"],pt=new Set(ft),re=/^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/,gt=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})$/;function kt(t){return typeof t=="string"&&pt.has(t)}function yt(t){if(typeof t!="object"||t===null)return["event"];let r=t,n=[];if((typeof r.id!="string"||!re.test(r.id))&&n.push("id"),kt(r.type)||n.push("type"),(typeof r.entity!="string"||!re.test(r.entity))&&n.push("entity"),(typeof r.actor!="string"||r.actor.length===0)&&n.push("actor"),(typeof r.ts!="string"||!gt.test(r.ts)||Number.isNaN(Date.parse(r.ts)))&&n.push("ts"),r.source!=="human"&&r.source!=="agent"&&n.push("source"),r.data!==void 0&&(typeof r.data!="object"||r.data===null)&&n.push("data"),r.type==="decision.recorded"){let e=r.data??{};for(let s of["title","why"]){let o=e[s];(typeof o!="string"||o.trim().length===0)&&n.push(`data.${s}`)}}if(r.type==="doc.written"){let e=r.data??{};for(let o of["title","body"]){let i=e[o];(typeof i!="string"||i.trim().length===0)&&n.push(`data.${o}`)}let s=e.parents;(!Array.isArray(s)||!s.every(o=>typeof o=="string"&&re.test(o)))&&n.push("data.parents")}return n}var Ne=["description","body"];function Re(t){let r=Ne.filter(e=>{let s=t.data?.[e];return typeof s=="string"&&s.includes(`
|
|
184
|
+
`)});if(r.length===0)return JSON.stringify(t);let n={...t.data};for(let e of r)n[e]=n[e].split(`
|
|
185
|
+
`);return JSON.stringify({...t,data:n},null,2)}function M(t){let r;try{r=JSON.parse(t)}catch(e){return{event:null,error:`unreadable JSON: ${e.message}`,unknownType:!1}}r=wt(r);let n=yt(r);return n.length===1&&n[0]==="type"?{event:null,error:null,unknownType:!0}:n.length>0?{event:null,error:`invalid fields: ${n.join(", ")}`,unknownType:!1}:{event:r,error:null,unknownType:!1}}function wt(t){if(typeof t!="object"||t===null)return t;let r=t,n=r.data;if(typeof n!="object"||n===null)return t;let e=Ne.filter(o=>Array.isArray(n[o]));if(e.length===0)return t;let s={...n};for(let o of e)s[o]=s[o].filter(i=>typeof i=="string").join(`
|
|
186
|
+
`);return{...r,data:s}}var $t=.2;function U(t){return D(t,".kadence")}function P(t){return D(U(t),"events")}function z(t){return D(P(t),"archive")}function St(t){return t.slice(0,7)}function x(t,r){let n=D(P(t),St(r.ts));Oe(n,{recursive:!0});let e=D(n,`${r.id}.json`),s=D(n,`.${r.id}.tmp`);Be(s,`${Re(r)}
|
|
187
|
+
`,"utf8"),Pe(s,e)}function X(t){let r=P(t),n=[],e=[],s=new Set,o=0,i=Y(z(t)),d=new Set(i);for(let l of Y(r)){let u;try{u=Z(l,"utf8")}catch{e.push(l);continue}if(d.has(l)){let g;try{g=JSON.parse(u)}catch{e.push(l);continue}if(!Array.isArray(g)){e.push(l);continue}for(let f of g){let h=M(JSON.stringify(f));h.unknownType?o++:h.error!==null||h.event===null?e.push(l):s.has(h.event.id)||(s.add(h.event.id),n.push(h.event))}continue}let c=M(u);if(c.unknownType){o++;continue}if(c.error!==null||c.event===null){e.push(l);continue}s.has(c.event.id)||(s.add(c.event.id),n.push(c.event))}n.sort((l,u)=>l.id<u.id?-1:l.id>u.id?1:0);let a=n.length+e.length;return{events:n,corrupted:e,unknownTypes:o,systemicCorruption:a>0&&e.length/a>$t}}function Y(t){let r;try{r=se(t,{withFileTypes:!0})}catch{return[]}let n=[];for(let e of r){let s=D(t,e.name);e.isDirectory()?n.push(...Y(s)):e.name.endsWith(".json")&&n.push(s)}return n}function Ie(t){if(!bt(t))return[];try{let r=JSON.parse(Z(t,"utf8"));return Array.isArray(r)?r.map(n=>M(JSON.stringify(n)).event).filter(n=>n!==null):null}catch{return null}}function On(t,r){let n=P(t),e=[],s=0,o;try{o=se(n,{withFileTypes:!0})}catch{return{months:[],events:0}}for(let i of o){if(!i.isDirectory()||i.name==="archive"||i.name>=r)continue;let d=new Set((Ie(D(z(t),`${i.name}.json`))??[]).map(u=>u.id)),a=0,l=!1;for(let u of Y(D(n,i.name))){let c=M(Z(u,"utf8")).event;c===null?l=!0:d.has(c.id)||(a+=1)}l||a===0||(e.push({month:i.name,events:a}),s+=a)}return e.sort((i,d)=>i.month<d.month?-1:1),{months:e,events:s}}function Pn(t,r){let n=P(t),e=[],s=[],o=0,i;try{i=se(n,{withFileTypes:!0})}catch{return{archivedMonths:[],archivedEvents:0,skipped:[]}}for(let d of i){if(!d.isDirectory()||d.name==="archive"||d.name>=r)continue;let a=D(n,d.name),l=Y(a),u=[],c=!1;for(let w of l){let j=M(Z(w,"utf8"));j.event!==null?u.push(j.event):c=!0}if(c){s.push(a);continue}if(u.length===0)continue;Oe(z(t),{recursive:!0});let g=D(z(t),`${d.name}.json`),f=Ie(g);if(f===null){s.push(g);continue}let h=new Map;for(let w of f)h.set(w.id,w);let m=0;for(let w of u)h.has(w.id)||(m+=1),h.set(w.id,w);let $=[...h.values()].sort((w,j)=>w.id<j.id?-1:1),k=`${g}.tmp`;Be(k,JSON.stringify($),"utf8"),Pe(k,g),vt(a,{recursive:!0,force:!0}),e.push(d.name),o+=m}return{archivedMonths:e.sort(),archivedEvents:o,skipped:s.sort()}}import{randomFillSync as xt}from"node:crypto";var Fe="0123456789ABCDEFGHJKMNPQRSTVWXYZ",jt=10,Tt=16,Le=2**48-1;function Ct(t){let r="",n=t;for(let e=jt-1;e>=0;e--)r=Fe[n%32]+r,n=Math.floor(n/32);return r}function Et(t){let r="",n=0,e=0;for(let s of t){for(n=n<<8|s,e+=8;e>=5;)e-=5,r+=Fe[n>>>e&31];n&=(1<<e)-1}return r.slice(0,Tt)}function _t(t){for(let r=t.length-1;r>=0;r--){let n=t[r];if(n<255){t[r]=n+1;return}t[r]=0}throw new Error("ULID: random space exhausted within a single millisecond")}function Dt(){let t=-1,r=new Uint8Array(10);return function(e=Date.now()){if(!Number.isInteger(e)||e<0||e>Le)throw new RangeError(`ULID: time out of range 0..${Le}: ${e}`);return e>t?(t=e,xt(r)):_t(r),Ct(t)+Et(r)}}var S=Dt();import{readFileSync as At,writeFileSync as Nt,renameSync as Rt,readdirSync as Ot,mkdirSync as Pt,statSync as Bt}from"node:fs";import{join as Ue}from"node:path";var Me="kadence-snapshot/12";function Ke(t){return Ue(U(t),"state.json")}function Je(t,r){let n=It(P(t)),e=Lt(t);if(e!==null&&e.version===Me&&e.lastEventId===n.lastEventId&&e.eventCount===n.count&&e.eventBytes===n.bytes)return{state:e.state,fromCache:!0,incomingEvents:0,health:e.health};let s=X(t),o=me(s.events),i={corrupted:s.corrupted.length,total:s.corrupted.length+s.events.length,unknownTypes:s.unknownTypes,systemic:s.systemicCorruption},d=e===null||r===void 0?0:Mt(s.events,e,r);return i.corrupted===0&&Ft(t,{version:Me,lastEventId:n.lastEventId,eventCount:n.count,eventBytes:n.bytes,health:i,state:o}),{state:o,fromCache:!1,incomingEvents:d,health:i}}function It(t){let r="",n=0,e=0,s=o=>{let i;try{i=Ot(o,{withFileTypes:!0})}catch{return}for(let d of i){let a=Ue(o,d.name);if(d.isDirectory())s(a);else if(d.name.endsWith(".json")){n++;let l=d.name.slice(0,-5);l>r&&(r=l),e+=Bt(a,{throwIfNoEntry:!1})?.size??0}}};return s(t),{lastEventId:r,count:n,bytes:e}}function Lt(t){try{let r=JSON.parse(At(Ke(t),"utf8"));if(typeof r!="object"||r===null)return null;let n=r;if(typeof n.version!="string"||typeof n.lastEventId!="string"||typeof n.eventCount!="number"||typeof n.state!="object")return null;let e=n.health;return e===void 0||typeof e.corrupted!="number"||typeof e.total!="number"||typeof e.unknownTypes!="number"||typeof e.systemic!="boolean"?null:n}catch{return null}}function Ft(t,r){try{Pt(U(t),{recursive:!0});let n=Ke(t),e=`${n}.tmp`;Nt(e,JSON.stringify(r),"utf8"),Rt(e,n)}catch{}}function Mt(t,r,n){let e=0,s=0;for(let i of t)i.id<=r.lastEventId?e++:i.actor!==n&&s++;let o=Math.max(0,e-r.eventCount);return s+o}var K="\x1B[",T={dim:`${K}2m`,bold:`${K}1m`,green:`${K}32m`,yellow:`${K}33m`,blue:`${K}34m`,reset:`${K}0m`};function qe(t,r,n=0){let e=t.length,s=Math.max(0,n),o=r??De,i=o<=0?t.slice(s):t.slice(s,s+o);return{shown:[...i],total:e,offset:s,more:s+i.length<e}}function He(t,r){if(!t.more&&t.offset===0)return null;let n=t.offset+1,e=t.offset+t.shown.length,s=t.more?` (${r} --offset ${e} for the next)`:"";return`Showing ${n}\u2013${e} of ${t.total}.${s}`}function ie(t,r){return t.NO_COLOR!==void 0?!1:r}function A(t,r,n){return n?`${r}${t}${T.reset}`:t}var Ut={done:T.green,in_progress:T.blue,blocked:T.yellow,cancelled:T.dim},Kt=72;function Ye(t,r=Kt){let n=[...t];return n.length<=r?t:`${n.slice(0,r-1).join("")}\u2026`}function ae(t){return[...t].length}function We(t,r){let n=r-ae(t);return n>0?t+" ".repeat(n):t}function Ge(t,r){if(t.length===0)return`No tasks yet.
|
|
188
|
+
Create the first one:
|
|
189
|
+
kadence task add "title"`;let n=Math.max(...t.map(s=>ae(s.label)),2),e=Math.max(...t.map(s=>ae(s.status)),6);return t.map(s=>{let o=A(We(s.label,n),T.bold,r),i=A(We(s.status,e),Ut[s.status]??T.dim,r),d=s.estimate===null?"":A(` (${s.estimate})`,T.dim,r);return`${o} ${i} ${Ye(s.title)}${d}`}).join(`
|
|
190
|
+
`)}function oe(t){return t==="agent"?" [agent]":""}function Ve(t){return t<=0?null:`Merged ${t} ${t===1?"change":"changes"} from another branch, no conflicts.`}var Jt={urgent:"!!",high:"!",normal:"",low:"v"},Wt={bug:"BUG",story:"STORY",task:""};function ze(t,r){let n=[A(t.label,T.bold,r)],e=t.criteria.filter(d=>!d.checked).length;e>0&&n.push(A(`[${t.criteria.length-e}/${t.criteria.length}]`,T.yellow,r));let s=Jt[t.priority];s!==""&&n.push(A(s,t.priority==="low"?T.dim:T.yellow,r));let o=Wt[t.type]??"";o!==""&&n.push(A(o,T.dim,r)),n.push(Ye(t.title));let i=[];return t.assignee!==null&&i.push(`@${t.assignee.split("@")[0]}`),t.estimate!==null&&i.push(`${t.estimate}`),i.length>0&&n.push(A(`(${i.join(", ")})`,T.dim,r)),` ${n.join(" ")}`}function qn(t,r){if(Object.values(t).reduce((s,o)=>s+o.length,0)===0)return`No tasks yet.
|
|
191
|
+
Create the first one:
|
|
192
|
+
kadence task add "title"`;let e=[];for(let[s,o]of Object.entries(t)){if(o.length===0)continue;let i=o.reduce((a,l)=>a+(l.estimate??0),0),d=i>0?` \u2014 ${i}`:"";e.push(A(`${s} (${o.length}${d})`,T.bold,r),...o.map(a=>ze(a,r)),"")}return e.join(`
|
|
193
|
+
`).trimEnd()}function qt(t,r=new Date){let n=Date.parse(`${t}T00:00:00.000Z`),e=Date.parse(`${r.toISOString().slice(0,10)}T00:00:00.000Z`);if(Number.isNaN(n))return"";let s=Math.round((n-e)/864e5);return s<0?` (overdue by ${-s} day${s===-1?"":"s"})`:s===0?" (today)":s===1?" (tomorrow)":s<=7?` (in ${s} days)`:""}function Ze(t,r){if(t.length===0)return`No tasks yet.
|
|
194
|
+
Create the first one:
|
|
195
|
+
kadence task add "title"`;let n=new Set(t.map(d=>d.id)),e=new Map;for(let d of t){let a=d.parent!==null&&n.has(d.parent)?d.parent:null,l=e.get(a)??[];l.push(d),e.set(a,l)}let s=[],o=new Set,i=(d,a)=>{if(!(a>10))for(let l of e.get(d)??[]){if(o.has(l.id))continue;o.add(l.id);let u=" ".repeat(a),c=l.blockedBy.length>0?A(" [blocked]",T.yellow,r):"";s.push(`${u}${ze(l,r).trimStart()}${c}`),i(l.id,a+1)}};return i(null,0),s.join(`
|
|
196
|
+
`)}function Xe(t,r=[]){let n=[`${t.label} ${t.title}`,""];if(t.description!==null&&n.push(t.description,""),n.push(` Status: ${t.status}`),n.push(` Type: ${t.type}`),n.push(` Priority: ${t.priority}`),t.labels.length>0&&n.push(` Labels: ${t.labels.join(", ")}`),t.parent!==null&&n.push(` Parent: ${t.parent}`),t.blockedBy.length>0&&n.push(` Blocked by: ${t.blockedBy.length} task(s)`),n.push(` Assignee: ${t.assignee??"\u2014"}`),t.claimedBy!==null){let e=t.contestedBy.length>0?` (contested by ${t.contestedBy.join(", ")})`:"";n.push(` Claimed: ${t.claimedBy}${e}`)}if(n.push(` Reporter: ${t.reporter}`),t.due!==null&&n.push(` Due: ${t.due}${qt(t.due)}`),n.push(` Estimate: ${t.estimate??"\u2014"}`),t.comments.length>0){n.push("",` Comments (${t.comments.length}):`);for(let e of t.comments){n.push(` ${e.author}${oe(e.source)} \xB7 ${e.ts.slice(0,10)}`);for(let s of e.text.split(`
|
|
197
|
+
`))n.push(` ${s}`)}}if(t.criteria.length>0){let e=t.criteria.filter(s=>s.checked).length;n.push("",` Acceptance criteria (${e}/${t.criteria.length}):`);for(let s of t.criteria)n.push(` ${s.n}. [${s.checked?"x":" "}] ${s.text}`)}if(r.length>0){n.push("",` Notes (${r.length}):`);for(let e of r){n.push(` ${e.by}${oe(e.source)} \xB7 ${e.at.slice(0,10)}`);for(let s of e.text.split(`
|
|
198
|
+
`))n.push(` ${s}`)}}if(t.history.length>0){n.push("",` History (${t.history.length}):`);for(let e of t.history){let s=e.type==="task.moved"?` \u2192 ${String(e.data.to)}`:"";n.push(` ${e.ts.slice(0,16).replace("T"," ")} ${e.actor}${oe(e.source)} ${e.type}${s}`)}}return n.join(`
|
|
199
|
+
`)}import{existsSync as Qe,mkdirSync as zt,writeFileSync as Zt}from"node:fs";var sr=ce;function p(t,r,n,e={}){return{ok:!1,exitCode:t,message:n,error:{code:r,message:n,...e}}}function _(t){return p(1,"task_not_found",`No task ${t}.
|
|
200
|
+
kadence task list`,{received:t,hint:"kadence task list --json"})}function J(t,r,n,e){return p(2,t,`Unknown ${r} "${n}".
|
|
201
|
+
Available: ${e.join(", ")}`,{received:n,allowed:e})}function b(t,r){let n=$e(t);if(n===null)return p(1,"not_a_repository",`kadence lives inside a git repository, and there is none here.
|
|
202
|
+
Create one and try again:
|
|
203
|
+
git init`,{hint:"git init"});if(!Qe(U(n)))return p(1,"not_initialised",`No .kadence/ found here.
|
|
204
|
+
Run:
|
|
205
|
+
npx kadence init`,{hint:"kadence init"});let e=xe(n);if(e===null)return p(1,"no_git_identity",`Git does not know who you are, so there is no author for the event.
|
|
206
|
+
Run:
|
|
207
|
+
git config user.email you@example.com`,{hint:"git config user.email you@example.com"});let s=r.KADENCE_SOURCE==="agent"?"agent":"human";return{root:n,actor:e,source:s}}function et(t,r){let n=r.KADENCE_ACTOR?.trim();if(n!==void 0&&n.length>0)return n;if(t.source!=="agent")return t.actor;let e=Se(t.root);return e===null?t.actor:`${t.actor}#${e}`}function v(t){return"root"in t}function or(t,r,n,e={}){let s=b(t,r);if(!v(s))return s;let o=n.trim();if(o.length===0)return p(2,"invalid_argument","A task needs a title.",{hint:"kadence task list --json"});if(e.type!==void 0&&!N.includes(e.type))return J("unknown_type","type",e.type,N);if(e.priority!==void 0&&!R.includes(e.priority))return J("unknown_priority","priority",e.priority,R);let i,d=null;if(e.parent!==void 0){let{state:m}=y(s.root,s.actor);d=m;let $=E(m,e.parent);if($===void 0)return{..._(e.parent)};i=$.id}let a=S(),l={id:a,type:"task.created",entity:a,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{title:o,...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}:{},...e.labels!==void 0&&e.labels.length>0?{labels:e.labels}:{},...e.due!==void 0?{due:e.due}:{},...i!==void 0?{parent:i}:{},...e.estimate!==void 0?{estimate:e.estimate}:{}}};x(s.root,l);let u=d??(e.noDod!==!0||e.estimate===void 0?y(s.root,s.actor).state:null),c=e.noDod===!0||u===null?[]:u.dod;for(let m of c)x(s.root,{id:S(),type:"task.criterion_added",entity:a,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:m}});let g=e.estimate===void 0?u?.sprints.find(m=>m.status==="active"):void 0,f=g===void 0?"":`
|
|
208
|
+
Without an estimate this task adds no points to sprint "${g.name}". Add --estimate.`,h=c.length===0?"":`
|
|
209
|
+
${c.length} acceptance criteria from the board's definition of done.`;return{ok:!0,exitCode:0,message:`Created: ${o}${h}${f}`,data:{schema:"kadence/v1",ok:!0,task:{id:a,title:o},criteria:c.map((m,$)=>({n:$+1,text:m,checked:!1,checkedBy:null}))}}}function y(t,r){let n=Je(t,r),{health:e}=n,s=[],o=Ve(n.incomingEvents);return o!==null&&s.push(o),e.systemic?s.push(`The journal is badly damaged: ${e.corrupted} of ${e.total} events are unreadable. Try: git checkout .kadence/`):e.corrupted>0&&s.push(`Skipped ${e.corrupted} corrupted event(s).`),e.unknownTypes>0&&s.push(`Skipped ${e.unknownTypes} event(s) from a newer format. Update kadence.`),{state:n.state,warnings:s}}function E(t,r){let n=r.toUpperCase();return t.tasks.find(e=>e.id===n||e.label===n)}function tt(t,r){return p(2,"unknown_status",`Unknown status "${t}".
|
|
210
|
+
Available: ${r.join(", ")}
|
|
211
|
+
kadence board config --statuses "todo,doing,done"`,{received:t,allowed:r,hint:"kadence board config --json"})}function ar(t,r,n){let e=b(t,r);if(!v(e))return e;if(n.type!==void 0&&!N.includes(n.type))return J("unknown_type","type",n.type,N);if(n.priority!==void 0&&!R.includes(n.priority))return J("unknown_priority","priority",n.priority,R);let{fields:s,error:o}=Xt(n.fields);if(o!==null)return o;if(n.sort!==void 0&&!be(n.sort))return{...p(2,"invalid_argument",`Unknown sort key "${n.sort}".
|
|
212
|
+
Available: ${te.join(", ")}`,{received:n.sort,allowed:te})};let{state:i,warnings:d}=y(e.root,e.actor);if(n.status!==void 0&&!i.statuses.includes(n.status))return tt(n.status,i.statuses);let{sort:a,tree:l,branch:u,base:c,json:g,...f}=n,h=pe(i,f),m=null;if(u===!0){let w=je(e.root);if(w===null)return p(1,"conflicting_state",`HEAD is detached, so there is no branch to compare.
|
|
213
|
+
Check one out, or name the range yourself:
|
|
214
|
+
git checkout main`,{hint:"git checkout main"});let j=c??Te(e.root),L=Ce(e.root,j,w);if(L===null)return p(2,"invalid_argument",`There is no branch "${j}" to compare against.
|
|
215
|
+
Name one that exists:
|
|
216
|
+
kadence task list --branch --base <name>`,{received:j,hint:"git branch --list"});m={name:w,base:j},h=h.filter(le=>L.has(le.id)||le.history.some(lt=>L.has(lt.id)))}let $=a===void 0?h:ge(h,a),k=qe($,n.limit,n.offset);return $.length===0?{ok:!0,exitCode:0,warnings:d,message:m===null?ke(f):`Branch ${m.name} has introduced no task changes since ${m.base}.
|
|
217
|
+
See the whole board:
|
|
218
|
+
kadence task list`,data:{schema:"kadence/v1",ok:!0,tasks:[],tasksTotal:0,tasksOffset:0,...m===null?{}:{branch:m}}}:{ok:!0,exitCode:0,warnings:d,message:[l===!0?Ze(k.shown,ie(r,process.stdout.isTTY===!0)):Ge(k.shown,ie(r,process.stdout.isTTY===!0)),He(k,"kadence task list")].filter(w=>w!==null).join(`
|
|
219
|
+
`),data:{schema:"kadence/v1",ok:!0,tasks:k.shown.map(w=>Qt(nt(w,s,i),s)),tasksTotal:k.total,tasksOffset:k.offset,cycles:i.cycles,...m===null?{}:{branch:m}}}}function ir(t,r,n,e){let s=b(t,r);if(!v(s))return s;let{state:o,warnings:i}=y(s.root,s.actor);if(!o.statuses.includes(e))return tt(e,o.statuses);let{tasks:d,error:a}=B(o,n);if(a!==null)return a;let l=[],u=[],c="";for(let h of d){if(h.status===e){u.push(h);continue}let m=h.status;x(s.root,{id:S(),type:"task.moved",entity:h.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{from:m,to:e}}),l.push(h),d.length===1&&o.statuses.indexOf(e)-o.statuses.indexOf(m)>1&&(c=`
|
|
220
|
+
Moved straight from ${m} to ${e}, skipping the stages between.`)}if(l.length===0)return{ok:!0,exitCode:0,warnings:i,message:C("already there",u,`is already ${e}.`)};let g=u.length>0?`
|
|
221
|
+
${u.length} already ${e}.`:"",f=e===ee?l.filter(h=>h.criteria.some(m=>!m.checked)).map(h=>{let m=h.criteria.filter($=>!$.checked).length;return`${h.label} moved to ${e} with ${m} of ${h.criteria.length} acceptance criteria unchecked. See: kadence task ac list ${h.label}`}):[];return{ok:!0,exitCode:0,warnings:[...i,...f],message:`${C("moved",l,`${l[0].status} \u2192 ${e}`)}${c}${g}`,data:{schema:"kadence/v1",ok:!0,moved:l.map(h=>({id:h.id,label:h.label,to:e}))}}}function Xt(t){if(t===void 0)return{fields:null,error:null};let r=t.split(",").map(e=>e.trim()).filter(e=>e.length>0);if(r.length===0)return{fields:null,error:p(2,"invalid_argument","--fields needs at least one field name.",{allowed:F})};let n=r.find(e=>!F.includes(e));return n!==void 0?{fields:null,error:p(2,"unknown_field",`Unknown field "${n}".
|
|
222
|
+
Available: ${F.join(", ")}`,{received:n,allowed:F})}:{fields:r,error:null}}function Qt(t,r){if(r!==null)return t;let{history:n,...e}=t;return e}function nt(t,r=null,n=null){let e=en(t,n);return r===null?e:Object.fromEntries(r.map(s=>[s,e[s]]))}function en(t,r=null){return{id:t.id,label:t.label,title:t.title,description:t.description,type:t.type,priority:t.priority,status:t.status,labels:t.labels,assignee:t.assignee,reporter:t.reporter,sprint:t.sprint,milestone:t.milestone===null?null:r?.milestones.find(n=>n.id===t.milestone)?.label??null,loggedHours:t.loggedHours,parent:t.parent,blockedBy:t.blockedBy,due:t.due,claimedBy:t.claimedBy,contestedBy:t.contestedBy,comments:t.comments,docs:t.docs,estimate:t.estimate,criteria:t.criteria.map(n=>({n:n.n,text:n.text,checked:n.checked,checkedBy:n.checkedBy})),openCriteria:t.criteria.filter(n=>!n.checked).length,history:t.history}}function dr(t,r,n,e){let s=b(t,r);if(!v(s))return s;let{state:o,warnings:i}=y(s.root,s.actor),{tasks:d,error:a}=B(o,n);if(a!==null)return a;let l=e.trim().toLowerCase()==="none"?null:e.trim(),u=d.filter(c=>c.assignee!==l);if(u.length===0)return{ok:!0,exitCode:0,warnings:i,message:l===null?C("already unassigned",d,"is already unassigned."):C("already assigned",d,`is already assigned to ${l}.`)};for(let c of u)x(s.root,{id:S(),type:"task.assigned",entity:c.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{assignee:l}});return{ok:!0,exitCode:0,warnings:i,message:l===null?C("unassigned",u,"unassigned."):C("assigned",u,`\u2192 ${l}`),data:{schema:"kadence/v1",ok:!0,assigned:u.map(c=>({id:c.id,label:c.label,assignee:l}))}}}function lr(t,r,n){let e=b(t,r);if(!v(e))return e;let{state:s,warnings:o}=y(e.root,e.actor),i=E(s,n);if(i===void 0)return _(n);let d=s.decisions.filter(c=>c.task===i.id&&c.supersededBy===null).map(c=>({id:c.id,label:c.label,title:c.title,why:c.why,rejected:c.rejected,docs:c.docs,source:c.source})),a=s.notes.filter(c=>c.task===i.id),l=s.documents.filter(c=>c.tasks.includes(i.id)).map(c=>({id:c.id,label:c.label,title:c.title,bytes:Buffer.byteLength(c.body,"utf8"),updatedAt:c.updatedAt,conflicted:c.conflicts.length>0})),u=l.length===0?"":`
|
|
223
|
+
|
|
224
|
+
Documentation:
|
|
225
|
+
${l.map(c=>` ${c.label} ${c.title} kadence doc show ${c.label}`).join(`
|
|
226
|
+
`)}`;return{ok:!0,exitCode:0,warnings:o,message:Xe(i,a)+u,data:{schema:"kadence/v1",ok:!0,task:{...nt(i,null,s),decisions:d,notes:a.map(c=>({id:c.id,text:c.text,at:c.at,by:c.by,source:c.source})),documentation:l}}}}function tn(t,r){let n=o=>o.map(i=>i.trim()).filter(i=>i.length>0),e=r.labels!==void 0?[...new Set(n(r.labels))]:[...new Set([...t.labels.filter(o=>!n(r.removeLabels??[]).includes(o)),...n(r.addLabels??[])])],s=[];for(let o of t.labels)e.includes(o)||s.push({type:"task.label_removed",label:o});for(let o of e)t.labels.includes(o)||s.push({type:"task.label_added",label:o});return s}function cr(t,r,n,e){let s=b(t,r);if(!v(s))return s;if(e.type!==void 0&&!N.includes(e.type))return J("unknown_type","type",e.type,N);if(e.priority!==void 0&&!R.includes(e.priority))return J("unknown_priority","priority",e.priority,R);if(e.due!==void 0&&e.due!==""&&!nn(e.due))return{...p(2,"invalid_argument",`Due date must be YYYY-MM-DD, got "${e.due}".
|
|
227
|
+
kadence task edit KAD-1 --due 2026-09-30`,{received:e.due})};if(e.labels!==void 0&&(e.addLabels!==void 0||e.removeLabels!==void 0))return p(2,"invalid_argument","--label replaces the whole set; --add-label and --remove-label change one. Use one or the other.",{hint:"kadence task edit KAD-1 --add-label impact-high"});let o=(e.addLabels??[]).filter(f=>(e.removeLabels??[]).includes(f));if(o.length>0)return p(2,"invalid_argument",`A label cannot be added and removed in one edit: ${o.join(", ")}.`,{received:o.join(","),hint:"kadence task edit KAD-1 --add-label impact-high"});let{state:i,warnings:d}=y(s.root,s.actor),{tasks:a,error:l}=B(i,n);if(l!==null)return l;if(e.title!==void 0&&a.length>1)return{...p(2,"invalid_argument","A title can only be set on one task at a time.")};let u=[],c=new Set;for(let f of a){let h={},m=[];e.title!==void 0&&e.title!==f.title&&(h.title=e.title,m.push("title")),e.description!==void 0&&e.description!==(f.description??"")&&(h.description=e.description,m.push("description")),e.type!==void 0&&e.type!==f.type&&(h.type=e.type,m.push("type")),e.priority!==void 0&&e.priority!==f.priority&&(h.priority=e.priority,m.push("priority")),e.due!==void 0&&e.due!==(f.due??"")&&(h.due=e.due,m.push("due")),e.estimate!==void 0&&e.estimate!==f.estimate&&(h.estimate=e.estimate,m.push("estimate"));let $=e.labels===void 0&&e.addLabels===void 0&&e.removeLabels===void 0?[]:tn(f,e);if($.length>0&&m.push("labels"),m.length!==0){Object.keys(h).length>0&&x(s.root,{id:S(),type:"task.updated",entity:f.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:h});for(let k of $)x(s.root,{id:S(),type:k.type,entity:f.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{label:k.label}});u.push(f);for(let k of m)c.add(k)}}if(u.length===0)return{ok:!0,exitCode:0,warnings:d,message:C("unchanged",a,"nothing changed.")};let g=[...c].join(", ");return{ok:!0,exitCode:0,warnings:d,message:C("updated",u,`updated ${g}.`),data:{schema:"kadence/v1",ok:!0,updated:u.map(f=>({id:f.id,label:f.label})),changed:[...c]}}}function nn(t){if(!/^\d{4}-\d{2}-\d{2}$/.test(t))return!1;let r=new Date(`${t}T00:00:00.000Z`);return!Number.isNaN(r.getTime())&&r.toISOString().slice(0,10)===t}function ur(t,r,n){let e=b(t,r);if(!v(e))return e;let{state:s,warnings:o}=y(e.root,e.actor),{tasks:i,error:d}=B(s,n);if(d!==null)return d;let a=i.filter(l=>l.status!=="cancelled");if(a.length===0)return{ok:!0,exitCode:0,warnings:o,message:C("already cancelled",i,"is already cancelled.")};for(let l of a)x(e.root,{id:S(),type:"task.cancelled",entity:l.id,actor:e.actor,ts:new Date().toISOString(),source:e.source,data:{}});return{ok:!0,exitCode:0,warnings:o,message:`${C("cancelled",a,"cancelled.")}
|
|
228
|
+
Cancelled work stays in history and does not count as missed.`,data:{schema:"kadence/v1",ok:!0,cancelled:a.map(l=>({id:l.id,label:l.label}))}}}function mr(t,r,n){let e=b(t,r);if(!v(e))return e;let{state:s,warnings:o}=y(e.root,e.actor),{tasks:i,error:d}=B(s,n);if(d!==null)return d;for(let l of i)x(e.root,{id:S(),type:"task.deleted",entity:l.id,actor:e.actor,ts:new Date().toISOString(),source:e.source,data:{title:l.title}});let a=i.length===1?`${i[0].label} deleted: "${i[0].title}".`:`${i.length} tasks deleted: ${i.map(l=>l.label).join(", ")}.`;return{ok:!0,exitCode:0,warnings:o,message:`${a}
|
|
229
|
+
The events stay in the journal \u2014 history is never rewritten.`,data:{schema:"kadence/v1",ok:!0,deleted:i.map(l=>({id:l.id,label:l.label}))}}}function rn(t,r){return[`# ${t}`,"",`Written for ${r}. Link it back with \`kadence task show ${r}\`.`,"",""].join(`
|
|
230
|
+
`)}var rt=/^[a-z][a-z0-9+.-]*:\/\//i;function sn(t,r,n){let e=r.trim();if(e.length===0)return p(2,"invalid_argument","A path is required.",{hint:n});if(rt.test(e))return p(2,"invalid_argument",`${r} is a URL. kadence links documents that live in the repository:
|
|
231
|
+
${n}`,{received:r,hint:n});let s=Vt(t,e),o=Yt(t,s);return o.length===0||o.startsWith("..")||Ht(o)?p(2,"invalid_argument",`${r} is outside the repository.
|
|
232
|
+
Use a path inside it:
|
|
233
|
+
${n}`,{received:r,hint:n}):{rel:o,full:s}}function hr(t,r,n,e,s=!1){let o=b(t,r);if(!v(o))return o;if(e.trim().length===0)return p(2,"invalid_argument","A link needs a path.",{hint:"kadence task doc KAD-1 docs/design.md"});let{state:i,warnings:d}=y(o.root,o.actor),a=E(i,n);if(a===void 0)return _(n);if(rt.test(e.trim()))return p(2,"invalid_argument",`${e} is a URL. kadence keeps documentation in the repository's journal, not as links:
|
|
234
|
+
kadence doc add "${a.title}" --file <path> --task ${a.label}`,{received:e,hint:`kadence doc add "\u2026" --body "\u2026" --task ${a.label}`});let l=sn(o.root,e,"kadence task doc KAD-1 docs/design.md");if("exitCode"in l)return l;let{rel:u,full:c}=l,g=!Qe(c),f=!1;return s&&g&&(zt(Gt(c),{recursive:!0}),Zt(c,rn(a.title,a.label),"utf8"),g=!1,f=!0),x(o.root,{id:S(),type:"task.doc_linked",entity:a.id,actor:o.actor,ts:new Date().toISOString(),source:o.source,data:{path:u}}),{ok:!0,exitCode:0,warnings:[...d,...g?[`No file at ${u} \u2014 the link is recorded anyway; it may arrive later.`]:[]],message:f?`Created ${u} and linked it to ${a.label}.`:`${u} linked to ${a.label}.`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label},doc:u,created:f}}}function fr(t,r,n,e={}){let s=b(t,r);if(!v(s))return s;let o=et(s,r),{state:i,warnings:d}=y(s.root,s.actor),a;if(n===void 0||n.trim().length===0){let c=e.assignee===void 0?{}:{assignee:e.assignee},g={statuses:i.statuses,started:i.started},f=ye(i.tasks,{viewer:o,...g,...c});if(a=f.find(h=>h.claimedBy===null||h.claimedBy===o),a===void 0){let h=f.filter(m=>m.claimedBy!==null);if(h.length>0){let m=h.map($=>`${$.label} (${$.claimedBy}${$.contestedBy.length>0?", contested":""})`).join(", ");return p(1,"nothing_ready",`Nothing free to claim. ${h.length===1?"The one task":`All ${h.length} tasks`} ready for you ${h.length===1?"is":"are"} already claimed: ${m}.
|
|
235
|
+
Name one to join the contest on purpose, or pick something else:
|
|
236
|
+
kadence task claim ${h[0].label}
|
|
237
|
+
kadence task list`,{hint:`kadence task claim ${h[0].label}`})}return p(1,"nothing_ready",we(i.tasks,o,e.assignee,g),{hint:"kadence task list"})}}else if(a=E(i,n),a===void 0)return _(n);if(a.status===ee||a.status===ue)return p(1,"conflicting_state",`${a.label} is ${a.status}, so there is nothing to claim.
|
|
238
|
+
Reopen it first:
|
|
239
|
+
kadence task move ${a.label} in_progress`,{hint:`kadence task move ${a.label} in_progress`});if(a.claimedBy===o||a.contestedBy.includes(o)){let c=a.claimedBy===o;return{ok:!0,exitCode:0,warnings:d,message:c?`${a.label} is already yours.`:`${a.label} already carries your contested claim; ${a.claimedBy??"nobody"} holds it.`,data:{schema:"kadence/v1",ok:!0,claim:c?"already_yours":"contested",task:{id:a.id,label:a.label,claimedBy:a.claimedBy,contestedBy:a.contestedBy}}}}x(s.root,{id:S(),type:"task.claimed",entity:a.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{by:o}});let l=y(s.root,s.actor).state.tasks.find(c=>c.id===a.id)??a,u=l.claimedBy===o;return{ok:!0,exitCode:0,warnings:u?d:[...d,`${l.label} is contested: ${l.claimedBy??"nobody"} claimed it first and keeps it. Both claims stay in the journal \u2014 talk to them before starting.`],message:u?`${l.label} ${l.title} \u2014 claimed by ${o}.
|
|
240
|
+
Another machine may have claimed it before your push; the merge will show that.`:`${l.label} claim recorded, contested with ${l.claimedBy??"nobody"}.`,data:{schema:"kadence/v1",ok:!0,claim:u?"claimed":"contested",task:{id:l.id,label:l.label,claimedBy:l.claimedBy,contestedBy:l.contestedBy}}}}function pr(t,r,n){let e=b(t,r);if(!v(e))return e;let s=et(e,r),{state:o,warnings:i}=y(e.root,e.actor),d=E(o,n);if(d===void 0)return _(n);let a=d.claimedBy===s,l=d.contestedBy.includes(s);return!a&&!l?{ok:!0,exitCode:0,warnings:i,message:d.claimedBy===null?`${d.label} is not claimed.`:`${d.label} is claimed by ${d.claimedBy}, not by you. Nothing released.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label,claimedBy:d.claimedBy}}}:(x(e.root,{id:S(),type:"task.released",entity:d.id,actor:e.actor,ts:new Date().toISOString(),source:e.source,data:{by:s}}),{ok:!0,exitCode:0,warnings:i,message:a?`${d.label} released.`:`${d.label} withdrawn from \u2014 ${d.claimedBy??"nobody"} keeps the claim.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label,claimedBy:a?null:d.claimedBy,contestedBy:a?[]:d.contestedBy.filter(u=>u!==s)}}})}function gr(t,r,n,e){let s=b(t,r);if(!v(s))return s;let o=e.trim();if(o.length===0)return p(2,"invalid_argument","A criterion needs text.",{hint:`kadence task ac add ${n} "tests green"`});let{state:i,warnings:d}=y(s.root,s.actor),a=E(i,n);if(a===void 0)return _(n);x(s.root,{id:S(),type:"task.criterion_added",entity:a.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:o}});let l=a.criteria.length+1;return{ok:!0,exitCode:0,warnings:d,message:`${a.label} criterion ${l}: ${o}`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label},criterion:{n:l,text:o,checked:!1,checkedBy:null}}}}function on(t,r){let n=Number(r),e=t.criteria.map(o=>o.n);if(!Number.isInteger(n))return p(2,"invalid_argument",`"${r}" is not a criterion number.`,{received:r,hint:`kadence task ac list ${t.label}`});let s=t.criteria.find(o=>o.n===n);return s===void 0?p(2,"invalid_argument",t.criteria.length===0?`${t.label} has no criteria yet.
|
|
241
|
+
kadence task ac add ${t.label} "tests green"`:`${t.label} has no criterion ${n}.
|
|
242
|
+
Available: ${e.join(", ")}`,{received:r,hint:`kadence task ac list ${t.label}`}):s}function kr(t,r,n,e,s){let o=b(t,r);if(!v(o))return o;let{state:i,warnings:d}=y(o.root,o.actor),a=E(i,n);if(a===void 0)return _(n);let l=on(a,e);if(!("id"in l))return l;let u=!s;if(l.checked===u)return{ok:!0,exitCode:0,warnings:d,message:`${a.label} criterion ${l.n} is already ${u?"checked":"unchecked"}.`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label}}};x(o.root,{id:S(),type:u?"task.criterion_checked":"task.criterion_unchecked",entity:a.id,actor:o.actor,ts:new Date().toISOString(),source:o.source,data:{criterion:l.id}});let c=a.criteria.filter(g=>g.id===l.id?u:g.checked).length;return{ok:!0,exitCode:0,warnings:d,message:`${a.label} criterion ${l.n} ${u?"checked":"unchecked"} \u2014 ${c} of ${a.criteria.length} done.`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label},criterion:{n:l.n,text:l.text,checked:u,checkedBy:u?o.actor:null},done:c,total:a.criteria.length}}}function yr(t,r,n){let e=b(t,r);if(!v(e))return e;let{state:s,warnings:o}=y(e.root,e.actor),i=E(s,n);return i===void 0?_(n):i.criteria.length===0?{ok:!0,exitCode:0,warnings:o,message:`${i.label} has no acceptance criteria.
|
|
243
|
+
Add one:
|
|
244
|
+
kadence task ac add ${i.label} "tests green"`,data:{schema:"kadence/v1",ok:!0,task:{id:i.id,label:i.label},criteria:[]}}:{ok:!0,exitCode:0,warnings:o,message:i.criteria.map(d=>` ${d.n}. [${d.checked?"x":" "}] ${d.text}`).join(`
|
|
245
|
+
`),data:{schema:"kadence/v1",ok:!0,task:{id:i.id,label:i.label},criteria:an(i)}}}function an(t){return t.criteria.map(r=>({n:r.n,text:r.text,checked:r.checked,checkedBy:r.checkedBy}))}function wr(t,r,n,e){let s=b(t,r);if(!v(s))return s;let o=e.trim();if(o.length===0)return p(2,"invalid_argument","A comment needs text.",{received:n});let{state:i,warnings:d}=y(s.root,s.actor),a=E(i,n);return a===void 0?_(n):(x(s.root,{id:S(),type:"task.commented",entity:a.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:o}}),{ok:!0,exitCode:0,warnings:d,message:`Comment added to ${a.label}.`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label}}})}function B(t,r){let n=r.split(",").map(i=>i.trim()).filter(i=>i.length>0);if(n.length===0)return{tasks:[],error:p(2,"invalid_argument","No task given.")};let e=[],s=[];for(let i of n){let d=E(t,i);d===void 0?s.push(i):e.push(d)}return s.length>0?{tasks:[],error:p(1,"task_not_found",`No task ${s.join(", ")} \u2014 nothing was changed.
|
|
246
|
+
All ids must exist before a bulk change runs.
|
|
247
|
+
kadence task list`,{received:s.join(","),hint:"kadence task list --json"})}:{tasks:[...new Map(e.map(i=>[i.id,i])).values()],error:null}}function C(t,r,n){return r.length===1?`${r[0].label}: ${n}`:`${r.length} tasks ${t}: ${r.map(e=>e.label).join(", ")}`}function br(t,r,n,e){let s=b(t,r);if(!v(s))return s;let{state:o,warnings:i}=y(s.root,s.actor),{tasks:d,error:a}=B(o,n);if(a!==null)return a;let l=e.trim().toLowerCase()==="none",u=null;if(!l){let f=E(o,e);if(f===void 0)return _(e);if(d.some(h=>h.id===f.id))return p(2,"invalid_argument","A task cannot be its own parent.",{received:n});u=f.id}let c=d.filter(f=>f.parent!==u);if(c.length===0)return{ok:!0,exitCode:0,warnings:i,message:C("unchanged",d,"already there.")};for(let f of c)x(s.root,{id:S(),type:"task.parent_set",entity:f.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{parent:u}});let g=l?"detached":E(o,e).label;return{ok:!0,exitCode:0,warnings:[...i,...st(s.root,s.actor)],message:l?C("detached",c,"detached from its parent."):C("moved",c,`\u2192 child of ${g}`),data:{schema:"kadence/v1",ok:!0,parent:u}}}function vr(t,r,n,e,s){let o=b(t,r);if(!v(o))return o;let{state:i,warnings:d}=y(o.root,o.actor),{tasks:a,error:l}=B(i,n);if(l!==null)return l;let u=E(i,e);if(u===void 0)return _(e);if(a.some(g=>g.id===u.id))return p(2,"invalid_argument","A task cannot block itself.",{received:n});let c=a.filter(g=>s?g.blockedBy.includes(u.id):!g.blockedBy.includes(u.id));if(c.length===0)return{ok:!0,exitCode:0,warnings:d,message:C("unchanged",a,s?"was not blocked by it.":"is already blocked by it.")};for(let g of c)x(o.root,{id:S(),type:s?"task.blocked_by_removed":"task.blocked_by_added",entity:g.id,actor:o.actor,ts:new Date().toISOString(),source:o.source,data:{blocker:u.id}});return{ok:!0,exitCode:0,warnings:[...d,...st(o.root,o.actor)],message:s?C("unblocked",c,`no longer blocked by ${u.label}.`):C("blocked",c,`blocked by ${u.label}.`),data:{schema:"kadence/v1",ok:!0,blocker:u.id}}}function st(t,r){let{state:n}=y(t,r),e=new Map(n.tasks.map(s=>[s.id,s.label]));return n.cycles.map(s=>{let o=s.path.map(i=>e.get(i)??i).join(" \u2192 ");return`Dependency cycle (${s.kind}): ${o}. Both edges were kept \u2014 resolve it when you can.`})}function dn(t){let r=/^(-?\d+(?:\.\d+)?)\s*(h|hours?|m|min|minutes?)?$/i.exec(t.trim());if(r===null)return null;let n=Number(r[1]);return!Number.isFinite(n)||n===0?null:(r[2]??"h").toLowerCase().startsWith("m")?n/60:n}function $r(t,r,n,e){let s=b(t,r);if(!v(s))return s;let o=dn(e);if(o===null)return{...p(2,"invalid_argument",`Cannot read "${e}" as a duration.
|
|
248
|
+
Use hours or minutes:
|
|
249
|
+
kadence task log KAD-1 2h
|
|
250
|
+
kadence task log KAD-1 90m
|
|
251
|
+
kadence task log KAD-1 -30m to correct a mistake`,{received:e})};let{state:i,warnings:d}=y(s.root,s.actor),a=E(i,n);if(a===void 0)return _(n);x(s.root,{id:S(),type:"task.time_logged",entity:a.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{hours:o}});let l=Math.max(0,a.loggedHours+o),u=a.estimate===null?`
|
|
252
|
+
No estimate on this task, so there is nothing to compare against.`:"";return{ok:!0,exitCode:0,warnings:d,message:`${a.label}: ${l.toFixed(1)}h logged in total.${u}`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label,loggedHours:l}}}}function Q(t,r){let n=t.sprints.find(m=>m.id===r);if(n===void 0)return null;let e=t.tasks.filter(m=>m.sprint===r),s=e.filter(m=>m.status==="cancelled"),o=e.filter(m=>m.status!=="cancelled"),i=o.filter(m=>m.status==="done"),d=o.filter(m=>m.status!=="done"),a=W(o.map(m=>m.estimate??0)),l=W(i.map(m=>m.estimate??0)),u=i.filter(m=>m.estimate===null),c=i.map(m=>ln(m,t)).filter(m=>m!==null),g=c.length>0?W(c):null,f=W(i.filter(m=>m.estimate!==null).map(m=>m.estimate)),h=g!==null&&f>0?g/f:null;return{id:n.id,name:n.name,status:n.status,committed:a,velocity:l,actualHours:g,hoursPerPoint:h,done:i,carriedOver:d,cancelled:s,unestimated:u,loggedHours:W(e.map(m=>m.loggedHours))}}function ln(t,r){let n=he(t,r.started,r.statuses,r.startedChanges),e=fe(t);if(n===null||e===null)return null;let s=Date.parse(e)-Date.parse(n);return s>0?s/36e5:null}function W(t){return t.reduce((r,n)=>r+n,0)}var cn=4;function jr(t,r=8){let n=t.sprints.filter(a=>a.status==="closed").sort((a,l)=>(a.closedBy??"")<(l.closedBy??"")?-1:1).slice(-r),e=[];for(let a of n){let l=Q(t,a.id);l!==null&&e.push({name:l.name,committed:l.committed,velocity:l.velocity,carriedOver:W(l.carriedOver.map(u=>u.estimate??0)),unestimated:l.unestimated.length})}let s=[];if(e.length===0)return s.push("No closed sprint yet. Velocity is what a finished sprint leaves behind."),{rows:[],median:null,low:null,high:null,notes:s};let o=e.map(a=>a.velocity).sort((a,l)=>a-l),i=o[Math.floor((o.length-1)/2)];return e.length<cn&&s.push(`${e.length} closed sprint${e.length===1?"":"s"}. Velocity takes four to eight to stabilise, so treat this as a sighting rather than a forecast.`),e.filter(a=>a.committed>a.velocity*1.5).length===e.length&&e.length>=2&&s.push("Every sprint here took in half again more than it finished."),{rows:e,median:i,low:o[0],high:o[o.length-1],notes:s}}function G(t){return new Date(t).toISOString().slice(0,10)}function un(t,r){let n=[];for(let e=Date.parse(`${t}T00:00:00.000Z`);e<=Date.parse(`${r}T00:00:00.000Z`);e+=864e5)n.push(G(e));return n}function ot(t,r,n,e=G(Date.now())){let s=t.tasks.filter(k=>k.sprint===n.id&&k.status!=="cancelled"),o=new Map(s.map(k=>[k.id,k.estimate??0])),i=[...o.values()].reduce((k,w)=>k+w,0),d=r.filter(k=>o.has(k.entity));if(d.length===0)return null;let a=n.startDate??G(d[0].ts),l=n.endDate??G(d.at(-1).ts),u=n.status==="closed"||l<e?l:e;if(u<a)return null;let c=new Set,g=new Map;for(let k of d){let w=G(k.ts);if(w<a)continue;let j=k.type==="task.moved"?k.data?.to:void 0,L=o.get(k.entity)??0;j==="done"&&!c.has(k.entity)?(c.add(k.entity),g.set(w,(g.get(w)??0)+L)):j!==void 0&&j!=="done"&&c.has(k.entity)&&(c.delete(k.entity),g.set(w,(g.get(w)??0)-L))}let f=un(a,u),h=Math.max(f.length-1,1),m=i,$=f.map((k,w)=>{let j=g.get(k)??0;return m-=j,{date:k,remaining:Math.max(0,m),ideal:Math.max(0,i-i/h*w),completed:j}});return{sprintName:n.name,committed:i,finalRemaining:n.status==="closed"?$[$.length-1]?.remaining??0:null,days:$}}function at(t,r=40){if(t.committed===0)return`"${t.sprintName}": nothing to burn down \u2014 no estimated tasks.`;let n=[`"${t.sprintName}" \u2014 ${t.committed} points committed`,""];for(let s of t.days){let o=Math.round(s.remaining/t.committed*r),i=Math.round(s.ideal/t.committed*r),d=[];for(let u=0;u<r;u++)u===i&&u>=o?d.push("\u250A"):u<o?d.push(u===i?"\u2503":"\u2588"):d.push(" ");let a=s.remaining-s.ideal,l=Math.abs(a)<.5?"":a>0?` +${a.toFixed(0)}`:` ${a.toFixed(0)}`;n.push(`${s.date.slice(5)} ${d.join("")} ${String(s.remaining).padStart(3)}${l}`)}let e=t.days.at(-1);return e!==void 0&&(n.push(""),n.push(e.remaining===0?" All committed work is done.":` ${e.remaining} of ${t.committed} points remain.`+(e.remaining>e.ideal?" Behind the ideal line.":" On or ahead of the line."))),n.join(`
|
|
253
|
+
`)}function I(t){return t.sprints.find(r=>r.status==="active")}function mn(t){return t.sprints.filter(r=>r.status==="planned")}function q(t,r){let n=r.trim().toLowerCase();return t.sprints.find(e=>e.name.toLowerCase()===n)}function H(t,r,n,e){x(t.root,{id:S(),type:r,entity:n,actor:t.actor,ts:new Date().toISOString(),source:t.source,data:e})}function Or(t,r,n){let e=b(t,r);if(!v(e))return e;let s=n.trim();if(s.length===0)return p(2,"invalid_argument","A sprint needs a name.",{hint:"kadence sprint list --json"});let{state:o,warnings:i}=y(e.root,e.actor);if(q(o,s)!==void 0)return p(2,"invalid_argument",`Sprint "${s}" already exists.`,{received:s,hint:"kadence sprint list --json"});let d=I(o)!==void 0,a=S();return H(e,"sprint.created",a,{name:s}),d||H(e,"sprint.started",a,{}),{ok:!0,exitCode:0,warnings:i,message:d?`Sprint "${s}" planned.
|
|
254
|
+
|
|
255
|
+
kadence sprint add KAD-1 --sprint "${s}"
|
|
256
|
+
kadence sprint start "${s}"`:`Sprint "${s}" started.
|
|
257
|
+
|
|
258
|
+
kadence sprint add KAD-1
|
|
259
|
+
kadence sprint status`,data:{schema:"kadence/v1",ok:!0,sprint:{id:a,name:s,status:d?"planned":"active"}}}}function Pr(t,r,n,e={}){let s=b(t,r);if(!v(s))return s;let{state:o,warnings:i}=y(s.root,s.actor),d=e.sprint===void 0?I(o):q(o,e.sprint);if(d===void 0)return e.sprint===void 0?{...p(1,"sprint_not_found",`No active sprint.
|
|
260
|
+
kadence sprint create "Sprint 1"`,{hint:"kadence sprint list --json"})}:p(1,"sprint_not_found",`No sprint named "${e.sprint}".
|
|
261
|
+
kadence sprint list`,{received:e.sprint,hint:"kadence sprint list --json"});if(d.status==="closed")return p(1,"conflicting_state",`Sprint "${d.name}" is closed \u2014 its velocity is not rewritten.`,{received:d.name});let a=E(o,n);if(a===void 0)return _(n);if(a.sprint===d.id)return{ok:!0,exitCode:0,warnings:i,message:`${a.label} is already in the sprint.`};H(s,"sprint.task_added",d.id,{task:a.id});let l=a.estimate===null?`
|
|
262
|
+
Without an estimate this task adds no points to sprint "${d.name}". Add --estimate.`:"";return{ok:!0,exitCode:0,warnings:i,message:`${a.label} \u2192 "${d.name}"${l}`,data:{schema:"kadence/v1",ok:!0,task:{id:a.id,label:a.label},sprint:d.id}}}function Br(t,r){let n=b(t,r);if(!v(n))return n;let{state:e,warnings:s}=y(n.root,n.actor),o=I(e);if(o===void 0)return{...p(1,"sprint_not_found",`No active sprint.
|
|
263
|
+
kadence sprint create "Sprint 1"`,{hint:"kadence sprint list --json"})};let i=Q(e,o.id);H(n,"sprint.closed",o.id,{velocity:i.velocity});let d=[...s];return i.unestimated.length>0&&d.push(`${i.unestimated.length} completed task(s) without an estimate were left out of velocity: `+i.unestimated.map(a=>a.label).join(", ")),{ok:!0,exitCode:0,warnings:d,message:hn(i),data:{schema:"kadence/v1",ok:!0,report:it(i)}}}function Ir(t,r){let n=b(t,r);if(!v(n))return n;let{state:e,warnings:s}=y(n.root,n.actor),o=I(e);if(o===void 0)return{ok:!0,exitCode:0,warnings:s,message:`No active sprint.
|
|
264
|
+
kadence sprint create "Sprint 1"`,data:{schema:"kadence/v1",ok:!0,sprint:null}};let i=Q(e,o.id),d=[`"${i.name}" \u2014 ${i.velocity} of ${i.committed} points`,"",...i.done.map(a=>` \u2713 ${a.label} ${a.title}`),...i.carriedOver.map(a=>` \xB7 ${a.label} ${a.title} (${a.status})`)];return{ok:!0,exitCode:0,warnings:s,message:d.join(`
|
|
265
|
+
`),data:{schema:"kadence/v1",ok:!0,report:it(i)}}}function hn(t){let r=[`Sprint "${t.name}" closed.`,""];if(r.push(` Velocity: ${t.velocity} of ${t.committed} points`),t.hoursPerPoint!==null&&t.actualHours!==null&&t.actualHours>=1/60&&r.push(` Actual: ${de(t.actualHours)} \u2014 ${de(t.hoursPerPoint)} per point`),t.loggedHours>0&&r.push(` Logged: ${de(t.loggedHours)} entered by hand`),t.carriedOver.length>0){r.push("",` Carried over (${t.carriedOver.length}):`);for(let n of t.carriedOver)r.push(` \xB7 ${n.label} ${n.title}`)}return t.cancelled.length>0&&r.push("",` Cancelled (${t.cancelled.length}) \u2014 not counted towards velocity`),r.join(`
|
|
266
|
+
`)}function de(t){return t<1?`${Math.round(t*60)}m`:t<10?`${t.toFixed(1)}h`:`${Math.round(t)}h`}function it(t){return{id:t.id,name:t.name,status:t.status,velocity:t.velocity,committed:t.committed,actualHours:t.actualHours,hoursPerPoint:t.hoursPerPoint,done:t.done.map(r=>r.label),carriedOver:t.carriedOver.map(r=>r.label),cancelled:t.cancelled.map(r=>r.label),unestimated:t.unestimated.map(r=>r.label),loggedHours:t.loggedHours}}function Lr(t,r,n){let e=b(t,r);if(!v(e))return e;let{state:s,warnings:o}=y(e.root,e.actor),i=I(s);if(i!==void 0)return{...p(1,"conflicting_state",`Sprint "${i.name}" is still active.
|
|
267
|
+
Close it so velocity can be computed:
|
|
268
|
+
kadence sprint close`,{received:i.name})};let d=mn(s),a=n===void 0?d[0]:q(s,n);return a===void 0?{...p(1,"sprint_not_found",n===void 0?`No planned sprints.
|
|
269
|
+
kadence sprint create "Sprint 2"`:`No sprint named "${n}".
|
|
270
|
+
kadence sprint list`,{...n!==void 0?{received:n}:{},hint:"kadence sprint list --json"})}:a.status!=="planned"?p(1,"conflicting_state",`Sprint "${a.name}" is already ${a.status}.`,{received:a.name}):(H(e,"sprint.started",a.id,{}),{ok:!0,exitCode:0,warnings:o,message:`Sprint "${a.name}" started.
|
|
271
|
+
|
|
272
|
+
kadence sprint status`,data:{schema:"kadence/v1",ok:!0,sprint:{id:a.id,name:a.name}}})}function Fr(t,r){let n=b(t,r);if(!v(n))return n;let{state:e,warnings:s}=y(n.root,n.actor);if(e.sprints.length===0)return{ok:!0,exitCode:0,warnings:s,message:`No sprints yet.
|
|
273
|
+
kadence sprint create "Sprint 1"`,data:{schema:"kadence/v1",ok:!0,sprints:[]}};let o=e.sprints.map(i=>{let d=e.tasks.filter(l=>l.sprint===i.id),a=d.reduce((l,u)=>l+(u.estimate??0),0);return{sprint:i,tasks:d,points:a}});return{ok:!0,exitCode:0,warnings:s,message:o.map(({sprint:i,tasks:d,points:a})=>{let l=i.status==="active"?"\u2192":" ",u=d.length===0?"":` ${d.length} tasks, ${a} points`;return`${l} ${i.name.padEnd(20)} ${i.status.padEnd(8)}${u}`}).join(`
|
|
274
|
+
`),data:{schema:"kadence/v1",ok:!0,sprints:o.map(({sprint:i,tasks:d,points:a})=>({id:i.id,name:i.name,status:i.status,taskIds:i.taskIds,taskCount:d.length,points:a}))}}}function fn(t){if(!/^\d{4}-\d{2}-\d{2}$/.test(t))return!1;let r=new Date(`${t}T00:00:00.000Z`);return!Number.isNaN(r.getTime())&&r.toISOString().slice(0,10)===t}function Mr(t,r,n,e){let s=b(t,r);if(!v(s))return s;for(let[g,f]of[["--start",e.startDate],["--end",e.endDate]])if(f!==void 0&&f!==""&&!fn(f))return{...p(2,"invalid_argument",`${g} must be YYYY-MM-DD, got "${f}".
|
|
275
|
+
kadence sprint edit --start 2026-09-01`,{received:f})};let{state:o,warnings:i}=y(s.root,s.actor),d=n===void 0?I(o):q(o,n);if(d===void 0)return{...p(1,"sprint_not_found",n===void 0?`No active sprint.
|
|
276
|
+
kadence sprint list`:`No sprint named "${n}".
|
|
277
|
+
kadence sprint list`,{...n!==void 0?{received:n}:{},hint:"kadence sprint list --json"})};if(d.status==="closed")return{...p(1,"conflicting_state",`Sprint "${d.name}" is closed \u2014 its record is not rewritten.`,{received:d.name})};if(e.name!==void 0&&e.name!==d.name&&q(o,e.name)!==void 0)return p(2,"invalid_argument",`Sprint "${e.name}" already exists.`,{received:e.name});let a=e.startDate??d.startDate??"",l=e.endDate??d.endDate??"";if(a!==""&&l!==""&&l<a)return{...p(2,"invalid_argument",`The end date (${l}) is before the start date (${a}).`,{received:l})};let u={},c=[];return e.name!==void 0&&e.name!==d.name&&(u.name=e.name,c.push("name")),e.description!==void 0&&e.description!==(d.description??"")&&(u.description=e.description,c.push("description")),e.startDate!==void 0&&e.startDate!==(d.startDate??"")&&(u.startDate=e.startDate,c.push("start")),e.endDate!==void 0&&e.endDate!==(d.endDate??"")&&(u.endDate=e.endDate,c.push("end")),c.length===0?{ok:!0,exitCode:0,warnings:i,message:`"${d.name}": nothing changed.`}:(H(s,"sprint.updated",d.id,u),{ok:!0,exitCode:0,warnings:i,message:`"${d.name}": updated ${c.join(", ")}.`,data:{schema:"kadence/v1",ok:!0,sprint:{id:d.id,changed:c}}})}function pn(t,r,n){let e=n===void 0?I(r):q(r,n);return e===void 0?p(1,"sprint_not_found",n===void 0?`No active sprint.
|
|
278
|
+
kadence sprint list`:`No sprint named "${n}".
|
|
279
|
+
kadence sprint list`,{...n!==void 0?{received:n}:{},hint:"kadence sprint list --json"}):{sprint:e,chart:ot(r,X(t).events,e)}}function Ur(t,r,n){let e=b(t,r);if(!v(e))return e;let{state:s,warnings:o}=y(e.root,e.actor),i=pn(e.root,s,n);if("exitCode"in i)return i;let{sprint:d,chart:a}=i;return a===null?{ok:!0,exitCode:0,warnings:o,message:`"${d.name}" has no tasks yet.
|
|
280
|
+
kadence sprint add KAD-1`,data:{schema:"kadence/v1",ok:!0,burndown:null}}:{ok:!0,exitCode:0,warnings:o,message:at(a),data:{schema:"kadence/v1",ok:!0,burndown:a}}}import{spawnSync as gn}from"node:child_process";import{mkdtempSync as kn,readFileSync as yn,rmSync as wn,writeFileSync as bn}from"node:fs";import{tmpdir as vn}from"node:os";import{join as dt}from"node:path";var V="#";function $n(t){return t.GIT_EDITOR??t.VISUAL??t.EDITOR??"vi"}function Yr(t,r,n){let e=kn(dt(vn(),"kadence-edit-")),s=dt(e,"KADENCE_EDITMSG.md"),o=["",`${V} ${n}`,`${V} Lines starting with '${V}' are ignored.`,`${V} Save an empty file to abort.`].join(`
|
|
281
|
+
`);try{bn(s,`${r}${o}
|
|
282
|
+
`,"utf8");let i=$n(t),d=gn(i,[s],{stdio:"inherit",shell:!0});if(d.error!==void 0||d.status!==null&&d.status!==0)return{text:null,error:`Editor "${i}" exited without saving.
|
|
283
|
+
Set one explicitly:
|
|
284
|
+
export EDITOR=nano`};let l=yn(s,"utf8").split(`
|
|
285
|
+
`).filter(u=>!u.startsWith(V)).join(`
|
|
286
|
+
`).trim();return{text:l.length===0?null:l,error:null}}catch(i){return{text:null,error:`Could not open an editor: ${i.message}`}}finally{wn(e,{recursive:!0,force:!0})}}function Gr(t,r){return t.KADENCE_SOURCE==="agent"?!1:r}export{$e as a,je as b,Te as c,Ce as d,U as e,P as f,x as g,X as h,On as i,Pn as j,Tn as k,Cn as l,En as m,S as n,qe as o,He as p,ie as q,ze as r,qn as s,sr as t,p as u,_ as v,b as w,et as x,v as y,or as z,y as A,E as B,ar as C,ir as D,Xt as E,nt as F,dr as G,lr as H,cr as I,ur as J,mr as K,sn as L,hr as M,fr as N,pr as O,gr as P,kr as Q,yr as R,wr as S,br as T,vr as U,$r as V,Q as W,jr as X,ot as Y,at as Z,Or as _,Pr as $,Br as aa,Ir as ba,Lr as ca,Fr as da,Mr as ea,pn as fa,Ur as ga,Yr as ha,Gr as ia};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var j=["backlog","todo","in_progress","blocked","in_review","done","cancelled"],x="done",S="cancelled",F="in_progress",G="backlog",gt=["task","bug","story","epic"],pt=["low","normal","high","urgent"];function yt(t){let n=b(t.data?.title),e=t.data?.body,d=t.data?.parents;return n===null||typeof e!="string"||e.trim().length===0||!Array.isArray(d)?null:{title:n,body:e,parents:d.filter(o=>typeof o=="string")}}function ht(t,n,e){let d=[];for(let[o,a]of t){let s=a.filter((f,y)=>a.findIndex(m=>m.id===f.id)===y).map(f=>({e:f,r:yt(f)})).filter(f=>f.r!==null);if(s.length===0)continue;let l=new Set(s.flatMap(f=>f.r.parents)),i=s.filter(f=>!l.has(f.e.id));i.length===0&&(i=[s[s.length-1]]);let g=i[i.length-1],p=s[0];d.push({id:o,label:"",title:g.r.title,body:g.r.body,tasks:(n.get(o)??[]).filter(f=>e.has(f)),revision:g.e.id,revisions:s.length,conflicts:i.slice(0,-1).map(f=>({revision:f.e.id,title:f.r.title,body:f.r.body,at:f.e.ts,by:f.e.actor,source:f.e.source})),createdAt:p.e.ts,createdBy:p.e.actor,updatedAt:g.e.ts,updatedBy:g.e.actor,source:g.e.source})}return d.sort((o,a)=>o.id<a.id?-1:1),d.forEach((o,a)=>{o.label=`DOC-${a+1}`}),d}function L(t,n){for(let e=t.length-1;e>=0;e--)if(t[e].id<n)return t[e].started;return F}function Lt(t){let n=[...t].sort((r,u)=>r.id<u.id?-1:r.id>u.id?1:0),e=new Map,d=new Map,o=new Map,a=new Map,c=new Map,s=[],l=new Map,i=new Map,g=null,p=[],f={started:F,changes:[]},y=[],m=[];for(let r of n)r.type==="task.created"?V(e,r):r.type==="sprint.created"?J(d,r):r.type==="milestone.created"&&Z(o,r);for(let r of n){if(r.type==="decision.recorded"){let u=r.data??{},h=v=>typeof u[v]=="string"&&u[v].length>0?u[v]:null,w=h("title"),A=h("why");w!==null&&A!==null&&c.set(r.id,{id:r.id,label:"",title:w,why:A,rejected:h("rejected"),task:h("task"),docs:Array.isArray(u.docs)?u.docs.filter(v=>typeof v=="string"):[],supersedes:h("supersedes"),supersededBy:null,at:r.ts,by:r.actor,source:r.source});continue}if(r.type==="note.recorded"){let u=b(r.data?.text);u!==null&&s.push({id:r.id,text:u,task:b(r.data?.task),at:r.ts,by:r.actor,source:r.source});continue}if(r.type==="doc.written"){let u=l.get(r.entity)??[];u.push(r),l.set(r.entity,u);continue}if(r.type==="doc.linked"){let u=b(r.data?.task);if(u!==null){let h=i.get(r.entity)??[];h.includes(u)||h.push(u),i.set(r.entity,h)}continue}if(r.type==="template.saved"){let u=typeof r.data?.name=="string"?r.data.name:null;u!==null&&a.set(u,{name:u,fields:r.data?.fields??{},author:r.actor});continue}if(r.type==="board.configured"){let u=r.data?.statuses;if(Array.isArray(u)){let A=u.filter(v=>typeof v=="string"&&v.length>0);A.length>0&&(g=A)}let h=r.data?.dod;Array.isArray(h)&&(p=h.filter(A=>typeof A=="string"&&A.length>0));let w=b(r.data?.started);w!==null&&(f.started=w,f.changes.push({id:r.id,started:w}));continue}if(r.type==="template.deleted"){let u=typeof r.data?.name=="string"?r.data.name:null;u!==null&&a.delete(u);continue}mt(r,e,d,o,y,m,f)}let k=m,T=[...e.values()].sort((r,u)=>r.id<u.id?-1:1);T.forEach((r,u)=>{r.label=`KAD-${u+1}`,kt(r),r.criteria.sort((h,w)=>h.id<w.id?-1:1),r.criteria.forEach((h,w)=>{h.n=w+1}),bt(r)});let M=new Set(T.map(r=>r.id));for(let r of T)r.parent!==null&&!M.has(r.parent)&&(r.parent=null),r.blockedBy=r.blockedBy.filter(u=>M.has(u));let R=[...c.values()].sort((r,u)=>r.id<u.id?-1:1);R.forEach((r,u)=>{r.label=`DEC-${u+1}`});for(let r of R){if(r.supersedes===null)continue;let u=c.get(r.supersedes);u!==void 0&&(u.supersededBy=r.id)}let _=[...o.values()].sort((r,u)=>r.id<u.id?-1:1);_.forEach((r,u)=>{r.label=`MS-${u+1}`});let C=new Map;for(let r of T){if(r.milestone===null||!o.has(r.milestone))continue;let u=C.get(r.milestone)??[];u.push(r.id),C.set(r.milestone,u)}for(let r of _){r.taskIds=C.get(r.id)??[];let u=r.taskIds.map(h=>e.get(h)).filter(h=>h!==void 0);r.totalTasks=u.length,r.doneTasks=u.filter(h=>h.status===x).length,r.totalPoints=u.reduce((h,w)=>h+(w.estimate??0),0),r.donePoints=u.filter(h=>h.status===x).reduce((h,w)=>h+(w.estimate??0),0)}for(let r of R)r.task!==null&&!M.has(r.task)&&(r.task=null);for(let r of s)r.task!==null&&!M.has(r.task)&&(r.task=null);let K=ht(l,i,M);return{tasks:T,decisions:R,documents:K,milestones:_,notes:s,sprints:[...d.values()].sort((r,u)=>r.id<u.id?-1:1),statuses:g??[...j],dod:p,started:f.started,startedChanges:[...f.changes],orphanStatuses:[...new Set(T.map(r=>r.status).filter(r=>!(g??j).includes(r)))].sort(),templates:[...a.values()].sort((r,u)=>r.name.localeCompare(u.name)),cycles:wt(T),pending:k,rejected:y}}function V(t,n){if(t.has(n.entity))return;let e=n.data??{};t.set(n.entity,{id:n.entity,label:"",title:typeof e.title=="string"?e.title:"(untitled)",description:b(e.description),type:Q(e.type),priority:X(e.priority),status:G,labels:tt(e.labels),assignee:b(e.assignee),reporter:n.actor,sprint:null,milestone:null,parent:b(e.parent),blockedBy:[],due:b(e.due),claimedBy:null,claimedAt:null,claimedEventId:null,contestedBy:[],criteria:[],comments:[],docs:[],estimate:typeof e.estimate=="number"?e.estimate:null,loggedHours:0,createdAt:n.ts,updatedAt:n.ts,history:[]})}function J(t,n){if(t.has(n.entity))return;let e=n.data??{};t.set(n.entity,{id:n.entity,name:typeof e.name=="string"?e.name:"(untitled)",description:b(e.description),startDate:b(e.startDate),endDate:b(e.endDate),status:"planned",closedBy:null,taskIds:[]})}function Z(t,n){if(t.has(n.entity))return;let e=n.data??{};t.set(n.entity,{id:n.entity,label:"",name:typeof e.name=="string"?e.name:"(untitled)",due:b(e.due),status:"open",closedBy:null,taskIds:[],donePoints:0,totalPoints:0,doneTasks:0,totalTasks:0})}function mt(t,n,e,d,o,a,c){let s=t.data??{};if(t.type==="milestone.created")return Z(d,t),!0;if(t.type==="milestone.task_added"){let i=b(s.task);if(i===null)return!0;let g=n.get(i);return g===void 0?(a.push(t),!1):(g.milestone=t.entity,!0)}if(t.type==="milestone.closed"){let i=d.get(t.entity);return i===void 0?(a.push(t),!1):(i.closedBy===null?(i.status="closed",i.closedBy=t.id):o.push(t),!0)}if(t.type==="task.created")return V(n,t),q(n.get(t.entity),t),!0;if(t.type==="sprint.created")return J(e,t),!0;if(t.type.startsWith("sprint.")){let i=e.get(t.entity);if(i===void 0)return a.push(t),!1;if(t.type==="sprint.started")return i.status==="planned"&&(i.status="active"),!0;if(t.type==="sprint.updated")return i.status==="closed"?(o.push(t),!0):(typeof s.name=="string"&&(i.name=s.name),s.description!==void 0&&(i.description=b(s.description)),s.startDate!==void 0&&(i.startDate=b(s.startDate)),s.endDate!==void 0&&(i.endDate=b(s.endDate)),!0);if(t.type==="sprint.closed")return i.closedBy===null?(i.status="closed",i.closedBy=t.id):o.push(t),!0;if(t.type==="sprint.cancelled")return i.status==="planned"?i.status="cancelled":o.push(t),!0;if(t.type==="sprint.task_added"){if(i.status==="closed")return o.push(t),!0;let g=typeof s.task=="string"?s.task:null;if(g===null)return!0;let p=n.get(g);return p===void 0?(a.push(t),!1):(p.sprint=i.id,i.taskIds.includes(g)||i.taskIds.push(g),q(p,t),!0)}return!0}let l=n.get(t.entity);if(l===void 0)return a.push(t),!1;if(t.type==="task.deleted")return n.delete(t.entity),!0;switch(t.type){case"task.moved":{let i=s.to;typeof i=="string"&&i.length>0&&(l.status=i);break}case"task.cancelled":l.status=S;break;case"task.reopened":l.status=L(c.changes,t.id);break;case"task.assigned":l.assignee=b(s.assignee);break;case"task.updated":typeof s.title=="string"&&(l.title=s.title),s.description!==void 0&&(l.description=b(s.description)),s.type!==void 0&&(l.type=Q(s.type)),s.priority!==void 0&&(l.priority=X(s.priority)),s.labels!==void 0&&(l.labels=tt(s.labels)),s.due!==void 0&&(l.due=b(s.due)),typeof s.estimate=="number"&&(l.estimate=s.estimate);break;case"task.parent_set":l.parent=b(s.parent);break;case"task.blocked_by_added":{let i=b(s.blocker);i!==null&&!l.blockedBy.includes(i)&&l.blockedBy.push(i);break}case"task.blocked_by_removed":{let i=b(s.blocker);i!==null&&(l.blockedBy=l.blockedBy.filter(g=>g!==i));break}case"task.label_added":{let i=b(s.label);i!==null&&!l.labels.includes(i)&&l.labels.push(i);break}case"task.label_removed":{let i=b(s.label);i!==null&&(l.labels=l.labels.filter(g=>g!==i));break}case"task.criterion_added":{let i=b(s.text);i!==null&&l.criteria.push({id:t.id,n:0,text:i,checked:!1,checkedBy:null});break}case"task.criterion_checked":case"task.criterion_unchecked":break;case"task.doc_linked":{let i=b(s.path);i!==null&&!l.docs.includes(i)&&l.docs.push(i);break}case"task.time_logged":{let i=s.hours;typeof i=="number"&&Number.isFinite(i)&&(l.loggedHours=Math.max(0,l.loggedHours+i));break}case"task.commented":{let i=b(s.text);i!==null&&l.comments.push({id:t.id,author:t.actor,ts:t.ts,text:i,source:t.source});break}default:break}return q(l,t),!0}function bt(t){let n=new Map(t.criteria.map(e=>[e.id,e]));for(let e of t.criteria)e.checked=!1,e.checkedBy=null;for(let e of t.history){if(e.type!=="task.criterion_checked"&&e.type!=="task.criterion_unchecked")continue;let d=b(e.data.criterion),o=d===null?void 0:n.get(d);if(o===void 0)continue;let a=e.type==="task.criterion_checked";o.checked=a,o.checkedBy=a?e.actor:null}}function kt(t){let n=null,e=null,d=null,o=[],a=()=>{n=null,e=null,d=null,o=[]};for(let c of t.history)if(c.type==="task.claimed"){let s=b(c.data.by)??c.actor;n===null?(n=s,e=c.ts,d=c.id):s!==n&&!o.includes(s)&&o.push(s)}else if(c.type==="task.released"){let s=b(c.data.by)??c.actor;n===s?a():o=o.filter(l=>l!==s)}else(c.type==="task.cancelled"||c.type==="task.moved"&&c.data.to===x)&&a();t.claimedBy=n,t.claimedAt=e,t.claimedEventId=d,t.contestedBy=o}function q(t,n){t.history.push({id:n.id,type:n.type,actor:n.actor,ts:n.ts,source:n.source,data:n.data??{}}),n.ts>t.updatedAt&&(t.updatedAt=n.ts)}function b(t){return typeof t=="string"&&t.length>0?t:null}function Q(t){return gt.includes(t)?t:"task"}function X(t){return pt.includes(t)?t:"normal"}function tt(t){return Array.isArray(t)?t.filter(n=>typeof n=="string"&&n.length>0):[]}function wt(t){let n=new Map(t.map(a=>[a.id,a])),e=[],d=new Set,o=(a,c)=>{let s=new Map,l=(i,g)=>{let p=s.get(i);if(p==="done")return;if(p==="visiting"){let y=g.indexOf(i),m=g.slice(y),k=`${a}:${[...m].sort().join(",")}`;d.has(k)||(d.add(k),e.push({kind:a,path:[...m,i]}));return}s.set(i,"visiting");let f=n.get(i);if(f!==void 0)for(let y of c(f))l(y,[...g,i]);s.set(i,"done")};for(let i of t)l(i.id,[])};return o("parent",a=>a.parent===null?[]:[a.parent]),o("blocking",a=>a.blockedBy),e}var $t=["task","decision","note","doc","sprint","milestone"];var Tt=new Set(["a","an","and","are","as","at","be","but","by","can","did","do","does","for","from","had","has","have","how","i","if","in","is","it","its","of","on","or","our","so","than","that","the","their","them","then","there","these","they","this","to","was","we","were","what","when","where","which","who","why","will","with","would","you","your"]),xt=.2,St=.35,At=new RegExp("\\p{Cc}","gu");function N(t){return t.toLowerCase().match(/[\p{L}\p{N}](?:[\p{L}\p{N}_.-]*[\p{L}\p{N}])?/gu)??[]}function it(t){return t.replace(At," ").replace(/\s+/gu," ").trim()}function vt(t,n){let e=t.split(`
|
|
3
|
+
`),d=[],o=n,a=[],c=1,s=l=>{let i=a.join(`
|
|
4
|
+
`).trim();i.length>0&&d.push({heading:o,text:i,from:c,to:l})};for(let l=0;l<e.length;l++){let i=/^(#{1,6})\s+(.*)$/.exec(e[l]);i!==null?(s(l),o=i[2].trim(),a=[],c=l+1):a.push(e[l])}return s(e.length),d}function Dt(t){let n=[];for(let e of t.tasks){let d=[e.title,e.description??"",...e.criteria.map(o=>o.text),...e.comments.map(o=>o.text)];n.push({kind:"task",id:e.id,label:e.label,title:e.title,heading:e.title,subject:"",text:d.filter(o=>o.length>0).join(`
|
|
5
|
+
`),lines:null,at:e.history[0]?.ts??"",by:e.reporter,superseded:!1})}for(let e of t.decisions)n.push({kind:"decision",id:e.id,label:e.label,title:e.title,heading:e.title,subject:"",text:[e.title,e.why,e.rejected??""].filter(d=>d.length>0).join(`
|
|
6
|
+
`),lines:null,at:e.at,by:e.by,superseded:e.supersededBy!==null});for(let e of t.notes)n.push({kind:"note",id:e.id,label:null,title:it(e.text).slice(0,60),heading:"",subject:"",text:e.text,lines:null,at:e.at,by:e.by,superseded:!1});for(let e of t.documents)for(let d of vt(e.body,e.title))n.push({kind:"doc",id:e.id,label:e.label,title:e.title,heading:d.heading,subject:e.title,text:`${d.heading}
|
|
7
|
+
${d.text}`,lines:{from:d.from,to:d.to},at:e.updatedAt,by:e.updatedBy,superseded:!1});for(let e of t.sprints)n.push({kind:"sprint",id:e.id,label:null,title:e.name,heading:e.name,subject:"",text:[e.name,e.description??""].filter(d=>d.length>0).join(`
|
|
8
|
+
`),lines:null,at:"",by:"",superseded:!1});for(let e of t.milestones)n.push({kind:"milestone",id:e.id,label:e.label,title:e.name,heading:e.name,subject:"",text:e.name,lines:null,at:"",by:"",superseded:!1});return n}var et=1.2,nt=.45,st=3,rt=2;function Et(t,n){let e=new Map,d=new Array(t.length),o=new Float64Array(t.length),a=0;for(let c=0;c<t.length;c++){let s=new Map,l=0;for(let i of N(t[c].heading))l+=st,n.has(i)&&s.set(i,(s.get(i)??0)+st);for(let i of N(t[c].subject))l+=rt,n.has(i)&&s.set(i,(s.get(i)??0)+rt);for(let i of N(t[c].text))l+=1,n.has(i)&&s.set(i,(s.get(i)??0)+1);for(let i of s.keys())e.set(i,(e.get(i)??0)+1);d[c]=s,o[c]=l,a+=l}return{df:e,frequency:d,length:o,average:t.length===0?1:a/t.length,count:t.length}}function Mt(t){let e=N(t).filter(d=>!Tt.has(d));return e.length>0?e:[]}function Rt(t,n){let e=it(t),d=e.toLowerCase(),o=-1;for(let s of n){let l=d.indexOf(s);l>=0&&(o<0||l<o)&&(o=l)}if(o<0)return{text:e.slice(0,180),start:0,end:Math.min(180,e.length)};let a=Math.max(0,o-Math.floor(180/3)),c=Math.min(e.length,a+180);if(a>0){let s=e.indexOf(" ",a);s>=0&&s<o&&(a=s+1)}if(c<e.length){let s=e.lastIndexOf(" ",c);s>a&&(c=s)}return{text:e.slice(a,c),start:a,end:c}}function _t(t,n){let e=Et(t,n),d=s=>{let l=e.df.get(s)??0;return Math.log(1+(e.count-l+.5)/(l+.5))},o=new Map([...n].map(s=>[s,d(s)])),a=0;for(let s of o.values())a+=s;let c=new Map;for(let s=0;s<t.length;s++){let l=e.frequency[s];if(l.size===0)continue;let i=1-nt+nt*(e.length[s]/e.average),g=0,p=0;for(let[f,y]of l){let m=o.get(f);g+=m*(y*(et+1))/(y+et*i),p+=m}p>=a*St&&c.set(s,{score:g,coverage:a===0?1:p/a})}return c}function ot(t,n,e={}){let d=Mt(n);if(d.length===0)return[];let o=e.kinds===void 0?null:new Set(e.kinds),a=Dt(t).filter(f=>(o===null||o.has(f.kind))&&(e.all===!0||!f.superseded));if(a.length===0)return[];let c=new Set(d),s=_t(a,c);if(s.size===0)return[];let l=[...s.entries()].sort((f,y)=>y[1].score!==f[1].score?y[1].score-f[1].score:a[f[0]].id<a[y[0]].id?-1:1),i=l[0][1].score,g=e.every===!0?l.length:e.limit??5,p=e.every===!0?0:i*xt;return l.filter(([,f])=>f.score>=p).slice(0,g).map(([f,{score:y,coverage:m}])=>{let k=a[f];return{kind:k.kind,id:k.id,label:k.label,title:k.title,score:Math.round(y*100)/100,coverage:Math.round(m*100)/100,span:Rt(k.text,d),lines:k.lines,at:k.at,by:k.by}})}var D=864e5;function P(t){return new Date(t).toISOString().slice(0,10)}function E(t){return Date.parse(`${t}T00:00:00.000Z`)}function Ct(t){let n=new Date(E(t)),e=(n.getUTCDay()+6)%7;return P(n.getTime()-e*D)}function lt(t,n){let e=P(t.getTime());return{from:P(E(e)-(n-1)*D),to:e,days:n}}function at(t,n){let e=t.slice(0,10);return e>=n.from&&e<=n.to}function $(t,n){if(t.length===0)return 0;let e=Math.max(1,Math.ceil(n*t.length));return t[e-1]}var B=t=>Math.round(t*10)/10;function z(t){if(t.length===0)return null;let n=[...t].sort((e,d)=>e-d);return{shown:{n:n.length,p50:B($(n,.5)),p85:B($(n,.85)),p95:B($(n,.95))},rawP85:$(n,.85)}}function U(t,n){let e=t.indexOf(n);return e===-1?null:new Set(t.slice(0,e))}function Y(t,n,e=[],d=[]){let o=U(e,n),a=s=>s===x||s===S?!1:o===null?s===n:s===n||!o.has(s);return t.history.find(s=>s.type==="task.reopened"?a(L(d,s.id)):s.type==="task.moved"&&typeof s.data.to=="string"&&a(s.data.to))?.ts??null}function It(t,n){if(n.status===x||n.status===S)return!1;let e=U(t.statuses,t.started);return e===null?n.status===t.started:e.has(n.status)?!1:Y(n,t.started,t.statuses,t.startedChanges)!==null}function I(t){if(t.status!==x)return null;for(let n=t.history.length-1;n>=0;n--){let e=t.history[n];if(e.type==="task.moved"&&e.data.to===x)return e.ts}return null}function Bt(t){if(t.status!==S)return null;for(let n=t.history.length-1;n>=0;n--){let e=t.history[n];if(e.type==="task.cancelled"||e.type==="task.moved"&&e.data.to===S)return e.ts}return null}function O(t,n){return Math.max(0,(Date.parse(n)-Date.parse(t))/D)}function Pt(t,n,e,d){let o=I(t)??Bt(t),a=o===null?d:Date.parse(o),c=[],s=new Map;for(let y of t.history){let m=typeof y.data.blocker=="string"?y.data.blocker:null;if(m!==null){if(y.type==="task.blocked_by_added")s.has(m)||s.set(m,Date.parse(y.ts));else if(y.type==="task.blocked_by_removed"){let k=s.get(m);k!==void 0&&(c.push([k,Math.min(Date.parse(y.ts),a)]),s.delete(m))}}}for(let[y,m]of s){let k=Math.min(n.get(y)??d,a);c.push([m,Math.max(m,k)])}let l=E(e.from),i=E(e.to)+D,g=c.map(([y,m])=>[Math.max(y,l),Math.min(m,i)]).filter(([y,m])=>m>y).sort((y,m)=>y[0]-m[0]),p=0,f=null;for(let[y,m]of g)f===null||y>f[1]?(f!==null&&(p+=f[1]-f[0]),f=[y,m]):m>f[1]&&(f[1]=m);return f!==null&&(p+=f[1]-f[0]),p/D}function Ht(t,n=new Date,e=30){let d=lt(n,e),o=n.getTime(),a=[],c=t.tasks.filter(r=>r.status!==S),s=c.filter(r=>{let u=I(r);return u!==null&&at(u,d)}),l=[],i=[],g=[];for(let r of s){let u=I(r),h=Y(r,t.started,t.statuses,t.startedChanges);i.push(O(r.createdAt,u)),h!==null&&(l.push(O(h,u)),g.push(O(r.createdAt,h)))}let p=z(l),f=p?.shown??null,y=c.filter(r=>It(t,r)),m=y.map(r=>{let u=O(Y(r,t.started,t.statuses,t.startedChanges),n.toISOString());return{label:r.label,title:r.title,status:r.status,ageDays:B(u),overP85:p!==null&&u>p.rawP85}}).sort((r,u)=>u.ageDays-r.ageDays),k=new Map,T=r=>{let u=Ct(r),h=k.get(u);return h===void 0&&(h={week:u,finished:0,created:0},k.set(u,h)),h};for(let r=E(d.from);r<=E(d.to);r+=D)T(P(r));for(let r of s)T(I(r).slice(0,10)).finished+=1;for(let r of c)at(r.createdAt,d)&&(T(r.createdAt.slice(0,10)).created+=1);let M=[...k.values()].sort((r,u)=>r.week<u.week?-1:1),R=new Map;for(let r of t.tasks){let u=I(r);u!==null&&R.set(r.id,Date.parse(u))}let _=0,C=0;for(let r of c){let u=Pt(r,R,d,o);u>0&&(_+=1,C+=u)}t.tasks.length===0?a.push("No tasks yet."):(y.length===0&&s.length===0&&a.push(`Nothing has crossed "${t.started}" yet. If work starts in another column, say so: kadence board config --started <status>`),s.length===0?a.push(`Nothing finished between ${d.from} and ${d.to}; cycle time needs finished work.`):l.length===0&&a.push(`${s.length} item(s) finished without ever entering "${t.started}", so there is no cycle time to measure.`),t.statuses.includes(t.started)||a.push(`"${t.started}" is not one of the board's columns; set one with kadence board config --started.`));let K=f===null?null:`${f.n<10?`From ${f.n} finished items (a small sample): `:""}85% of finished items took ${f.p85} calendar days or less.`;return{window:d,started:t.started,unit:"calendar days",wip:y.length,finished:s.length,perWeek:M,cycleTime:f,leadTime:z(i)?.shown??null,responseTime:z(g)?.shown??null,sle:K,aging:m,blocked:{tasks:_,days:B(C)},notes:a}}function Wt(t,n=new Date,e=30){let d=lt(n,e),o=[...t.statuses];for(let l of t.tasks)o.includes(l.status)||o.push(l.status);let a=G;!o.includes(a)&&t.tasks.length>0&&o.unshift(a);let c=t.tasks.map(l=>{let i=[];for(let g of l.history)g.type==="task.moved"&&typeof g.data.to=="string"?i.push({at:Date.parse(g.ts),status:g.data.to}):g.type==="task.cancelled"?i.push({at:Date.parse(g.ts),status:S}):g.type==="task.reopened"&&i.push({at:Date.parse(g.ts),status:L(t.startedChanges,g.id)});return{createdAt:Date.parse(l.createdAt),changes:i,next:0,status:a}}),s=[];for(let l=E(d.from);l<=E(d.to);l+=D){let i=l+D,g={};for(let p of o)g[p]=0;for(let p of c){for(;p.next<p.changes.length&&p.changes[p.next].at<i;)p.status=p.changes[p.next].status,p.next+=1;p.createdAt>=i||(g[p.status]=(g[p.status]??0)+1)}s.push({date:P(l),counts:g})}return{window:d,statuses:o,days:s}}var H={urgent:0,high:1,normal:2,low:3};function dt(t,n,e=new Date){let d=e.toISOString().slice(0,10);return t.filter(o=>{if(n.status!==void 0&&o.status!==n.status||n.type!==void 0&&o.type!==n.type||n.priority!==void 0&&o.priority!==n.priority)return!1;if(n.assignee!==void 0){let a=n.assignee.toLowerCase(),c=(o.assignee??"").toLowerCase();if(a==="none"?c!=="":c!==a)return!1}if(n.label!==void 0){let a=n.label.toLowerCase();if(!o.labels.some(c=>c.toLowerCase()===a))return!1}return!(n.sprint!==void 0&&o.sprint!==n.sprint||n.overdue===!0&&(o.due===null||o.due>=d)||n.dueBefore!==void 0&&(o.due===null||o.due>=n.dueBefore))})}function Yt(t,n,e=new Date){let d=n.search?.trim()??"";if(d.length===0)return dt(t.tasks,n,e);let o=new Map(ot(t,d,{kinds:["task"],every:!0}).map((a,c)=>[a.id,c]));return dt(t.tasks.filter(a=>o.has(a.id)),n,e).sort((a,c)=>o.get(a.id)-o.get(c.id))}function Vt(t,n){let e=[...t];switch(n){case"priority":return e.sort((d,o)=>H[d.priority]-H[o.priority]);case"due":return e.sort((d,o)=>ut(d.due,o.due,(a,c)=>a<c?-1:a>c?1:0));case"estimate":return e.sort((d,o)=>ut(d.estimate,o.estimate,(a,c)=>c-a));default:return e.sort((d,o)=>d.id<o.id?-1:d.id>o.id?1:0)}}function ut(t,n,e){return t===null&&n===null?0:t===null?1:n===null?-1:e(t,n)}function Jt(t){let n=[];return t.search!==void 0&&n.push(`search="${t.search}"`),t.status!==void 0&&n.push(`status=${t.status}`),t.type!==void 0&&n.push(`type=${t.type}`),t.priority!==void 0&&n.push(`priority=${t.priority}`),t.assignee!==void 0&&n.push(`assignee=${t.assignee}`),t.label!==void 0&&n.push(`label=${t.label}`),t.overdue===!0&&n.push("overdue"),t.dueBefore!==void 0&&n.push(`due before ${t.dueBefore}`),n.length===0?`No tasks yet.
|
|
9
|
+
Create the first one:
|
|
10
|
+
kadence task add "title"`:`No tasks match ${n.join(" and ")}.
|
|
11
|
+
Try fewer filters:
|
|
12
|
+
kadence task list`}function ct(t,n){return t===null||n===null?!1:t===n?!0:!n.includes("#")&&t.startsWith(`${n}#`)}function jt(t){let n=t.indexOf("#");return n===-1?t:t.slice(0,n)}function ft(t){return U(t.statuses??j,t.started??F)}function W(t){return t.status===x||t.status===S}function Zt(t,n={}){let e=n.viewer??null,d=new Set(t.filter(W).map(s=>s.id)),o=n.assignee==="me"?e===null?null:jt(e):n.assignee??null,a=ft(n);return t.filter(s=>{if(W(s)||a!==null&&!a.has(s.status)||s.blockedBy.some(l=>!d.has(l))||s.claimedBy!==null&&!ct(s.claimedBy,e)&&s.contestedBy.length===0)return!1;if(o!==null){if(o==="none")return s.assignee===null;if(s.assignee!==o)return!1}return!0}).sort((s,l)=>H[s.priority]-H[l.priority]||(s.id<l.id?-1:1))}function Qt(t,n,e,d={}){if(t.length===0)return`No tasks yet.
|
|
13
|
+
Create the first one:
|
|
14
|
+
kadence task add "title"`;let o=t.filter(f=>!W(f));if(o.length===0)return"Nothing ready: every task is done or cancelled.";let a=new Set(t.filter(W).map(f=>f.id)),c=ft(d),s=c===null?0:o.filter(f=>!c.has(f.status)).length,l=o.filter(f=>f.blockedBy.some(y=>!a.has(y))).length,i=o.filter(f=>f.claimedBy!==null&&!ct(f.claimedBy,n)&&f.contestedBy.length===0).length,g=[];return s>0&&g.push(`${s} already started`),l>0&&g.push(`${l} blocked`),i>0&&g.push(`${i} claimed by someone else`),e!==void 0&&g.push(`filtered to assignee=${e}`),`Nothing ready${g.length>0?` (${g.join(", ")})`:""}.
|
|
15
|
+
See the whole board:
|
|
16
|
+
kadence task list`}var Ft=["created","priority","due","estimate"];function Xt(t){return Ft.includes(t)}export{j as a,x as b,S as c,gt as d,pt as e,Lt as f,$t as g,ot as h,Y as i,It as j,I as k,Ht as l,Wt as m,Yt as n,Vt as o,Jt as p,ct as q,W as r,Zt as s,Qt as t,Ft as u,Xt as v};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{$ as
|
|
2
|
+
import{$ as N,A as p,D as f,G as d,I as m,K as S,N as x,O as E,Q as y,S as B,V as A,aa as R,b as g,ba as F,c as b,ca as P,d as T,ga as U,ha as j,ia as O,w as h,y as k,z as C}from"./chunk-E7WLBKNZ.js";import"./chunk-IP426V3B.js";async function q(t,e){let i=h(t,e);if(!k(i))return i;if(process.stdout.isTTY!==!0)return{ok:!1,exitCode:1,message:`The interactive board needs a terminal.
|
|
3
3
|
For pipes and scripts use:
|
|
4
|
-
kadence board --json`};let c;try{c=await import("./board-
|
|
4
|
+
kadence board --json`};let c;try{c=await import("./board-2HUYLTND.js")}catch(r){return{ok:!1,exitCode:1,message:`The interactive board could not start: ${r.message}
|
|
5
5
|
The plain board always works:
|
|
6
6
|
kadence board`}}return c.runBoardUi({reload:()=>p(i.root,i.actor),actor:i.actor,branchEventIds:()=>{let r=g(i.root);if(r===null)return{reason:"detached"};let n=b(i.root),a=T(i.root,n,r);return a===null?{reason:"no-base",base:n}:{ids:a,name:r,base:n}},move:(r,n)=>o(f(t,e,r,n)),assign:(r,n)=>o(d(t,e,r,n)),create:r=>o(C(t,e,r,{})),remove:r=>o(S(t,e,r)),edit:r=>{if(!O(e,!0))return"No editor available.";let{state:n}=p(i.root,i.actor),a=n.tasks.find(u=>u.id===r);if(a===void 0)return"Task not found.";let s=j(e,a.description??"",`Editing the description of ${a.label}.`);return s.error!==null?s.error:s.text===null?"Aborted \u2014 nothing changed.":o(m(t,e,r,{description:s.text}))},comment:(r,n)=>o(B(t,e,r,n)),setField:(r,n,a)=>{let s=a.trim();if(n==="status")return o(f(t,e,r,s));if(n==="assignee")return o(d(t,e,r,s.length===0?"none":s));if(n==="estimate"){let u=Number(s);return!Number.isFinite(u)||u<0?"Estimate must be a positive number.":o(m(t,e,r,{estimate:u}))}if(n==="labels"){let u=s.split(",").map(l=>l.trim()).filter(l=>l.length>0);return o(m(t,e,r,{labels:u}))}return o(m(t,e,r,{[n]:s}))},logTime:(r,n)=>o(A(t,e,r,n)),setPriority:(r,n)=>o(m(t,e,r,{priority:n})),addToSprint:r=>o(N(t,e,r,{})),claim:r=>o(x(t,e,r,{})),release:r=>o(E(t,e,r)),toggleCriterion:(r,n,a)=>o(y(t,e,r,String(n),a)),sprintStatus:()=>F(t,e).message,burndown:()=>U(t,e,void 0).message,sprintStart:()=>o(P(t,e,void 0)),sprintClose:()=>o(R(t,e))}),{ok:!0,exitCode:0,message:""}}function o(t){return t.message.split(`
|
|
7
7
|
`)[0]??""}export{q as runUi};
|