kadence 0.4.1 → 0.6.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 +255 -0
- package/README.md +131 -87
- package/dist/chunks/board-5SQP5RX6.js +12 -0
- package/dist/chunks/chunk-CR5YON2J.js +10 -0
- package/dist/chunks/chunk-J7QEHKDW.js +248 -0
- package/dist/chunks/{ui-BZTPZE22.js → ui-OTWFK76P.js} +3 -3
- package/dist/cli.js +225 -125
- package/package.json +10 -8
- package/scripts/reference.mjs +120 -0
- package/dist/chunks/board-YUJ6VL5O.js +0 -12
- package/dist/chunks/chunk-CDGGOWGY.js +0 -10
- package/dist/chunks/chunk-FKPFXIL4.js +0 -224
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{a as ie,b as z,c as de,d as R,e as N,f as ce,g as le,i as ue,l as me,m as fe,n as he,p as pe,q as ge,r as X,s as ke}from"./chunk-CDGGOWGY.js";import{execFileSync as et}from"node:child_process";import{basename as tt}from"node:path";function I(e,r){try{return et("git",r,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}}function ye(e){let r=I(e,["rev-parse","--show-toplevel"]);return r===null||r.length===0?null:r}function be(e){let r=I(e,["config","user.email"]);return r===null||r.length===0?null:r}function we(e){let r=I(e,["rev-parse","--abbrev-ref","HEAD"]);return r===null||r.length===0||r==="HEAD"?null:r}function ve(e){if(nt(e,"main"))return"main";let r=I(e,["config","init.defaultBranch"]);return r!==null&&r.length>0?r:"main"}function nt(e,r){return I(e,["rev-parse","--verify","--quiet",`${r}^{commit}`])!==null}function $e(e,r,n){let t=I(e,["log",`${r}..${n}`,"--name-only","--pretty=format:","--",".kadence/events"]);if(t===null)return null;let s=new Set;for(let a of t.split(`
|
|
3
|
-
`)){let o=a.trim();o.endsWith(".json")&&s.add(tt(o,".json"))}return s}function Se(e){return`<!-- generated by kadence ${e} \u2014 refresh with \`kadence init\` -->`}function fn(e){return`${Se(e)}
|
|
4
|
-
# kadence \u2014 for AI agents
|
|
5
|
-
|
|
6
|
-
This project's tasks live here as plain files. Read them directly, or through
|
|
7
|
-
the CLI. No server required.
|
|
8
|
-
|
|
9
|
-
## Commands
|
|
10
|
-
|
|
11
|
-
kadence prime start here \u2014 the live state, in 40 lines
|
|
12
|
-
kadence ready --json what can be started right now
|
|
13
|
-
kadence task claim KAD-42 take it; no argument takes the top of ready
|
|
14
|
-
kadence board --json the whole board
|
|
15
|
-
kadence task list --json all tasks
|
|
16
|
-
kadence task show KAD-42 --json one task with its history
|
|
17
|
-
kadence task add "title" -d "..." --type bug --estimate 3
|
|
18
|
-
kadence task move KAD-42 in_progress
|
|
19
|
-
kadence task assign KAD-42 you@example.com
|
|
20
|
-
kadence sprint status --json current sprint progress
|
|
21
|
-
kadence note "text" --task KAD-42 something learned; a choice is a decision
|
|
22
|
-
|
|
23
|
-
## JSON contract
|
|
24
|
-
|
|
25
|
-
Every \`--json\` response carries \`schema: "kadence/v1"\`. stdout holds JSON and
|
|
26
|
-
nothing else; warnings go to stderr. Exit codes: 0 success, 1 runtime error,
|
|
27
|
-
2 bad arguments.
|
|
28
|
-
|
|
29
|
-
The contract itself is machine-readable \u2014 read it instead of guessing:
|
|
30
|
-
|
|
31
|
-
kadence schema --json
|
|
32
|
-
|
|
33
|
-
It lists every command, the fields you can rely on, and every error code. It
|
|
34
|
-
works outside a repository, so you can read it before \`init\`.
|
|
35
|
-
|
|
36
|
-
## When something fails
|
|
37
|
-
|
|
38
|
-
A failed \`--json\` call carries \`error.code\` \u2014 branch on that, not on the
|
|
39
|
-
sentence. Where the valid set is knowable it also carries \`allowed\`:
|
|
40
|
-
|
|
41
|
-
{ "ok": false,
|
|
42
|
-
"error": { "code": "unknown_status", "received": "shipped",
|
|
43
|
-
"allowed": ["backlog", "in_progress", "done"],
|
|
44
|
-
"hint": "kadence board statuses --json" } }
|
|
45
|
-
|
|
46
|
-
Statuses are configured per project, so \`allowed\` is the only reliable source \u2014
|
|
47
|
-
never assume the defaults. Retrying an identical command never helps: there is no
|
|
48
|
-
network and no lock, so the same input always fails the same way.
|
|
49
|
-
|
|
50
|
-
## Working as an agent
|
|
51
|
-
|
|
52
|
-
Set \`KADENCE_SOURCE=agent\` so events record your authorship. Without it an
|
|
53
|
-
event is marked as human \u2014 we do not guess.
|
|
54
|
-
|
|
55
|
-
## Why to ask rather than read the files
|
|
56
|
-
|
|
57
|
-
You cannot find a task by its label in the journal. \`KAD-3\` is derived while
|
|
58
|
-
folding and is never stored in an event, so \`grep -r "KAD-3" .kadence/events/\`
|
|
59
|
-
returns nothing. Folding the journal yourself is the only alternative to asking.
|
|
60
|
-
|
|
61
|
-
Asking is also cheaper, and stays cheap: \`task show --json\` is about 950 bytes
|
|
62
|
-
whether the project holds ten tasks or a thousand, while the journal behind it
|
|
63
|
-
grows past 500 KB.
|
|
64
|
-
|
|
65
|
-
\`board --json\` is the exception: it returns every task in full, which on a large
|
|
66
|
-
project is a lot of description you did not ask for. Narrow it:
|
|
67
|
-
|
|
68
|
-
kadence board --json --fields label,status,assignee
|
|
69
|
-
kadence task list --json --fields id,label,status
|
|
70
|
-
|
|
71
|
-
\`kadence schema --json\` lists every selectable field.
|
|
72
|
-
|
|
73
|
-
## Recording why
|
|
74
|
-
|
|
75
|
-
The journal holds what happened. \`decision\` holds why, which the code and
|
|
76
|
-
\`git log\` never carry:
|
|
77
|
-
|
|
78
|
-
kadence decision add "Use ULIDs" --why "Clocks disagree between machines" \\
|
|
79
|
-
--rejected "Auto-increment: collides across branches" --task KAD-1
|
|
80
|
-
|
|
81
|
-
\`--why\` is required: without it the record is a changelog line. \`--rejected\` is
|
|
82
|
-
what stops the next agent proposing the thing that was already turned down.
|
|
83
|
-
|
|
84
|
-
When a decision stops being true, do not edit it \u2014 supersede it:
|
|
85
|
-
|
|
86
|
-
kadence decision add "Use signed tokens" --why "..." --supersedes DEC-1
|
|
87
|
-
|
|
88
|
-
One event writes both directions. \`decision list\` then returns only what is
|
|
89
|
-
still in force; pass \`--all\` when you need the history too. **Read the default,
|
|
90
|
-
not \`--all\`** \u2014 a reversed reason quoted as current is worse than no memory.
|
|
91
|
-
|
|
92
|
-
## Documents
|
|
93
|
-
|
|
94
|
-
\`kadence task doc KAD-1 docs/design.md\` links a document to a task, and
|
|
95
|
-
\`task show --json\` returns the links. kadence does not store the document: it
|
|
96
|
-
stays plain markdown in the repository. The link is the part git cannot express.
|
|
97
|
-
|
|
98
|
-
## What not to do
|
|
99
|
-
|
|
100
|
-
Do not hand-edit files under \`.kadence/events/\`: the journal is appended to,
|
|
101
|
-
never modified. To correct the state, add a new event through the CLI.
|
|
102
|
-
`}var xe="<!-- kadence:begin -->",Q="<!-- kadence:end -->";function rt(e){return`${xe}
|
|
103
|
-
${Se(e)}
|
|
104
|
-
## Project tasks \u2014 kadence
|
|
105
|
-
|
|
106
|
-
Tasks live in \`.kadence/\` as plain files. Read them directly or via the CLI:
|
|
107
|
-
|
|
108
|
-
kadence prime start here: sprint, your work, what is ready
|
|
109
|
-
kadence board --json --summary the board's state, without the history
|
|
110
|
-
kadence board --json the whole board, history included
|
|
111
|
-
kadence task list --json all tasks
|
|
112
|
-
kadence task move KAD-1 done change state
|
|
113
|
-
|
|
114
|
-
kadence schema --json the contract: commands, fields, error codes
|
|
115
|
-
kadence decision list --json why the current choices were made
|
|
116
|
-
|
|
117
|
-
\`--json\` responses carry \`schema: "kadence/v1"\`; stdout is JSON only. A failure
|
|
118
|
-
carries \`error.code\` and, where knowable, \`allowed\`.
|
|
119
|
-
When acting as an agent, set \`KADENCE_SOURCE=agent\`.
|
|
120
|
-
|
|
121
|
-
Details: \`.kadence/README.md\`
|
|
122
|
-
${Q}`}function hn(e,r){let n=rt(r);if(e===null||e.trim().length===0)return`${n}
|
|
123
|
-
`;let t=e.indexOf(xe),s=e.indexOf(Q);if(t!==-1&&s!==-1&&s>t){let o=e.slice(0,t),i=e.slice(s+Q.length);return`${o}${n}${i}`}let a=e.endsWith(`
|
|
124
|
-
`)?`
|
|
125
|
-
`:`
|
|
126
|
-
|
|
127
|
-
`;return`${e}${a}${n}
|
|
128
|
-
`}var L=["id","label","title","description","type","priority","status","labels","assignee","reporter","sprint","milestone","loggedHours","parent","blockedBy","due","claimedBy","contestedBy","criteria","openCriteria","comments","estimate","history"],Ce=["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"],st={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.",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 pn(e){return{version:"kadence/v1",tool:{name:"kadence",version:e},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.',NO_COLOR:"Any value disables colour."},errors:Ce.map(r=>({code:r,meaning:st[r]})),shapes:{task:{required:["id","label","title","status","type","priority","estimate","assignee","blockedBy","loggedHours","claimedBy","contestedBy","labels"],selectable:[...L],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."}},board:{required:["schema","ok","columns"],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."}},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","ready","attention","attentionTotal","decisions","notes","commands"],notes:{mine:"Capped; `mineTotal` is the real count.",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."}},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:"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.",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:"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.",flags:["--since","--json"],json:!0},{name:"report cfd",summary:"Cumulative flow: tasks per column at the end of each day in the window.",flags:["--since","--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.",flags:["--since","--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}]}}import{isAbsolute as Bt,relative as It,dirname as Lt,resolve as Mt}from"node:path";import{existsSync as ut,mkdirSync as Ee,readdirSync as ee,readFileSync as V,renameSync as _e,rmSync as mt,writeFileSync as De}from"node:fs";import{join as _}from"node:path";var ot=["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"],at=new Set(ot),Te=/^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/,it=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})$/;function dt(e){return typeof e=="string"&&at.has(e)}function ct(e){if(typeof e!="object"||e===null)return["event"];let r=e,n=[];if((typeof r.id!="string"||!Te.test(r.id))&&n.push("id"),dt(r.type)||n.push("type"),(typeof r.entity!="string"||!Te.test(r.entity))&&n.push("entity"),(typeof r.actor!="string"||r.actor.length===0)&&n.push("actor"),(typeof r.ts!="string"||!it.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 t=r.data??{};for(let s of["title","why"]){let a=t[s];(typeof a!="string"||a.trim().length===0)&&n.push(`data.${s}`)}}return n}function je(e){let r=e.data?.description;if(typeof r!="string"||!r.includes(`
|
|
129
|
-
`))return JSON.stringify(e);let n={...e,data:{...e.data,description:r.split(`
|
|
130
|
-
`)}};return JSON.stringify(n,null,2)}function M(e){let r;try{r=JSON.parse(e)}catch(t){return{event:null,error:`unreadable JSON: ${t.message}`,unknownType:!1}}r=lt(r);let n=ct(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 lt(e){if(typeof e!="object"||e===null)return e;let r=e,n=r.data;if(typeof n!="object"||n===null)return e;let t=n.description;return Array.isArray(t)?{...r,data:{...n,description:t.filter(s=>typeof s=="string").join(`
|
|
131
|
-
`)}}:e}var ft=.2;function F(e){return _(e,".kadence")}function A(e){return _(F(e),"events")}function Z(e){return _(A(e),"archive")}function ht(e){return e.slice(0,7)}function S(e,r){let n=_(A(e),ht(r.ts));Ee(n,{recursive:!0});let t=_(n,`${r.id}.json`),s=_(n,`.${r.id}.tmp`);De(s,`${je(r)}
|
|
132
|
-
`,"utf8"),_e(s,t)}function K(e){let r=A(e),n=[],t=[],s=new Set,a=0,o=H(Z(e)),i=new Set(o);for(let c of H(r)){let l;try{l=V(c,"utf8")}catch{t.push(c);continue}if(i.has(c)){let g;try{g=JSON.parse(l)}catch{t.push(c);continue}if(!Array.isArray(g)){t.push(c);continue}for(let u of g){let h=M(JSON.stringify(u));h.unknownType?a++:h.error!==null||h.event===null?t.push(c):s.has(h.event.id)||(s.add(h.event.id),n.push(h.event))}continue}let m=M(l);if(m.unknownType){a++;continue}if(m.error!==null||m.event===null){t.push(c);continue}s.has(m.event.id)||(s.add(m.event.id),n.push(m.event))}n.sort((c,l)=>c.id<l.id?-1:c.id>l.id?1:0);let d=n.length+t.length;return{events:n,corrupted:t,unknownTypes:a,systemicCorruption:d>0&&t.length/d>ft}}function H(e){let r;try{r=ee(e,{withFileTypes:!0})}catch{return[]}let n=[];for(let t of r){let s=_(e,t.name);t.isDirectory()?n.push(...H(s)):t.name.endsWith(".json")&&n.push(s)}return n}function Re(e){if(!ut(e))return[];try{let r=JSON.parse(V(e,"utf8"));return Array.isArray(r)?r.map(n=>M(JSON.stringify(n)).event).filter(n=>n!==null):null}catch{return null}}function vn(e,r){let n=A(e),t=[],s=0,a;try{a=ee(n,{withFileTypes:!0})}catch{return{months:[],events:0}}for(let o of a){if(!o.isDirectory()||o.name==="archive"||o.name>=r)continue;let i=new Set((Re(_(Z(e),`${o.name}.json`))??[]).map(c=>c.id)),d=0;for(let c of H(_(n,o.name))){let l=M(V(c,"utf8")).event;l!==null&&!i.has(l.id)&&(d+=1)}d!==0&&(t.push({month:o.name,events:d}),s+=d)}return t.sort((o,i)=>o.month<i.month?-1:1),{months:t,events:s}}function $n(e,r){let n=A(e),t=[],s=[],a=0,o;try{o=ee(n,{withFileTypes:!0})}catch{return{archivedMonths:[],archivedEvents:0,skipped:[]}}for(let i of o){if(!i.isDirectory()||i.name==="archive"||i.name>=r)continue;let d=_(n,i.name),c=H(d),l=[];for(let y of c){let x=M(V(y,"utf8"));x.event!==null&&l.push(x.event)}if(l.length===0)continue;Ee(Z(e),{recursive:!0});let m=_(Z(e),`${i.name}.json`),g=Re(m);if(g===null){s.push(m);continue}let u=new Map;for(let y of g)u.set(y.id,y);let h=0;for(let y of l)u.has(y.id)||(h+=1),u.set(y.id,y);let f=[...u.values()].sort((y,x)=>y.id<x.id?-1:1),k=`${m}.tmp`;De(k,JSON.stringify(f),"utf8"),_e(k,m),mt(d,{recursive:!0,force:!0}),t.push(i.name),a+=h}return{archivedMonths:t.sort(),archivedEvents:a,skipped:s.sort()}}import{randomFillSync as pt}from"node:crypto";var Ae="0123456789ABCDEFGHJKMNPQRSTVWXYZ",gt=10,kt=16,Ne=2**48-1;function yt(e){let r="",n=e;for(let t=gt-1;t>=0;t--)r=Ae[n%32]+r,n=Math.floor(n/32);return r}function bt(e){let r="",n=0,t=0;for(let s of e){for(n=n<<8|s,t+=8;t>=5;)t-=5,r+=Ae[n>>>t&31];n&=(1<<t)-1}return r.slice(0,kt)}function wt(e){for(let r=e.length-1;r>=0;r--){let n=e[r];if(n<255){e[r]=n+1;return}e[r]=0}throw new Error("ULID: random space exhausted within a single millisecond")}function vt(){let e=-1,r=new Uint8Array(10);return function(t=Date.now()){if(!Number.isInteger(t)||t<0||t>Ne)throw new RangeError(`ULID: time out of range 0..${Ne}: ${t}`);return t>e?(e=t,pt(r)):wt(r),yt(e)+bt(r)}}var $=vt();import{readFileSync as $t,writeFileSync as St,renameSync as xt,readdirSync as Ct,mkdirSync as Tt}from"node:fs";import{join as Pe}from"node:path";var Oe="kadence-snapshot/11";function Be(e){return Pe(F(e),"state.json")}function Ie(e,r){let n=jt(A(e)),t=Et(e);if(t!==null&&t.version===Oe&&t.lastEventId===n.lastEventId&&t.eventCount===n.count)return{state:t.state,fromCache:!0,incomingEvents:0};let s=K(e),a=ce(s.events),o=t===null||r===void 0?0:Dt(s.events,t,r);return _t(e,{version:Oe,lastEventId:n.lastEventId,eventCount:n.count,state:a}),{state:a,fromCache:!1,incomingEvents:o}}function jt(e){let r="",n=0,t=s=>{let a;try{a=Ct(s,{withFileTypes:!0})}catch{return}for(let o of a)if(o.isDirectory())t(Pe(s,o.name));else if(o.name.endsWith(".json")){n++;let i=o.name.slice(0,-5);i>r&&(r=i)}};return t(e),{lastEventId:r,count:n}}function Et(e){try{let r=JSON.parse($t(Be(e),"utf8"));if(typeof r!="object"||r===null)return null;let n=r;return typeof n.version!="string"||typeof n.lastEventId!="string"||typeof n.eventCount!="number"||typeof n.state!="object"?null:n}catch{return null}}function _t(e,r){try{Tt(F(e),{recursive:!0});let n=Be(e),t=`${n}.tmp`;St(t,JSON.stringify(r),"utf8"),xt(t,n)}catch{}}function Dt(e,r,n){let t=0,s=0;for(let o of e)o.id<=r.lastEventId?t++:o.actor!==n&&s++;let a=Math.max(0,t-r.eventCount);return s+a}var C={dim:"\x1B[2m",bold:"\x1B[1m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",reset:"\x1B[0m"};function re(e,r){return e.NO_COLOR!==void 0?!1:r}function D(e,r,n){return n?`${r}${e}${C.reset}`:e}var Rt={done:C.green,in_progress:C.blue,blocked:C.yellow,cancelled:C.dim},Nt=72;function Me(e,r=Nt){let n=[...e];return n.length<=r?e:`${n.slice(0,r-1).join("")}\u2026`}function ne(e){return[...e].length}function Le(e,r){let n=r-ne(e);return n>0?e+" ".repeat(n):e}function Fe(e,r){if(e.length===0)return`No tasks yet.
|
|
133
|
-
Create the first one:
|
|
134
|
-
kadence task add "title"`;let n=Math.max(...e.map(s=>ne(s.label)),2),t=Math.max(...e.map(s=>ne(s.status)),6);return e.map(s=>{let a=D(Le(s.label,n),C.bold,r),o=D(Le(s.status,t),Rt[s.status]??C.dim,r),i=s.estimate===null?"":D(` (${s.estimate})`,C.dim,r);return`${a} ${o} ${Me(s.title)}${i}`}).join(`
|
|
135
|
-
`)}function te(e){return e==="agent"?" [agent]":""}function Ke(e){return e<=0?null:`Merged ${e} ${e===1?"change":"changes"} from another branch, no conflicts.`}var At={urgent:"!!",high:"!",normal:"",low:"v"},Ot={bug:"BUG",story:"STORY",task:""};function Ue(e,r){let n=[D(e.label,C.bold,r)],t=e.criteria.filter(i=>!i.checked).length;t>0&&n.push(D(`[${e.criteria.length-t}/${e.criteria.length}]`,C.yellow,r));let s=At[e.priority];s!==""&&n.push(D(s,e.priority==="low"?C.dim:C.yellow,r));let a=Ot[e.type]??"";a!==""&&n.push(D(a,C.dim,r)),n.push(Me(e.title));let o=[];return e.assignee!==null&&o.push(`@${e.assignee.split("@")[0]}`),e.estimate!==null&&o.push(`${e.estimate}`),o.length>0&&n.push(D(`(${o.join(", ")})`,C.dim,r)),` ${n.join(" ")}`}function Rn(e,r){if(Object.values(e).reduce((s,a)=>s+a.length,0)===0)return`No tasks yet.
|
|
136
|
-
Create the first one:
|
|
137
|
-
kadence task add "title"`;let t=[];for(let[s,a]of Object.entries(e)){if(a.length===0)continue;let o=a.reduce((d,c)=>d+(c.estimate??0),0),i=o>0?` \u2014 ${o}`:"";t.push(D(`${s} (${a.length}${i})`,C.bold,r),...a.map(d=>Ue(d,r)),"")}return t.join(`
|
|
138
|
-
`).trimEnd()}function Pt(e,r=new Date){let n=Date.parse(`${e}T00:00:00.000Z`),t=Date.parse(`${r.toISOString().slice(0,10)}T00:00:00.000Z`);if(Number.isNaN(n))return"";let s=Math.round((n-t)/864e5);return s<0?` (overdue by ${-s} day${s===-1?"":"s"})`:s===0?" (today)":s===1?" (tomorrow)":s<=7?` (in ${s} days)`:""}function Je(e,r){if(e.length===0)return`No tasks yet.
|
|
139
|
-
Create the first one:
|
|
140
|
-
kadence task add "title"`;let n=new Set(e.map(i=>i.id)),t=new Map;for(let i of e){let d=i.parent!==null&&n.has(i.parent)?i.parent:null,c=t.get(d)??[];c.push(i),t.set(d,c)}let s=[],a=new Set,o=(i,d)=>{if(!(d>10))for(let c of t.get(i)??[]){if(a.has(c.id))continue;a.add(c.id);let l=" ".repeat(d),m=c.blockedBy.length>0?D(" [blocked]",C.yellow,r):"";s.push(`${l}${Ue(c,r).trimStart()}${m}`),o(c.id,d+1)}};return o(null,0),s.join(`
|
|
141
|
-
`)}function We(e,r=[]){let n=[`${e.label} ${e.title}`,""];if(e.description!==null&&n.push(e.description,""),n.push(` Status: ${e.status}`),n.push(` Type: ${e.type}`),n.push(` Priority: ${e.priority}`),e.labels.length>0&&n.push(` Labels: ${e.labels.join(", ")}`),e.parent!==null&&n.push(` Parent: ${e.parent}`),e.blockedBy.length>0&&n.push(` Blocked by: ${e.blockedBy.length} task(s)`),n.push(` Assignee: ${e.assignee??"\u2014"}`),e.claimedBy!==null){let t=e.contestedBy.length>0?` (contested by ${e.contestedBy.join(", ")})`:"";n.push(` Claimed: ${e.claimedBy}${t}`)}if(n.push(` Reporter: ${e.reporter}`),e.due!==null&&n.push(` Due: ${e.due}${Pt(e.due)}`),n.push(` Estimate: ${e.estimate??"\u2014"}`),e.comments.length>0){n.push("",` Comments (${e.comments.length}):`);for(let t of e.comments){n.push(` ${t.author}${te(t.source)} \xB7 ${t.ts.slice(0,10)}`);for(let s of t.text.split(`
|
|
142
|
-
`))n.push(` ${s}`)}}if(e.criteria.length>0){let t=e.criteria.filter(s=>s.checked).length;n.push("",` Acceptance criteria (${t}/${e.criteria.length}):`);for(let s of e.criteria)n.push(` ${s.n}. [${s.checked?"x":" "}] ${s.text}`)}if(r.length>0){n.push("",` Notes (${r.length}):`);for(let t of r){n.push(` ${t.by}${te(t.source)} \xB7 ${t.at.slice(0,10)}`);for(let s of t.text.split(`
|
|
143
|
-
`))n.push(` ${s}`)}}if(e.history.length>0){n.push("",` History (${e.history.length}):`);for(let t of e.history){let s=t.type==="task.moved"?` \u2192 ${String(t.data.to)}`:"";n.push(` ${t.ts.slice(0,16).replace("T"," ")} ${t.actor}${te(t.source)} ${t.type}${s}`)}}return n.join(`
|
|
144
|
-
`)}import{existsSync as He,mkdirSync as Ft,writeFileSync as Kt}from"node:fs";var Wn=ie;function p(e,r,n,t={}){return{ok:!1,exitCode:e,message:n,error:{code:r,message:n,...t}}}function E(e){return p(1,"task_not_found",`No task ${e}.
|
|
145
|
-
kadence task list`,{received:e,hint:"kadence task list --json"})}function U(e,r,n,t){return p(2,e,`Unknown ${r} "${n}".
|
|
146
|
-
Available: ${t.join(", ")}`,{received:n,allowed:t})}function w(e,r){let n=ye(e);if(n===null)return p(1,"not_a_repository",`kadence lives inside a git repository, and there is none here.
|
|
147
|
-
Create one and try again:
|
|
148
|
-
git init`,{hint:"git init"});if(!He(F(n)))return p(1,"not_initialised",`No .kadence/ found here.
|
|
149
|
-
Run:
|
|
150
|
-
npx kadence init`,{hint:"kadence init"});let t=be(n);if(t===null)return p(1,"no_git_identity",`Git does not know who you are, so there is no author for the event.
|
|
151
|
-
Run:
|
|
152
|
-
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:t,source:s}}function v(e){return"root"in e}function Hn(e,r,n,t={}){let s=w(e,r);if(!v(s))return s;let a=n.trim();if(a.length===0)return p(2,"invalid_argument","A task needs a title.",{hint:"kadence task list --json"});if(t.type!==void 0&&!R.includes(t.type))return U("unknown_type","type",t.type,R);if(t.priority!==void 0&&!N.includes(t.priority))return U("unknown_priority","priority",t.priority,N);let o,i=null;if(t.parent!==void 0){let{state:u}=b(s.root,s.actor);i=u;let h=j(u,t.parent);if(h===void 0)return{...E(t.parent)};o=h.id}let d=$(),c={id:d,type:"task.created",entity:d,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{title:a,...t.description!==void 0?{description:t.description}:{},...t.type!==void 0?{type:t.type}:{},...t.priority!==void 0?{priority:t.priority}:{},...t.assignee!==void 0?{assignee:t.assignee}:{},...t.labels!==void 0&&t.labels.length>0?{labels:t.labels}:{},...t.due!==void 0?{due:t.due}:{},...o!==void 0?{parent:o}:{},...t.estimate!==void 0?{estimate:t.estimate}:{}}};S(s.root,c);let l=t.noDod===!0?[]:(i??b(s.root,s.actor).state).dod;for(let u of l)S(s.root,{id:$(),type:"task.criterion_added",entity:d,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:u}});let m=t.estimate===void 0?`
|
|
153
|
-
Without an estimate this task will not count towards velocity. Add --estimate.`:"",g=l.length===0?"":`
|
|
154
|
-
${l.length} acceptance criteria from the board's definition of done.`;return{ok:!0,exitCode:0,message:`Created: ${a}${g}${m}`,data:{schema:"kadence/v1",ok:!0,task:{id:d,title:a},criteria:l.map((u,h)=>({n:h+1,text:u,checked:!1,checkedBy:null}))}}}function b(e,r){let n=Ie(e,r),t=K(e),s=[],a=Ke(n.incomingEvents);return a!==null&&s.push(a),t.systemicCorruption?s.push(`The journal is badly damaged: ${t.corrupted.length} of ${t.corrupted.length+t.events.length} events are unreadable. Try: git checkout .kadence/`):t.corrupted.length>0&&s.push(`Skipped ${t.corrupted.length} corrupted event(s).`),t.unknownTypes>0&&s.push(`Skipped ${t.unknownTypes} event(s) from a newer format. Update kadence.`),{state:n.state,warnings:s}}function j(e,r){let n=r.toUpperCase();return e.tasks.find(t=>t.id===n||t.label===n)}function qe(e,r){return p(2,"unknown_status",`Unknown status "${e}".
|
|
155
|
-
Available: ${r.join(", ")}
|
|
156
|
-
kadence board config --statuses "todo,doing,done"`,{received:e,allowed:r,hint:"kadence board config --json"})}function qn(e,r,n){let t=w(e,r);if(!v(t))return t;if(n.type!==void 0&&!R.includes(n.type))return U("unknown_type","type",n.type,R);if(n.priority!==void 0&&!N.includes(n.priority))return U("unknown_priority","priority",n.priority,N);let{fields:s,error:a}=Ut(n.fields);if(a!==null)return a;if(n.sort!==void 0&&!ke(n.sort))return{...p(2,"invalid_argument",`Unknown sort key "${n.sort}".
|
|
157
|
-
Available: ${X.join(", ")}`,{received:n.sort,allowed:X})};let{state:o,warnings:i}=b(t.root,t.actor);if(n.status!==void 0&&!o.statuses.includes(n.status))return qe(n.status,o.statuses);let{sort:d,tree:c,branch:l,base:m,json:g,...u}=n,h=me(o.tasks,u),f=null;if(l===!0){let y=we(t.root);if(y===null)return p(1,"conflicting_state",`HEAD is detached, so there is no branch to compare.
|
|
158
|
-
Check one out, or name the range yourself:
|
|
159
|
-
git checkout main`,{hint:"git checkout main"});let x=m??ve(t.root),B=$e(t.root,x,y);if(B===null)return p(2,"invalid_argument",`There is no branch "${x}" to compare against.
|
|
160
|
-
Name one that exists:
|
|
161
|
-
kadence task list --branch --base <name>`,{received:x,hint:"git branch --list"});f={name:y,base:x},h=h.filter(ae=>B.has(ae.id)||ae.history.some(Qe=>B.has(Qe.id)))}let k=d===void 0?h:fe(h,d);return k.length===0?{ok:!0,exitCode:0,warnings:i,message:f===null?he(u):`Branch ${f.name} has introduced no task changes since ${f.base}.
|
|
162
|
-
See the whole board:
|
|
163
|
-
kadence task list`,data:{schema:"kadence/v1",ok:!0,tasks:[],...f===null?{}:{branch:f}}}:{ok:!0,exitCode:0,warnings:i,message:c===!0?Je(k,re(r,process.stdout.isTTY===!0)):Fe(k,re(r,process.stdout.isTTY===!0)),data:{schema:"kadence/v1",ok:!0,tasks:k.map(y=>Ye(y,s,o)),cycles:o.cycles,...f===null?{}:{branch:f}}}}function Yn(e,r,n,t){let s=w(e,r);if(!v(s))return s;let{state:a,warnings:o}=b(s.root,s.actor);if(!a.statuses.includes(t))return qe(t,a.statuses);let{tasks:i,error:d}=O(a,n);if(d!==null)return d;let c=[],l=[],m="";for(let h of i){if(h.status===t){l.push(h);continue}let f=h.status;S(s.root,{id:$(),type:"task.moved",entity:h.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{from:f,to:t}}),c.push(h),i.length===1&&a.statuses.indexOf(t)-a.statuses.indexOf(f)>1&&(m=`
|
|
164
|
-
Moved straight from ${f} to ${t}, skipping the stages between.`)}if(c.length===0)return{ok:!0,exitCode:0,warnings:o,message:T("already there",l,`is already ${t}.`)};let g=l.length>0?`
|
|
165
|
-
${l.length} already ${t}.`:"",u=t===z?c.filter(h=>h.criteria.some(f=>!f.checked)).map(h=>{let f=h.criteria.filter(k=>!k.checked).length;return`${h.label} moved to ${t} with ${f} of ${h.criteria.length} acceptance criteria unchecked. See: kadence task ac list ${h.label}`}):[];return{ok:!0,exitCode:0,warnings:[...o,...u],message:`${T("moved",c,`${c[0].status} \u2192 ${t}`)}${m}${g}`,data:{schema:"kadence/v1",ok:!0,moved:c.map(h=>({id:h.id,label:h.label,to:t}))}}}function Ut(e){if(e===void 0)return{fields:null,error:null};let r=e.split(",").map(t=>t.trim()).filter(t=>t.length>0);if(r.length===0)return{fields:null,error:p(2,"invalid_argument","--fields needs at least one field name.",{allowed:L})};let n=r.find(t=>!L.includes(t));return n!==void 0?{fields:null,error:p(2,"unknown_field",`Unknown field "${n}".
|
|
166
|
-
Available: ${L.join(", ")}`,{received:n,allowed:L})}:{fields:r,error:null}}function Ye(e,r=null,n=null){let t=Jt(e,n);return r===null?t:Object.fromEntries(r.map(s=>[s,t[s]]))}function Jt(e,r=null){return{id:e.id,label:e.label,title:e.title,description:e.description,type:e.type,priority:e.priority,status:e.status,labels:e.labels,assignee:e.assignee,reporter:e.reporter,sprint:e.sprint,milestone:e.milestone===null?null:r?.milestones.find(n=>n.id===e.milestone)?.label??null,loggedHours:e.loggedHours,parent:e.parent,blockedBy:e.blockedBy,due:e.due,claimedBy:e.claimedBy,contestedBy:e.contestedBy,comments:e.comments,docs:e.docs,estimate:e.estimate,criteria:e.criteria.map(n=>({n:n.n,text:n.text,checked:n.checked,checkedBy:n.checkedBy})),openCriteria:e.criteria.filter(n=>!n.checked).length,history:e.history}}function Gn(e,r,n,t){let s=w(e,r);if(!v(s))return s;let{state:a,warnings:o}=b(s.root,s.actor),{tasks:i,error:d}=O(a,n);if(d!==null)return d;let c=t.trim().toLowerCase()==="none"?null:t.trim(),l=i.filter(m=>m.assignee!==c);if(l.length===0)return{ok:!0,exitCode:0,warnings:o,message:c===null?T("already unassigned",i,"is already unassigned."):T("already assigned",i,`is already assigned to ${c}.`)};for(let m of l)S(s.root,{id:$(),type:"task.assigned",entity:m.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{assignee:c}});return{ok:!0,exitCode:0,warnings:o,message:c===null?T("unassigned",l,"unassigned."):T("assigned",l,`\u2192 ${c}`),data:{schema:"kadence/v1",ok:!0,assigned:l.map(m=>({id:m.id,label:m.label,assignee:c}))}}}function Zn(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),o=j(s,n);if(o===void 0)return E(n);let i=s.decisions.filter(c=>c.task===o.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})),d=s.notes.filter(c=>c.task===o.id);return{ok:!0,exitCode:0,warnings:a,message:We(o,d),data:{schema:"kadence/v1",ok:!0,task:{...Ye(o,null,s),decisions:i,notes:d.map(c=>({id:c.id,text:c.text,at:c.at,by:c.by,source:c.source}))}}}}function Wt(e,r){let n=a=>a.map(o=>o.trim()).filter(o=>o.length>0),t=r.labels!==void 0?[...new Set(n(r.labels))]:[...new Set([...e.labels.filter(a=>!n(r.removeLabels??[]).includes(a)),...n(r.addLabels??[])])],s=[];for(let a of e.labels)t.includes(a)||s.push({type:"task.label_removed",label:a});for(let a of t)e.labels.includes(a)||s.push({type:"task.label_added",label:a});return s}function Vn(e,r,n,t){let s=w(e,r);if(!v(s))return s;if(t.type!==void 0&&!R.includes(t.type))return U("unknown_type","type",t.type,R);if(t.priority!==void 0&&!N.includes(t.priority))return U("unknown_priority","priority",t.priority,N);if(t.due!==void 0&&t.due!==""&&!Ht(t.due))return{...p(2,"invalid_argument",`Due date must be YYYY-MM-DD, got "${t.due}".
|
|
167
|
-
kadence task edit KAD-1 --due 2026-09-30`,{received:t.due})};if(t.labels!==void 0&&(t.addLabels!==void 0||t.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 a=(t.addLabels??[]).filter(u=>(t.removeLabels??[]).includes(u));if(a.length>0)return p(2,"invalid_argument",`A label cannot be added and removed in one edit: ${a.join(", ")}.`,{received:a.join(","),hint:"kadence task edit KAD-1 --add-label impact-high"});let{state:o,warnings:i}=b(s.root,s.actor),{tasks:d,error:c}=O(o,n);if(c!==null)return c;if(t.title!==void 0&&d.length>1)return{...p(2,"invalid_argument","A title can only be set on one task at a time.")};let l=[],m=new Set;for(let u of d){let h={},f=[];t.title!==void 0&&t.title!==u.title&&(h.title=t.title,f.push("title")),t.description!==void 0&&t.description!==(u.description??"")&&(h.description=t.description,f.push("description")),t.type!==void 0&&t.type!==u.type&&(h.type=t.type,f.push("type")),t.priority!==void 0&&t.priority!==u.priority&&(h.priority=t.priority,f.push("priority")),t.due!==void 0&&t.due!==(u.due??"")&&(h.due=t.due,f.push("due")),t.estimate!==void 0&&t.estimate!==u.estimate&&(h.estimate=t.estimate,f.push("estimate"));let k=t.labels===void 0&&t.addLabels===void 0&&t.removeLabels===void 0?[]:Wt(u,t);if(k.length>0&&f.push("labels"),f.length!==0){Object.keys(h).length>0&&S(s.root,{id:$(),type:"task.updated",entity:u.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:h});for(let y of k)S(s.root,{id:$(),type:y.type,entity:u.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{label:y.label}});l.push(u);for(let y of f)m.add(y)}}if(l.length===0)return{ok:!0,exitCode:0,warnings:i,message:T("unchanged",d,"nothing changed.")};let g=[...m].join(", ");return{ok:!0,exitCode:0,warnings:i,message:T("updated",l,`updated ${g}.`),data:{schema:"kadence/v1",ok:!0,updated:l.map(u=>({id:u.id,label:u.label})),changed:[...m]}}}function Ht(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;let r=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(r.getTime())&&r.toISOString().slice(0,10)===e}function zn(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),{tasks:o,error:i}=O(s,n);if(i!==null)return i;let d=o.filter(c=>c.status!=="cancelled");if(d.length===0)return{ok:!0,exitCode:0,warnings:a,message:T("already cancelled",o,"is already cancelled.")};for(let c of d)S(t.root,{id:$(),type:"task.cancelled",entity:c.id,actor:t.actor,ts:new Date().toISOString(),source:t.source,data:{}});return{ok:!0,exitCode:0,warnings:a,message:`${T("cancelled",d,"cancelled.")}
|
|
168
|
-
Cancelled work stays in history and does not count as missed.`,data:{schema:"kadence/v1",ok:!0,cancelled:d.map(c=>({id:c.id,label:c.label}))}}}function Xn(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),{tasks:o,error:i}=O(s,n);if(i!==null)return i;for(let c of o)S(t.root,{id:$(),type:"task.deleted",entity:c.id,actor:t.actor,ts:new Date().toISOString(),source:t.source,data:{title:c.title}});let d=o.length===1?`${o[0].label} deleted: "${o[0].title}".`:`${o.length} tasks deleted: ${o.map(c=>c.label).join(", ")}.`;return{ok:!0,exitCode:0,warnings:a,message:`${d}
|
|
169
|
-
The events stay in the journal \u2014 history is never rewritten.`,data:{schema:"kadence/v1",ok:!0,deleted:o.map(c=>({id:c.id,label:c.label}))}}}function qt(e,r){return[`# ${e}`,"",`Written for ${r}. Link it back with \`kadence task show ${r}\`.`,"",""].join(`
|
|
170
|
-
`)}function Yt(e,r,n){let t=r.trim();if(t.length===0)return p(2,"invalid_argument","A path is required.",{hint:n});let s=Mt(e,t),a=It(e,s);return a.length===0||a.startsWith("..")||Bt(a)?p(2,"invalid_argument",`${r} is outside the repository.
|
|
171
|
-
Use a path inside it:
|
|
172
|
-
${n}`,{received:r,hint:n}):{rel:a,full:s}}function Qn(e,r,n,t,s=!1){let a=w(e,r);if(!v(a))return a;if(t.trim().length===0)return p(2,"invalid_argument","A link needs a path.",{hint:"kadence task doc KAD-1 docs/design.md"});let{state:o,warnings:i}=b(a.root,a.actor),d=j(o,n);if(d===void 0)return E(n);let c=Yt(a.root,t,"kadence task doc KAD-1 docs/design.md");if("exitCode"in c)return c;let{rel:l,full:m}=c,g=!He(m),u=!1;return s&&g&&(Ft(Lt(m),{recursive:!0}),Kt(m,qt(d.title,d.label),"utf8"),g=!1,u=!0),S(a.root,{id:$(),type:"task.doc_linked",entity:d.id,actor:a.actor,ts:new Date().toISOString(),source:a.source,data:{path:l}}),{ok:!0,exitCode:0,warnings:[...i,...g?[`No file at ${l} \u2014 the link is recorded anyway; it may arrive later.`]:[]],message:u?`Created ${l} and linked it to ${d.label}.`:`${l} linked to ${d.label}.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label},doc:l,created:u}}}function er(e,r,n,t={}){let s=w(e,r);if(!v(s))return s;let{state:a,warnings:o}=b(s.root,s.actor),i;if(n===void 0||n.trim().length===0){let l=t.assignee===void 0?{}:{assignee:t.assignee},m={statuses:a.statuses,started:a.started},g=pe(a.tasks,{viewer:s.actor,...m,...l});if(i=g.find(u=>u.claimedBy===null||u.claimedBy===s.actor),i===void 0){let u=g.filter(h=>h.claimedBy!==null);if(u.length>0){let h=u.map(f=>`${f.label} (${f.claimedBy})`).join(", ");return p(1,"nothing_ready",`Nothing free to claim. ${u.length===1?"The one task":`All ${u.length} tasks`} ready for you ${u.length===1?"is":"are"} already contested: ${h}.
|
|
173
|
-
Name one to join the contest on purpose, or pick something else:
|
|
174
|
-
kadence task claim ${u[0].label}
|
|
175
|
-
kadence task list`,{hint:`kadence task claim ${u[0].label}`})}return p(1,"nothing_ready",ge(a.tasks,s.actor,t.assignee,m),{hint:"kadence task list"})}}else if(i=j(a,n),i===void 0)return E(n);if(i.status===z||i.status===de)return p(1,"conflicting_state",`${i.label} is ${i.status}, so there is nothing to claim.
|
|
176
|
-
Reopen it first:
|
|
177
|
-
kadence task move ${i.label} in_progress`,{hint:`kadence task move ${i.label} in_progress`});if(i.claimedBy===s.actor||i.contestedBy.includes(s.actor))return{ok:!0,exitCode:0,warnings:o,message:i.claimedBy===s.actor?`${i.label} is already yours.`:`${i.label} already carries your contested claim; ${i.claimedBy??"nobody"} holds it.`,data:{schema:"kadence/v1",ok:!0,task:{id:i.id,label:i.label,claimedBy:i.claimedBy,contestedBy:i.contestedBy}}};let d=i.claimedBy;S(s.root,{id:$(),type:"task.claimed",entity:i.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{by:s.actor}});let c=d===null?[]:[`${i.label} is contested: ${d} claimed it first and keeps it. Both claims stay in the journal \u2014 talk to them before starting.`];return{ok:!0,exitCode:0,warnings:[...o,...c],message:d===null?`${i.label} ${i.title} \u2014 claimed by ${s.actor}.
|
|
178
|
-
Another machine may have claimed it before your push; the merge will show that.`:`${i.label} claim recorded, contested with ${d}.`,data:{schema:"kadence/v1",ok:!0,task:{id:i.id,label:i.label,claimedBy:d??s.actor,contestedBy:d===null?[]:[...i.contestedBy,s.actor]}}}}function tr(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),o=j(s,n);if(o===void 0)return E(n);let i=o.claimedBy===t.actor,d=o.contestedBy.includes(t.actor);return!i&&!d?{ok:!0,exitCode:0,warnings:a,message:o.claimedBy===null?`${o.label} is not claimed.`:`${o.label} is claimed by ${o.claimedBy}, not by you. Nothing released.`,data:{schema:"kadence/v1",ok:!0,task:{id:o.id,label:o.label,claimedBy:o.claimedBy}}}:(S(t.root,{id:$(),type:"task.released",entity:o.id,actor:t.actor,ts:new Date().toISOString(),source:t.source,data:{by:t.actor}}),{ok:!0,exitCode:0,warnings:a,message:i?`${o.label} released.`:`${o.label} withdrawn from \u2014 ${o.claimedBy??"nobody"} keeps the claim.`,data:{schema:"kadence/v1",ok:!0,task:{id:o.id,label:o.label,claimedBy:i?null:o.claimedBy,contestedBy:i?[]:o.contestedBy.filter(c=>c!==t.actor)}}})}function nr(e,r,n,t){let s=w(e,r);if(!v(s))return s;let a=t.trim();if(a.length===0)return p(2,"invalid_argument","A criterion needs text.",{hint:`kadence task ac add ${n} "tests green"`});let{state:o,warnings:i}=b(s.root,s.actor),d=j(o,n);if(d===void 0)return E(n);S(s.root,{id:$(),type:"task.criterion_added",entity:d.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:a}});let c=d.criteria.length+1;return{ok:!0,exitCode:0,warnings:i,message:`${d.label} criterion ${c}: ${a}`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label},criterion:{n:c,text:a,checked:!1,checkedBy:null}}}}function Gt(e,r){let n=Number(r),t=e.criteria.map(a=>a.n);if(!Number.isInteger(n))return p(2,"invalid_argument",`"${r}" is not a criterion number.`,{received:r,hint:`kadence task ac list ${e.label}`});let s=e.criteria.find(a=>a.n===n);return s===void 0?p(2,"invalid_argument",e.criteria.length===0?`${e.label} has no criteria yet.
|
|
179
|
-
kadence task ac add ${e.label} "tests green"`:`${e.label} has no criterion ${n}.
|
|
180
|
-
Available: ${t.join(", ")}`,{received:r,hint:`kadence task ac list ${e.label}`}):s}function rr(e,r,n,t,s){let a=w(e,r);if(!v(a))return a;let{state:o,warnings:i}=b(a.root,a.actor),d=j(o,n);if(d===void 0)return E(n);let c=Gt(d,t);if(!("id"in c))return c;let l=!s;if(c.checked===l)return{ok:!0,exitCode:0,warnings:i,message:`${d.label} criterion ${c.n} is already ${l?"checked":"unchecked"}.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label}}};S(a.root,{id:$(),type:l?"task.criterion_checked":"task.criterion_unchecked",entity:d.id,actor:a.actor,ts:new Date().toISOString(),source:a.source,data:{criterion:c.id}});let m=d.criteria.filter(g=>g.id===c.id?l:g.checked).length;return{ok:!0,exitCode:0,warnings:i,message:`${d.label} criterion ${c.n} ${l?"checked":"unchecked"} \u2014 ${m} of ${d.criteria.length} done.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label},criterion:{n:c.n,text:c.text,checked:l,checkedBy:l?a.actor:null},done:m,total:d.criteria.length}}}function sr(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),o=j(s,n);return o===void 0?E(n):o.criteria.length===0?{ok:!0,exitCode:0,warnings:a,message:`${o.label} has no acceptance criteria.
|
|
181
|
-
Add one:
|
|
182
|
-
kadence task ac add ${o.label} "tests green"`,data:{schema:"kadence/v1",ok:!0,task:{id:o.id,label:o.label},criteria:[]}}:{ok:!0,exitCode:0,warnings:a,message:o.criteria.map(i=>` ${i.n}. [${i.checked?"x":" "}] ${i.text}`).join(`
|
|
183
|
-
`),data:{schema:"kadence/v1",ok:!0,task:{id:o.id,label:o.label},criteria:Zt(o)}}}function Zt(e){return e.criteria.map(r=>({n:r.n,text:r.text,checked:r.checked,checkedBy:r.checkedBy}))}function or(e,r,n,t){let s=w(e,r);if(!v(s))return s;let a=t.trim();if(a.length===0)return p(2,"invalid_argument","A comment needs text.",{received:n});let{state:o,warnings:i}=b(s.root,s.actor),d=j(o,n);return d===void 0?E(n):(S(s.root,{id:$(),type:"task.commented",entity:d.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{text:a}}),{ok:!0,exitCode:0,warnings:i,message:`Comment added to ${d.label}.`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label}}})}function O(e,r){let n=r.split(",").map(o=>o.trim()).filter(o=>o.length>0);if(n.length===0)return{tasks:[],error:p(2,"invalid_argument","No task given.")};let t=[],s=[];for(let o of n){let i=j(e,o);i===void 0?s.push(o):t.push(i)}return s.length>0?{tasks:[],error:p(1,"task_not_found",`No task ${s.join(", ")} \u2014 nothing was changed.
|
|
184
|
-
All ids must exist before a bulk change runs.
|
|
185
|
-
kadence task list`,{received:s.join(","),hint:"kadence task list --json"})}:{tasks:[...new Map(t.map(o=>[o.id,o])).values()],error:null}}function T(e,r,n){return r.length===1?`${r[0].label}: ${n}`:`${r.length} tasks ${e}: ${r.map(t=>t.label).join(", ")}`}function ar(e,r,n,t){let s=w(e,r);if(!v(s))return s;let{state:a,warnings:o}=b(s.root,s.actor),{tasks:i,error:d}=O(a,n);if(d!==null)return d;let c=t.trim().toLowerCase()==="none",l=null;if(!c){let u=j(a,t);if(u===void 0)return E(t);if(i.some(h=>h.id===u.id))return p(2,"invalid_argument","A task cannot be its own parent.",{received:n});l=u.id}let m=i.filter(u=>u.parent!==l);if(m.length===0)return{ok:!0,exitCode:0,warnings:o,message:T("unchanged",i,"already there.")};for(let u of m)S(s.root,{id:$(),type:"task.parent_set",entity:u.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{parent:l}});let g=c?"detached":j(a,t).label;return{ok:!0,exitCode:0,warnings:[...o,...Ge(s.root,s.actor)],message:c?T("detached",m,"detached from its parent."):T("moved",m,`\u2192 child of ${g}`),data:{schema:"kadence/v1",ok:!0,parent:l}}}function ir(e,r,n,t,s){let a=w(e,r);if(!v(a))return a;let{state:o,warnings:i}=b(a.root,a.actor),{tasks:d,error:c}=O(o,n);if(c!==null)return c;let l=j(o,t);if(l===void 0)return E(t);if(d.some(g=>g.id===l.id))return p(2,"invalid_argument","A task cannot block itself.",{received:n});let m=d.filter(g=>s?g.blockedBy.includes(l.id):!g.blockedBy.includes(l.id));if(m.length===0)return{ok:!0,exitCode:0,warnings:i,message:T("unchanged",d,s?"was not blocked by it.":"is already blocked by it.")};for(let g of m)S(a.root,{id:$(),type:s?"task.blocked_by_removed":"task.blocked_by_added",entity:g.id,actor:a.actor,ts:new Date().toISOString(),source:a.source,data:{blocker:l.id}});return{ok:!0,exitCode:0,warnings:[...i,...Ge(a.root,a.actor)],message:s?T("unblocked",m,`no longer blocked by ${l.label}.`):T("blocked",m,`blocked by ${l.label}.`),data:{schema:"kadence/v1",ok:!0,blocker:l.id}}}function Ge(e,r){let{state:n}=b(e,r),t=new Map(n.tasks.map(s=>[s.id,s.label]));return n.cycles.map(s=>{let a=s.path.map(o=>t.get(o)??o).join(" \u2192 ");return`Dependency cycle (${s.kind}): ${a}. Both edges were kept \u2014 resolve it when you can.`})}function Vt(e){let r=/^(-?\d+(?:\.\d+)?)\s*(h|hours?|m|min|minutes?)?$/i.exec(e.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 dr(e,r,n,t){let s=w(e,r);if(!v(s))return s;let a=Vt(t);if(a===null)return{...p(2,"invalid_argument",`Cannot read "${t}" as a duration.
|
|
186
|
-
Use hours or minutes:
|
|
187
|
-
kadence task log KAD-1 2h
|
|
188
|
-
kadence task log KAD-1 90m
|
|
189
|
-
kadence task log KAD-1 -30m to correct a mistake`,{received:t})};let{state:o,warnings:i}=b(s.root,s.actor),d=j(o,n);if(d===void 0)return E(n);S(s.root,{id:$(),type:"task.time_logged",entity:d.id,actor:s.actor,ts:new Date().toISOString(),source:s.source,data:{hours:a}});let c=Math.max(0,d.loggedHours+a),l=d.estimate===null?`
|
|
190
|
-
No estimate on this task, so there is nothing to compare against.`:"";return{ok:!0,exitCode:0,warnings:i,message:`${d.label}: ${c.toFixed(1)}h logged in total.${l}`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label,loggedHours:c}}}}import{spawnSync as zt}from"node:child_process";import{mkdtempSync as Xt,readFileSync as Qt,rmSync as en,writeFileSync as tn}from"node:fs";import{tmpdir as nn}from"node:os";import{join as Ze}from"node:path";var q="#";function rn(e){return e.GIT_EDITOR??e.VISUAL??e.EDITOR??"vi"}function hr(e,r,n){let t=Xt(Ze(nn(),"kadence-edit-")),s=Ze(t,"KADENCE_EDITMSG.md"),a=["",`${q} ${n}`,`${q} Lines starting with '${q}' are ignored.`,`${q} Save an empty file to abort.`].join(`
|
|
191
|
-
`);try{tn(s,`${r}${a}
|
|
192
|
-
`,"utf8");let o=rn(e),i=zt(o,[s],{stdio:"inherit",shell:!0});if(i.error!==void 0||i.status!==null&&i.status!==0)return{text:null,error:`Editor "${o}" exited without saving.
|
|
193
|
-
Set one explicitly:
|
|
194
|
-
export EDITOR=nano`};let c=Qt(s,"utf8").split(`
|
|
195
|
-
`).filter(l=>!l.startsWith(q)).join(`
|
|
196
|
-
`).trim();return{text:c.length===0?null:c,error:null}}catch(o){return{text:null,error:`Could not open an editor: ${o.message}`}}finally{en(t,{recursive:!0,force:!0})}}function pr(e,r){return e.KADENCE_SOURCE==="agent"?!1:r}function se(e,r){let n=e.sprints.find(f=>f.id===r);if(n===void 0)return null;let t=e.tasks.filter(f=>f.sprint===r),s=t.filter(f=>f.status==="cancelled"),a=t.filter(f=>f.status!=="cancelled"),o=a.filter(f=>f.status==="done"),i=a.filter(f=>f.status!=="done"),d=Y(a.map(f=>f.estimate??0)),c=Y(o.map(f=>f.estimate??0)),l=o.filter(f=>f.estimate===null),m=o.map(f=>sn(f,e)).filter(f=>f!==null),g=m.length>0?Y(m):null,u=Y(o.filter(f=>f.estimate!==null).map(f=>f.estimate)),h=g!==null&&u>0?g/u:null;return{id:n.id,name:n.name,status:n.status,committed:d,velocity:c,actualHours:g,hoursPerPoint:h,done:o,carriedOver:i,cancelled:s,unestimated:l,loggedHours:Y(t.map(f=>f.loggedHours))}}function sn(e,r){let n=le(e,r.started,r.statuses,r.startedChanges),t=ue(e);if(n===null||t===null)return null;let s=Date.parse(t)-Date.parse(n);return s>0?s/36e5:null}function Y(e){return e.reduce((r,n)=>r+n,0)}function G(e){return new Date(e).toISOString().slice(0,10)}function on(e,r){let n=[];for(let t=Date.parse(`${e}T00:00:00.000Z`);t<=Date.parse(`${r}T00:00:00.000Z`);t+=864e5)n.push(G(t));return n}function Ve(e,r,n,t=G(Date.now())){let s=e.tasks.filter(k=>k.sprint===n.id&&k.status!=="cancelled"),a=new Map(s.map(k=>[k.id,k.estimate??0])),o=[...a.values()].reduce((k,y)=>k+y,0),i=r.filter(k=>a.has(k.entity));if(i.length===0)return null;let d=n.startDate??G(i[0].ts),c=n.endDate??G(i.at(-1).ts),l=n.status==="closed"||c<t?c:t;if(l<d)return null;let m=new Set,g=new Map;for(let k of i){let y=G(k.ts);if(y<d)continue;let x=k.type==="task.moved"?k.data?.to:void 0,B=a.get(k.entity)??0;x==="done"&&!m.has(k.entity)?(m.add(k.entity),g.set(y,(g.get(y)??0)+B)):x!==void 0&&x!=="done"&&m.has(k.entity)&&(m.delete(k.entity),g.set(y,(g.get(y)??0)-B))}let u=on(d,l),h=Math.max(u.length-1,1),f=o;return{sprintName:n.name,committed:o,finalRemaining:(n.status==="closed",null),days:u.map((k,y)=>{let x=g.get(k)??0;return f-=x,{date:k,remaining:Math.max(0,f),ideal:Math.max(0,o-o/h*y),completed:x}})}}function ze(e,r=40){if(e.committed===0)return`"${e.sprintName}": nothing to burn down \u2014 no estimated tasks.`;let n=[`"${e.sprintName}" \u2014 ${e.committed} points committed`,""];for(let s of e.days){let a=Math.round(s.remaining/e.committed*r),o=Math.round(s.ideal/e.committed*r),i=[];for(let l=0;l<r;l++)l===o&&l>=a?i.push("\u250A"):l<a?i.push(l===o?"\u2503":"\u2588"):i.push(" ");let d=s.remaining-s.ideal,c=Math.abs(d)<.5?"":d>0?` +${d.toFixed(0)}`:` ${d.toFixed(0)}`;n.push(`${s.date.slice(5)} ${i.join("")} ${String(s.remaining).padStart(3)}${c}`)}let t=e.days.at(-1);return t!==void 0&&(n.push(""),n.push(t.remaining===0?" All committed work is done.":` ${t.remaining} of ${e.committed} points remain.`+(t.remaining>t.ideal?" Behind the ideal line.":" On or ahead of the line."))),n.join(`
|
|
197
|
-
`)}function P(e){return e.sprints.find(r=>r.status==="active")}function an(e){return e.sprints.filter(r=>r.status==="planned")}function J(e,r){let n=r.trim().toLowerCase();return e.sprints.find(t=>t.name.toLowerCase()===n)}function W(e,r,n,t){S(e.root,{id:$(),type:r,entity:n,actor:e.actor,ts:new Date().toISOString(),source:e.source,data:t})}function Tr(e,r,n){let t=w(e,r);if(!v(t))return t;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:a,warnings:o}=b(t.root,t.actor);if(J(a,s)!==void 0)return p(2,"invalid_argument",`Sprint "${s}" already exists.`,{received:s,hint:"kadence sprint list --json"});let i=P(a)!==void 0,d=$();return W(t,"sprint.created",d,{name:s}),i||W(t,"sprint.started",d,{}),{ok:!0,exitCode:0,warnings:o,message:i?`Sprint "${s}" planned.
|
|
198
|
-
|
|
199
|
-
kadence sprint add KAD-1 --sprint "${s}"
|
|
200
|
-
kadence sprint start "${s}"`:`Sprint "${s}" started.
|
|
201
|
-
|
|
202
|
-
kadence sprint add KAD-1
|
|
203
|
-
kadence sprint status`,data:{schema:"kadence/v1",ok:!0,sprint:{id:d,name:s,status:i?"planned":"active"}}}}function jr(e,r,n,t={}){let s=w(e,r);if(!v(s))return s;let{state:a,warnings:o}=b(s.root,s.actor),i=t.sprint===void 0?P(a):J(a,t.sprint);if(i===void 0)return t.sprint===void 0?{...p(1,"sprint_not_found",`No active sprint.
|
|
204
|
-
kadence sprint create "Sprint 1"`,{hint:"kadence sprint list --json"})}:p(1,"sprint_not_found",`No sprint named "${t.sprint}".
|
|
205
|
-
kadence sprint list`,{received:t.sprint,hint:"kadence sprint list --json"});if(i.status==="closed")return p(1,"conflicting_state",`Sprint "${i.name}" is closed \u2014 its velocity is not rewritten.`,{received:i.name});let d=j(a,n);if(d===void 0)return E(n);if(d.sprint===i.id)return{ok:!0,exitCode:0,warnings:o,message:`${d.label} is already in the sprint.`};W(s,"sprint.task_added",i.id,{task:d.id});let c=d.estimate===null?`
|
|
206
|
-
Without an estimate this task will not count towards velocity.`:"";return{ok:!0,exitCode:0,warnings:o,message:`${d.label} \u2192 "${i.name}"${c}`,data:{schema:"kadence/v1",ok:!0,task:{id:d.id,label:d.label},sprint:i.id}}}function Er(e,r){let n=w(e,r);if(!v(n))return n;let{state:t,warnings:s}=b(n.root,n.actor),a=P(t);if(a===void 0)return{...p(1,"sprint_not_found",`No active sprint.
|
|
207
|
-
kadence sprint create "Sprint 1"`,{hint:"kadence sprint list --json"})};let o=se(t,a.id);W(n,"sprint.closed",a.id,{velocity:o.velocity});let i=[...s];return o.unestimated.length>0&&i.push(`${o.unestimated.length} completed task(s) without an estimate were left out of velocity: `+o.unestimated.map(d=>d.label).join(", ")),{ok:!0,exitCode:0,warnings:i,message:dn(o),data:{schema:"kadence/v1",ok:!0,report:Xe(o)}}}function _r(e,r){let n=w(e,r);if(!v(n))return n;let{state:t,warnings:s}=b(n.root,n.actor),a=P(t);if(a===void 0)return{ok:!0,exitCode:0,warnings:s,message:`No active sprint.
|
|
208
|
-
kadence sprint create "Sprint 1"`,data:{schema:"kadence/v1",ok:!0,sprint:null}};let o=se(t,a.id),i=[`"${o.name}" \u2014 ${o.velocity} of ${o.committed} points`,"",...o.done.map(d=>` \u2713 ${d.label} ${d.title}`),...o.carriedOver.map(d=>` \xB7 ${d.label} ${d.title} (${d.status})`)];return{ok:!0,exitCode:0,warnings:s,message:i.join(`
|
|
209
|
-
`),data:{schema:"kadence/v1",ok:!0,report:Xe(o)}}}function dn(e){let r=[`Sprint "${e.name}" closed.`,""];if(r.push(` Velocity: ${e.velocity} of ${e.committed} points`),e.hoursPerPoint!==null&&e.actualHours!==null&&e.actualHours>=1/60&&r.push(` Actual: ${oe(e.actualHours)} \u2014 ${oe(e.hoursPerPoint)} per point`),e.loggedHours>0&&r.push(` Logged: ${oe(e.loggedHours)} entered by hand`),e.carriedOver.length>0){r.push("",` Carried over (${e.carriedOver.length}):`);for(let n of e.carriedOver)r.push(` \xB7 ${n.label} ${n.title}`)}return e.cancelled.length>0&&r.push("",` Cancelled (${e.cancelled.length}) \u2014 not counted towards velocity`),r.join(`
|
|
210
|
-
`)}function oe(e){return e<1?`${Math.round(e*60)}m`:e<10?`${e.toFixed(1)}h`:`${Math.round(e)}h`}function Xe(e){return{id:e.id,name:e.name,status:e.status,velocity:e.velocity,committed:e.committed,actualHours:e.actualHours,hoursPerPoint:e.hoursPerPoint,done:e.done.map(r=>r.label),carriedOver:e.carriedOver.map(r=>r.label),cancelled:e.cancelled.map(r=>r.label),unestimated:e.unestimated.map(r=>r.label),loggedHours:e.loggedHours}}function Dr(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),o=P(s);if(o!==void 0)return{...p(1,"conflicting_state",`Sprint "${o.name}" is still active.
|
|
211
|
-
Close it so velocity can be computed:
|
|
212
|
-
kadence sprint close`,{received:o.name})};let i=an(s),d=n===void 0?i[0]:J(s,n);return d===void 0?{...p(1,"sprint_not_found",n===void 0?`No planned sprints.
|
|
213
|
-
kadence sprint create "Sprint 2"`:`No sprint named "${n}".
|
|
214
|
-
kadence sprint list`,{...n!==void 0?{received:n}:{},hint:"kadence sprint list --json"})}:d.status!=="planned"?p(1,"conflicting_state",`Sprint "${d.name}" is already ${d.status}.`,{received:d.name}):(W(t,"sprint.started",d.id,{}),{ok:!0,exitCode:0,warnings:a,message:`Sprint "${d.name}" started.
|
|
215
|
-
|
|
216
|
-
kadence sprint status`,data:{schema:"kadence/v1",ok:!0,sprint:{id:d.id,name:d.name}}})}function Rr(e,r){let n=w(e,r);if(!v(n))return n;let{state:t,warnings:s}=b(n.root,n.actor);if(t.sprints.length===0)return{ok:!0,exitCode:0,warnings:s,message:`No sprints yet.
|
|
217
|
-
kadence sprint create "Sprint 1"`,data:{schema:"kadence/v1",ok:!0,sprints:[]}};let a=t.sprints.map(o=>{let i=t.tasks.filter(c=>c.sprint===o.id),d=i.reduce((c,l)=>c+(l.estimate??0),0);return{sprint:o,tasks:i,points:d}});return{ok:!0,exitCode:0,warnings:s,message:a.map(({sprint:o,tasks:i,points:d})=>{let c=o.status==="active"?"\u2192":" ",l=i.length===0?"":` ${i.length} tasks, ${d} points`;return`${c} ${o.name.padEnd(20)} ${o.status.padEnd(8)}${l}`}).join(`
|
|
218
|
-
`),data:{schema:"kadence/v1",ok:!0,sprints:a.map(({sprint:o,tasks:i,points:d})=>({id:o.id,name:o.name,status:o.status,taskIds:o.taskIds,taskCount:i.length,points:d}))}}}function cn(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;let r=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(r.getTime())&&r.toISOString().slice(0,10)===e}function Nr(e,r,n,t){let s=w(e,r);if(!v(s))return s;for(let[g,u]of[["--start",t.startDate],["--end",t.endDate]])if(u!==void 0&&u!==""&&!cn(u))return{...p(2,"invalid_argument",`${g} must be YYYY-MM-DD, got "${u}".
|
|
219
|
-
kadence sprint edit --start 2026-09-01`,{received:u})};let{state:a,warnings:o}=b(s.root,s.actor),i=n===void 0?P(a):J(a,n);if(i===void 0)return{...p(1,"sprint_not_found",n===void 0?`No active sprint.
|
|
220
|
-
kadence sprint list`:`No sprint named "${n}".
|
|
221
|
-
kadence sprint list`,{...n!==void 0?{received:n}:{},hint:"kadence sprint list --json"})};if(i.status==="closed")return{...p(1,"conflicting_state",`Sprint "${i.name}" is closed \u2014 its record is not rewritten.`,{received:i.name})};if(t.name!==void 0&&t.name!==i.name&&J(a,t.name)!==void 0)return p(2,"invalid_argument",`Sprint "${t.name}" already exists.`,{received:t.name});let d=t.startDate??i.startDate??"",c=t.endDate??i.endDate??"";if(d!==""&&c!==""&&c<d)return{...p(2,"invalid_argument",`The end date (${c}) is before the start date (${d}).`,{received:c})};let l={},m=[];return t.name!==void 0&&t.name!==i.name&&(l.name=t.name,m.push("name")),t.description!==void 0&&t.description!==(i.description??"")&&(l.description=t.description,m.push("description")),t.startDate!==void 0&&t.startDate!==(i.startDate??"")&&(l.startDate=t.startDate,m.push("start")),t.endDate!==void 0&&t.endDate!==(i.endDate??"")&&(l.endDate=t.endDate,m.push("end")),m.length===0?{ok:!0,exitCode:0,warnings:o,message:`"${i.name}": nothing changed.`}:(W(s,"sprint.updated",i.id,l),{ok:!0,exitCode:0,warnings:o,message:`"${i.name}": updated ${m.join(", ")}.`,data:{schema:"kadence/v1",ok:!0,sprint:{id:i.id,changed:m}}})}function Ar(e,r,n){let t=w(e,r);if(!v(t))return t;let{state:s,warnings:a}=b(t.root,t.actor),o=n===void 0?P(s):J(s,n);if(o===void 0)return{...p(1,"sprint_not_found",n===void 0?`No active sprint.
|
|
222
|
-
kadence sprint list`:`No sprint named "${n}".
|
|
223
|
-
kadence sprint list`,{...n!==void 0?{received:n}:{},hint:"kadence sprint list --json"})};let i=Ve(s,K(t.root).events,o);return i===null?{ok:!0,exitCode:0,warnings:a,message:`"${o.name}" has no tasks yet.
|
|
224
|
-
kadence sprint add KAD-1`,data:{schema:"kadence/v1",ok:!0,burndown:null}}:{ok:!0,exitCode:0,warnings:a,message:ze(i),data:{schema:"kadence/v1",ok:!0,burndown:i}}}export{ye as a,we as b,ve as c,$e as d,F as e,A as f,S as g,K as h,vn as i,$n as j,fn as k,hn as l,pn as m,$ as n,re as o,Ue as p,Rn as q,Wn as r,p as s,E as t,w as u,v,Hn as w,b as x,j as y,qn as z,Yn as A,Ut as B,Ye as C,Gn as D,Zn as E,Vn as F,zn as G,Xn as H,Yt as I,Qn as J,er as K,tr as L,nr as M,rr as N,sr as O,or as P,ar as Q,ir as R,dr as S,se as T,hr as U,pr as V,Ve as W,Tr as X,jr as Y,Er as Z,_r as _,Dr as $,Rr as aa,Nr as ba,Ar as ca};
|