kaizen-agent 1.5.5 → 1.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/README.md +3 -0
- package/cli/settings.ts +52 -29
- package/cli/state.test.ts +15 -2
- package/cli/state.ts +44 -9
- package/package.json +1 -1
- package/skills/kaizen/README.md +1 -1
- package/skills/kaizen/SKILL.md +11 -1
- package/skills/kaizen/config.default.yml +24 -2
- package/skills/kaizen/spec.md +12 -0
- package/skills/kaizen-help/SKILL.md +4 -1
- package/web/board.html +1 -1
package/README.md
CHANGED
|
@@ -543,6 +543,9 @@ in one can be finished in another. See [adapters.md](https://github.com/hfadhlul
|
|
|
543
543
|
| `auto_fix.min_severity` | `high` | Findings this bad or worse get fixed without asking |
|
|
544
544
|
| `approvals.plan` | `true` | Stop and show the plan before anything is built |
|
|
545
545
|
| `ui.mouse` | `false` | Click to select, click again to act; while on, the terminal cannot select text |
|
|
546
|
+
| `subagents.model` | `inherit` | Model the stage subagents run on: `inherit`, `opus`, `sonnet`, `haiku` (Claude Code only) |
|
|
547
|
+
| `agent.<cmd>.model`, `agent.<cmd>.effort` | — | Model/effort the board launches a session with, per tool: `claude`, `codex`, `agy`, `opencode`, `gemini`. Model names are the tool's own (`agy models`) |
|
|
548
|
+
| `subagents.effort` | `inherit` | Reasoning effort for the stage subagents: `inherit`, `low`, `medium`, `high` (Claude Code only) |
|
|
546
549
|
|
|
547
550
|
```bash
|
|
548
551
|
kaizen settings
|
package/cli/settings.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from
|
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
|
|
7
|
-
type Setting = { key: string; values: string[]; help: string };
|
|
7
|
+
type Setting = { key: string; label: string; group: string; values: string[]; help: string };
|
|
8
8
|
|
|
9
9
|
export type PresetName = "low" | "medium" | "ultra";
|
|
10
10
|
|
|
@@ -93,24 +93,26 @@ export function applyPreset(text: string, preset: PresetName, defaults: string):
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
const SETTINGS: Setting[] = [
|
|
96
|
-
{ key: "preset", values: ["low", "medium", "ultra", "custom"], help: "
|
|
97
|
-
{ key: "mode", values: ["approve", "auto", "plan-only", "review-only"], help: "
|
|
98
|
-
{ key: "
|
|
99
|
-
{ key: "
|
|
100
|
-
{ key: "
|
|
101
|
-
{ key: "approvals.
|
|
102
|
-
{ key: "
|
|
103
|
-
{ key: "
|
|
104
|
-
{ key: "
|
|
105
|
-
{ key: "
|
|
106
|
-
{ key: "review
|
|
107
|
-
{ key: "
|
|
108
|
-
{ key: "
|
|
109
|
-
{ key: "
|
|
110
|
-
{ key: "
|
|
111
|
-
{ key: "
|
|
112
|
-
{ key: "
|
|
113
|
-
{ key: "
|
|
96
|
+
{ key: "preset", label: "Preset", group: "Preset", values: ["low", "medium", "ultra", "custom"], help: "Sets every row below at once. low: you do it here, step by step. medium: balanced. ultra: hands-off. Change any row and it becomes custom" },
|
|
97
|
+
{ key: "mode", label: "Stop for approval", group: "Run", values: ["approve", "auto", "plan-only", "review-only"], help: "approve: pause and ask you at each enabled checkpoint. auto: never pause. plan-only: write the plan and stop. review-only: just audit existing work" },
|
|
98
|
+
{ key: "git.auto_commit", label: "Commit when done", group: "Run", values: ["false", "true"], help: "Commit the work when a run finishes. Never pushes" },
|
|
99
|
+
{ key: "state.keep_runs", label: "Runs to keep", group: "Run", values: ["5", "10", "20", "50"], help: "Finished runs kept before the oldest is pruned. Open backlog items are rescued first" },
|
|
100
|
+
{ key: "ui.mouse", label: "Enable mouse", group: "Run", values: ["false", "true"], help: "Click to select, click again to act. While on, the terminal cannot select text with the mouse" },
|
|
101
|
+
{ key: "approvals.plan", label: "Approve the plan first", group: "Planning", values: ["true", "false"], help: "Show the plan and wait for your OK before anything is built" },
|
|
102
|
+
{ key: "track.default", label: "Kind of work", group: "Planning", values: ["auto", "software", "writing", "communication", "operations", "research"], help: "Pin what runs in this project are (a docs repo is always writing). auto: the planner infers it per run" },
|
|
103
|
+
{ key: "build.executor", label: "Who builds", group: "Building", values: ["subagent", "inline", "ask"], help: "subagent: a separate agent builds and reports back. inline: this session builds while you watch. ask: choose at the plan approval" },
|
|
104
|
+
{ key: "approvals.each_file", label: "Confirm every file edit", group: "Building", values: ["true", "false"], help: "Ask before each individual file change. Maximum control, slowest" },
|
|
105
|
+
{ key: "git.branch_before_implement", label: "Branch before building", group: "Building", values: ["true", "false"], help: "Create a branch before building when you are on the default branch" },
|
|
106
|
+
{ key: "approvals.review", label: "Approve after review", group: "Review", values: ["true", "false"], help: "After the review, wait for your OK before the run is called done" },
|
|
107
|
+
{ key: "auto_fix.enabled", label: "Fix findings automatically", group: "Review", values: ["true", "false"], help: "true: findings severe enough get fixed and re-reviewed without asking. false: every finding stops and asks you" },
|
|
108
|
+
{ key: "auto_fix.min_severity", label: "Auto-fix from severity", group: "Review", values: ["critical", "high", "medium", "low"], help: "The least severe finding still fixed automatically. low fixes everything; critical fixes almost nothing" },
|
|
109
|
+
{ key: "auto_fix.max_iterations", label: "Max fix rounds", group: "Review", values: ["1", "2", "3", "4", "5"], help: "Fix-then-recheck rounds before leftovers are handed to you. Each round is a full build plus a full review, the priciest knob here" },
|
|
110
|
+
{ key: "review.write_memory", label: "Remember lessons", group: "Review", values: ["true", "false"], help: "Let the reviewer save what it learned to .kaizen/memory.md, which every future run reads" },
|
|
111
|
+
{ key: "review.memory_max_lines", label: "Memory size cap", group: "Review", values: ["50", "100", "200", "400"], help: "Lines memory.md may hold before old lessons are pruned. Every run reads it, so bigger costs more" },
|
|
112
|
+
{ key: "runner", label: "Stage isolation", group: "Agents", values: ["full", "lite"], help: "full: each stage is a fresh agent, so the reviewer never saw the work (costs more). lite: this one session does every stage, cheaper, reviewer may miss its own mistakes" },
|
|
113
|
+
{ key: "subagents.model", label: "Model for stage agents", group: "Agents", values: ["inherit", "opus", "sonnet", "haiku"], help: "Which Claude model the planner, builder and reviewer run on. inherit: same as your main session. Claude Code only" },
|
|
114
|
+
{ key: "subagents.effort", label: "Effort for stage agents", group: "Agents", values: ["inherit", "low", "medium", "high"], help: "How hard the stage agents think. low is faster and cheaper; high finds more. Claude Code only" },
|
|
115
|
+
{ key: "agent.default", label: "Tool the board launches", group: "Agents", values: ["auto", "claude", "codex", "agy", "opencode", "gemini"], help: "Coding tool opened when you start a run from the board. auto: Claude Code if installed, else the first one found" },
|
|
114
116
|
];
|
|
115
117
|
|
|
116
118
|
// The same rows and the same write the TUI uses, for `kaizen web`'s settings panel:
|
|
@@ -124,7 +126,7 @@ export function listSettings(repoRoot: string) {
|
|
|
124
126
|
file: tilde(file),
|
|
125
127
|
rows: SETTINGS.map((s) => ({
|
|
126
128
|
...s,
|
|
127
|
-
value: s.key === "preset" ? preset : read(text, s.key)
|
|
129
|
+
value: s.key === "preset" ? preset : (read(text, s.key) || read(defaults, s.key)) ?? s.values[0]!,
|
|
128
130
|
})),
|
|
129
131
|
};
|
|
130
132
|
}
|
|
@@ -176,26 +178,48 @@ export async function settings(repoRoot: string, standalone = true) {
|
|
|
176
178
|
|
|
177
179
|
const defaults = readFileSync(join(repoRoot, "skills/kaizen/config.default.yml"), "utf8");
|
|
178
180
|
let active = 0, saved = "";
|
|
181
|
+
// Screen line -> setting index, rebuilt on every draw; the mouse clicks by line.
|
|
182
|
+
let rowAt: number[] = [];
|
|
179
183
|
const draw = () => {
|
|
180
184
|
const text = readFileSync(file, "utf8");
|
|
181
185
|
const currentPreset = detectPreset(text, defaults);
|
|
186
|
+
// Booleans as a checkbox, numeric scales as a level bar; the file still holds the words.
|
|
187
|
+
const show = (s: Setting, v: string) => {
|
|
188
|
+
if (s.values.length === 2 && s.values.includes("true")) return v === "true" ? "[✓] on" : "[ ] off";
|
|
189
|
+
if (s.values.every((x) => /^\d+$/.test(x))) {
|
|
190
|
+
const at = s.values.indexOf(v);
|
|
191
|
+
return at < 0 ? v : "▮".repeat(at + 1) + "▯".repeat(s.values.length - at - 1) + " " + v;
|
|
192
|
+
}
|
|
193
|
+
return v;
|
|
194
|
+
};
|
|
182
195
|
const rows = SETTINGS.map((s) => ({
|
|
183
196
|
s,
|
|
184
|
-
|
|
197
|
+
// Older configs spell "infer the track" as an empty string; show what it means.
|
|
198
|
+
value: s.key === "preset" ? currentPreset : show(s, (read(text, s.key) || (s.key === "track.default" ? "auto" : undefined)) ?? c.dim("(default)")),
|
|
185
199
|
}));
|
|
186
|
-
const width = Math.max(...SETTINGS.map((s) => s.
|
|
200
|
+
const width = Math.max(...SETTINGS.map((s) => s.label.length)) + 4;
|
|
187
201
|
|
|
188
202
|
stdout.write("\x1b[H\x1b[2J"); // home, clear
|
|
189
|
-
stdout.write(`\n ${c.bold("kaizen settings")} ${c.dim(tilde(file))}\n
|
|
203
|
+
stdout.write(`\n ${c.bold("kaizen settings")} ${c.dim(tilde(file))}\n`);
|
|
204
|
+
rowAt = [];
|
|
205
|
+
let line = 2, group = "";
|
|
190
206
|
for (const [i, { s, value }] of rows.entries()) {
|
|
207
|
+
if (s.group !== group) {
|
|
208
|
+
group = s.group;
|
|
209
|
+
stdout.write(`\n ${c.dim(group.toUpperCase())}\n`);
|
|
210
|
+
line += 2;
|
|
211
|
+
}
|
|
191
212
|
const on = i === active;
|
|
192
|
-
const name = s.
|
|
213
|
+
const name = s.label.padEnd(width);
|
|
193
214
|
stdout.write(on
|
|
194
215
|
? ` ${c.cyan("›")} ${c.bold(name)}${c.inv(` ${value} `)}\n`
|
|
195
216
|
: ` ${c.dim(name)}${value}\n`);
|
|
217
|
+
rowAt[line++] = i;
|
|
196
218
|
}
|
|
197
|
-
|
|
198
|
-
stdout.write(`\n ${c.dim("
|
|
219
|
+
const cur = SETTINGS[active]!;
|
|
220
|
+
stdout.write(`\n ${c.dim(cur.key)} ${c.dim("options:")} ${cur.values.map((v) => v || '""').join(c.dim(" / "))}\n`);
|
|
221
|
+
stdout.write(` ${cur.help}\n`);
|
|
222
|
+
stdout.write(`\n ${c.dim("↑↓ pick a setting · ←→ or enter to change it (saved instantly) · esc back")} ${saved}\n`);
|
|
199
223
|
};
|
|
200
224
|
|
|
201
225
|
stdout.write("\x1b[?1049h\x1b[?25l"); // alternate screen, hide cursor
|
|
@@ -245,7 +269,6 @@ export async function settings(repoRoot: string, standalone = true) {
|
|
|
245
269
|
const keys = chunk.toString();
|
|
246
270
|
for (let i = 0; i < keys.length; i++) {
|
|
247
271
|
const rest = keys.slice(i);
|
|
248
|
-
// Rows begin on the fourth line: blank, title, blank, then the settings.
|
|
249
272
|
const m = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])/.exec(rest);
|
|
250
273
|
if (m) {
|
|
251
274
|
i += m[0].length - 1;
|
|
@@ -253,8 +276,8 @@ export async function settings(repoRoot: string, standalone = true) {
|
|
|
253
276
|
if (button === 64) active = (active - 1 + SETTINGS.length) % SETTINGS.length;
|
|
254
277
|
else if (button === 65) active = (active + 1) % SETTINGS.length;
|
|
255
278
|
else if (press && button === 0) {
|
|
256
|
-
const row = y -
|
|
257
|
-
if (row
|
|
279
|
+
const row = rowAt[y - 1];
|
|
280
|
+
if (row === undefined) continue;
|
|
258
281
|
// Click to select; click the selected row to cycle it, which is what
|
|
259
282
|
// the right arrow already does.
|
|
260
283
|
if (row === active) { cycle(1); continue; }
|
package/cli/state.test.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import {
|
|
8
8
|
readRuns, readInbox, writeInbox, replaceIdea, abandonRun, allBacklog, backlog,
|
|
9
9
|
statusOf, columnOf, parseItem, startedRuns, boardCards, readArchive, setArchived,
|
|
10
|
-
requestOf, readNotes, writeNotes, manualCommand, parseNotes, formatNote, appendNote, saveAttachment,
|
|
10
|
+
requestOf, agentFlags, readNotes, writeNotes, manualCommand, parseNotes, formatNote, appendNote, saveAttachment,
|
|
11
11
|
} from "./state.ts";
|
|
12
12
|
|
|
13
13
|
let state: string;
|
|
@@ -57,7 +57,11 @@ test("statusOf: waiting beats stage, mtime decides running vs stalled", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
test("columnOf: every stage word lands somewhere", () => {
|
|
60
|
-
expect(["plan", "build", "review", "done", "abandoned", "weird"].map(columnOf)).toEqual([1, 2, 3, 4, 4, 1]);
|
|
60
|
+
expect(["plan", "build", "review", "done", "abandoned", "weird"].map((s) => columnOf(s))).toEqual([1, 2, 3, 4, 4, 1]);
|
|
61
|
+
expect(columnOf("implement", "approvals.review")).toBe(3);
|
|
62
|
+
expect(columnOf("implement", "findings")).toBe(3);
|
|
63
|
+
expect(columnOf("implement", "approvals.each_file")).toBe(2);
|
|
64
|
+
expect(columnOf("plan", "approvals.plan")).toBe(1);
|
|
61
65
|
});
|
|
62
66
|
|
|
63
67
|
test("inbox round-trips and ignores malformed lines", () => {
|
|
@@ -182,3 +186,12 @@ test("notes log: append to a run and to an idea; attachments saved and made abso
|
|
|
182
186
|
expect(requestOf("x", ``, state)).toBe(`x\n\n})`);
|
|
183
187
|
expect(saveAttachment(state, "2026-09-01-waiting", "b.txt", new Uint8Array([2]))).toMatch(/^runs\/2026-09-01-waiting\/attachments\//);
|
|
184
188
|
});
|
|
189
|
+
|
|
190
|
+
test("agentFlags: per-tool model and effort from the agent block, inline or nested", () => {
|
|
191
|
+
const cfg = `agent:\n default: auto\n codex: { model: gpt-5, effort: low }\n claude:\n model: sonnet\n effort: low\n gemini:\n model: "gemini-2.5-pro"\n`;
|
|
192
|
+
expect(agentFlags("/p", "codex", cfg)).toEqual(["-m", "gpt-5", "-c", "model_reasoning_effort=low"]);
|
|
193
|
+
expect(agentFlags("/p", "claude", cfg)).toEqual(["--model", "sonnet", "--effort", "low"]);
|
|
194
|
+
expect(agentFlags("/p", "gemini", cfg)).toEqual(["-m", "gemini-2.5-pro"]);
|
|
195
|
+
expect(agentFlags("/p", "agy", cfg)).toEqual([]);
|
|
196
|
+
expect(agentFlags("/p", "codex", "agent:\n default: codex\n")).toEqual([]);
|
|
197
|
+
});
|
package/cli/state.ts
CHANGED
|
@@ -41,6 +41,35 @@ export function detectDefaultAgent(projectDir: string): { name: string; cmd: str
|
|
|
41
41
|
return { name: "Claude Code", cmd: "claude" };
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Flags that pin the launched session's model and effort, from the config chain:
|
|
45
|
+
// agent:
|
|
46
|
+
// codex: { model: gpt-5, effort: low }
|
|
47
|
+
// Each tool spells the flag its own way; a tool not listed here takes none.
|
|
48
|
+
const MODEL_FLAGS: Record<string, (m: string) => string[]> = {
|
|
49
|
+
claude: (m) => ["--model", m], agy: (m) => ["--model", m], codex: (m) => ["-m", m],
|
|
50
|
+
gemini: (m) => ["-m", m], opencode: (m) => ["-m", m],
|
|
51
|
+
};
|
|
52
|
+
const EFFORT_FLAGS: Record<string, (e: string) => string[]> = {
|
|
53
|
+
claude: (e) => ["--effort", e], agy: (e) => ["--effort", e],
|
|
54
|
+
codex: (e) => ["-c", `model_reasoning_effort=${e}`],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export function agentFlags(projectDir: string, cmd: string, configText?: string): string[] {
|
|
58
|
+
if (configText === undefined) {
|
|
59
|
+
const f = [join(projectDir, ".kaizen", "config.yml"), join(home, ".kaizen", "config.yml")].find(existsSync);
|
|
60
|
+
configText = f ? readFileSync(f, "utf8") : "";
|
|
61
|
+
}
|
|
62
|
+
// ponytail: regex over the yaml, like detectDefaultAgent; a parser when a third key needs it
|
|
63
|
+
const block = new RegExp(`^agent:\\n(?:[ \\t]+.*\\n)*?[ \\t]+${cmd}:[ \\t]*(\\{[^}]*\\}|\\n(?:[ \\t]+[a-z]+:.*\\n?)+)`, "m").exec(configText);
|
|
64
|
+
if (!block) return [];
|
|
65
|
+
const get = (k: string) => new RegExp(`\\b${k}:[ \\t]*["']?([^"',}\\s]+)`).exec(block[1])?.[1];
|
|
66
|
+
const model = get("model"), effort = get("effort");
|
|
67
|
+
return [
|
|
68
|
+
...(model && MODEL_FLAGS[cmd] ? MODEL_FLAGS[cmd](model) : []),
|
|
69
|
+
...(effort && EFFORT_FLAGS[cmd] ? EFFORT_FLAGS[cmd](effort) : []),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
44
73
|
export function findTerminal(cwd: string, fullCmd: string[]): { cmd: string[]; detached: boolean } | null {
|
|
45
74
|
const hasDisplay = Boolean(process.env.WAYLAND_DISPLAY || process.env.DISPLAY);
|
|
46
75
|
const inTmux = Boolean(process.env.TMUX);
|
|
@@ -348,12 +377,16 @@ export function statusOf(r: Run, now: number): Status {
|
|
|
348
377
|
return now - r.moved < RUNNING_WITHIN_MS ? "running" : "stalled";
|
|
349
378
|
}
|
|
350
379
|
|
|
351
|
-
// A stage this build has never heard of still belongs somewhere visible
|
|
352
|
-
//
|
|
353
|
-
|
|
380
|
+
// A stage this build has never heard of still belongs somewhere visible. What the
|
|
381
|
+
// run is waiting on says where it really is (a run awaiting the review approval is
|
|
382
|
+
// in Review whatever its stage word), and PLANNING is the honest guess otherwise:
|
|
383
|
+
// the run has started and has not finished.
|
|
384
|
+
export function columnOf(stage: string, awaiting: string | null = null) {
|
|
354
385
|
if (stage === "build") return 2;
|
|
355
386
|
if (stage === "review") return 3;
|
|
356
387
|
if (stage === "done" || stage === "abandoned") return 4;
|
|
388
|
+
if (awaiting === "approvals.review" || awaiting === "findings") return 3;
|
|
389
|
+
if (awaiting === "approvals.each_file") return 2;
|
|
357
390
|
return 1;
|
|
358
391
|
}
|
|
359
392
|
|
|
@@ -588,7 +621,7 @@ export function boardCards(states: string[], now: number): Card[] {
|
|
|
588
621
|
}
|
|
589
622
|
for (const r of readRuns(st)) {
|
|
590
623
|
next.push({
|
|
591
|
-
kind: "run", id: r.id, status: statusOf(r, now), text: short(r.id), state: st, where, column: columnOf(r.stage), moved: r.moved,
|
|
624
|
+
kind: "run", id: r.id, status: statusOf(r, now), text: short(r.id), state: st, where, column: columnOf(r.stage, r.awaiting), moved: r.moved,
|
|
592
625
|
title: requestTitle(body(st, r.id)),
|
|
593
626
|
awaiting: r.stage === "abandoned" ? null : r.awaiting,
|
|
594
627
|
dim: r.stage === "abandoned",
|
|
@@ -613,10 +646,11 @@ export function projectOf(from: string) {
|
|
|
613
646
|
: (from.endsWith(".kaizen") ? dirname(from) : from);
|
|
614
647
|
}
|
|
615
648
|
|
|
616
|
-
export function manualCommand(projectDir: string, agentCmd: string, prompt: string) {
|
|
649
|
+
export function manualCommand(projectDir: string, agentCmd: string, prompt: string, flags: string[] = []) {
|
|
650
|
+
const bin = [agentCmd, ...flags].join(" ");
|
|
617
651
|
return process.platform === "win32"
|
|
618
|
-
? `cd '${projectDir}'; & ${
|
|
619
|
-
: `cd "${projectDir}" && ${
|
|
652
|
+
? `cd '${projectDir}'; & ${bin} '${prompt.replace(/'/g, "''")}'`
|
|
653
|
+
: `cd "${projectDir}" && ${bin} "${prompt}"`;
|
|
620
654
|
}
|
|
621
655
|
|
|
622
656
|
export function launchRun(it: Item, from: string): Launch {
|
|
@@ -626,8 +660,9 @@ export function launchRun(it: Item, from: string): Launch {
|
|
|
626
660
|
const agent = detectDefaultAgent(projectDir);
|
|
627
661
|
const agentBin = Bun.which(agent.cmd) ?? agent.cmd;
|
|
628
662
|
const prompt = requestOf(it.where ? `/kaizen ${it.text} (${it.where})` : `/kaizen ${it.text}`, it.notes, from);
|
|
629
|
-
const
|
|
630
|
-
const
|
|
663
|
+
const flags = agentFlags(projectDir, agent.cmd);
|
|
664
|
+
const fullCmd = [agentBin, ...flags, prompt];
|
|
665
|
+
const manual = manualCommand(projectDir, agent.cmd, prompt, flags);
|
|
631
666
|
|
|
632
667
|
const term = findTerminal(projectDir, fullCmd);
|
|
633
668
|
if (!term) {
|
package/package.json
CHANGED
package/skills/kaizen/README.md
CHANGED
|
@@ -206,7 +206,7 @@ the defaults.
|
|
|
206
206
|
| `auto_fix.max_iterations` | `2` | Fix/recheck rounds before whatever is left is escalated to you |
|
|
207
207
|
| `review.memory_max_lines` | `100` | The cap on `memory.md`, which every stage of every run reads. Raise only if the lessons are genuinely all load-bearing |
|
|
208
208
|
| `git.auto_commit` | `false` | Leave it. Kaizen does not commit or push unless you ask |
|
|
209
|
-
| `track.default` | `
|
|
209
|
+
| `track.default` | `auto` (inferred) | A repo that is always one kind of work, e.g. a docs repo |
|
|
210
210
|
| `backlog.enabled` | `true` | Set `false` to skip both backlog capture points; the run still executes, just without a `06-backlog.md` |
|
|
211
211
|
| `backlog.file` | `06-backlog.md` | The per-run file name. Rarely. |
|
|
212
212
|
| `backlog.statuses` | `[open, done, rejected]` | Rarely. Only `open` items are printed |
|
package/skills/kaizen/SKILL.md
CHANGED
|
@@ -100,6 +100,8 @@ nearest first, so a change lands globally unless this project keeps its own conf
|
|
|
100
100
|
| `git.auto_commit` | `true`, `false` | Commit the work when a run finishes |
|
|
101
101
|
| `git.branch_before_implement` | `true`, `false` | Branch before building when on the default branch |
|
|
102
102
|
| `ui.mouse` | `false`, `true` | Click to select, click again to act; while on, the terminal cannot select text |
|
|
103
|
+
| `subagents.model` | `inherit`, `opus`, `sonnet`, `haiku` | Model the stage subagents run on (Claude Code only) |
|
|
104
|
+
| `subagents.effort` | `inherit`, `low`, `medium`, `high` | Reasoning effort for the stage subagents (Claude Code only) |
|
|
103
105
|
| `agent.default` | `auto`, `claude`, `codex`, `agy`, `opencode`, `gemini` | Coding agent to launch from dashboard backlog |
|
|
104
106
|
|
|
105
107
|
Rules for writing the file:
|
|
@@ -114,7 +116,8 @@ Rules for writing the file:
|
|
|
114
116
|
- After writing, print the setting and its new value, and nothing else. No summary of
|
|
115
117
|
the whole file.
|
|
116
118
|
|
|
117
|
-
Settings not in the table — the review checks, backlog and state paths
|
|
119
|
+
Settings not in the table — the review checks, backlog and state paths, and the per-tool
|
|
120
|
+
`agent.<cmd>.model` / `agent.<cmd>.effort` the board launches a session with — are edited by
|
|
118
121
|
hand in `config.yml`, which is commented throughout. Say so rather than offering a
|
|
119
122
|
picker with thirty options.
|
|
120
123
|
|
|
@@ -318,6 +321,13 @@ the choice at the plan approval. Building inline does not make the review less
|
|
|
318
321
|
independent: the reviewer is a separate agent in every case, and never sees the
|
|
319
322
|
conversation the work happened in.
|
|
320
323
|
|
|
324
|
+
`subagents.model` and `subagents.effort` apply to every stage dispatched this way.
|
|
325
|
+
Pass `model` on the Agent call when it is not `inherit`. Effort has no call-time
|
|
326
|
+
knob: when it is not `inherit`, make sure the frontmatter of the agent's definition
|
|
327
|
+
(`~/.claude/agents/kaizen-<stage>.md`, or the project's `.claude/agents/` copy)
|
|
328
|
+
carries `effort: <value>` — edit or add that one line, nothing else — before the
|
|
329
|
+
first dispatch of the run. `inherit` means leave the file alone.
|
|
330
|
+
|
|
321
331
|
After a subagent returns, update `state.json` yourself in the main thread. Subagents
|
|
322
332
|
report; the main thread owns the state machine.
|
|
323
333
|
|
|
@@ -64,8 +64,8 @@ track:
|
|
|
64
64
|
# request by the planner and stated back in the plan's Framing for the user to
|
|
65
65
|
# correct at the approval — the user is never asked to categorize their own request.
|
|
66
66
|
# Set this only to pin a default for a repo that is always one kind of work
|
|
67
|
-
# (e.g. a docs repo).
|
|
68
|
-
default:
|
|
67
|
+
# (e.g. a docs repo). auto (or empty) infers per run.
|
|
68
|
+
default: auto
|
|
69
69
|
|
|
70
70
|
review:
|
|
71
71
|
# Checks that run in every track.
|
|
@@ -138,8 +138,30 @@ ui:
|
|
|
138
138
|
# run id out of the dashboard stops working until you leave.
|
|
139
139
|
mouse: false
|
|
140
140
|
|
|
141
|
+
subagents:
|
|
142
|
+
# Claude Code only: other tools (Codex, Antigravity, opencode, gemini) have no
|
|
143
|
+
# per-stage model knob and run every stage on whatever the tool itself is set to.
|
|
144
|
+
# Model each stage subagent runs on, when the runner is full.
|
|
145
|
+
# inherit - whatever the main session uses. The default.
|
|
146
|
+
# opus | sonnet | haiku - pin it. A cheaper model for the builder is the usual
|
|
147
|
+
# reason to touch this; the reviewer on a cheaper model finds less.
|
|
148
|
+
model: inherit
|
|
149
|
+
# Reasoning effort for the stage subagents: inherit | low | medium | high.
|
|
150
|
+
# Set through the agent definition's frontmatter.
|
|
151
|
+
effort: inherit
|
|
152
|
+
|
|
141
153
|
agent:
|
|
142
154
|
# Default coding agent to launch from dashboard backlog (auto | claude | codex | agy | opencode | gemini).
|
|
143
155
|
# auto picks Claude Code if installed, otherwise the first agent found on PATH.
|
|
144
156
|
default: auto
|
|
157
|
+
# Per-tool model and effort for the session the board launches, passed as that
|
|
158
|
+
# tool's own flags (claude --model/--effort, agy --model/--effort, codex -m and
|
|
159
|
+
# model_reasoning_effort, gemini -m, opencode -m). Model names are the tool's own:
|
|
160
|
+
# `agy models`, `opencode models`, Claude's opus|sonnet|haiku. Absent = tool default.
|
|
161
|
+
# Uncomment the tool you launch:
|
|
162
|
+
# claude: { model: opus, effort: high }
|
|
163
|
+
# codex: { model: gpt-5, effort: low }
|
|
164
|
+
# agy: { model: gemini-3.1-pro-low }
|
|
165
|
+
# opencode: { model: anthropic/claude-sonnet-4-6 }
|
|
166
|
+
# gemini: { model: gemini-2.5-pro }
|
|
145
167
|
|
package/skills/kaizen/spec.md
CHANGED
|
@@ -104,6 +104,18 @@ Write into `00-request.md`: the verbatim request, the deliverable, what done mea
|
|
|
104
104
|
and what wrong means. Record in `state.json`: `id`, `mode`, `runner`, `agent`, `track`,
|
|
105
105
|
`stage: "plan"`, `awaiting: null`.
|
|
106
106
|
|
|
107
|
+
`stage` takes exactly one of five words, and boards key their columns on them:
|
|
108
|
+
`plan`, `build`, `review`, `done`, `abandoned`. Set it when the stage starts —
|
|
109
|
+
`build` before the builder touches anything, `review` before the reviewer reads — as
|
|
110
|
+
its own write, not tacked onto a longer command that may not finish. Any other word
|
|
111
|
+
(`implement`, `fix`, `building`) is unknown to every reader and shows as still planning.
|
|
112
|
+
|
|
113
|
+
`awaiting` is `null` or the config key of the approval being waited on —
|
|
114
|
+
`approvals.plan`, `approvals.review`, `approvals.each_file` — or `findings` when the
|
|
115
|
+
run has stopped for the user to pick which review findings to fix. Boards read it to
|
|
116
|
+
say what a run is waiting for and where it sits; a word outside this list is shown but
|
|
117
|
+
not understood.
|
|
118
|
+
|
|
107
119
|
`agent` is the tool this run is happening in — `claude`, `codex`, `agy`, `opencode`,
|
|
108
120
|
`gemini`, or `cursor` — written once at intake and never changed, so a board can say
|
|
109
121
|
which agent to look for when a run is waiting. The tool running this stage knows what
|
|
@@ -85,7 +85,7 @@ Each iteration is written to `runs/<id>/05-iterations/`.
|
|
|
85
85
|
| `auto_fix.enabled` | `true` | Auto-fix findings vs. escalate everything |
|
|
86
86
|
| `auto_fix.min_severity` | `high` | Threshold for auto-fix (`critical\|high\|medium\|low`) |
|
|
87
87
|
| `auto_fix.max_iterations` | `2` | Fix/recheck rounds before escalating remaining findings |
|
|
88
|
-
| `track.default` | `
|
|
88
|
+
| `track.default` | `auto` (inferred per run) | Pin a track for a repo that is always one kind of work |
|
|
89
89
|
| `review.write_memory` | `true` | Let the reviewer append lessons to `.kaizen/memory.md` |
|
|
90
90
|
| `backlog.enabled` | `true` | Capture what a run deliberately did not do |
|
|
91
91
|
| `backlog.file` | `06-backlog.md` | Per-run backlog file |
|
|
@@ -94,7 +94,10 @@ Each iteration is written to `runs/<id>/05-iterations/`.
|
|
|
94
94
|
| `state.keep_runs` | `20` | Completed runs kept before pruning oldest (rescues open backlog first) |
|
|
95
95
|
| `git.auto_commit` | `false` | Never commit/push/PR unless the user explicitly asks |
|
|
96
96
|
| `git.branch_before_implement` | `true` | Branch before implement stage when on the default branch |
|
|
97
|
+
| `subagents.model` | `inherit` | Model the stage subagents run on (opus/sonnet/haiku, Claude Code only) |
|
|
98
|
+
| `subagents.effort` | `inherit` | Reasoning effort for the stage subagents (low/medium/high, Claude Code only) |
|
|
97
99
|
| `agent.default` | `auto` | Coding agent to launch from dashboard backlog |
|
|
100
|
+
| `agent.<cmd>.model`, `.effort` | — | Model/effort flags for the launched session, per tool (`claude`, `codex`, `agy`, `opencode`, `gemini`); model names are the tool's own |
|
|
98
101
|
|
|
99
102
|
`.kaizen/config.yml` overrides only the keys it sets; everything else falls back to
|
|
100
103
|
this skill's `config.default.yml`.
|
package/web/board.html
CHANGED
|
@@ -411,7 +411,7 @@ async function openSettings(){
|
|
|
411
411
|
$("psub").textContent=cfg?`theme is this browser's; the rest is ${cfg.file}`:"theme is this browser's";
|
|
412
412
|
$("pbody").innerHTML=`<div class="set"><label>Theme</label>${opts("th",["system","light","dark"],cur)}
|
|
413
413
|
<label>Notifications</label><div class="opts" id="nt"><button>${typeof Notification==="undefined"?"unsupported":Notification.permission==="granted"?"on":"ask"}</button></div></div>
|
|
414
|
-
${cfg?`<hr style="border:0;border-top:1px solid var(--line);margin:16px 0"><div class="set">${cfg.rows.map(r=>`<label title="${esc(r.help)}">${r.
|
|
414
|
+
${cfg?`<hr style="border:0;border-top:1px solid var(--line);margin:16px 0"><div class="set">${cfg.rows.map(r=>`<label title="${esc(r.key+" — "+r.help)}">${r.label}</label>${opts("s-"+r.key.replace(/\./g,"-"),r.values,r.value)}`).join("")}</div>`:`<p style="color:var(--dim);margin-top:20px">Settings could not be read. Run <code>kaizen settings</code>.</p>`}`;
|
|
415
415
|
$("th").querySelectorAll("button").forEach(b=>b.onclick=()=>{const t=b.dataset.v;if(t==="system")delete document.documentElement.dataset.theme;else document.documentElement.dataset.theme=t;try{t==="system"?localStorage.removeItem("kz-theme"):localStorage.setItem("kz-theme",t)}catch(e){}openSettings()});
|
|
416
416
|
$("nt").querySelector("button").onclick=()=>{askNotify();setTimeout(openSettings,300)};
|
|
417
417
|
if(cfg)cfg.rows.forEach(r=>$("s-"+r.key.replace(/\./g,"-")).querySelectorAll("button").forEach(b=>b.onclick=async()=>{if(b.classList.contains("on"))return;const j=await post("/settings",{key:r.key,value:b.dataset.v});if(j.ok)toast(`saved ${r.key} = ${b.dataset.v||'""'}`);openSettings()}))}
|