kaizen-agent 1.22.1 → 1.23.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/cli/settings.ts +1 -0
- package/cli/state.test.ts +20 -0
- package/cli/state.ts +20 -7
- package/cli/web.ts +9 -0
- package/package.json +1 -1
- package/skills/kaizen/config.default.yml +3 -0
package/cli/settings.ts
CHANGED
|
@@ -117,6 +117,7 @@ const SETTINGS: Setting[] = [
|
|
|
117
117
|
{ key: "dictation.model", label: "Dictation model", group: "Board", values: ["tiny", "base", "small", "large-v3-turbo"], help: "Whisper model the web board dictates with, in the browser. tiny ~60 MB rough; base ~165 MB English-leaning; small ~410 MB accurate across languages; large-v3-turbo ~1.5 GB best, needs a capable GPU. Downloaded once on the next mic press" },
|
|
118
118
|
{ key: "sources.every", label: "Gather requests", group: "Board", values: ["off", "1h", "6h", "24h"], help: "How often the board checks your request sources for new ideas while it is running. off: only when you press Gather now" },
|
|
119
119
|
{ key: "board.projects_dir", label: "Projects folder", group: "Board", values: [], text: true, folder: true, help: `A folder your projects live in, e.g. ${process.platform === "win32" ? join(homedir(), "Projects") : "~/Projects"}. Every folder inside it appears in the board's project list, ready to choose; choosing one sets it up. Empty: only projects kaizen already knows` },
|
|
120
|
+
{ key: "board.open", label: "Open the board in", group: "Board", values: ["browser", "app"], help: "browser: a tab in your default browser. app: a Chrome, Edge or Brave window with no tabs or address bar, like a desktop app; your default browser when none is installed" },
|
|
120
121
|
{ key: "ui.mouse", label: "Enable mouse", group: "Terminal", values: ["false", "true"], help: "Click to select, click again to act. While on, the terminal cannot select text with the mouse" },
|
|
121
122
|
];
|
|
122
123
|
|
package/cli/state.test.ts
CHANGED
|
@@ -463,6 +463,26 @@ test("nativePath on Windows reads the spellings Git Bash writes into the project
|
|
|
463
463
|
} finally { Object.defineProperty(process, "platform", platform); }
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
+
test("findTerminal on macOS opens $TERMINAL as an app; iTerm through a script", () => {
|
|
467
|
+
const keys = ["PATH", "TERMINAL", "TMUX", "HERDR_ENV"];
|
|
468
|
+
const saved = keys.map((k) => process.env[k]);
|
|
469
|
+
const platform = Object.getOwnPropertyDescriptor(process, "platform")!;
|
|
470
|
+
try {
|
|
471
|
+
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
472
|
+
process.env.PATH = mkdtempSync(join(tmpdir(), "kz-empty-"));
|
|
473
|
+
delete process.env.TMUX; delete process.env.HERDR_ENV;
|
|
474
|
+
process.env.TERMINAL = "Ghostty";
|
|
475
|
+
expect(findTerminal("/p", ["claude", "x"])!.cmd).toEqual(["open", "-na", "Ghostty", "--args", "--working-directory=/p", "-e", "claude", "x"]);
|
|
476
|
+
process.env.TERMINAL = "iTerm";
|
|
477
|
+
const i = findTerminal("/p", ["claude", "x"])!;
|
|
478
|
+
expect(i.cmd[2]).toContain('create window with default profile command');
|
|
479
|
+
expect(i.cmd.slice(4)).toEqual(["'/p'", "'claude' 'x'"]);
|
|
480
|
+
} finally {
|
|
481
|
+
Object.defineProperty(process, "platform", platform);
|
|
482
|
+
keys.forEach((k, i) => (saved[i] === undefined ? delete process.env[k] : (process.env[k] = saved[i])));
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
|
|
466
486
|
test("findTerminal on Windows names cmd and PowerShell by full path, so a PATH without System32 still launches", () => {
|
|
467
487
|
const keys = ["PATH", "ComSpec", "SystemRoot", "windir"];
|
|
468
488
|
const saved = keys.map((k) => process.env[k]);
|
package/cli/state.ts
CHANGED
|
@@ -210,13 +210,26 @@ if ($o -match '"pane_id":"([^"]*)"') { & ${q(herdr)} pane run $Matches[1] ${q(ty
|
|
|
210
210
|
return { cmd: ["tmux", "new-window", "-c", cwd, ...fullCmd], detached: false };
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
213
|
+
// $TERMINAL: a command on PATH, or on macOS an app's name (TERMINAL=Ghostty, iTerm).
|
|
214
|
+
const term = process.env.TERMINAL;
|
|
215
|
+
if (term) {
|
|
216
|
+
const t = term.toLowerCase();
|
|
217
|
+
const args = t.includes("kitty") ? ["--directory", cwd, ...fullCmd]
|
|
218
|
+
: t.includes("alacritty") ? ["--working-directory", cwd, "-e", ...fullCmd]
|
|
219
|
+
: t.includes("ghostty") ? [`--working-directory=${cwd}`, "-e", ...fullCmd]
|
|
220
|
+
: t.includes("wezterm") ? ["start", "--cwd", cwd, "--", ...fullCmd]
|
|
221
|
+
: t.includes("foot") ? ["-D", cwd, ...fullCmd]
|
|
222
|
+
: ["-e", ...fullCmd];
|
|
223
|
+
if (process.platform !== "darwin") { if (Bun.which(term)) return { cmd: [term, ...args], detached: true }; }
|
|
224
|
+
else if (t.includes("iterm")) {
|
|
225
|
+
// iTerm takes a command line, not argv: the line goes into a one-shot script, as
|
|
226
|
+
// with Terminal below, and iTerm runs `sh <path>`.
|
|
227
|
+
const script = `f=$(mktemp) && printf 'rm -f "$0"\\n{ cd %s || (exit 127); } && %s\\n${hold}\\n' "$1" "$2" > "$f" && osascript -e "tell application \\"iTerm\\" to create window with default profile command \\"sh $f\\"" -e 'tell application "iTerm" to activate'`;
|
|
228
|
+
return { cmd: ["/bin/sh", "-c", script, "sh", shq(cwd), line], detached: true };
|
|
229
|
+
}
|
|
230
|
+
// On macOS a terminal is an app, started through open (Ghostty's binary cannot open
|
|
231
|
+
// a window itself). -n: a new instance, so the args reach it when it is already open.
|
|
232
|
+
else return { cmd: ["open", "-na", term, "--args", ...args], detached: true };
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
if (hasDisplay && Bun.which("xdg-terminal-exec")) {
|
package/cli/web.ts
CHANGED
|
@@ -676,6 +676,11 @@ export async function web(repoDir: string, opts: Opts = {}) {
|
|
|
676
676
|
// A profile folder gives the window a browser instance of its own (Kaizen Bot's app);
|
|
677
677
|
// on macOS that takes -n, as `open` otherwise hands the args to a running browser.
|
|
678
678
|
export async function openApp(url: string, profile?: string) {
|
|
679
|
+
// Kaizen Bot's window (a profile) is always an app; the board asks the setting.
|
|
680
|
+
if (!profile) {
|
|
681
|
+
const { listSettings } = await import("./settings.ts");
|
|
682
|
+
if (listSettings(join(import.meta.dir, "..")).rows.find((r) => r.key === "board.open")?.value !== "app") return openDefault(url);
|
|
683
|
+
}
|
|
679
684
|
const args = [`--app=${url}`, ...(profile ? [`--user-data-dir=${profile}`] : [])];
|
|
680
685
|
const chromes = process.platform === "darwin"
|
|
681
686
|
? ["Google Chrome", "Chromium", "Microsoft Edge", "Brave Browser"]
|
|
@@ -697,6 +702,10 @@ export async function openApp(url: string, profile?: string) {
|
|
|
697
702
|
}
|
|
698
703
|
}
|
|
699
704
|
} catch { /* fall through to a plain tab */ }
|
|
705
|
+
return openDefault(url);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async function openDefault(url: string) {
|
|
700
709
|
// `start` is a cmd builtin, not a program, so it has to go through cmd.
|
|
701
710
|
if (process.platform === "win32") {
|
|
702
711
|
try { await Bun.$`${cmdExe()} /c start "" ${url}`.quiet(); return true; } catch { return false; }
|
package/package.json
CHANGED
|
@@ -178,6 +178,9 @@ board:
|
|
|
178
178
|
# not itself a project (no .git, no package.json) is read as a group, and the
|
|
179
179
|
# folders inside it are offered too.
|
|
180
180
|
projects_dir: ""
|
|
181
|
+
# Where `kaizen web` opens the board: browser (a tab in your default browser) or
|
|
182
|
+
# app (a Chrome, Edge or Brave window with no tabs or address bar).
|
|
183
|
+
open: browser
|
|
181
184
|
|
|
182
185
|
sources:
|
|
183
186
|
# How often the web board checks your request sources (links added in the board's
|