kaizen-agent 1.14.0 → 1.16.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/state.test.ts +21 -2
- package/cli/state.ts +19 -1
- package/package.json +1 -1
package/cli/state.test.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// State module against a throwaway .kaizen/. Nothing here spawns a terminal: the
|
|
2
2
|
// launch path is covered only up to the command it would run.
|
|
3
3
|
import { test, expect, beforeAll, afterAll } from "bun:test";
|
|
4
|
-
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, utimesSync, existsSync, readdirSync } from "node:fs";
|
|
4
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, utimesSync, existsSync, readdirSync, realpathSync } from "node:fs";
|
|
5
5
|
import { join, dirname } from "node:path";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
|
-
import { issueUrl, systemInfo, scrubHome, REPO_URL, home as realHome } from "./state.ts";
|
|
7
|
+
import { issueUrl, systemInfo, scrubHome, REPO_URL, home as realHome, trustFolder } from "./state.ts";
|
|
8
8
|
import {
|
|
9
9
|
readRuns, readInbox, writeInbox, replaceIdea, abandonRun, allBacklog, backlog,
|
|
10
10
|
statusOf, columnOf, parseItem, startedRuns, boardCards, readArchive, setArchived,
|
|
@@ -798,3 +798,22 @@ test("scrubHome hides a Windows home however the error spells it", () => {
|
|
|
798
798
|
// Case matters off Windows: /home/Ann is not /home/ann.
|
|
799
799
|
expect(scrubHome("/home/ann/a /home/Ann/b", ["/home/ann"], false)).toBe("~/a /home/Ann/b");
|
|
800
800
|
});
|
|
801
|
+
|
|
802
|
+
test("trustFolder: marks the repo root trusted in a temp config, keeps the rest, survives a bad file", () => {
|
|
803
|
+
const t = mkdtempSync(join(tmpdir(), "kz-trust-"));
|
|
804
|
+
const repo = join(t, "repo"), sub = join(repo, "sub"), plain = join(t, "plain");
|
|
805
|
+
mkdirSync(sub, { recursive: true }); mkdirSync(plain);
|
|
806
|
+
Bun.spawnSync(["git", "init", "-q", repo]);
|
|
807
|
+
const cfg = join(t, ".claude.json");
|
|
808
|
+
writeFileSync(cfg, JSON.stringify({ keep: 1, projects: { [plain]: { x: 2 } } }));
|
|
809
|
+
trustFolder(sub, cfg);
|
|
810
|
+
trustFolder(plain, cfg);
|
|
811
|
+
const d = JSON.parse(readFileSync(cfg, "utf8"));
|
|
812
|
+
expect(d.keep).toBe(1);
|
|
813
|
+
expect(d.projects[realpathSync(repo)]).toEqual({ hasTrustDialogAccepted: true });
|
|
814
|
+
expect(d.projects[plain]).toEqual({ x: 2, hasTrustDialogAccepted: true });
|
|
815
|
+
writeFileSync(cfg, "{bad");
|
|
816
|
+
trustFolder(plain, cfg);
|
|
817
|
+
expect(readFileSync(cfg, "utf8")).toBe("{bad");
|
|
818
|
+
rmSync(t, { recursive: true, force: true });
|
|
819
|
+
});
|
package/cli/state.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Everything kaizen knows about a project's state, read from and written to the
|
|
2
2
|
// `.kaizen/` directory. No terminal, no HTTP: the TUI (`dashboard.ts`) and the web
|
|
3
3
|
// board (`web.ts`) both import this and draw it their own way.
|
|
4
|
-
import { existsSync, readFileSync, readdirSync, appendFileSync, writeFileSync, mkdirSync, statSync, realpathSync, rmSync } from "node:fs";
|
|
4
|
+
import { existsSync, readFileSync, readdirSync, appendFileSync, writeFileSync, mkdirSync, statSync, realpathSync, rmSync, renameSync } from "node:fs";
|
|
5
5
|
import { join, dirname, win32 } from "node:path";
|
|
6
6
|
import { homedir, arch, cpus, release, totalmem, version as osVersion } from "node:os";
|
|
7
7
|
|
|
@@ -1098,6 +1098,23 @@ export function sessionLaunch(projectDir: string, cmd: string, base: string[], p
|
|
|
1098
1098
|
return { fullCmd, pidFile: join(sessionsDir(st), key + ".pid"), record };
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
|
+
// Claude Code asks "do you trust this folder?" the first time it opens one, and inside
|
|
1102
|
+
// a git repo it looks for the answer only up to the repo root. A launch from the board
|
|
1103
|
+
// is the user opening the project on purpose, so record yes for that root ahead of
|
|
1104
|
+
// time, in Claude's own config. Best effort: any failure just means Claude asks.
|
|
1105
|
+
export function trustFolder(dir: string, cfg = join(process.env.CLAUDE_CONFIG_DIR ?? homedir(), ".claude.json")) {
|
|
1106
|
+
try {
|
|
1107
|
+
const top = Bun.spawnSync(["git", "-C", dir, "rev-parse", "--show-toplevel"], { stderr: "ignore" });
|
|
1108
|
+
const root = top.success ? top.stdout.toString().trim() || dir : dir;
|
|
1109
|
+
const d = JSON.parse(readFileSync(cfg, "utf8"));
|
|
1110
|
+
if (d.projects?.[root]?.hasTrustDialogAccepted) return;
|
|
1111
|
+
d.projects ??= {};
|
|
1112
|
+
d.projects[root] = { ...d.projects[root], hasTrustDialogAccepted: true };
|
|
1113
|
+
writeFileSync(cfg + ".tmp", JSON.stringify(d, null, 2));
|
|
1114
|
+
renameSync(cfg + ".tmp", cfg);
|
|
1115
|
+
} catch { /* Claude asks as before */ }
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1101
1118
|
// run: the run a decision is for. Without it, an idea starting a new run.
|
|
1102
1119
|
// pick: the tool, model and yolo chosen for a new run; a decision takes the run's own
|
|
1103
1120
|
// tool and model, and never yolo.
|
|
@@ -1107,6 +1124,7 @@ export function launchRun(it: Item, from: string, run?: string, pick: { agent?:
|
|
|
1107
1124
|
remember(projectDir);
|
|
1108
1125
|
const own = run && !pick.agent ? runAgent(join(projectDir, ".kaizen"), run) : null;
|
|
1109
1126
|
const agent = detectDefaultAgent(projectDir, pick.agent ?? own?.cmd);
|
|
1127
|
+
if (agent.cmd === "claude") trustFolder(projectDir);
|
|
1110
1128
|
const agentBin = Bun.which(agent.cmd) ?? agent.cmd;
|
|
1111
1129
|
const prompt = requestOf(it.where ? `/kaizen ${it.text} (${it.where})` : `/kaizen ${it.text}`, it.notes, from);
|
|
1112
1130
|
const wanted = pick.model ?? (own?.cmd === agent.cmd ? own.model : null) ?? undefined;
|
package/package.json
CHANGED