@sma1lboy/kobe 0.5.27 → 0.5.28

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
@@ -253,16 +253,19 @@ If the daemon isn't running, `kobe api ...` exits 2 with
253
253
 
254
254
  ### Install the agent skill
255
255
 
256
- `kobe api` gives the *capability*. The bundled SKILL.md gives the model the
256
+ `kobe api` gives the *capability*. The SKILL.md gives the model the
257
257
  *intent* — when to fan out, how to scope subtask prompts, how to read results
258
- back. Install it once:
258
+ back. Install it via the [Vercel Labs agent-skills CLI](https://github.com/vercel-labs/skills):
259
259
 
260
260
  ```bash
261
- kobe skill install # writes ~/.claude/skills/kobe/SKILL.md
262
- kobe skill install --yes # overwrite an existing copy
263
- kobe skill uninstall # remove it
261
+ npx skills add Sma1lboy/kobe --skill kobe --agent claude-code
264
262
  ```
265
263
 
264
+ The skill source lives at `.agents/skills/kobe/SKILL.md` in this repo,
265
+ which is one of the directories `npx skills` scans by default. The
266
+ agent-skills CLI fetches it directly from GitHub and writes it to
267
+ `~/.claude/skills/kobe/SKILL.md`.
268
+
266
269
  After install, Claude Code automatically picks up the skill on its next launch.
267
270
  For project-level overrides, copy the file to `<repo>/.claude/skills/kobe/SKILL.md`
268
271
  and customise — Claude Code's discovery order is project > user > none.
@@ -270,6 +273,10 @@ and customise — Claude Code's discovery order is project > user > none.
270
273
  `kobe diagnose` reports whether the skill is installed, which is the
271
274
  fastest way to confirm.
272
275
 
276
+ > `kobe skill install` is deprecated as of v0.6 — use the `npx skills`
277
+ > command above. `kobe skill uninstall` still works for cleaning up an
278
+ > older copy.
279
+
273
280
  ## Coming later
274
281
 
275
282
  - Homebrew tap (mirroring [`sma1lboy/homebrew-codefox`](https://github.com/sma1lboy/homebrew-codefox)) so you can `brew install kobe` without touching Bun directly.
package/dist/cli/index.js CHANGED
@@ -17481,7 +17481,7 @@ var init_package = __esm(() => {
17481
17481
  package_default = {
17482
17482
  $schema: "https://json.schemastore.org/package.json",
17483
17483
  name: "@sma1lboy/kobe",
17484
- version: "0.5.27",
17484
+ version: "0.5.28",
17485
17485
  description: "TUI orchestrator for Claude Code (codename)",
17486
17486
  type: "module",
17487
17487
  packageManager: "bun@1.3.13",
@@ -17685,59 +17685,39 @@ var init_version = __esm(() => {
17685
17685
  var exports_skill_cmd = {};
17686
17686
  __export(exports_skill_cmd, {
17687
17687
  runSkillSubcommand: () => runSkillSubcommand,
17688
- probeInstalledSkill: () => probeInstalledSkill
17688
+ probeInstalledSkill: () => probeInstalledSkill,
17689
+ ensureSkillInstalled: () => ensureSkillInstalled
17689
17690
  });
17691
+ import { spawn as spawn7 } from "child_process";
17690
17692
  import { existsSync as existsSync3 } from "fs";
17691
- import { copyFile, mkdir as mkdir2, readFile as readFile3, stat as stat2, unlink as unlink2 } from "fs/promises";
17693
+ import { stat as stat2, unlink as unlink2 } from "fs/promises";
17692
17694
  import { homedir as homedir7 } from "os";
17693
- import { dirname as dirname3, join as join7, resolve } from "path";
17694
- import { fileURLToPath } from "url";
17695
- function resolveBundledSkill() {
17696
- const here = fileURLToPath(import.meta.url);
17697
- if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
17698
- throw new Error("kobe skill install is not yet supported from the standalone binary. " + "Install kobe from npm (`npm install -g @sma1lboy/kobe`) and re-run, " + "or copy share/skills/kobe/SKILL.md from the kobe source tree manually.");
17699
- }
17700
- const dir = dirname3(here);
17701
- const sourcePath = resolve(dir, "../../share/skills/kobe/SKILL.md");
17702
- if (existsSync3(sourcePath))
17703
- return sourcePath;
17704
- const distPath = resolve(dir, "../share/skills/kobe/SKILL.md");
17705
- if (existsSync3(distPath))
17706
- return distPath;
17707
- throw new Error(`kobe: bundled SKILL.md not found (looked at ${sourcePath} and ${distPath}). If you are running from source, the share/ directory should sit next to src/.`);
17708
- }
17695
+ import { join as join7 } from "path";
17709
17696
  function resolveInstallTarget() {
17710
17697
  const home = process.env.HOME ?? homedir7();
17711
17698
  return join7(home, SKILL_REL_PATH);
17712
17699
  }
17713
- async function readIfExists(path7) {
17714
- try {
17715
- return await readFile3(path7, "utf8");
17716
- } catch (err) {
17717
- if (err.code === "ENOENT")
17718
- return null;
17719
- throw err;
17720
- }
17721
- }
17722
- async function runInstall(argv) {
17723
- const force = argv.includes("--yes") || argv.includes("--force");
17724
- const source = resolveBundledSkill();
17700
+ async function ensureSkillInstalled() {
17701
+ if (process.env.KOBE_NO_SKILL_AUTOINSTALL === "1")
17702
+ return;
17725
17703
  const target = resolveInstallTarget();
17726
- const existing = await readIfExists(target);
17727
- const bundled = await readFile3(source, "utf8");
17728
- if (existing === bundled) {
17729
- console.log(`kobe skill: already installed at ${target}`);
17704
+ if (existsSync3(target))
17730
17705
  return;
17731
- }
17732
- if (existing !== null && !force) {
17733
- const existingBytes = Buffer.byteLength(existing, "utf8");
17734
- const bundledBytes = Buffer.byteLength(bundled, "utf8");
17735
- console.error(`kobe skill: ${target} exists and differs from the bundled version (installed=${existingBytes}B, bundled=${bundledBytes}B). Re-run with --yes to overwrite, or \`kobe skill uninstall\` first.`);
17736
- process.exit(2);
17737
- }
17738
- await mkdir2(dirname3(target), { recursive: true });
17739
- await copyFile(source, target);
17740
- console.log(`kobe skill: installed ${target}`);
17706
+ console.error("kobe: first-run setup \u2014 installing skill via `npx skills add`. " + "Press Ctrl+C to skip (set KOBE_NO_SKILL_AUTOINSTALL=1 to opt out long-term).");
17707
+ await new Promise((resolve) => {
17708
+ const proc = spawn7("npx", ["--yes", "skills", "add", "Sma1lboy/kobe", "--skill", "kobe", "--agent", "claude-code"], { stdio: "inherit" });
17709
+ proc.on("error", () => resolve());
17710
+ proc.on("exit", () => resolve());
17711
+ });
17712
+ }
17713
+ async function runInstall() {
17714
+ console.error(`kobe skill install is deprecated as of v0.6.
17715
+ The skill is now distributed via the Vercel Labs agent-skills CLI:
17716
+
17717
+ ${NPX_HINT}
17718
+
17719
+ See https://github.com/vercel-labs/skills for the install tool.`);
17720
+ process.exit(2);
17741
17721
  }
17742
17722
  async function runUninstall() {
17743
17723
  const target = resolveInstallTarget();
@@ -17751,23 +17731,23 @@ async function runUninstall() {
17751
17731
  console.log(`kobe skill: removed ${target}`);
17752
17732
  }
17753
17733
  async function runSkillSubcommand(argv) {
17754
- const [command, ...rest] = argv;
17734
+ const [command] = argv;
17755
17735
  if (command === "install") {
17756
- await runInstall(rest);
17736
+ await runInstall();
17757
17737
  return;
17758
17738
  }
17759
17739
  if (command === "uninstall" || command === "remove") {
17760
17740
  await runUninstall();
17761
17741
  return;
17762
17742
  }
17763
- console.error("usage: kobe skill <install|uninstall> [--yes]");
17743
+ console.error("usage: kobe skill <install|uninstall>");
17764
17744
  process.exit(2);
17765
17745
  }
17766
17746
  function probeInstalledSkill() {
17767
17747
  const target = resolveInstallTarget();
17768
17748
  return { path: target, installed: existsSync3(target) };
17769
17749
  }
17770
- var SKILL_REL_PATH = ".claude/skills/kobe/SKILL.md";
17750
+ var SKILL_REL_PATH = ".claude/skills/kobe/SKILL.md", NPX_HINT = "npx skills add Sma1lboy/kobe --skill kobe --agent claude-code";
17771
17751
  var init_skill_cmd = () => {};
17772
17752
 
17773
17753
  // src/cli/diagnose.ts
@@ -17994,7 +17974,7 @@ async function buildDiagnoseReport() {
17994
17974
  try {
17995
17975
  const probe = probeInstalledSkill();
17996
17976
  lines.push(formatKv("path:", probe.path));
17997
- lines.push(formatKv("installed:", probe.installed ? "yes" : "no (run `kobe skill install`)"));
17977
+ lines.push(formatKv("installed:", probe.installed ? "yes" : "no (run `npx skills add Sma1lboy/kobe --skill kobe --agent claude-code`)"));
17998
17978
  } catch (err) {
17999
17979
  lines.push(formatKv("status:", `(unavailable: ${err.message})`));
18000
17980
  }
@@ -18254,7 +18234,7 @@ __export(exports_theme, {
18254
18234
  runThemeSubcommand: () => runThemeSubcommand
18255
18235
  });
18256
18236
  import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, readdirSync as readdirSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
18257
- import { basename, join as join10, resolve as resolve2 } from "path";
18237
+ import { basename, join as join10, resolve } from "path";
18258
18238
  function fail2(message) {
18259
18239
  process.stderr.write(`kobe theme: ${message}
18260
18240
  `);
@@ -18304,7 +18284,7 @@ async function readSource(source) {
18304
18284
  const defaultName2 = file2.endsWith(".json") ? file2.slice(0, -".json".length) : file2;
18305
18285
  return { text: text2, defaultName: defaultName2 };
18306
18286
  }
18307
- const abs = resolve2(process.cwd(), source);
18287
+ const abs = resolve(process.cwd(), source);
18308
18288
  let text;
18309
18289
  try {
18310
18290
  text = readFileSync6(abs, "utf8");
@@ -18465,8 +18445,8 @@ class SocketClient {
18465
18445
  connected;
18466
18446
  constructor(path7, onClose) {
18467
18447
  this.socket = connect(path7);
18468
- this.connected = new Promise((resolve3, reject) => {
18469
- this.socket.once("connect", resolve3);
18448
+ this.connected = new Promise((resolve2, reject) => {
18449
+ this.socket.once("connect", resolve2);
18470
18450
  this.socket.once("error", reject);
18471
18451
  });
18472
18452
  this.socket.on("data", (chunk) => this.onData(chunk.toString("utf8")));
@@ -18514,8 +18494,8 @@ class SocketClient {
18514
18494
  async call(method, params) {
18515
18495
  await this.connected;
18516
18496
  const id = this.nextId++;
18517
- const promise = new Promise((resolve3, reject) => {
18518
- this.pending.set(id, { resolve: resolve3, reject });
18497
+ const promise = new Promise((resolve2, reject) => {
18498
+ this.pending.set(id, { resolve: resolve2, reject });
18519
18499
  });
18520
18500
  this.socket.write(`${JSON.stringify({ id, method, params })}
18521
18501
  `);
@@ -18881,19 +18861,19 @@ class KobeDaemonClient {
18881
18861
  if (!socket)
18882
18862
  throw new Error("daemon connection is not open");
18883
18863
  const id = String(this.nextId++);
18884
- const promise = new Promise((resolve3, reject) => {
18885
- this.pending.set(id, { resolve: (value) => resolve3(value), reject });
18864
+ const promise = new Promise((resolve2, reject) => {
18865
+ this.pending.set(id, { resolve: (value) => resolve2(value), reject });
18886
18866
  });
18887
18867
  socket.write(frameToLine({ type: "request", id, name, payload }));
18888
18868
  return promise;
18889
18869
  }
18890
18870
  openSocket() {
18891
- return new Promise((resolve3, reject) => {
18871
+ return new Promise((resolve2, reject) => {
18892
18872
  const socket = connect2(this.socketPath);
18893
18873
  this.socket = socket;
18894
18874
  const onConnect = () => {
18895
18875
  socket.off("error", onError);
18896
- resolve3();
18876
+ resolve2();
18897
18877
  };
18898
18878
  const onError = (err) => {
18899
18879
  socket.off("connect", onConnect);
@@ -18965,23 +18945,23 @@ class KobeDaemonClient {
18965
18945
  var init_client = () => {};
18966
18946
 
18967
18947
  // src/client/daemon-process.ts
18968
- import { spawn as spawn7 } from "child_process";
18948
+ import { spawn as spawn8 } from "child_process";
18969
18949
  import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync3, openSync } from "fs";
18970
18950
  import { unlink as unlink3 } from "fs/promises";
18971
18951
  import { homedir as homedir10 } from "os";
18972
- import { dirname as dirname4, join as join12, resolve as resolve3 } from "path";
18973
- import { fileURLToPath as fileURLToPath2 } from "url";
18952
+ import { dirname as dirname3, join as join12, resolve as resolve2 } from "path";
18953
+ import { fileURLToPath } from "url";
18974
18954
  function spawnDetachedDaemon(command, args, env, logPath) {
18975
18955
  let stdio = "ignore";
18976
18956
  let logFd;
18977
18957
  try {
18978
- mkdirSync3(dirname4(logPath), { recursive: true });
18958
+ mkdirSync3(dirname3(logPath), { recursive: true });
18979
18959
  logFd = openSync(logPath, "a");
18980
18960
  stdio = ["ignore", logFd, logFd];
18981
18961
  } catch {
18982
18962
  stdio = "ignore";
18983
18963
  }
18984
- const child = spawn7(command, [...args], { detached: true, stdio, env });
18964
+ const child = spawn8(command, [...args], { detached: true, stdio, env });
18985
18965
  child.unref();
18986
18966
  if (logFd !== undefined) {
18987
18967
  try {
@@ -19061,15 +19041,15 @@ async function testCanConnect(socketPath) {
19061
19041
  }
19062
19042
  }
19063
19043
  function resolveKobeSpawn(subcommand) {
19064
- const here = fileURLToPath2(import.meta.url);
19044
+ const here = fileURLToPath(import.meta.url);
19065
19045
  if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
19066
19046
  return [process.execPath, ...subcommand];
19067
19047
  }
19068
- const dir = dirname4(here);
19069
- const sourceEntry = resolve3(dir, "../cli/index.ts");
19048
+ const dir = dirname3(here);
19049
+ const sourceEntry = resolve2(dir, "../cli/index.ts");
19070
19050
  if (existsSync6(sourceEntry))
19071
19051
  return [process.execPath, sourceEntry, ...subcommand];
19072
- const distEntry = resolve3(dir, "../cli/index.js");
19052
+ const distEntry = resolve2(dir, "../cli/index.js");
19073
19053
  if (existsSync6(distEntry))
19074
19054
  return [process.execPath, distEntry, ...subcommand];
19075
19055
  throw new Error(`kobe: could not locate kobe entry near ${dir}; expected ../cli/index.{ts,js}`);
@@ -19164,9 +19144,9 @@ function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
19164
19144
  }
19165
19145
 
19166
19146
  // src/engine/command-discovery.ts
19167
- import { readFile as readFile4, readdir as readdir2, realpath, stat as stat4 } from "fs/promises";
19147
+ import { readFile as readFile3, readdir as readdir2, realpath, stat as stat4 } from "fs/promises";
19168
19148
  import { homedir as homedir11 } from "os";
19169
- import { dirname as dirname5, join as join13 } from "path";
19149
+ import { dirname as dirname4, join as join13 } from "path";
19170
19150
  function resolveHome() {
19171
19151
  return process.env.HOME ?? homedir11();
19172
19152
  }
@@ -19260,7 +19240,7 @@ async function readSkillDoc(skillMd, fallbackName) {
19260
19240
  if (!await isFile(skillMd))
19261
19241
  return null;
19262
19242
  try {
19263
- const content = await readFile4(skillMd, "utf-8");
19243
+ const content = await readFile3(skillMd, "utf-8");
19264
19244
  return {
19265
19245
  name: extractFrontmatterField(content, "name") || fallbackName,
19266
19246
  description: extractFrontmatterField(content, "description") ?? "",
@@ -19300,7 +19280,7 @@ async function findUpwardSkillRoots(cwd, relativeRoot) {
19300
19280
  const candidate = join13(cur, relativeRoot);
19301
19281
  if (await isDir(candidate))
19302
19282
  out.push(candidate);
19303
- const parent = dirname5(cur);
19283
+ const parent = dirname4(cur);
19304
19284
  if (parent === cur)
19305
19285
  break;
19306
19286
  cur = parent;
@@ -19336,7 +19316,7 @@ var init_builtin_slashes = __esm(() => {
19336
19316
  });
19337
19317
 
19338
19318
  // src/engine/claude-code-local/commands.ts
19339
- import { readFile as readFile5, stat as stat5 } from "fs/promises";
19319
+ import { readFile as readFile4, stat as stat5 } from "fs/promises";
19340
19320
  import { join as join14 } from "path";
19341
19321
  async function isFile2(path7) {
19342
19322
  try {
@@ -19356,7 +19336,7 @@ async function scanCommandsDir(dir) {
19356
19336
  continue;
19357
19337
  let description = "";
19358
19338
  try {
19359
- description = extractFrontmatterField(await readFile5(full, "utf-8"), "description") ?? "";
19339
+ description = extractFrontmatterField(await readFile4(full, "utf-8"), "description") ?? "";
19360
19340
  } catch {
19361
19341
  description = "";
19362
19342
  }
@@ -19462,7 +19442,7 @@ function normalizeClaudeContent(content) {
19462
19442
 
19463
19443
  // src/engine/claude-code-local/history.ts
19464
19444
  import { randomUUID } from "crypto";
19465
- import { appendFile, mkdir as mkdir3, readFile as readFile6, readdir as readdir3, unlink as unlink4, writeFile as writeFile2 } from "fs/promises";
19445
+ import { appendFile, mkdir as mkdir2, readFile as readFile5, readdir as readdir3, unlink as unlink4, writeFile as writeFile2 } from "fs/promises";
19466
19446
  import { homedir as homedir12 } from "os";
19467
19447
  import path7 from "path";
19468
19448
  function encodeCwd(cwd) {
@@ -19566,13 +19546,13 @@ async function appendInterruptedUserPrompt(sessionId, cwd, prompt, deps = defaul
19566
19546
  const filePath = path7.join(projectDir, `${sessionId}.jsonl`);
19567
19547
  let lines = [];
19568
19548
  try {
19569
- const raw = await readFile6(filePath, "utf8");
19549
+ const raw = await readFile5(filePath, "utf8");
19570
19550
  lines = raw.split(`
19571
19551
  `).filter((l2) => l2.length > 0);
19572
19552
  } catch (err) {
19573
19553
  if (err.code !== "ENOENT")
19574
19554
  throw err;
19575
- await mkdir3(projectDir, { recursive: true });
19555
+ await mkdir2(projectDir, { recursive: true });
19576
19556
  }
19577
19557
  let lastConvIdx = -1;
19578
19558
  let lastConvRecord = null;
@@ -19643,7 +19623,7 @@ var init_history = __esm(() => {
19643
19623
  }
19644
19624
  },
19645
19625
  async readFile(p2) {
19646
- return await readFile6(p2, "utf8");
19626
+ return await readFile5(p2, "utf8");
19647
19627
  },
19648
19628
  async pathExists(p2) {
19649
19629
  try {
@@ -19713,21 +19693,21 @@ function signalTree(proc, signal) {
19713
19693
  } catch {}
19714
19694
  }
19715
19695
  function waitForExit(proc) {
19716
- return new Promise((resolve4) => {
19696
+ return new Promise((resolve3) => {
19717
19697
  if (proc.exitCode !== null || proc.signalCode !== null) {
19718
- resolve4();
19698
+ resolve3();
19719
19699
  return;
19720
19700
  }
19721
- proc.once("close", () => resolve4());
19722
- proc.once("exit", () => resolve4());
19701
+ proc.once("close", () => resolve3());
19702
+ proc.once("exit", () => resolve3());
19723
19703
  });
19724
19704
  }
19725
19705
  function delay(ms) {
19726
- return new Promise((resolve4) => setTimeout(resolve4, ms));
19706
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
19727
19707
  }
19728
19708
 
19729
19709
  // src/engine/claude-code-local/sessions.ts
19730
- import { readFile as readFile7, readdir as readdir4, stat as stat6 } from "fs/promises";
19710
+ import { readFile as readFile6, readdir as readdir4, stat as stat6 } from "fs/promises";
19731
19711
  import { homedir as homedir13 } from "os";
19732
19712
  import path8 from "path";
19733
19713
  async function listSessionsForCwd(cwd, deps = defaultDeps3) {
@@ -19806,7 +19786,7 @@ var init_sessions = __esm(() => {
19806
19786
  }
19807
19787
  },
19808
19788
  async readFile(p2) {
19809
- return await readFile7(p2, "utf8");
19789
+ return await readFile6(p2, "utf8");
19810
19790
  },
19811
19791
  async stat(p2) {
19812
19792
  const s2 = await stat6(p2);
@@ -19816,10 +19796,10 @@ var init_sessions = __esm(() => {
19816
19796
  });
19817
19797
 
19818
19798
  // src/engine/claude-code-local/spawn.ts
19819
- import { spawn as spawn8 } from "child_process";
19799
+ import { spawn as spawn9 } from "child_process";
19820
19800
  function spawnClaudeProcess(opts) {
19821
19801
  const args = buildArgs(opts);
19822
- const proc = spawn8(opts.binaryPath, args, {
19802
+ const proc = spawn9(opts.binaryPath, args, {
19823
19803
  cwd: opts.cwd,
19824
19804
  env: { ...process.env, ...opts.env ?? {} },
19825
19805
  stdio: ["pipe", "pipe", "pipe"],
@@ -20052,7 +20032,7 @@ class ClaudeCodeLocal {
20052
20032
  }
20053
20033
  if (session.closed)
20054
20034
  return;
20055
- await new Promise((resolve4) => session.waiters.push(resolve4));
20035
+ await new Promise((resolve3) => session.waiters.push(resolve3));
20056
20036
  }
20057
20037
  }
20058
20038
  };
@@ -20219,7 +20199,7 @@ var init_claude_code_local = __esm(() => {
20219
20199
  });
20220
20200
 
20221
20201
  // src/engine/codex-local/app-server.ts
20222
- import { spawn as spawn9 } from "child_process";
20202
+ import { spawn as spawn10 } from "child_process";
20223
20203
  import { readFileSync as readFileSync7 } from "fs";
20224
20204
  function resolveCodexBackend(env = process.env, readPreference = readCodexBackendPreference) {
20225
20205
  if (env.KOBE_CODEX_BACKEND === "exec")
@@ -20241,7 +20221,7 @@ function readCodexBackendPreference() {
20241
20221
  }
20242
20222
  function spawnCodexAppServerTurn(opts) {
20243
20223
  const args = buildAppServerArgs(opts);
20244
- const proc = spawn9(opts.binaryPath, args, {
20224
+ const proc = spawn10(opts.binaryPath, args, {
20245
20225
  cwd: opts.cwd,
20246
20226
  env: { ...process.env, ...opts.env ?? {} },
20247
20227
  stdio: ["pipe", "pipe", "pipe"],
@@ -20317,16 +20297,16 @@ class AppServerRpc {
20317
20297
  nextId = 1;
20318
20298
  readyResolve = () => {};
20319
20299
  readyReject = () => {};
20320
- ready = new Promise((resolve4, reject) => {
20321
- this.readyResolve = resolve4;
20300
+ ready = new Promise((resolve3, reject) => {
20301
+ this.readyResolve = resolve3;
20322
20302
  this.readyReject = reject;
20323
20303
  });
20324
20304
  closed;
20325
20305
  constructor(proc, opts) {
20326
20306
  this.proc = proc;
20327
20307
  this.opts = opts;
20328
- this.closed = new Promise((resolve4) => {
20329
- proc.once("exit", (code, signal) => resolve4({ code, signal }));
20308
+ this.closed = new Promise((resolve3) => {
20309
+ proc.once("exit", (code, signal) => resolve3({ code, signal }));
20330
20310
  });
20331
20311
  proc.stdout.setEncoding("utf8");
20332
20312
  proc.stdout.on("data", (chunk) => this.onStdout(chunk));
@@ -20403,8 +20383,8 @@ class AppServerRpc {
20403
20383
  call(method, params) {
20404
20384
  const id = this.nextId++;
20405
20385
  this.send({ jsonrpc: "2.0", id, method, params });
20406
- return new Promise((resolve4, reject) => {
20407
- this.pending.set(id, { resolve: resolve4, reject });
20386
+ return new Promise((resolve3, reject) => {
20387
+ this.pending.set(id, { resolve: resolve3, reject });
20408
20388
  });
20409
20389
  }
20410
20390
  notify(method, params) {
@@ -20826,7 +20806,7 @@ function validPositive(v2) {
20826
20806
  }
20827
20807
 
20828
20808
  // src/engine/codex-local/history.ts
20829
- import { readFile as readFile8, readdir as readdir5, unlink as unlink5 } from "fs/promises";
20809
+ import { readFile as readFile7, readdir as readdir5, unlink as unlink5 } from "fs/promises";
20830
20810
  import { homedir as homedir15 } from "os";
20831
20811
  import path10 from "path";
20832
20812
  async function listRolloutFiles(deps = defaultDeps5) {
@@ -21113,7 +21093,7 @@ var init_history2 = __esm(() => {
21113
21093
  }
21114
21094
  },
21115
21095
  async readFile(p2) {
21116
- return await readFile8(p2, "utf8");
21096
+ return await readFile7(p2, "utf8");
21117
21097
  }
21118
21098
  };
21119
21099
  });
@@ -21276,10 +21256,10 @@ var init_sessions2 = __esm(() => {
21276
21256
  });
21277
21257
 
21278
21258
  // src/engine/codex-local/spawn.ts
21279
- import { spawn as spawn10 } from "child_process";
21259
+ import { spawn as spawn11 } from "child_process";
21280
21260
  function spawnCodexProcess(opts) {
21281
21261
  const args = buildArgs2(opts);
21282
- const proc = spawn10(opts.binaryPath, args, {
21262
+ const proc = spawn11(opts.binaryPath, args, {
21283
21263
  cwd: opts.cwd,
21284
21264
  env: { ...process.env, ...opts.env ?? {} },
21285
21265
  stdio: ["pipe", "pipe", "pipe"],
@@ -21530,7 +21510,7 @@ class CodexLocal {
21530
21510
  }
21531
21511
  if (session.closed)
21532
21512
  return;
21533
- await new Promise((resolve4) => session.waiters.push(resolve4));
21513
+ await new Promise((resolve3) => session.waiters.push(resolve3));
21534
21514
  }
21535
21515
  }
21536
21516
  };
@@ -21723,7 +21703,7 @@ class CodexLocal {
21723
21703
  signal: null,
21724
21704
  seen: false
21725
21705
  };
21726
- const exitObserved = new Promise((resolve4) => {
21706
+ const exitObserved = new Promise((resolve3) => {
21727
21707
  spawned.proc.once("exit", (code, signal) => {
21728
21708
  exitInfo.code = code;
21729
21709
  exitInfo.signal = signal;
@@ -21731,7 +21711,7 @@ class CodexLocal {
21731
21711
  if (!bound) {
21732
21712
  rejectHandle(new Error(formatExitMsg("codex exited before session id was captured", code, signal, stderrTail)));
21733
21713
  }
21734
- resolve4();
21714
+ resolve3();
21735
21715
  });
21736
21716
  });
21737
21717
  (async () => {
@@ -21904,7 +21884,7 @@ var init_binary3 = __esm(() => {
21904
21884
  });
21905
21885
 
21906
21886
  // src/engine/gemini-local/history.ts
21907
- import { readFile as readFile9, readdir as readdir6, unlink as unlink6 } from "fs/promises";
21887
+ import { readFile as readFile8, readdir as readdir6, unlink as unlink6 } from "fs/promises";
21908
21888
  import { homedir as homedir17 } from "os";
21909
21889
  import path12 from "path";
21910
21890
  async function listChatFiles(deps = defaultDeps7) {
@@ -22153,14 +22133,14 @@ var init_history3 = __esm(() => {
22153
22133
  }
22154
22134
  },
22155
22135
  async readFile(p2) {
22156
- return await readFile9(p2, "utf8");
22136
+ return await readFile8(p2, "utf8");
22157
22137
  },
22158
22138
  unlink: unlink6
22159
22139
  };
22160
22140
  });
22161
22141
 
22162
22142
  // src/engine/gemini-local/sessions.ts
22163
- import { readFile as readFile10, readdir as readdir7, stat as stat8 } from "fs/promises";
22143
+ import { readFile as readFile9, readdir as readdir7, stat as stat8 } from "fs/promises";
22164
22144
  import { homedir as homedir18 } from "os";
22165
22145
  import path13 from "path";
22166
22146
  async function listSessionsForCwd3(cwd, deps) {
@@ -22241,17 +22221,17 @@ var init_sessions3 = __esm(() => {
22241
22221
  }
22242
22222
  },
22243
22223
  async readFile(p2) {
22244
- return await readFile10(p2, "utf8");
22224
+ return await readFile9(p2, "utf8");
22245
22225
  },
22246
22226
  async unlink() {}
22247
22227
  };
22248
22228
  });
22249
22229
 
22250
22230
  // src/engine/gemini-local/spawn.ts
22251
- import { spawn as spawn11 } from "child_process";
22231
+ import { spawn as spawn12 } from "child_process";
22252
22232
  function spawnGeminiProcess(opts) {
22253
22233
  const args = buildArgs3(opts);
22254
- const proc = spawn11(opts.binaryPath, args, {
22234
+ const proc = spawn12(opts.binaryPath, args, {
22255
22235
  cwd: opts.cwd,
22256
22236
  env: { ...process.env, ...opts.env ?? {} },
22257
22237
  stdio: ["pipe", "pipe", "pipe"]
@@ -22449,7 +22429,7 @@ class GeminiLocal {
22449
22429
  }
22450
22430
  if (session.closed)
22451
22431
  return;
22452
- await new Promise((resolve4) => session.waiters.push(resolve4));
22432
+ await new Promise((resolve3) => session.waiters.push(resolve3));
22453
22433
  }
22454
22434
  }
22455
22435
  };
@@ -22528,14 +22508,14 @@ class GeminiLocal {
22528
22508
  signal: null,
22529
22509
  seen: false
22530
22510
  };
22531
- const exitObserved = new Promise((resolve4) => {
22511
+ const exitObserved = new Promise((resolve3) => {
22532
22512
  spawned.proc.once("exit", (code, signal) => {
22533
22513
  exitInfo.code = code;
22534
22514
  exitInfo.signal = signal;
22535
22515
  exitInfo.seen = true;
22536
22516
  if (!bound)
22537
22517
  rejectHandle(new Error(formatExitMsg2("gemini exited before session id was captured", code, signal, stderrTail)));
22538
- resolve4();
22518
+ resolve3();
22539
22519
  });
22540
22520
  });
22541
22521
  (async () => {
@@ -22634,11 +22614,11 @@ var init_default_engines = __esm(() => {
22634
22614
  });
22635
22615
 
22636
22616
  // src/orchestrator/bridge/server.ts
22637
- import { mkdir as mkdir4, unlink as unlink7 } from "fs/promises";
22617
+ import { mkdir as mkdir3, unlink as unlink7 } from "fs/promises";
22638
22618
  import { createServer } from "net";
22639
- import { dirname as dirname6 } from "path";
22619
+ import { dirname as dirname5 } from "path";
22640
22620
  async function startBridgeServer(orch, socketPath) {
22641
- await mkdir4(dirname6(socketPath), { recursive: true });
22621
+ await mkdir3(dirname5(socketPath), { recursive: true });
22642
22622
  await unlink7(socketPath).catch(() => {});
22643
22623
  const conns = new Set;
22644
22624
  const server = createServer((conn) => {
@@ -22666,11 +22646,11 @@ async function startBridgeServer(orch, socketPath) {
22666
22646
  });
22667
22647
  conn.on("error", () => {});
22668
22648
  });
22669
- await new Promise((resolve4, reject) => {
22649
+ await new Promise((resolve3, reject) => {
22670
22650
  server.once("error", reject);
22671
22651
  server.listen(socketPath, () => {
22672
22652
  server.removeListener("error", reject);
22673
- resolve4();
22653
+ resolve3();
22674
22654
  });
22675
22655
  });
22676
22656
  return {
@@ -22679,7 +22659,7 @@ async function startBridgeServer(orch, socketPath) {
22679
22659
  for (const conn of conns)
22680
22660
  conn.destroy();
22681
22661
  conns.clear();
22682
- await new Promise((resolve4) => server.close(() => resolve4()));
22662
+ await new Promise((resolve3) => server.close(() => resolve3()));
22683
22663
  await unlink7(socketPath).catch(() => {});
22684
22664
  }
22685
22665
  };
@@ -22772,10 +22752,10 @@ __export(exports_bridge, {
22772
22752
  startBridge: () => startBridge,
22773
22753
  bridgeSocketPathForHome: () => bridgeSocketPathForHome
22774
22754
  });
22775
- import { mkdir as mkdir5, unlink as unlink8, writeFile as writeFile3 } from "fs/promises";
22755
+ import { mkdir as mkdir4, unlink as unlink8, writeFile as writeFile3 } from "fs/promises";
22776
22756
  import { homedir as homedir19 } from "os";
22777
22757
  import { join as join16 } from "path";
22778
- import { fileURLToPath as fileURLToPath3 } from "url";
22758
+ import { fileURLToPath as fileURLToPath2 } from "url";
22779
22759
  function bridgeSocketPathForHome(home, pid = process.pid) {
22780
22760
  const runDir = join16(home, ".kobe", "run");
22781
22761
  return fitSocketPath(join16(runDir, `bridge-${pid}.sock`), home, "bridge", pid);
@@ -22785,11 +22765,11 @@ async function startBridge(orch, opts = {}) {
22785
22765
  const runDir = join16(home, ".kobe", "run");
22786
22766
  const socketPath = bridgeSocketPathForHome(home);
22787
22767
  const mcpConfigPath = join16(runDir, `mcp-${process.pid}.json`);
22788
- await mkdir5(runDir, { recursive: true });
22768
+ await mkdir4(runDir, { recursive: true });
22789
22769
  const server = await startBridgeServer(orch, socketPath);
22790
- await mkdir5(runDir, { recursive: true });
22770
+ await mkdir4(runDir, { recursive: true });
22791
22771
  const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
22792
- const entry = fileURLToPath3(new URL(`../../cli/index${moduleExt}`, import.meta.url));
22772
+ const entry = fileURLToPath2(new URL(`../../cli/index${moduleExt}`, import.meta.url));
22793
22773
  const mcpConfig = {
22794
22774
  mcpServers: {
22795
22775
  kobe: {
@@ -23267,12 +23247,12 @@ class MetadataSuggester {
23267
23247
  ...context.permissionMode ? { permissionMode: context.permissionMode } : {}
23268
23248
  };
23269
23249
  let timer = null;
23270
- const timeout = new Promise((resolve4) => {
23250
+ const timeout = new Promise((resolve3) => {
23271
23251
  timer = setTimeout(() => {
23272
23252
  timedOut = true;
23273
23253
  if (handle)
23274
23254
  context.engine.stop(handle).catch(() => {});
23275
- resolve4(null);
23255
+ resolve3(null);
23276
23256
  }, SUGGESTION_TIMEOUT_MS);
23277
23257
  });
23278
23258
  const run = (async () => {
@@ -23865,8 +23845,8 @@ class TaskRunner {
23865
23845
  });
23866
23846
  } else {
23867
23847
  let releaseLatch = () => {};
23868
- const latch = new Promise((resolve4) => {
23869
- releaseLatch = resolve4;
23848
+ const latch = new Promise((resolve3) => {
23849
+ releaseLatch = resolve3;
23870
23850
  });
23871
23851
  this.firstSpawnLatches.set(key, latch);
23872
23852
  try {
@@ -24471,8 +24451,8 @@ class SlugAllocator {
24471
24451
  async allocate(repo) {
24472
24452
  const previous = this.chain;
24473
24453
  let release;
24474
- this.chain = new Promise((resolve4) => {
24475
- release = resolve4;
24454
+ this.chain = new Promise((resolve3) => {
24455
+ release = resolve3;
24476
24456
  });
24477
24457
  await previous;
24478
24458
  try {
@@ -25696,7 +25676,7 @@ var onRejection, onException;
25696
25676
  // src/engine/claude-code-local/plan-usage.ts
25697
25677
  import { execFile } from "child_process";
25698
25678
  import { createHash as createHash2 } from "crypto";
25699
- import { readFile as readFile11 } from "fs/promises";
25679
+ import { readFile as readFile10 } from "fs/promises";
25700
25680
  import { homedir as homedir21, userInfo } from "os";
25701
25681
  import { join as join17 } from "path";
25702
25682
  import { promisify } from "util";
@@ -25731,7 +25711,7 @@ async function readPlainTextToken() {
25731
25711
  const configDir = process.env.CLAUDE_CONFIG_DIR ?? join17(homedir21(), ".claude");
25732
25712
  const path16 = join17(configDir, ".credentials.json");
25733
25713
  try {
25734
- const raw = await readFile11(path16, "utf8");
25714
+ const raw = await readFile10(path16, "utf8");
25735
25715
  return parseStoredOAuth(raw);
25736
25716
  } catch {
25737
25717
  return null;
@@ -25853,12 +25833,12 @@ var init_plan_usage_poller = __esm(() => {
25853
25833
  });
25854
25834
 
25855
25835
  // src/daemon/rc-bridge.ts
25856
- import { spawn as spawn12 } from "child_process";
25836
+ import { spawn as spawn13 } from "child_process";
25857
25837
  function createRcBridge(options = {}) {
25858
25838
  const stopGraceMs = options.stopGraceMs ?? 5000;
25859
25839
  const readyTimeoutMs = options.readyTimeoutMs ?? 30000;
25860
25840
  const binaryPathResolver = options.binaryPathResolver ?? findClaudeBinary;
25861
- const spawner = options.spawner ?? ((cmd, args, cwd) => spawn12(cmd, [...args], {
25841
+ const spawner = options.spawner ?? ((cmd, args, cwd) => spawn13(cmd, [...args], {
25862
25842
  cwd,
25863
25843
  stdio: ["ignore", "pipe", "pipe"],
25864
25844
  env: { ...process.env }
@@ -25888,11 +25868,11 @@ function createRcBridge(options = {}) {
25888
25868
  }
25889
25869
  function settleReady(status) {
25890
25870
  clearReadyTimer();
25891
- const resolve4 = readyResolve;
25871
+ const resolve3 = readyResolve;
25892
25872
  readyResolve = null;
25893
25873
  readyReject = null;
25894
- if (resolve4)
25895
- resolve4(status);
25874
+ if (resolve3)
25875
+ resolve3(status);
25896
25876
  }
25897
25877
  function failReady(err) {
25898
25878
  clearReadyTimer();
@@ -25978,8 +25958,8 @@ function createRcBridge(options = {}) {
25978
25958
  update({ state: "error", errorMessage: err.message });
25979
25959
  failReady(err);
25980
25960
  });
25981
- return new Promise((resolve4, reject) => {
25982
- readyResolve = resolve4;
25961
+ return new Promise((resolve3, reject) => {
25962
+ readyResolve = resolve3;
25983
25963
  readyReject = reject;
25984
25964
  readyTimer = setTimeout(() => {
25985
25965
  if (proc) {
@@ -26001,8 +25981,8 @@ function createRcBridge(options = {}) {
26001
25981
  return snapshot;
26002
25982
  }
26003
25983
  update({ ...snapshot, state: "stopping" });
26004
- const onExitPromise = new Promise((resolve4) => {
26005
- const onceExit = () => resolve4();
25984
+ const onExitPromise = new Promise((resolve3) => {
25985
+ const onceExit = () => resolve3();
26006
25986
  child.once("exit", onceExit);
26007
25987
  });
26008
25988
  try {
@@ -26031,17 +26011,17 @@ var init_rc_bridge = __esm(() => {
26031
26011
  });
26032
26012
 
26033
26013
  // src/daemon/server.ts
26034
- import { mkdir as mkdir6, readFile as readFile12, unlink as unlink9, writeFile as writeFile4 } from "fs/promises";
26014
+ import { mkdir as mkdir5, readFile as readFile11, unlink as unlink9, writeFile as writeFile4 } from "fs/promises";
26035
26015
  import { createServer as createServer2 } from "net";
26036
- import { dirname as dirname7 } from "path";
26016
+ import { dirname as dirname6 } from "path";
26037
26017
  async function startDaemonServer(orch, options = {}) {
26038
26018
  const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
26039
26019
  const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
26040
26020
  const startedAt = options.startedAt ?? new Date;
26041
26021
  const clients = new Set;
26042
26022
  let nextClientId = 1;
26043
- await mkdir6(dirname7(socketPath), { recursive: true });
26044
- await mkdir6(dirname7(pidPath), { recursive: true });
26023
+ await mkdir5(dirname6(socketPath), { recursive: true });
26024
+ await mkdir5(dirname6(pidPath), { recursive: true });
26045
26025
  await unlink9(socketPath).catch(() => {});
26046
26026
  const server = createServer2((socket) => {
26047
26027
  const client = {
@@ -26086,17 +26066,17 @@ async function startDaemonServer(orch, options = {}) {
26086
26066
  client.subscriptions.clear();
26087
26067
  client.socket.destroy();
26088
26068
  }
26089
- await new Promise((resolve4) => server.close(() => resolve4()));
26069
+ await new Promise((resolve3) => server.close(() => resolve3()));
26090
26070
  await unlink9(socketPath).catch(() => {});
26091
26071
  await unlink9(pidPath).catch(() => {});
26092
26072
  }
26093
26073
  };
26094
26074
  planUsagePoller.start();
26095
- await new Promise((resolve4, reject) => {
26075
+ await new Promise((resolve3, reject) => {
26096
26076
  server.once("error", reject);
26097
26077
  server.listen(socketPath, () => {
26098
26078
  server.removeListener("error", reject);
26099
- resolve4();
26079
+ resolve3();
26100
26080
  });
26101
26081
  });
26102
26082
  await writeFile4(pidPath, `${process.pid}
@@ -26464,7 +26444,7 @@ async function startDaemonServer(orch, options = {}) {
26464
26444
  }
26465
26445
  async function readPidFile(pidPath) {
26466
26446
  try {
26467
- const raw = await readFile12(pidPath, "utf8");
26447
+ const raw = await readFile11(pidPath, "utf8");
26468
26448
  const pid = Number(raw.trim());
26469
26449
  return Number.isFinite(pid) ? pid : null;
26470
26450
  } catch {
@@ -26647,7 +26627,7 @@ async function runDaemonSubcommand(argv) {
26647
26627
  const stopRequest = client.request("daemon.stop").catch(() => {
26648
26628
  return;
26649
26629
  });
26650
- const stopTimeout = new Promise((resolve4) => setTimeout(resolve4, 2000));
26630
+ const stopTimeout = new Promise((resolve3) => setTimeout(resolve3, 2000));
26651
26631
  await Promise.race([stopRequest, stopTimeout]);
26652
26632
  client.close();
26653
26633
  if (oldPid && oldPid !== process.pid) {
@@ -26665,12 +26645,12 @@ async function runDaemonSubcommand(argv) {
26665
26645
  } catch {}
26666
26646
  escalated = true;
26667
26647
  }
26668
- await new Promise((resolve4) => setTimeout(resolve4, 50));
26648
+ await new Promise((resolve3) => setTimeout(resolve3, 50));
26669
26649
  }
26670
26650
  try {
26671
26651
  process.kill(oldPid, 0);
26672
26652
  process.kill(oldPid, "SIGKILL");
26673
- await new Promise((resolve4) => setTimeout(resolve4, 100));
26653
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
26674
26654
  } catch {}
26675
26655
  }
26676
26656
  await unlink10(socketPath).catch(() => {});
@@ -28663,12 +28643,12 @@ var init_settings_dialog = __esm(() => {
28663
28643
  init_model();
28664
28644
  init_sections();
28665
28645
  SettingsDialog.show = (dialog, kv, orchestrator) => {
28666
- return new Promise((resolve4) => {
28646
+ return new Promise((resolve3) => {
28667
28647
  dialog.replace(() => createComponent2(SettingsDialog, {
28668
28648
  kv,
28669
28649
  orchestrator,
28670
- onClose: () => resolve4()
28671
- }), () => resolve4());
28650
+ onClose: () => resolve3()
28651
+ }), () => resolve3());
28672
28652
  });
28673
28653
  };
28674
28654
  });
@@ -28767,7 +28747,7 @@ var init_pulse = () => {};
28767
28747
  // src/tui/lib/sound.ts
28768
28748
  import { existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
28769
28749
  import { tmpdir as tmpdir4 } from "os";
28770
- import { basename as basename2, isAbsolute, join as join19, resolve as resolve4 } from "path";
28750
+ import { basename as basename2, isAbsolute, join as join19, resolve as resolve3 } from "path";
28771
28751
  function args(player, file, volume) {
28772
28752
  if (player === "ffplay")
28773
28753
  return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
@@ -28825,7 +28805,7 @@ function pulse(volume = 0.4) {
28825
28805
  var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
28826
28806
  var init_sound = __esm(() => {
28827
28807
  init_pulse();
28828
- pulseAsset = isAbsolute(pulse_default) ? pulse_default : resolve4(import.meta.dir, pulse_default);
28808
+ pulseAsset = isAbsolute(pulse_default) ? pulse_default : resolve3(import.meta.dir, pulse_default);
28829
28809
  DIR = join19(tmpdir4(), "kobe-sfx");
28830
28810
  PLAYERS = [
28831
28811
  "ffplay",
@@ -28845,7 +28825,7 @@ var init_sound = __esm(() => {
28845
28825
 
28846
28826
  // src/tui/context/kv.tsx
28847
28827
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
28848
- import { dirname as dirname8 } from "path";
28828
+ import { dirname as dirname7 } from "path";
28849
28829
  function loadInitial() {
28850
28830
  const statePath2 = kvStatePath();
28851
28831
  try {
@@ -28877,7 +28857,7 @@ var init_kv = __esm(() => {
28877
28857
  writeTimer = null;
28878
28858
  const statePath2 = kvStatePath();
28879
28859
  try {
28880
- mkdirSync5(dirname8(statePath2), {
28860
+ mkdirSync5(dirname7(statePath2), {
28881
28861
  recursive: true
28882
28862
  });
28883
28863
  const tmp = `${statePath2}.tmp`;
@@ -28916,7 +28896,7 @@ var init_kv = __esm(() => {
28916
28896
  }
28917
28897
  const statePath2 = kvStatePath();
28918
28898
  try {
28919
- mkdirSync5(dirname8(statePath2), {
28899
+ mkdirSync5(dirname7(statePath2), {
28920
28900
  recursive: true
28921
28901
  });
28922
28902
  const tmp = `${statePath2}.tmp`;
@@ -30479,7 +30459,7 @@ var init_create_pr_button = __esm(() => {
30479
30459
  });
30480
30460
 
30481
30461
  // src/tui/lib/worktree-opener.ts
30482
- import { spawn as spawn13 } from "child_process";
30462
+ import { spawn as spawn14 } from "child_process";
30483
30463
  import { existsSync as existsSync10 } from "fs";
30484
30464
  import { basename as basename4, delimiter, isAbsolute as isAbsolute2, join as join20 } from "path";
30485
30465
  function executableOnPath(command, env, exists) {
@@ -30538,7 +30518,7 @@ function buildOpenWorktreeCommand(worktreePath, opener) {
30538
30518
  function openWorktree(worktreePath, opener, deps = {}) {
30539
30519
  if (!worktreePath)
30540
30520
  return false;
30541
- const spawnFn = deps.spawn ?? spawn13;
30521
+ const spawnFn = deps.spawn ?? spawn14;
30542
30522
  const [command, args2] = buildOpenWorktreeCommand(worktreePath, opener);
30543
30523
  try {
30544
30524
  const child = spawnFn(command, args2, { detached: true, stdio: "ignore" });
@@ -32121,7 +32101,7 @@ class FakeAIEngine {
32121
32101
  }
32122
32102
  if (q2.closed)
32123
32103
  return;
32124
- await new Promise((resolve5) => q2.waiters.push(resolve5));
32104
+ await new Promise((resolve4) => q2.waiters.push(resolve4));
32125
32105
  }
32126
32106
  }
32127
32107
  };
@@ -32455,7 +32435,7 @@ async function mountFakeEngineServer(fake) {
32455
32435
  }
32456
32436
  });
32457
32437
  });
32458
- await new Promise((resolve5) => server.listen(port, "127.0.0.1", () => resolve5()));
32438
+ await new Promise((resolve4) => server.listen(port, "127.0.0.1", () => resolve4()));
32459
32439
  server.unref();
32460
32440
  }
32461
32441
  var init_engine_bootstrap = __esm(() => {
@@ -32621,7 +32601,7 @@ var init_use_pane_sizes = __esm(() => {
32621
32601
  });
32622
32602
 
32623
32603
  // src/tui/component/new-task-dialog/state.ts
32624
- import { spawn as spawn14 } from "child_process";
32604
+ import { spawn as spawn15 } from "child_process";
32625
32605
  import * as fs5 from "fs";
32626
32606
  import * as os from "os";
32627
32607
  import * as path17 from "path";
@@ -32907,10 +32887,10 @@ function findAvailableFolderName(parentDir, base) {
32907
32887
  return trimmed;
32908
32888
  }
32909
32889
  function cloneRepo(url, target, onProgress) {
32910
- return new Promise((resolve5) => {
32890
+ return new Promise((resolve4) => {
32911
32891
  let stderrBuf = "";
32912
32892
  try {
32913
- const child = spawn14("git", ["clone", "--progress", url, target], {
32893
+ const child = spawn15("git", ["clone", "--progress", url, target], {
32914
32894
  stdio: ["ignore", "ignore", "pipe"]
32915
32895
  });
32916
32896
  child.stderr?.setEncoding("utf-8");
@@ -32924,18 +32904,18 @@ function cloneRepo(url, target, onProgress) {
32924
32904
  }
32925
32905
  });
32926
32906
  child.on("error", (err) => {
32927
- resolve5({ ok: false, error: err.message });
32907
+ resolve4({ ok: false, error: err.message });
32928
32908
  });
32929
32909
  child.on("close", (code) => {
32930
32910
  if (code === 0) {
32931
- resolve5({ ok: true, path: target });
32911
+ resolve4({ ok: true, path: target });
32932
32912
  return;
32933
32913
  }
32934
32914
  const tail = stderrBuf.split(/[\r\n]+/).filter((s2) => s2.trim().length > 0).pop() ?? `git clone exited with ${code}`;
32935
- resolve5({ ok: false, error: tail });
32915
+ resolve4({ ok: false, error: tail });
32936
32916
  });
32937
32917
  } catch (err) {
32938
- resolve5({ ok: false, error: err instanceof Error ? err.message : String(err) });
32918
+ resolve4({ ok: false, error: err instanceof Error ? err.message : String(err) });
32939
32919
  }
32940
32920
  });
32941
32921
  }
@@ -33728,16 +33708,16 @@ var init_dialog2 = __esm(() => {
33728
33708
 
33729
33709
  // src/tui/component/new-task-dialog/index.tsx
33730
33710
  function show(dialog, defaultRepo, savedRepos, options) {
33731
- return new Promise((resolve5) => {
33711
+ return new Promise((resolve4) => {
33732
33712
  dialog.replace(() => createComponent2(NewTaskDialogView, {
33733
33713
  defaultRepo,
33734
33714
  savedRepos,
33735
33715
  get defaultCloneParent() {
33736
33716
  return options?.defaultCloneParent;
33737
33717
  },
33738
- onSubmit: (v2) => resolve5(v2),
33739
- onCancel: () => resolve5(undefined)
33740
- }), () => resolve5(undefined));
33718
+ onSubmit: (v2) => resolve4(v2),
33719
+ onCancel: () => resolve4(undefined)
33720
+ }), () => resolve4(undefined));
33741
33721
  dialog.setSize("medium");
33742
33722
  });
33743
33723
  }
@@ -34462,7 +34442,7 @@ var init_dialog3 = __esm(() => {
34462
34442
 
34463
34443
  // src/tui/component/quick-fork-dialog/index.tsx
34464
34444
  function show2(dialog, input) {
34465
- return new Promise((resolve5) => {
34445
+ return new Promise((resolve4) => {
34466
34446
  dialog.replace(() => createComponent2(QuickForkDialogView, {
34467
34447
  get repo() {
34468
34448
  return input.repo;
@@ -34476,9 +34456,9 @@ function show2(dialog, input) {
34476
34456
  get effort() {
34477
34457
  return input.effort;
34478
34458
  },
34479
- onSubmit: (result) => resolve5(result),
34480
- onCancel: () => resolve5(undefined)
34481
- }), () => resolve5(undefined));
34459
+ onSubmit: (result) => resolve4(result),
34460
+ onCancel: () => resolve4(undefined)
34461
+ }), () => resolve4(undefined));
34482
34462
  dialog.setSize("large");
34483
34463
  });
34484
34464
  }
@@ -34569,15 +34549,15 @@ var init_dialog4 = __esm(() => {
34569
34549
 
34570
34550
  // src/tui/component/rename-task-dialog/index.tsx
34571
34551
  function show3(dialog, currentTitle, opts = {}) {
34572
- return new Promise((resolve5) => {
34552
+ return new Promise((resolve4) => {
34573
34553
  dialog.replace(() => createComponent2(RenameTaskDialogView, {
34574
34554
  currentTitle,
34575
34555
  get dialogTitle() {
34576
34556
  return opts.dialogTitle;
34577
34557
  },
34578
- onSubmit: (v2) => resolve5(v2),
34579
- onCancel: () => resolve5(undefined)
34580
- }), () => resolve5(undefined));
34558
+ onSubmit: (v2) => resolve4(v2),
34559
+ onCancel: () => resolve4(undefined)
34560
+ }), () => resolve4(undefined));
34581
34561
  });
34582
34562
  }
34583
34563
  var RenameTaskDialog;
@@ -36001,8 +35981,8 @@ function makeDropdownWindow(items, cursor, maxVisible) {
36001
35981
 
36002
35982
  // src/tui/panes/chat/composer/history-store.ts
36003
35983
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
36004
- import { appendFile as appendFile2, mkdir as mkdir7, readFile as readFile13, rename as rename2, writeFile as writeFile5 } from "fs/promises";
36005
- import { dirname as dirname9, join as join22 } from "path";
35984
+ import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile12, rename as rename2, writeFile as writeFile5 } from "fs/promises";
35985
+ import { dirname as dirname8, join as join22 } from "path";
36006
35986
  function defaultHistoryPath() {
36007
35987
  return join22(kobeStateDir(), "composer-history.jsonl");
36008
35988
  }
@@ -36048,8 +36028,8 @@ function loadFromDisk(path18 = defaultHistoryPath()) {
36048
36028
  function appendToDisk(entry, path18 = defaultHistoryPath()) {
36049
36029
  const next = writeQueue.then(async () => {
36050
36030
  try {
36051
- const dir = dirname9(path18);
36052
- await mkdir7(dir, { recursive: true });
36031
+ const dir = dirname8(path18);
36032
+ await mkdir6(dir, { recursive: true });
36053
36033
  const line = `${JSON.stringify(entry)}
36054
36034
  `;
36055
36035
  await appendFile2(path18, line, { encoding: "utf8", mode: 384 });
@@ -36064,7 +36044,7 @@ async function pruneToCap(path18 = defaultHistoryPath(), cap = DISK_HISTORY_CAP)
36064
36044
  try {
36065
36045
  if (!existsSync12(path18))
36066
36046
  return;
36067
- const text = await readFile13(path18, "utf8");
36047
+ const text = await readFile12(path18, "utf8");
36068
36048
  const lines = text.split(`
36069
36049
  `).filter((l2) => l2.length > 0);
36070
36050
  if (lines.length <= cap)
@@ -36466,7 +36446,7 @@ var init_HistoryPalette = __esm(() => {
36466
36446
 
36467
36447
  // src/tui/panes/chat/composer/history-palette.tsx
36468
36448
  function show4(dialog, opts) {
36469
- return new Promise((resolve5) => {
36449
+ return new Promise((resolve4) => {
36470
36450
  dialog.replace(() => createComponent2(HistoryPaletteView, {
36471
36451
  get taskLabelFor() {
36472
36452
  return opts.taskLabelFor;
@@ -36474,9 +36454,9 @@ function show4(dialog, opts) {
36474
36454
  get currentProject() {
36475
36455
  return opts.currentProject;
36476
36456
  },
36477
- onSubmit: (v2) => resolve5(v2),
36478
- onCancel: () => resolve5(undefined)
36479
- }), () => resolve5(undefined));
36457
+ onSubmit: (v2) => resolve4(v2),
36458
+ onCancel: () => resolve4(undefined)
36459
+ }), () => resolve4(undefined));
36480
36460
  });
36481
36461
  }
36482
36462
  var HistoryPalette;
@@ -41428,15 +41408,15 @@ var init_ModelPicker = __esm(() => {
41428
41408
  init_keymap();
41429
41409
  init_dialog();
41430
41410
  ModelPicker.show = (dialog, current, currentEffort, currentVendor, lockedVendor) => {
41431
- return new Promise((resolve5) => {
41411
+ return new Promise((resolve4) => {
41432
41412
  dialog.replace(() => createComponent2(ModelPicker, {
41433
41413
  current,
41434
41414
  currentEffort,
41435
41415
  currentVendor,
41436
41416
  lockedVendor,
41437
- onPick: (choice) => resolve5(choice),
41438
- onCancel: () => resolve5(undefined)
41439
- }), () => resolve5(undefined));
41417
+ onPick: (choice) => resolve4(choice),
41418
+ onCancel: () => resolve4(undefined)
41419
+ }), () => resolve4(undefined));
41440
41420
  });
41441
41421
  };
41442
41422
  });
@@ -42035,13 +42015,13 @@ var init_store2 = __esm(() => {
42035
42015
  });
42036
42016
 
42037
42017
  // src/tui/panes/chat/bash-mode.ts
42038
- import { spawn as spawn15 } from "child_process";
42018
+ import { spawn as spawn16 } from "child_process";
42039
42019
  async function runBashCommand(opts) {
42040
42020
  const shell = resolveShell();
42041
- return await new Promise((resolve5, reject) => {
42021
+ return await new Promise((resolve4, reject) => {
42042
42022
  let child;
42043
42023
  try {
42044
- child = spawn15(shell, ["-c", opts.command], {
42024
+ child = spawn16(shell, ["-c", opts.command], {
42045
42025
  cwd: opts.cwd,
42046
42026
  stdio: ["ignore", "pipe", "pipe"],
42047
42027
  env: process.env,
@@ -42060,7 +42040,7 @@ async function runBashCommand(opts) {
42060
42040
  if (timeoutHandle)
42061
42041
  clearTimeout(timeoutHandle);
42062
42042
  opts.signal.removeEventListener("abort", onAbort);
42063
- resolve5(result);
42043
+ resolve4(result);
42064
42044
  };
42065
42045
  child.stdout?.setEncoding("utf8");
42066
42046
  child.stderr?.setEncoding("utf8");
@@ -45182,7 +45162,7 @@ var init_tui = __esm(() => {
45182
45162
  });
45183
45163
 
45184
45164
  // src/cli/index.ts
45185
- import { resolve as resolve5 } from "path";
45165
+ import { resolve as resolve4 } from "path";
45186
45166
 
45187
45167
  // src/cli/daemon-mode.ts
45188
45168
  function parseCliArgs(argv) {
@@ -45208,7 +45188,7 @@ function parseCliArgs(argv) {
45208
45188
 
45209
45189
  // src/cli/index.ts
45210
45190
  async function runAddSubcommand(arg) {
45211
- const target = resolve5(process.cwd(), arg && arg.length > 0 ? arg : ".");
45191
+ const target = resolve4(process.cwd(), arg && arg.length > 0 ? arg : ".");
45212
45192
  const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
45213
45193
  const result = addSavedRepo2(target);
45214
45194
  if (result.added) {
@@ -45281,6 +45261,8 @@ async function main() {
45281
45261
  await runSkillSubcommand2(rest);
45282
45262
  return;
45283
45263
  }
45264
+ const { ensureSkillInstalled: ensureSkillInstalled2 } = await Promise.resolve().then(() => (init_skill_cmd(), exports_skill_cmd));
45265
+ await ensureSkillInstalled2();
45284
45266
  const { startTui: startTui2 } = await Promise.resolve().then(() => (init_tui(), exports_tui));
45285
45267
  await startTui2({ daemonMode: parsed.daemonMode });
45286
45268
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@sma1lboy/kobe",
4
- "version": "0.5.27",
4
+ "version": "0.5.28",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",
@@ -1,109 +0,0 @@
1
- ---
2
- name: kobe
3
- description: Spawn and coordinate parallel coding tasks via the kobe TUI from your shell. Use when the user asks to "try N approaches in parallel", "fan out", "compare implementations side-by-side", or "split this into subtasks". One task per attempt, each gets its own git worktree and its own agent session.
4
- ---
5
-
6
- # kobe — parallel coding tasks from your shell
7
-
8
- kobe is a local terminal UI that runs many AI coding sessions at once.
9
- Each task lives in its own git worktree with its own agent session.
10
- When the user asks for parallel exploration, fan out by spawning kobe
11
- tasks instead of doing N attempts sequentially in one chat.
12
-
13
- The daemon must already be running. The TUI auto-starts it; if not, run
14
- `kobe daemon start` once.
15
-
16
- ## Triggers — fire fan-out when the user says
17
-
18
- - "try N approaches in parallel", "并行试 N 个"
19
- - "fan out", "split this into subtasks"
20
- - "compare X and Y side-by-side"
21
- - "explore a few different ways to..."
22
- - explicit count: "spin up 3 tasks for..."
23
-
24
- Single ambiguous tasks → no fan-out. Just do them in your own chat.
25
-
26
- ## How — the five verbs
27
-
28
- Each verb is a one-shot shell command. Reads stdin: none. Writes a JSON
29
- object to stdout, exits 0. On error, writes
30
- `{"error":{"message":"...","code":"..."}}` to stderr and exits non-zero.
31
-
32
- ```bash
33
- # Spawn a new task. Returns task JSON; read `.taskId`.
34
- kobe api spawn-task --repo $PWD --prompt "<scoped prompt>" [--title T] [--base-branch B]
35
-
36
- # Create an extra chat tab on an existing task.
37
- kobe api create-tab --task-id <id> [--title T]
38
-
39
- # Send a follow-up prompt to a task (resumes the agent session).
40
- kobe api send --task-id <id> --prompt "<text>" [--tab-id TID]
41
-
42
- # Read a task's current state (status, branch, worktree, tabs).
43
- kobe api get-task --task-id <id>
44
-
45
- # Read a single tab off a task.
46
- kobe api get-tab --task-id <id> --tab-id <tab-id>
47
- ```
48
-
49
- Add `--pretty` to any verb to pretty-print stdout for inspection.
50
- Output to stdout is always one JSON object terminated with `\n`.
51
-
52
- ## Workflow — fan-out + report back
53
-
54
- ```bash
55
- # 1. Spawn 3 parallel attempts. Cap at 3-4.
56
- T1=$(kobe api spawn-task --repo "$PWD" --prompt "Approach A: use a state machine" | jq -r .taskId)
57
- T2=$(kobe api spawn-task --repo "$PWD" --prompt "Approach B: use event sourcing" | jq -r .taskId)
58
- T3=$(kobe api spawn-task --repo "$PWD" --prompt "Approach C: use a reducer pattern" | jq -r .taskId)
59
-
60
- # 2. Tell the user what you spawned so the sidebar reads back correctly.
61
- echo "Spawned three tasks: $T1 (state machine), $T2 (event sourcing), $T3 (reducer)"
62
-
63
- # 3. Poll until each is idle. Don't tight-loop; sleep 5-15s between checks.
64
- for ID in $T1 $T2 $T3; do
65
- while true; do
66
- STATUS=$(kobe api get-task --task-id "$ID" | jq -r .task.status)
67
- case "$STATUS" in
68
- idle|awaiting-approval|done) break ;;
69
- esac
70
- sleep 10
71
- done
72
- done
73
-
74
- # 4. Read each result and aggregate. The active tab's id is on the task.
75
- for ID in $T1 $T2 $T3; do
76
- TAB=$(kobe api get-task --task-id "$ID" | jq -r .task.activeTabId)
77
- kobe api get-tab --task-id "$ID" --tab-id "$TAB"
78
- done
79
- ```
80
-
81
- ## Do
82
-
83
- - Cap fan-out at 3-4 in parallel. The orchestrator caps total concurrency at 4.
84
- - Give each subtask its own scoped prompt — don't dump the whole parent conversation.
85
- - Tell the user what was spawned, with IDs, so they can follow along in the sidebar.
86
- - Poll status with `sleep` between calls. 5-15 seconds is fine.
87
- - Aggregate results back into your own chat before reporting to the user.
88
-
89
- ## Don't
90
-
91
- - Don't fan out for single simple tasks. One thing → do it yourself in chat.
92
- - Don't recursively spawn from inside a spawned task. There is no recursion
93
- guard yet; you will starve the concurrency cap and the inner spawn will
94
- hang.
95
- - Don't use `kobe api send` as a chat channel. Every send is a full agent
96
- turn — it costs tokens and time. Send a complete instruction, not a
97
- conversation.
98
- - Don't delete tasks. There is no `kobe api delete` verb on purpose. Cleanup
99
- happens in the TUI by the user.
100
-
101
- ## When the daemon is not running
102
-
103
- `kobe api ...` exits 2 with `{"error":{"code":"BAD_DAEMON",...}}` on stderr.
104
- If you see this:
105
-
106
- > The kobe daemon is not running. Ask the user to run `kobe daemon start`
107
- > (or launch the kobe TUI, which auto-starts it), then re-try.
108
-
109
- Don't try to start the daemon yourself; that's the user's call.