ai4kanban 0.7.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -57
- package/bin/ai4kanban.mjs +70 -5
- package/dist/kanban.mjs +16122 -8598
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,17 +65,12 @@ akb skill install # add it, or bring an older copy up to date
|
|
|
65
65
|
That writes one file into `.claude/skills/kanban/` (Claude Code) and
|
|
66
66
|
`.agents/skills/kanban/` (Codex): `SKILL.md`, a short note telling a coding agent the board
|
|
67
67
|
is here and that `akb` owns it. The board app does the same thing from a button:
|
|
68
|
-
**Configuration →
|
|
68
|
+
**Configuration → General**.
|
|
69
69
|
|
|
70
70
|
Nothing else is copied in. The flows ship inside the command (`akb guide`), so a newer
|
|
71
|
-
command is newer flows in every project at once
|
|
72
|
-
it
|
|
73
|
-
|
|
74
|
-
An agent that finds no `akb` on the PATH doesn't stop: the note's first section says what to
|
|
75
|
-
run instead — the copy in this project where there is one, and otherwise `npx --yes
|
|
76
|
-
ai4kanban@<the version that wrote the note>`, pinned so the rules match the board. A run
|
|
77
|
-
started from the app or the CLI is told the same thing in its own words, and `akb skill`
|
|
78
|
-
says it to you at the moment the note lands.
|
|
71
|
+
command is newer flows in every project at once, and a project's git history never carries
|
|
72
|
+
350 kB of it. An agent that finds no `akb` on the PATH doesn't stop: the note names what to
|
|
73
|
+
run instead, pinned to the version that wrote it so the rules match the board.
|
|
79
74
|
|
|
80
75
|
## Update
|
|
81
76
|
|
|
@@ -86,15 +81,11 @@ npm install -g ai4kanban@latest # a newer command
|
|
|
86
81
|
akb update # a repaired board, from the project root
|
|
87
82
|
```
|
|
88
83
|
|
|
89
|
-
`akb update` refreshes a skill folder that is already there — it never adds one
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
It can't do the first line to itself — replacing the file that is running is how you get
|
|
96
|
-
half a command — so it checks npm and names that line when it is behind, rather than
|
|
97
|
-
reporting success a release late.
|
|
84
|
+
`akb update` refreshes a skill folder that is already there — it never adds one — adds
|
|
85
|
+
whatever an older release never wrote to the board, clears out what it no longer writes, and
|
|
86
|
+
prints which version you moved from and to with a link to everything that changed in between.
|
|
87
|
+
Your cards, config, and memory are never touched. It can't do the first line to itself, so it
|
|
88
|
+
checks npm and names that line when it is behind.
|
|
98
89
|
|
|
99
90
|
## Put an agent to work
|
|
100
91
|
|
|
@@ -103,8 +94,10 @@ card can be built from a terminal, over ssh, or from a script, without a chat se
|
|
|
103
94
|
without a browser.
|
|
104
95
|
|
|
105
96
|
```bash
|
|
106
|
-
akb implement 12 # build the card
|
|
107
|
-
akb
|
|
97
|
+
akb implement 12 # build the card, then review what was built
|
|
98
|
+
akb review 12 # judge the delivery in flight on it again
|
|
99
|
+
akb refine 12 # sharpen it; defaults to standard QA
|
|
100
|
+
akb refine 12 --effort lightweight --print # use the chosen effort inline
|
|
108
101
|
akb create "add dark mode" # write the card(s) for it
|
|
109
102
|
akb propose # write the next tasks
|
|
110
103
|
akb archive 12 # finish it
|
|
@@ -118,11 +111,10 @@ point at, and the command that closes the job.
|
|
|
118
111
|
akb implement 12 --print # the steps, for whoever is asking
|
|
119
112
|
```
|
|
120
113
|
|
|
121
|
-
That is the mode for an agent already in a session: it does the job
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
started always gets the printed flow, so a run can't spawn a copy of itself.
|
|
114
|
+
That is the mode for an agent already in a session: it does the job where it is, rather than
|
|
115
|
+
paying for a second agent to do it. Start a run when you want the work to happen on its own.
|
|
116
|
+
An agent working inside a run always gets the printed flow, so a run can't spawn a copy of
|
|
117
|
+
itself. `akb help runs` carries the whole rule.
|
|
126
118
|
|
|
127
119
|
The run keeps working after the command returns. Watch it, or stop it, from anywhere —
|
|
128
120
|
including from the board app, which drives its buttons through these same commands:
|
|
@@ -168,31 +160,19 @@ akb chat 12 # the conversation so far
|
|
|
168
160
|
akb chat 12 --clear # forget it and start fresh
|
|
169
161
|
```
|
|
170
162
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
board when it needs it.
|
|
176
|
-
|
|
177
|
-
The reply arrives as it is written, and the next message lands in the same session — the
|
|
178
|
-
agent still has everything said before, so you can ask a follow-up without explaining your
|
|
179
|
-
project again. Every message is its own command, so the conversation is picked up from any
|
|
180
|
-
terminal and survives closing one. The board's conversation and each card's are separate,
|
|
181
|
-
and both live under `docs/kanban/.chats/`, on your machine and out of git.
|
|
182
|
-
|
|
183
|
-
From there, chat behaves exactly like using the kanban skill in your coding agent. The skill
|
|
184
|
-
prints the board-specific flow and does the work in the conversation by default; ask for a
|
|
185
|
-
background run when you want another agent to take it independently. If you explicitly say
|
|
186
|
-
which way, that wins.
|
|
187
|
-
|
|
188
|
-
A change to a card a run is already working on is refused, and the refusal names the card
|
|
189
|
-
and what that run is doing — the same rule that keeps two runs off one card.
|
|
163
|
+
Chat is the kanban skill in a persistent agent session — the first prompt is only the skill
|
|
164
|
+
invocation and your message, and the skill reads the current board itself. It prints the
|
|
165
|
+
board-specific flow and does the work in the conversation by default; ask for a background run
|
|
166
|
+
when you want another agent to take it independently.
|
|
190
167
|
|
|
191
|
-
|
|
192
|
-
|
|
168
|
+
Every message is its own command, so a conversation is picked up from any terminal and
|
|
169
|
+
survives closing one. The board's conversation and each card's are separate, both under
|
|
170
|
+
`docs/kanban/.chats/`, on your machine and out of git.
|
|
193
171
|
|
|
194
|
-
|
|
195
|
-
|
|
172
|
+
A chat is not a run: it never shows in `akb runs` and never holds a card. A change to a card a
|
|
173
|
+
run is already working on is refused, and the refusal names the card and what that run is
|
|
174
|
+
doing. Only an agent whose command can take a second message into its own session can hold a
|
|
175
|
+
conversation; on any other, chat says so and names the agents that can.
|
|
196
176
|
|
|
197
177
|
## The manual
|
|
198
178
|
|
|
@@ -205,19 +185,17 @@ The flows are `akb guide`:
|
|
|
205
185
|
```bash
|
|
206
186
|
akb guide # every flow, one line each
|
|
207
187
|
akb guide board # how the board works: card format, layout, memory
|
|
208
|
-
akb guide
|
|
188
|
+
akb guide qa-loop # settle one card's planning gaps
|
|
189
|
+
akb guide qa-lightweight # short check for clear, localized work
|
|
209
190
|
akb guide plan-release # fill a release from its goal
|
|
210
191
|
```
|
|
211
192
|
|
|
212
|
-
A printed flow already carries the ones its action needs, in full, so this is for the
|
|
213
|
-
rest.
|
|
193
|
+
A printed flow already carries the ones its action needs, in full, so this is for the rest.
|
|
214
194
|
|
|
215
|
-
A run that writes or changes a card is followed by `akb refine` on that card,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
its way. Each one is an ordinary run: it shows in `akb runs`, has its own log, and
|
|
220
|
-
`akb stop` ends it.
|
|
195
|
+
A run that writes or changes a card is followed by `akb refine` on that card, as a run of its
|
|
196
|
+
own — so `akb create`, `akb revise`, `akb resolve`, `akb propose` and `akb plan-release` all
|
|
197
|
+
come back with their cards refined. Archiving or rejecting a card does the same for every card
|
|
198
|
+
it was blocking that now has nothing left in its way.
|
|
221
199
|
|
|
222
200
|
## What it won't do
|
|
223
201
|
|
package/bin/ai4kanban.mjs
CHANGED
|
@@ -188,6 +188,13 @@ async function placeSkill(root, mode) {
|
|
|
188
188
|
const result = installSkill(root, mode === 'update' ? 'present' : undefined, PROGRAM)
|
|
189
189
|
for (const w of result.wrote) did.push(`${w.refreshed ? 'refreshed' : 'wrote'} ${w.path}/ — ${w.files} (${w.agent})`)
|
|
190
190
|
for (const s of result.skipped) notes.push(`${s.path} — ${s.why}`)
|
|
191
|
+
// The commit guard (#324), which goes in wherever the skill does. The FIRST write says
|
|
192
|
+
// so, so a user who ran `akb update` for something else learns it is there before a
|
|
193
|
+
// commit refuses; a rewrite of the board's own copy is silent, the way a refresh is.
|
|
194
|
+
if (result.hook?.wrote && !result.hook.wrote.refreshed) {
|
|
195
|
+
did.push(`wrote ${result.hook.wrote.path} — refuses a commit on the branch a delivery is landing on (\`--no-verify\` gets past it)`)
|
|
196
|
+
}
|
|
197
|
+
if (result.hook?.note) notes.push(result.hook.note)
|
|
191
198
|
// A project that has one agent's folder but not the other's. Update never writes a folder
|
|
192
199
|
// that isn't there — a plugin install keeps the skill in a read-only cache, and a board
|
|
193
200
|
// installed today has no folder at all until someone asks for one. Say the line that adds
|
|
@@ -227,14 +234,36 @@ function rescueSkillConfig(root, skillDir) {
|
|
|
227
234
|
|
|
228
235
|
// ---- the board -------------------------------------------------------------
|
|
229
236
|
|
|
237
|
+
// Run one board move against `root`.
|
|
238
|
+
//
|
|
239
|
+
// `--dir` is not optional here, even though the process already runs in that folder: with
|
|
240
|
+
// nothing named, the rules WALK UP from the working directory to the nearest docs/kanban/
|
|
241
|
+
// and work on that one. In a folder with no board of its own — every folder an install is
|
|
242
|
+
// pointed at — the nearest board can be anywhere above it, up to the user's home. Naming
|
|
243
|
+
// the folder is what keeps `install --dir X` from repairing somebody else's board and
|
|
244
|
+
// leaving X untouched.
|
|
230
245
|
function runKanban(rulesFile, root, args) {
|
|
231
|
-
const
|
|
246
|
+
const [move, ...rest] = args
|
|
247
|
+
const result = spawnSync(process.execPath, [rulesFile, move, '--dir', root, ...rest], {
|
|
232
248
|
cwd: root,
|
|
233
249
|
stdio: 'inherit',
|
|
234
250
|
})
|
|
235
251
|
if (result.status !== 0) fail(`\`kanban.mjs ${args.join(' ')}\` failed — nothing else was changed`)
|
|
236
252
|
}
|
|
237
253
|
|
|
254
|
+
// The nearest board strictly ABOVE `root`, when there is one. A second board underneath an
|
|
255
|
+
// existing one is legal and occasionally meant, but it is almost never what someone who ran
|
|
256
|
+
// `install` in a subfolder wanted, so it is said out loud.
|
|
257
|
+
function boardAbove(root) {
|
|
258
|
+
let at = path.dirname(path.resolve(root))
|
|
259
|
+
for (;;) {
|
|
260
|
+
if (fs.existsSync(path.join(at, 'docs', 'kanban'))) return at
|
|
261
|
+
const up = path.dirname(at)
|
|
262
|
+
if (up === at) return null
|
|
263
|
+
at = up
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
238
267
|
// The repair steps an update has to run on a board written by an older version. Each one is
|
|
239
268
|
// mechanical; anything with a choice in it becomes a note instead.
|
|
240
269
|
async function repairBoard(rulesFile, root) {
|
|
@@ -332,12 +361,17 @@ function checkModules(board) {
|
|
|
332
361
|
function cmdInstall(root, tracks) {
|
|
333
362
|
say(`ai4kanban ${VERSION} — installing into ${root}`)
|
|
334
363
|
say('')
|
|
364
|
+
const above = fs.existsSync(path.join(root, 'docs', 'kanban')) ? null : boardAbove(root)
|
|
365
|
+
if (above) {
|
|
366
|
+
notes.push(`there is already a board at ${above} — this makes a second one, and commands run here will find this one`)
|
|
367
|
+
}
|
|
335
368
|
runKanban(builtRules(), root, ['init', ...tracks])
|
|
336
369
|
sayNotes()
|
|
337
370
|
say('')
|
|
338
371
|
// Say what landed, so nobody goes looking for the flows in the repo. They ship with the
|
|
339
372
|
// command; a project holds its own board and nothing else.
|
|
340
|
-
say('That is the board
|
|
373
|
+
say('That is the board, under docs/kanban/. The one line written outside it is `.akb/` in')
|
|
374
|
+
say('.gitignore, which keeps the folders a delivery works in out of git.')
|
|
341
375
|
say('')
|
|
342
376
|
say('To drive this board from your coding agent, add the skill — from the button in the')
|
|
343
377
|
say('board UI (Configuration → Agent setup), or here:')
|
|
@@ -370,6 +404,7 @@ async function cmdSkill(root, mode) {
|
|
|
370
404
|
say(`ai4kanban ${VERSION} — the coding agent skill in ${root}`)
|
|
371
405
|
say('')
|
|
372
406
|
for (const folder of state.folders) say(` ${folder.path}/ — ${sayFolder(folder)} (${folder.agent})`)
|
|
407
|
+
await sayHookState(root)
|
|
373
408
|
say('')
|
|
374
409
|
if (!state.installed) say(`Not installed. \`${PROGRAM} skill install\` writes it, and so does the board UI's button.`)
|
|
375
410
|
else if (state.outdated) say(`Older than this command. \`${PROGRAM} skill install\` brings it up to date.`)
|
|
@@ -411,6 +446,16 @@ async function sayPathState() {
|
|
|
411
446
|
say(` ${GET_LINE}`)
|
|
412
447
|
}
|
|
413
448
|
|
|
449
|
+
// Where the commit guard stands (#324), said next to the folders an agent reads. A project
|
|
450
|
+
// that is not a git repository has no hook to have, and gets no line about one.
|
|
451
|
+
async function sayHookState(root) {
|
|
452
|
+
const { readCommitHook, sayCommitHook } = await rules()
|
|
453
|
+
if (typeof readCommitHook !== 'function' || typeof sayCommitHook !== 'function') return
|
|
454
|
+
const hook = readCommitHook(root)
|
|
455
|
+
if (hook.state === 'no-git') return
|
|
456
|
+
say(` ${hook.path} — ${sayCommitHook(hook.state)}`)
|
|
457
|
+
}
|
|
458
|
+
|
|
414
459
|
function sayFolder(folder) {
|
|
415
460
|
if (folder.state === 'absent') return 'not installed'
|
|
416
461
|
if (folder.state === 'linked') return 'a symlink into a source checkout — never written over'
|
|
@@ -534,7 +579,8 @@ ${INTRO}
|
|
|
534
579
|
akb install [--tracks a,b,c] scaffold docs/kanban/ — the board, and nothing else
|
|
535
580
|
akb skill whether a coding agent can drive this board
|
|
536
581
|
akb skill install add the skill: SKILL.md into .claude/skills/kanban/
|
|
537
|
-
and .agents/skills/kanban
|
|
582
|
+
and .agents/skills/kanban/, plus the commit guard in
|
|
583
|
+
.git/hooks/pre-commit
|
|
538
584
|
akb skill refresh rewrite a skill that is already here, and write none
|
|
539
585
|
that isn't — how the note learns a new spelling of the
|
|
540
586
|
command
|
|
@@ -568,8 +614,8 @@ Put an agent to work on the board:
|
|
|
568
614
|
akb agent which agent runs them, and how it is set up
|
|
569
615
|
|
|
570
616
|
A run keeps working after the command returns. Add \`--print\` to any of the first four and
|
|
571
|
-
nothing starts: it prints what to do instead, filled in for this board — which is how
|
|
572
|
-
agent already
|
|
617
|
+
nothing starts: it prints what to do instead, filled in for this board — which is how the
|
|
618
|
+
agent you are already talking to does the job itself rather than starting a run of its own.
|
|
573
619
|
|
|
574
620
|
\`akb help runs\` is the whole of it — every command a coding agent may call, and when.
|
|
575
621
|
\`akb agent\` is where you pick the agent, its model and its key.
|
|
@@ -626,6 +672,10 @@ async function cmdBoard(args) {
|
|
|
626
672
|
// command line is the command's own.
|
|
627
673
|
const RUN_COMMANDS = new Set([
|
|
628
674
|
'implement',
|
|
675
|
+
// The two runs a delivery makes after its build (#302). The board starts each itself;
|
|
676
|
+
// typed by hand they put a stopped delivery back in motion.
|
|
677
|
+
'review',
|
|
678
|
+
'correct',
|
|
629
679
|
'run',
|
|
630
680
|
'refine',
|
|
631
681
|
'resolve',
|
|
@@ -633,6 +683,9 @@ const RUN_COMMANDS = new Set([
|
|
|
633
683
|
'create',
|
|
634
684
|
'propose',
|
|
635
685
|
'plan-release',
|
|
686
|
+
// Write a closed version's changelog into its summary file (#232) — the run the board
|
|
687
|
+
// UI's close starts, and the one a terminal close names.
|
|
688
|
+
'changelog',
|
|
636
689
|
// Finish setting the board up (#173) — the steps that read the repo and think, as one
|
|
637
690
|
// run. It is a run like the rest: the board UI's button starts exactly this.
|
|
638
691
|
'setup',
|
|
@@ -647,7 +700,19 @@ const RUN_COMMANDS = new Set([
|
|
|
647
700
|
'log',
|
|
648
701
|
'stop',
|
|
649
702
|
'resume',
|
|
703
|
+
// End the DELIVERY in flight on a card and hand the card back (#301). `stop` ends one
|
|
704
|
+
// run of it; this ends the job.
|
|
705
|
+
'cancel',
|
|
706
|
+
// Throw a delivery's worktree and branch away (#303). Cancel leaves them where they are;
|
|
707
|
+
// this is the only thing that removes one.
|
|
708
|
+
'discard',
|
|
709
|
+
// Sign off the tree a delivery would land (#308), on a board that requires it. It ends
|
|
710
|
+
// nothing and starts nothing — it lets a delivery already waiting carry on.
|
|
711
|
+
'approve',
|
|
650
712
|
'agent',
|
|
713
|
+
// Which account this MACHINE is signed in to Cloud as (#326). Like `guide`, it needs no
|
|
714
|
+
// board: the sign-in belongs to the machine, not to any one project.
|
|
715
|
+
'cloud',
|
|
651
716
|
// The board's flows, shipped with this command rather than copied into each project.
|
|
652
717
|
'guide',
|
|
653
718
|
// What a started run's watcher is spawned as. Never typed by a person.
|