kaizen-agent 1.2.1 → 1.3.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 +57 -0
- package/cli/install.ts +38 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -226,6 +226,62 @@ kaizen Done, and reviewed. Two things found:
|
|
|
226
226
|
|
|
227
227
|
</details>
|
|
228
228
|
|
|
229
|
+
## The board
|
|
230
|
+
|
|
231
|
+
`kaizen` opens a dashboard; **Board** is the screen where your work lives. Ideas you have
|
|
232
|
+
not started sit on the left, runs sit under the stage they are actually in, and it redraws
|
|
233
|
+
itself as your agents write to disk — a run that moves while you are looking at it moves on
|
|
234
|
+
screen.
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Board ~/code/acme-api
|
|
238
|
+
|
|
239
|
+
IDEA PLANNING BUILDING REVIEW DONE
|
|
240
|
+
────────────────────── ─────────────────── ──────────────── ───────────── ──────────────
|
|
241
|
+
› · rate limit the ◌ nightly digest ● payroll export ● oauth login ○ rename cli
|
|
242
|
+
public API flags
|
|
243
|
+
● landing page ○ csv import
|
|
244
|
+
· retry failed
|
|
245
|
+
webhooks
|
|
246
|
+
|
|
247
|
+
↑↓←→ move n new e edit x reject d delete r run a all projects q back
|
|
248
|
+
● running ◐ stalled ◌ starting ● waiting on you ○ done · idea
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Throw an idea in with `n`** and it lands in `<state.dir>/inbox.md`, one line each, in the
|
|
252
|
+
same format a run's backlog already uses. `e` edits, `x` rejects with a reason, `d` deletes.
|
|
253
|
+
|
|
254
|
+
**`r` starts a run from an idea** — it asks `full` or `lite`, then opens your configured
|
|
255
|
+
agent with the request already typed. The idea retires from the board once a run exists
|
|
256
|
+
that was started from its text, whoever started it, so the same work never appears twice.
|
|
257
|
+
That check spans every project you have opened, because an idea thrown into the global
|
|
258
|
+
inbox becomes a run inside whichever project you launched the agent in.
|
|
259
|
+
|
|
260
|
+
**The dot is what the run is doing.** `●` cyan is running and `◐` is stalled — kaizen
|
|
261
|
+
records no liveness signal anywhere, so "running" means the run's `state.json` has changed
|
|
262
|
+
in the last half hour. A run whose terminal you closed three days ago shows as stalled,
|
|
263
|
+
which is the distinction the board exists to make. `●` amber, with the name in red, is a
|
|
264
|
+
run that cannot move until you decide something; the legend lists only the states actually
|
|
265
|
+
on screen.
|
|
266
|
+
|
|
267
|
+
**A run's stage is kaizen's, not yours.** You cannot drag a card between columns — a board
|
|
268
|
+
that could do that would be lying about what happened. The one exception is `x` on a run
|
|
269
|
+
card, which abandons it: the same transition `/kaizen abort` makes, with your reason
|
|
270
|
+
recorded in the run's approval file.
|
|
271
|
+
|
|
272
|
+
`a` switches between this project and every project kaizen knows about. Below a hundred
|
|
273
|
+
columns the board stacks into a list rather than squeezing five columns into sixty.
|
|
274
|
+
|
|
275
|
+
### Mouse
|
|
276
|
+
|
|
277
|
+
Off by default. Turn it on in `kaizen settings` under `ui.mouse`, and clicking a row or
|
|
278
|
+
card selects it, clicking it again opens or runs it, and the wheel scrolls — on the board,
|
|
279
|
+
the menu, runs, backlog, projects and settings.
|
|
280
|
+
|
|
281
|
+
It ships off because the trade is real: while the TUI holds the mouse, your terminal cannot
|
|
282
|
+
select text with it, so copying a run id out of the dashboard stops working until you
|
|
283
|
+
leave. Hold **Shift** and drag to select text anyway.
|
|
284
|
+
|
|
229
285
|
## Install
|
|
230
286
|
|
|
231
287
|
### Prerequisites: Bun
|
|
@@ -433,6 +489,7 @@ in one can be finished in another. See [adapters.md](https://github.com/hfadhlul
|
|
|
433
489
|
| `build.executor` | `subagent` | Who does the work: a separate agent, `inline` in the current one, or `ask` each time |
|
|
434
490
|
| `auto_fix.min_severity` | `high` | Findings this bad or worse get fixed without asking |
|
|
435
491
|
| `approvals.plan` | `true` | Stop and show the plan before anything is built |
|
|
492
|
+
| `ui.mouse` | `false` | Click to select, click again to act; while on, the terminal cannot select text |
|
|
436
493
|
|
|
437
494
|
```bash
|
|
438
495
|
kaizen settings
|
package/cli/install.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
symlinkSync, mkdirSync, readdirSync, lstatSync, readlinkSync, unlinkSync,
|
|
4
4
|
existsSync, copyFileSync, readFileSync, appendFileSync, rmSync, writeFileSync,
|
|
5
|
+
statSync, cpSync,
|
|
5
6
|
} from "node:fs";
|
|
6
7
|
import { join, dirname, basename } from "node:path";
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
|
+
import { homedir, tmpdir } from "node:os";
|
|
8
9
|
import { applyPreset, detectPreset, PRESETS, type PresetName } from "./settings.ts";
|
|
9
10
|
|
|
10
11
|
const home = homedir();
|
|
@@ -319,8 +320,18 @@ async function resolveRepo() {
|
|
|
319
320
|
}
|
|
320
321
|
return dest;
|
|
321
322
|
}
|
|
322
|
-
|
|
323
|
-
|
|
323
|
+
if (Bun.which("git")) {
|
|
324
|
+
step(`cloning into ${tilde(dest)}`);
|
|
325
|
+
await Bun.$`git clone --quiet ${REPO_URL} ${dest}`;
|
|
326
|
+
return dest;
|
|
327
|
+
}
|
|
328
|
+
// No git: the bunx download already holds everything a clone would, so copy it
|
|
329
|
+
// in place. Upgrades then come from re-running bunx rather than a pull.
|
|
330
|
+
step(`copying into ${tilde(dest)} ${c.dim("(no git)")}`);
|
|
331
|
+
cpSync(here, dest, {
|
|
332
|
+
recursive: true, dereference: true,
|
|
333
|
+
filter: (src) => basename(src) !== "node_modules",
|
|
334
|
+
});
|
|
324
335
|
return dest;
|
|
325
336
|
}
|
|
326
337
|
|
|
@@ -356,10 +367,30 @@ const links = targets.flatMap((a) => [
|
|
|
356
367
|
] : []),
|
|
357
368
|
]);
|
|
358
369
|
|
|
370
|
+
const isWindows = process.platform === "win32";
|
|
371
|
+
|
|
372
|
+
// Windows only hands out file symlinks to an admin or a Developer Mode box, so
|
|
373
|
+
// there the link is a directory junction (allowed for everyone) or, for a plain
|
|
374
|
+
// file, a copy that is refreshed whenever the source moves ahead.
|
|
375
|
+
function makeLink(src: string, dest: string) {
|
|
376
|
+
if (!isWindows) { symlinkSync(src, dest); return; }
|
|
377
|
+
if (statSync(src).isDirectory()) { symlinkSync(src, dest, "junction"); return; }
|
|
378
|
+
copyFileSync(src, dest);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function sameFile(a: string, b: string) {
|
|
382
|
+
try { return readFileSync(a).equals(readFileSync(b)); } catch { return false; }
|
|
383
|
+
}
|
|
384
|
+
|
|
359
385
|
function linkState(src: string, dest: string) {
|
|
360
386
|
try {
|
|
361
387
|
const st = lstatSync(dest);
|
|
362
|
-
if (!st.isSymbolicLink())
|
|
388
|
+
if (!st.isSymbolicLink()) {
|
|
389
|
+
// A copied link on Windows is a real file; it is ours as long as it still
|
|
390
|
+
// matches the source, and stale once the source changes.
|
|
391
|
+
if (isWindows && st.isFile()) return sameFile(src, dest) ? "linked" : "stale";
|
|
392
|
+
return "occupied";
|
|
393
|
+
}
|
|
363
394
|
return readlinkSync(dest) === src ? "linked" : "stale";
|
|
364
395
|
} catch {
|
|
365
396
|
return "missing";
|
|
@@ -385,8 +416,8 @@ for (const [src, sub] of links) {
|
|
|
385
416
|
continue;
|
|
386
417
|
}
|
|
387
418
|
mkdirSync(dir, { recursive: true });
|
|
388
|
-
if (state !== "missing")
|
|
389
|
-
|
|
419
|
+
if (state !== "missing") rmSync(dest, { recursive: true, force: true });
|
|
420
|
+
makeLink(src, dest);
|
|
390
421
|
wrote++;
|
|
391
422
|
if (verbose) console.log(c.dim(` link ${name}`));
|
|
392
423
|
}
|
|
@@ -603,7 +634,7 @@ function clean(out: string) {
|
|
|
603
634
|
// without re-resolving, so an upgrade that only pulls the clone still leaves the
|
|
604
635
|
// next `bunx kaizen-agent` running whatever was cached the first time.
|
|
605
636
|
async function clearBunxCache() {
|
|
606
|
-
const tmp = process.env.TMPDIR ??
|
|
637
|
+
const tmp = process.env.TMPDIR ?? tmpdir();
|
|
607
638
|
let removed = 0;
|
|
608
639
|
try {
|
|
609
640
|
for (const name of readdirSync(tmp)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kaizen-agent",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Staged workflow for AI agents
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Staged workflow for AI agents — code, writing, research, operations: plan, human approval, build, independent review, bounded fix loop.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kaizen-agent": "./cli/install.ts",
|