kaizen-agent 1.5.6 → 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 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: "Quick preset — low: inline & manual, medium: balanced, ultra: autonomous subagents" },
97
- { key: "mode", values: ["approve", "auto", "plan-only", "review-only"], help: "Where a run stops" },
98
- { key: "runner", values: ["full", "lite"], help: "full: every stage its own cold agent. lite: this session runs them all, cheaper, reviewer has seen the work" },
99
- { key: "build.executor", values: ["subagent", "inline", "ask"], help: "Who carries out the approved plan" },
100
- { key: "approvals.plan", values: ["true", "false"], help: "Stop and show the plan before anything is built" },
101
- { key: "approvals.review", values: ["true", "false"], help: "Stop after the review, before the run is done" },
102
- { key: "approvals.each_file", values: ["true", "false"], help: "Confirm every individual edit" },
103
- { key: "auto_fix.enabled", values: ["true", "false"], help: "Fix findings without asking" },
104
- { key: "auto_fix.min_severity", values: ["critical", "high", "medium", "low"], help: "How bad a finding must be to be fixed automatically" },
105
- { key: "auto_fix.max_iterations", values: ["1", "2", "3", "4", "5"], help: "Fix and recheck rounds before escalating" },
106
- { key: "review.write_memory", values: ["true", "false"], help: "Let the reviewer append lessons to memory.md" },
107
- { key: "review.memory_max_lines", values: ["50", "100", "200", "400"], help: "Size cap on memory.md, a tax on every run" },
108
- { key: "track.default", values: ["", "software", "writing", "communication", "operations", "research"], help: "Pin a track, or leave empty to infer per run" },
109
- { key: "state.keep_runs", values: ["5", "10", "20", "50"], help: "Completed runs kept before the oldest is pruned" },
110
- { key: "git.auto_commit", values: ["false", "true"], help: "Commit the work when a run finishes" },
111
- { key: "git.branch_before_implement", values: ["true", "false"], help: "Branch before building when on the default branch" },
112
- { key: "ui.mouse", values: ["false", "true"], help: "Click to select, click again to act. While on, the terminal cannot select text with the mouse" },
113
- { key: "agent.default", values: ["auto", "claude", "codex", "agy", "opencode", "gemini"], help: "Default coding agent to launch from dashboard backlog" },
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) ?? read(defaults, s.key) ?? s.values[0]!,
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
- value: s.key === "preset" ? currentPreset : (read(text, s.key) ?? c.dim("(default)")),
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.key.length)) + 4;
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\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.key.padEnd(width);
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
- stdout.write(`\n ${c.dim(SETTINGS[active]!.help)}\n`);
198
- stdout.write(`\n ${c.dim("↑↓ move · ←→ change · backspace back")} ${saved}\n`);
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 - 4;
257
- if (row < 0 || row >= SETTINGS.length) continue;
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;
@@ -186,3 +186,12 @@ test("notes log: append to a run and to an idea; attachments saved and made abso
186
186
  expect(requestOf("x", `![s](${rel})`, state)).toBe(`x\n\n![s](${join(state, rel)})`);
187
187
  expect(saveAttachment(state, "2026-09-01-waiting", "b.txt", new Uint8Array([2]))).toMatch(/^runs\/2026-09-01-waiting\/attachments\//);
188
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);
@@ -617,10 +646,11 @@ export function projectOf(from: string) {
617
646
  : (from.endsWith(".kaizen") ? dirname(from) : from);
618
647
  }
619
648
 
620
- 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(" ");
621
651
  return process.platform === "win32"
622
- ? `cd '${projectDir}'; & ${agentCmd} '${prompt.replace(/'/g, "''")}'`
623
- : `cd "${projectDir}" && ${agentCmd} "${prompt}"`;
652
+ ? `cd '${projectDir}'; & ${bin} '${prompt.replace(/'/g, "''")}'`
653
+ : `cd "${projectDir}" && ${bin} "${prompt}"`;
624
654
  }
625
655
 
626
656
  export function launchRun(it: Item, from: string): Launch {
@@ -630,8 +660,9 @@ export function launchRun(it: Item, from: string): Launch {
630
660
  const agent = detectDefaultAgent(projectDir);
631
661
  const agentBin = Bun.which(agent.cmd) ?? agent.cmd;
632
662
  const prompt = requestOf(it.where ? `/kaizen ${it.text} (${it.where})` : `/kaizen ${it.text}`, it.notes, from);
633
- const fullCmd = [agentBin, prompt];
634
- const manual = manualCommand(projectDir, agent.cmd, prompt);
663
+ const flags = agentFlags(projectDir, agent.cmd);
664
+ const fullCmd = [agentBin, ...flags, prompt];
665
+ const manual = manualCommand(projectDir, agent.cmd, prompt, flags);
635
666
 
636
667
  const term = findTerminal(projectDir, fullCmd);
637
668
  if (!term) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.5.6",
3
+ "version": "1.6.0",
4
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": {
@@ -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` | `""` (inferred) | A repo that is always one kind of work, e.g. a docs repo |
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 |
@@ -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 — are edited by
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). Leave empty to infer per run.
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
 
@@ -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` | `""` (empty, inferred per run) | Pin a track for a repo that is always one kind of work |
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.key}</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>`}`;
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()}))}