ai4kanban 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/bin/ai4kanban.mjs +106 -22
- package/dist/kanban.mjs +1763 -664
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,11 @@ without a chat session and without a browser.
|
|
|
24
24
|
|
|
25
25
|
## Get the command
|
|
26
26
|
|
|
27
|
+
The [board app](https://ai4kanban.dev/download) brings `akb` with it — the first open puts
|
|
28
|
+
it on your PATH and it updates when the app does, so if you have the app you have the
|
|
29
|
+
command. Install it on its own where the app can't run — a server, a container, or a Linux
|
|
30
|
+
box, whose AppImage leaves no command behind:
|
|
31
|
+
|
|
27
32
|
```bash
|
|
28
33
|
npm install -g ai4kanban
|
|
29
34
|
```
|
|
@@ -60,12 +65,17 @@ akb skill install # add it, or bring an older copy up to date
|
|
|
60
65
|
That writes one file into `.claude/skills/kanban/` (Claude Code) and
|
|
61
66
|
`.agents/skills/kanban/` (Codex): `SKILL.md`, a short note telling a coding agent the board
|
|
62
67
|
is here and that `akb` owns it. The board app does the same thing from a button:
|
|
63
|
-
**Configuration →
|
|
68
|
+
**Configuration → Agent setup**.
|
|
64
69
|
|
|
65
70
|
Nothing else is copied in. The flows ship inside the command (`akb guide`), so a newer
|
|
66
71
|
command is newer flows in every project at once — and the command itself stays where npm put
|
|
67
|
-
it, so a project's git history never carries 350 kB of it.
|
|
68
|
-
|
|
72
|
+
it, so a project's git history never carries 350 kB of 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.
|
|
69
79
|
|
|
70
80
|
## Update
|
|
71
81
|
|
|
@@ -124,7 +134,8 @@ akb stop 3f2a1b04 # end one
|
|
|
124
134
|
akb resume 3f2a1b04 # continue one that failed
|
|
125
135
|
```
|
|
126
136
|
|
|
127
|
-
Which agent runs them — Claude Code, Codex, Cursor or
|
|
137
|
+
Which agent runs them — Claude Code, Codex, Cursor, OpenCode or DeepSeek Harness — and
|
|
138
|
+
what it is set to:
|
|
128
139
|
|
|
129
140
|
```bash
|
|
130
141
|
akb agent # what runs, and how it is set up
|
package/bin/ai4kanban.mjs
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// `npx --yes ai4kanban@latest <command>` runs it without installing anything.
|
|
5
5
|
//
|
|
6
6
|
// akb install [--tracks a,b,c] scaffold docs/kanban/
|
|
7
|
-
// akb skill [install]
|
|
8
|
-
// whether it is there
|
|
7
|
+
// akb skill [install|refresh] add the coding agent skill to this project, rewrite the
|
|
8
|
+
// copy it has, or say whether it is there
|
|
9
9
|
// akb update refresh an installed skill, repair the board, and say
|
|
10
10
|
// the one line that puts a newer command on your path
|
|
11
11
|
// akb board <move> the board's own bookkeeping — the agent's commands
|
|
@@ -44,6 +44,50 @@ const REPO = 'https://github.com/ai4kanban/ai4kanban'
|
|
|
44
44
|
const GET_LINE = `npm install -g ${NAME}`
|
|
45
45
|
const NEWER_LINE = `npm install -g ${NAME}@latest`
|
|
46
46
|
|
|
47
|
+
// How this command was reached, spelled so what it prints can be pasted straight back.
|
|
48
|
+
//
|
|
49
|
+
// A global install puts it on the PATH as `akb`, and every example everywhere spells it
|
|
50
|
+
// that way. It is also run as a path — `node cli/bin/ai4kanban.mjs` in a source checkout,
|
|
51
|
+
// the copy inside the desktop app — and printing `akb` to a reader who has none is printing
|
|
52
|
+
// a line that ends in `command not found`. Whoever typed it is right here in argv, so ask
|
|
53
|
+
// that rather than guessing: only the PATH spellings are `akb`, `.mjs` never is.
|
|
54
|
+
//
|
|
55
|
+
// Not what's on the PATH, which is a different question with a different answer: `npx` puts
|
|
56
|
+
// an `akb` on the PATH of THIS process and nothing else, so the reader would still be given
|
|
57
|
+
// a line their own shell can't run. That one is spotted by the cache it runs out of, and
|
|
58
|
+
// answered with the same fetch pinned to this version — never `@latest`, which is how a
|
|
59
|
+
// board comes to be driven by two versions of its own rules.
|
|
60
|
+
//
|
|
61
|
+
// AI4KANBAN_COMMAND is how the desktop app's launcher says what it was typed as. That
|
|
62
|
+
// launcher is reached as `akb` on the PATH and then runs this file by its path, so argv
|
|
63
|
+
// alone would report the copy inside the app — a line no reader can paste.
|
|
64
|
+
const PROGRAM = (() => {
|
|
65
|
+
const named = (process.env.AI4KANBAN_COMMAND || '').trim()
|
|
66
|
+
if (named) return named
|
|
67
|
+
const entry = process.argv[1] || ''
|
|
68
|
+
if (/[\\/](_npx|dlx)[\\/]/.test(entry)) return `npx --yes ${NAME}@${VERSION}`
|
|
69
|
+
const base = path.basename(entry).toLowerCase().replace(/\.(cmd|ps1|exe)$/, '')
|
|
70
|
+
if (base === 'akb' || base === NAME) return 'akb'
|
|
71
|
+
// Relative to where it was typed when that is shorter and still runs — a checkout says
|
|
72
|
+
// `node cli/bin/ai4kanban.mjs`, which reads inside a sentence; an absolute path doesn't.
|
|
73
|
+
const near = path.relative(process.cwd(), entry)
|
|
74
|
+
return `node ${near && !near.startsWith('..') ? near : entry}`
|
|
75
|
+
})()
|
|
76
|
+
|
|
77
|
+
// One line, when this copy isn't `akb`, for the text that spells it `akb` throughout — the
|
|
78
|
+
// help, and the flows a `--print` hands over. Cheaper than rewriting either, and it holds
|
|
79
|
+
// for the lines inside them that this command never wrote.
|
|
80
|
+
const SPELLED = PROGRAM === 'akb' ? '' : `This copy isn't on your PATH as \`akb\` — every \`akb\` below is \`${PROGRAM}\` here.`
|
|
81
|
+
|
|
82
|
+
// How the help opens: what the command is called, plus that line when it isn't called that.
|
|
83
|
+
const INTRO = [
|
|
84
|
+
'Installed, it is `akb`. Without installing anything, every line below also works as\n' +
|
|
85
|
+
'`npx --yes ai4kanban@latest <command>` — the same command, fetched each time.',
|
|
86
|
+
SPELLED,
|
|
87
|
+
]
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.join('\n\n')
|
|
90
|
+
|
|
47
91
|
// The memory set used to sit at the board root before it moved into `memory/`.
|
|
48
92
|
const MEMORY_FILES = ['readme.md', 'goal.md', 'decisions.md', 'redesign.md', 'rejected.md']
|
|
49
93
|
|
|
@@ -127,7 +171,10 @@ async function placeSkill(root, mode) {
|
|
|
127
171
|
for (const folder of before) {
|
|
128
172
|
if (folder.state !== 'absent' && folder.state !== 'linked') rescueSkillConfig(root, path.join(root, folder.path))
|
|
129
173
|
}
|
|
130
|
-
|
|
174
|
+
// How this command was typed goes into the note: an install run through `npx` is the one
|
|
175
|
+
// case the rules can't read off the machine, because npx's `akb` is on this process's
|
|
176
|
+
// PATH and no shell the agent will ever open.
|
|
177
|
+
const result = installSkill(root, mode === 'update' ? 'present' : undefined, PROGRAM)
|
|
131
178
|
for (const w of result.wrote) did.push(`${w.refreshed ? 'refreshed' : 'wrote'} ${w.path}/ — ${w.files} (${w.agent})`)
|
|
132
179
|
for (const s of result.skipped) notes.push(`${s.path} — ${s.why}`)
|
|
133
180
|
// A project that has one agent's folder but not the other's. Update never writes a folder
|
|
@@ -282,27 +329,41 @@ function cmdInstall(root, tracks) {
|
|
|
282
329
|
say('That is the board. Nothing was written outside docs/kanban/.')
|
|
283
330
|
say('')
|
|
284
331
|
say('To drive this board from your coding agent, add the skill — from the button in the')
|
|
285
|
-
say('board UI (Configuration →
|
|
332
|
+
say('board UI (Configuration → Agent setup), or here:')
|
|
286
333
|
say('')
|
|
287
|
-
say(
|
|
334
|
+
say(` ${PROGRAM} skill`)
|
|
288
335
|
}
|
|
289
336
|
|
|
290
|
-
// Add the skill to a project, or say where it stands. The whole
|
|
291
|
-
// rules; this prints what they did.
|
|
292
|
-
|
|
337
|
+
// Add the skill to a project, rewrite the copy it has, or say where it stands. The whole
|
|
338
|
+
// move belongs to the built rules; this prints what they did.
|
|
339
|
+
//
|
|
340
|
+
// `refresh` writes no folder that isn't already there. It is how a note learns a new
|
|
341
|
+
// spelling of the command without a project gaining a skill it never asked for — the
|
|
342
|
+
// desktop app runs it right after putting `akb` on the PATH, so the note stops naming the
|
|
343
|
+
// copy inside the app.
|
|
344
|
+
async function cmdSkill(root, mode) {
|
|
293
345
|
const { readSkillState } = await rules()
|
|
294
346
|
if (typeof readSkillState !== 'function') {
|
|
295
347
|
fail('this copy of the board rules is too old to install the skill — `npm install -g ai4kanban@latest`')
|
|
296
348
|
}
|
|
297
|
-
if (
|
|
349
|
+
if (mode === 'refresh') {
|
|
350
|
+
const { result } = await placeSkill(root, 'update')
|
|
351
|
+
sayDid()
|
|
352
|
+
sayNotes()
|
|
353
|
+
if (!result.ok && result.error) fail(result.error)
|
|
354
|
+
if (!result.wrote.length) say('No skill in this project — nothing to rewrite.')
|
|
355
|
+
return
|
|
356
|
+
}
|
|
357
|
+
if (!mode) {
|
|
298
358
|
const state = readSkillState(root)
|
|
299
359
|
say(`ai4kanban ${VERSION} — the coding agent skill in ${root}`)
|
|
300
360
|
say('')
|
|
301
361
|
for (const folder of state.folders) say(` ${folder.path}/ — ${sayFolder(folder)} (${folder.agent})`)
|
|
302
362
|
say('')
|
|
303
|
-
if (!state.installed) say(
|
|
304
|
-
else if (state.outdated) say(
|
|
363
|
+
if (!state.installed) say(`Not installed. \`${PROGRAM} skill install\` writes it, and so does the board UI's button.`)
|
|
364
|
+
else if (state.outdated) say(`Older than this command. \`${PROGRAM} skill install\` brings it up to date.`)
|
|
305
365
|
else say('Up to date. Your coding agent can drive this board.')
|
|
366
|
+
await sayPathState()
|
|
306
367
|
return
|
|
307
368
|
}
|
|
308
369
|
say(`ai4kanban ${VERSION} — adding the coding agent skill to ${root}`)
|
|
@@ -312,14 +373,33 @@ async function cmdSkill(root, install) {
|
|
|
312
373
|
sayNotes()
|
|
313
374
|
if (!result.ok) fail(result.error || 'nothing was written')
|
|
314
375
|
say('')
|
|
315
|
-
say(
|
|
316
|
-
say('with it and no copy in this repo can fall behind.')
|
|
376
|
+
say(`The flows the agent works by ship with the command — \`${PROGRAM} guide\` — so they`)
|
|
377
|
+
say('upgrade with it and no copy in this repo can fall behind.')
|
|
378
|
+
await sayPathState()
|
|
317
379
|
say('')
|
|
318
380
|
say('Now say this to your coding agent to try it:')
|
|
319
381
|
say('')
|
|
320
382
|
say(` ${SETUP_INSTRUCTION}`)
|
|
321
383
|
}
|
|
322
384
|
|
|
385
|
+
// Whether the agent that reads the note it just got will find the command the note tells it
|
|
386
|
+
// to type. Said here, where the skill lands, rather than left for the agent's first board
|
|
387
|
+
// command to discover — that one comes back `command not found`, and an agent that meets
|
|
388
|
+
// that mid-task stops and asks instead of doing the work.
|
|
389
|
+
//
|
|
390
|
+
// The note itself carries the fallback, so nothing is broken either way. This is so the
|
|
391
|
+
// user knows what their agent is about to do, and what one line would spare it.
|
|
392
|
+
async function sayPathState() {
|
|
393
|
+
const { readCommandState } = await rules()
|
|
394
|
+
const command = typeof readCommandState === 'function' ? readCommandState() : null
|
|
395
|
+
if (!command || command.onPath) return
|
|
396
|
+
say('')
|
|
397
|
+
say('There is no `akb` on your PATH. The note tells your agent what to run instead — the')
|
|
398
|
+
say(`copy in this project, or \`npx --yes ${NAME}@${VERSION}\`. One line makes it direct:`)
|
|
399
|
+
say('')
|
|
400
|
+
say(` ${GET_LINE}`)
|
|
401
|
+
}
|
|
402
|
+
|
|
323
403
|
function sayFolder(folder) {
|
|
324
404
|
if (folder.state === 'absent') return 'not installed'
|
|
325
405
|
if (folder.state === 'linked') return 'a symlink into a source checkout — never written over'
|
|
@@ -438,13 +518,15 @@ const HELP = `ai4kanban ${VERSION} — set up and update the AI4Kanban board.
|
|
|
438
518
|
Get the command: ${GET_LINE}
|
|
439
519
|
Move to a newer one: ${NEWER_LINE}
|
|
440
520
|
|
|
441
|
-
|
|
442
|
-
\`npx --yes ai4kanban@latest <command>\` — the same command, fetched each time.
|
|
521
|
+
${INTRO}
|
|
443
522
|
|
|
444
523
|
akb install [--tracks a,b,c] scaffold docs/kanban/ — the board, and nothing else
|
|
445
524
|
akb skill whether a coding agent can drive this board
|
|
446
|
-
akb skill install add the skill: SKILL.md
|
|
447
|
-
|
|
525
|
+
akb skill install add the skill: SKILL.md into .claude/skills/kanban/
|
|
526
|
+
and .agents/skills/kanban/
|
|
527
|
+
akb skill refresh rewrite a skill that is already here, and write none
|
|
528
|
+
that isn't — how the note learns a new spelling of the
|
|
529
|
+
command
|
|
448
530
|
akb update refresh an installed skill, repair a board written by an
|
|
449
531
|
older version, and say if a newer command is out
|
|
450
532
|
akb version print this version
|
|
@@ -456,7 +538,7 @@ Options
|
|
|
456
538
|
|
|
457
539
|
Installing writes the board and nothing outside docs/kanban/. Driving that board from a
|
|
458
540
|
coding agent is a later extra — \`akb skill install\`, or the button in the board UI under
|
|
459
|
-
Configuration →
|
|
541
|
+
Configuration → Agent setup. The flows the agent works by are not copied anywhere either: they
|
|
460
542
|
ship inside this command (\`akb guide\`), so updating the command updates every flow in
|
|
461
543
|
every project at once.
|
|
462
544
|
|
|
@@ -522,9 +604,9 @@ function parse(argv) {
|
|
|
522
604
|
async function cmdBoard(args) {
|
|
523
605
|
const { runBoard } = await rules()
|
|
524
606
|
return runBoard(args, {
|
|
525
|
-
program:
|
|
607
|
+
program: `${PROGRAM} board`,
|
|
526
608
|
style: 'board',
|
|
527
|
-
installHint:
|
|
609
|
+
installHint: `\`${PROGRAM} install\``,
|
|
528
610
|
})
|
|
529
611
|
}
|
|
530
612
|
|
|
@@ -545,6 +627,8 @@ const RUN_COMMANDS = new Set([
|
|
|
545
627
|
'setup',
|
|
546
628
|
'archive',
|
|
547
629
|
'reject',
|
|
630
|
+
// Put a spec agent on a card — a named agent that fills one part of its spec (#187).
|
|
631
|
+
'spec',
|
|
548
632
|
'runs',
|
|
549
633
|
'log',
|
|
550
634
|
'stop',
|
|
@@ -561,7 +645,7 @@ async function cmdRun(args) {
|
|
|
561
645
|
if (typeof runAgent !== 'function') {
|
|
562
646
|
fail('this copy of the board rules is too old to run agents — `npm install -g ai4kanban@latest`, then `akb update`')
|
|
563
647
|
}
|
|
564
|
-
return runAgent(args, { program:
|
|
648
|
+
return runAgent(args, { program: PROGRAM, installHint: `\`${PROGRAM} install\`` })
|
|
565
649
|
}
|
|
566
650
|
|
|
567
651
|
async function main() {
|
|
@@ -601,7 +685,7 @@ async function main() {
|
|
|
601
685
|
// one on purpose — the same rule the run commands follow, where the move that changes
|
|
602
686
|
// something is asked for by name.
|
|
603
687
|
case 'skill':
|
|
604
|
-
return cmdSkill(opts.dir, rest[1] === 'install')
|
|
688
|
+
return cmdSkill(opts.dir, rest[1] === 'install' ? 'install' : rest[1] === 'refresh' ? 'refresh' : null)
|
|
605
689
|
case 'update':
|
|
606
690
|
return cmdUpdate(opts.dir)
|
|
607
691
|
case 'version':
|