@sma1lboy/kobe 0.7.2 → 0.7.3

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +697 -99
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -70,7 +70,7 @@ var init_package = __esm(() => {
70
70
  package_default = {
71
71
  $schema: "https://json.schemastore.org/package.json",
72
72
  name: "@sma1lboy/kobe",
73
- version: "0.7.2",
73
+ version: "0.7.3",
74
74
  description: "TUI orchestrator for Claude Code (codename)",
75
75
  type: "module",
76
76
  packageManager: "bun@1.3.13",
@@ -6327,32 +6327,74 @@ var init_worktree_changes = __esm(() => {
6327
6327
  // src/cli/api-cmd.ts
6328
6328
  var exports_api_cmd = {};
6329
6329
  __export(exports_api_cmd, {
6330
+ verbSchema: () => verbSchema,
6331
+ verbHelp: () => verbHelp,
6332
+ validateAgainstSpec: () => validateAgainstSpec,
6333
+ schemaIndex: () => schemaIndex,
6330
6334
  runApiSubcommand: () => runApiSubcommand,
6331
6335
  parseFlags: () => parseFlags,
6332
6336
  parseAgentsSpec: () => parseAgentsSpec,
6337
+ fullSchema: () => fullSchema,
6338
+ findVerb: () => findVerb,
6333
6339
  apiUsage: () => apiUsage,
6340
+ VERB_GROUPS: () => VERB_GROUPS,
6341
+ VERBS: () => VERBS,
6334
6342
  FANOUT_CAP: () => FANOUT_CAP,
6335
6343
  ApiError: () => ApiError,
6336
- API_VERBS: () => API_VERBS
6344
+ API_VERBS: () => API_VERBS,
6345
+ API_SCHEMA_VERSION: () => API_SCHEMA_VERSION
6337
6346
  });
6338
6347
  import { resolve as resolve4 } from "path";
6339
- function parseFlags(argv) {
6348
+ function groupOf(verbName) {
6349
+ for (const [group, names] of Object.entries(VERB_GROUPS)) {
6350
+ if (names.includes(verbName))
6351
+ return group;
6352
+ }
6353
+ return "other";
6354
+ }
6355
+ async function handleSchema(_client, parsed) {
6356
+ const { flags } = parsed;
6357
+ const verbName = optional(flags, "verb");
6358
+ if (verbName) {
6359
+ const v = findVerb(verbName);
6360
+ if (!v)
6361
+ throw new ApiError(`unknown verb: ${verbName}`, "BAD_VERB");
6362
+ return verbSchema(v);
6363
+ }
6364
+ const group = optional(flags, "group");
6365
+ if (group)
6366
+ return groupSchema(group);
6367
+ if (optionalBool(flags, "all"))
6368
+ return fullSchema();
6369
+ return schemaIndex();
6370
+ }
6371
+ function findVerb(name) {
6372
+ const canonical = VERB_ALIASES[name] ?? name;
6373
+ return VERBS.find((v) => v.name === canonical);
6374
+ }
6375
+ function parseFlags(argv, booleanFlags = new Set) {
6340
6376
  const flags = new Map;
6341
6377
  let pretty = false;
6378
+ let help = false;
6342
6379
  for (let i = 0;i < argv.length; i++) {
6343
6380
  const arg = argv[i];
6344
- if (!arg.startsWith("--")) {
6381
+ if (!arg.startsWith("--") && arg !== "-h") {
6345
6382
  throw new ApiError(`unexpected positional arg: ${arg}`, "BAD_FLAG");
6346
6383
  }
6384
+ if (arg === "-h") {
6385
+ help = true;
6386
+ continue;
6387
+ }
6347
6388
  const eq = arg.indexOf("=");
6348
6389
  if (eq !== -1) {
6349
6390
  const key2 = arg.slice(2, eq);
6350
6391
  const value = arg.slice(eq + 1);
6351
- if (key2 === "pretty") {
6392
+ if (key2 === "pretty")
6352
6393
  pretty = value !== "false" && value !== "0";
6353
- } else {
6394
+ else if (key2 === "help")
6395
+ help = value !== "false" && value !== "0";
6396
+ else
6354
6397
  flags.set(key2, value);
6355
- }
6356
6398
  continue;
6357
6399
  }
6358
6400
  const key = arg.slice(2);
@@ -6360,6 +6402,14 @@ function parseFlags(argv) {
6360
6402
  pretty = true;
6361
6403
  continue;
6362
6404
  }
6405
+ if (key === "help") {
6406
+ help = true;
6407
+ continue;
6408
+ }
6409
+ if (booleanFlags.has(key)) {
6410
+ flags.set(key, "true");
6411
+ continue;
6412
+ }
6363
6413
  const next = argv[i + 1];
6364
6414
  if (next === undefined || next.startsWith("--")) {
6365
6415
  throw new ApiError(`flag --${key} requires a value`, "BAD_FLAG");
@@ -6367,19 +6417,50 @@ function parseFlags(argv) {
6367
6417
  flags.set(key, next);
6368
6418
  i += 1;
6369
6419
  }
6370
- return { flags, pretty };
6420
+ return { flags, pretty, help };
6421
+ }
6422
+ function validateAgainstSpec(verb, flags) {
6423
+ const known = new Set(verb.flags.map((f) => f.name));
6424
+ for (const key of flags.keys()) {
6425
+ if (!known.has(key)) {
6426
+ throw new ApiError(`unknown flag --${key} for "${verb.name}". Run \`kobe api ${verb.name} --help\``, "BAD_FLAG");
6427
+ }
6428
+ }
6429
+ for (const f of verb.flags) {
6430
+ if (f.required && !flags.get(f.name))
6431
+ throw new ApiError(`--${f.name} is required for "${verb.name}"`, "MISSING_FLAG");
6432
+ if (f.type === "enum" && f.values) {
6433
+ const raw = flags.get(f.name);
6434
+ if (raw !== undefined && !f.values.includes(raw)) {
6435
+ throw new ApiError(`--${f.name} must be one of ${f.values.join(", ")}`, "BAD_FLAG");
6436
+ }
6437
+ }
6438
+ if (f.type === "int") {
6439
+ const raw = flags.get(f.name);
6440
+ if (raw !== undefined) {
6441
+ const n = Number.parseInt(raw, 10);
6442
+ if (!Number.isInteger(n) || n <= 0)
6443
+ throw new ApiError(`--${f.name} must be a positive integer`, "BAD_FLAG");
6444
+ }
6445
+ }
6446
+ }
6371
6447
  }
6372
6448
  function required(flags, key) {
6373
6449
  const v = flags.get(key);
6374
- if (v === undefined || v.length === 0) {
6450
+ if (v === undefined || v.length === 0)
6375
6451
  throw new ApiError(`--${key} is required`, "MISSING_FLAG");
6376
- }
6377
6452
  return v;
6378
6453
  }
6379
6454
  function optional(flags, key) {
6380
6455
  const v = flags.get(key);
6381
6456
  return v && v.length > 0 ? v : undefined;
6382
6457
  }
6458
+ function requireEnum(flags, key, values) {
6459
+ const v = required(flags, key);
6460
+ if (!values.includes(v))
6461
+ throw new ApiError(`--${key} must be one of ${values.join(", ")}`, "BAD_FLAG");
6462
+ return v;
6463
+ }
6383
6464
  function optionalVendor2(flags) {
6384
6465
  const raw = optional(flags, "vendor");
6385
6466
  if (raw === undefined)
@@ -6389,16 +6470,28 @@ function optionalVendor2(flags) {
6389
6470
  }
6390
6471
  return raw;
6391
6472
  }
6473
+ function optionalBool(flags, key) {
6474
+ const raw = optional(flags, key);
6475
+ if (raw === undefined)
6476
+ return;
6477
+ if (["true", "1", "yes"].includes(raw))
6478
+ return true;
6479
+ if (["false", "0", "no"].includes(raw))
6480
+ return false;
6481
+ throw new ApiError(`--${key} must be a boolean (true/false)`, "BAD_FLAG");
6482
+ }
6392
6483
  function optionalPositiveInt(flags, key) {
6393
6484
  const raw = optional(flags, key);
6394
6485
  if (raw === undefined)
6395
6486
  return;
6396
6487
  const n = Number.parseInt(raw, 10);
6397
- if (!Number.isInteger(n) || n <= 0) {
6488
+ if (!Number.isInteger(n) || n <= 0)
6398
6489
  throw new ApiError(`--${key} must be a positive integer`, "BAD_FLAG");
6399
- }
6400
6490
  return n;
6401
6491
  }
6492
+ function resolveRepoFlag(repo) {
6493
+ return resolve4(process.cwd(), repo);
6494
+ }
6402
6495
  function parseAgentsSpec(spec) {
6403
6496
  const out = [];
6404
6497
  for (const part of spec.split(",")) {
@@ -6423,19 +6516,102 @@ function parseAgentsSpec(spec) {
6423
6516
  throw new ApiError('--agents specified no agents (e.g. "claude:2,codex:1")', "BAD_FLAG");
6424
6517
  return out;
6425
6518
  }
6519
+ function flagJson(f) {
6520
+ return {
6521
+ name: f.name,
6522
+ type: f.type,
6523
+ required: f.required ?? false,
6524
+ ...f.values ? { values: f.values } : {},
6525
+ ...f.default !== undefined ? { default: f.default } : {},
6526
+ ...f.placeholder ? { placeholder: f.placeholder } : {},
6527
+ description: f.description
6528
+ };
6529
+ }
6530
+ function verbSchema(v) {
6531
+ return {
6532
+ name: v.name,
6533
+ group: groupOf(v.name),
6534
+ summary: v.summary,
6535
+ offline: v.offline ?? false,
6536
+ flags: v.flags.map(flagJson)
6537
+ };
6538
+ }
6539
+ function schemaIndex() {
6540
+ return {
6541
+ apiVersion: API_SCHEMA_VERSION,
6542
+ kobeVersion: CURRENT_VERSION,
6543
+ hint: "Compact index. Drill into ONE verb: `kobe api schema --verb <name>` (or `kobe api <verb> --help`). One group: `--group <g>`. Whole spec: `--all`.",
6544
+ groups: VERB_GROUPS,
6545
+ verbs: VERBS.map((v) => ({ name: v.name, group: groupOf(v.name), summary: v.summary })),
6546
+ globalFlags: GLOBAL_FLAGS,
6547
+ aliases: VERB_ALIASES
6548
+ };
6549
+ }
6550
+ function groupSchema(group) {
6551
+ const names = VERB_GROUPS[group];
6552
+ if (!names) {
6553
+ throw new ApiError(`unknown group: ${group}. Groups: ${Object.keys(VERB_GROUPS).join(", ")}`, "BAD_FLAG");
6554
+ }
6555
+ return {
6556
+ group,
6557
+ verbs: names.map((n) => {
6558
+ const v = findVerb(n);
6559
+ return { name: n, summary: v?.summary ?? "" };
6560
+ })
6561
+ };
6562
+ }
6563
+ function fullSchema() {
6564
+ return {
6565
+ apiVersion: API_SCHEMA_VERSION,
6566
+ kobeVersion: CURRENT_VERSION,
6567
+ output: {
6568
+ success: "one JSON object on stdout, newline-terminated, exit 0",
6569
+ error: '{"error":{"message","code"}} on stderr, exit != 0',
6570
+ pretty: "--pretty indents stdout JSON"
6571
+ },
6572
+ globalFlags: GLOBAL_FLAGS,
6573
+ aliases: VERB_ALIASES,
6574
+ groups: VERB_GROUPS,
6575
+ verbs: VERBS.map(verbSchema)
6576
+ };
6577
+ }
6578
+ function flagSignature(verb) {
6579
+ return verb.flags.map((f) => {
6580
+ const meta = f.type === "enum" && f.values ? f.values.join("|") : f.placeholder ?? (f.type === "bool" ? "" : "X");
6581
+ const core = meta ? `--${f.name} ${meta}` : `--${f.name}`;
6582
+ return f.required ? core : `[${core}]`;
6583
+ }).join(" ");
6584
+ }
6585
+ function verbHelp(verb) {
6586
+ const lines = [`kobe api ${verb.name} ${flagSignature(verb)}`.trimEnd(), "", verb.summary, ""];
6587
+ const alias = Object.entries(VERB_ALIASES).find(([, canon]) => canon === verb.name)?.[0];
6588
+ if (alias)
6589
+ lines.push(`Alias: ${alias}`, "");
6590
+ if (verb.flags.length > 0) {
6591
+ lines.push("Flags:");
6592
+ for (const f of verb.flags) {
6593
+ const req = f.required ? " (required)" : "";
6594
+ const def = f.default !== undefined ? ` [default: ${f.default}]` : "";
6595
+ const vals = f.type === "enum" && f.values ? ` {${f.values.join("|")}}` : "";
6596
+ lines.push(` --${f.name}${vals}${req}${def} ${f.description}`);
6597
+ }
6598
+ lines.push("");
6599
+ }
6600
+ lines.push("Global: [--pretty] [--help]");
6601
+ return lines.join(`
6602
+ `);
6603
+ }
6426
6604
  function apiUsage() {
6605
+ const rows = VERBS.map((v) => ` ${v.name.padEnd(18)} ${v.summary}`);
6427
6606
  return [
6428
- "usage: kobe api <verb> [flags] [--pretty]",
6607
+ "usage: kobe api <verb> [flags] [--pretty] [--help]",
6608
+ "",
6609
+ "Explore the full surface (names, flags, types) with: kobe api schema",
6429
6610
  "",
6430
6611
  "verbs:",
6431
- " spawn-task --repo PATH [--prompt TEXT] [--title T] [--base-branch B] [--vendor V]",
6432
- " fan-out --repo PATH --prompt TEXT [--count N | --agents claude:2,codex:1] [--base-branch B]",
6433
- " send [--task-id ID] --prompt TEXT",
6434
- " get-task --task-id ID",
6435
- " collect --task-ids a,b,c | --repo PATH",
6436
- " list",
6612
+ ...rows,
6437
6613
  "",
6438
- "Output is one JSON object on stdout (exit 0); errors are JSON on stderr (exit \u2260 0)."
6614
+ "Output is one JSON object on stdout (exit 0); errors are JSON on stderr (exit != 0)."
6439
6615
  ].join(`
6440
6616
  `);
6441
6617
  }
@@ -6492,12 +6668,22 @@ async function resolveActiveTaskId(client) {
6492
6668
  }
6493
6669
  return activeId;
6494
6670
  }
6495
- async function spawnTask(client, parsed) {
6671
+ async function simpleRpc(client, name, payload) {
6672
+ if (!client)
6673
+ throw new ApiError("daemon required", "BAD_DAEMON");
6674
+ return client.request(name, payload);
6675
+ }
6676
+ async function add(client, parsed) {
6677
+ if (!client)
6678
+ throw new ApiError("daemon required", "BAD_DAEMON");
6496
6679
  const { flags } = parsed;
6497
- const payload = { repo: required(flags, "repo") };
6680
+ const payload = { repo: resolveRepoFlag(required(flags, "repo")) };
6498
6681
  const title = optional(flags, "title");
6499
6682
  if (title)
6500
6683
  payload.title = title;
6684
+ const branch = optional(flags, "branch");
6685
+ if (branch)
6686
+ payload.branch = branch;
6501
6687
  const baseRef = optional(flags, "base-branch");
6502
6688
  if (baseRef)
6503
6689
  payload.baseRef = baseRef;
@@ -6505,25 +6691,26 @@ async function spawnTask(client, parsed) {
6505
6691
  if (vendor)
6506
6692
  payload.vendor = vendor;
6507
6693
  const res = await client.request("task.create", payload);
6508
- const prompt = optional(flags, "prompt");
6509
- if (!prompt) {
6510
- return { taskId: res.taskId, task: res.task, started: false };
6694
+ const taskId = res.taskId;
6695
+ const status = optional(flags, "status");
6696
+ if (status)
6697
+ await client.request("task.status", { taskId, status: requireEnum(flags, "status", TASK_STATUSES) });
6698
+ const pin = optionalBool(flags, "pin");
6699
+ if (pin !== undefined)
6700
+ await client.request("task.pin", { taskId, pinned: pin });
6701
+ let task = res.task;
6702
+ if (status || pin !== undefined) {
6703
+ task = (await client.request("task.get", { taskId })).task;
6511
6704
  }
6512
- const delivered = await deliverPrompt(client, {
6513
- id: res.taskId,
6514
- worktreePath: res.task.worktreePath,
6515
- vendor: res.task.vendor,
6516
- repo: res.task.repo
6517
- }, prompt);
6518
- return {
6519
- taskId: res.taskId,
6520
- task: res.task,
6521
- started: delivered.started,
6522
- engineReady: delivered.engineReady,
6523
- session: delivered.session
6524
- };
6705
+ const prompt = optional(flags, "prompt");
6706
+ if (!prompt)
6707
+ return { taskId, task, started: false };
6708
+ const delivered = await deliverPrompt(client, { id: taskId, worktreePath: task.worktreePath, vendor: task.vendor, repo: task.repo }, prompt);
6709
+ return { taskId, task, started: delivered.started, engineReady: delivered.engineReady, session: delivered.session };
6525
6710
  }
6526
6711
  async function send(client, parsed) {
6712
+ if (!client)
6713
+ throw new ApiError("daemon required", "BAD_DAEMON");
6527
6714
  const { flags } = parsed;
6528
6715
  const prompt = required(flags, "prompt");
6529
6716
  let taskId = optional(flags, "task-id");
@@ -6550,17 +6737,50 @@ async function send(client, parsed) {
6550
6737
  };
6551
6738
  }
6552
6739
  async function getTask(client, parsed) {
6740
+ if (!client)
6741
+ throw new ApiError("daemon required", "BAD_DAEMON");
6553
6742
  const taskId = required(parsed.flags, "task-id");
6554
6743
  const res = await client.request("task.get", { taskId });
6555
6744
  const running = await sessionExists(tmuxSessionName(taskId));
6556
6745
  return { task: res.task, running };
6557
6746
  }
6558
6747
  async function list(client) {
6748
+ if (!client)
6749
+ throw new ApiError("daemon required", "BAD_DAEMON");
6559
6750
  return client.request("task.list");
6560
6751
  }
6752
+ async function setActive(client, parsed) {
6753
+ if (!client)
6754
+ throw new ApiError("daemon required", "BAD_DAEMON");
6755
+ const none = optionalBool(parsed.flags, "none");
6756
+ const taskId = none ? null : required(parsed.flags, "task-id");
6757
+ await client.request("task.setActive", { taskId });
6758
+ return { ok: true, activeTaskId: taskId };
6759
+ }
6760
+ async function adopt(client, parsed) {
6761
+ if (!client)
6762
+ throw new ApiError("daemon required", "BAD_DAEMON");
6763
+ const { flags } = parsed;
6764
+ const input = {
6765
+ repo: resolveRepoFlag(required(flags, "repo")),
6766
+ worktreePath: resolveRepoFlag(required(flags, "worktree"))
6767
+ };
6768
+ const branch = optional(flags, "branch");
6769
+ if (branch)
6770
+ input.branch = branch;
6771
+ const vendor = optionalVendor2(flags);
6772
+ if (vendor)
6773
+ input.vendor = vendor;
6774
+ const title = optional(flags, "title");
6775
+ if (title)
6776
+ input.title = title;
6777
+ return client.request("worktree.adopt", input);
6778
+ }
6561
6779
  async function fanOut(client, parsed) {
6780
+ if (!client)
6781
+ throw new ApiError("daemon required", "BAD_DAEMON");
6562
6782
  const { flags } = parsed;
6563
- const repo = required(flags, "repo");
6783
+ const repo = resolveRepoFlag(required(flags, "repo"));
6564
6784
  const prompt = required(flags, "prompt");
6565
6785
  const title = optional(flags, "title");
6566
6786
  const baseRef = optional(flags, "base-branch");
@@ -6589,6 +6809,8 @@ async function fanOut(client, parsed) {
6589
6809
  return { count: tasks.length, tasks };
6590
6810
  }
6591
6811
  async function collect(client, parsed) {
6812
+ if (!client)
6813
+ throw new ApiError("daemon required", "BAD_DAEMON");
6592
6814
  const { flags } = parsed;
6593
6815
  const idsFlag = optional(flags, "task-ids");
6594
6816
  const repoFlag = optional(flags, "repo");
@@ -6597,7 +6819,7 @@ async function collect(client, parsed) {
6597
6819
  taskIds = idsFlag.split(",").map((s) => s.trim()).filter(Boolean);
6598
6820
  } else if (repoFlag) {
6599
6821
  const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
6600
- const target = resolveRepoRoot2(resolve4(process.cwd(), repoFlag));
6822
+ const target = resolveRepoRoot2(resolveRepoFlag(repoFlag));
6601
6823
  const { tasks } = await client.request("task.list");
6602
6824
  taskIds = tasks.filter((t) => !t.archived && resolveRepoRoot2(t.repo) === target).map((t) => t.id);
6603
6825
  } else {
@@ -6623,72 +6845,67 @@ async function collect(client, parsed) {
6623
6845
  return { tasks: out };
6624
6846
  }
6625
6847
  async function runApiSubcommand(argv) {
6626
- const [verb, ...rest] = argv;
6627
- if (!verb || verb === "--help" || verb === "-h" || verb === "help") {
6628
- if (!verb) {
6848
+ const [verbName, ...rest] = argv;
6849
+ if (!verbName || verbName === "--help" || verbName === "-h" || verbName === "help") {
6850
+ if (!verbName)
6629
6851
  fail(apiUsage(), "MISSING_VERB", 2);
6630
- }
6631
6852
  process.stdout.write(`${apiUsage()}
6632
6853
  `);
6633
6854
  return;
6634
6855
  }
6635
- if (!API_VERBS.includes(verb)) {
6636
- fail(`unknown verb: ${verb}
6856
+ const verb = findVerb(verbName);
6857
+ if (!verb)
6858
+ fail(`unknown verb: ${verbName}
6637
6859
  ${apiUsage()}`, "BAD_VERB", 2);
6638
- }
6860
+ const booleanFlags = new Set(verb.flags.filter((f) => f.type === "bool").map((f) => f.name));
6639
6861
  let parsed;
6640
6862
  try {
6641
- parsed = parseFlags(rest);
6863
+ parsed = parseFlags(rest, booleanFlags);
6642
6864
  } catch (err) {
6643
6865
  if (err instanceof ApiError)
6644
6866
  fail(err.message, err.code, 2);
6645
6867
  fail(err instanceof Error ? err.message : String(err), "BAD_FLAG", 2);
6646
6868
  }
6647
- let client;
6869
+ if (parsed.help) {
6870
+ process.stdout.write(`${verbHelp(verb)}
6871
+ `);
6872
+ return;
6873
+ }
6648
6874
  try {
6649
- client = await connectOrStartDaemon();
6875
+ validateAgainstSpec(verb, parsed.flags);
6650
6876
  } catch (err) {
6651
- fail(`could not reach or start the kobe daemon: ${err instanceof Error ? err.message : String(err)}`, "BAD_DAEMON", 2);
6877
+ if (err instanceof ApiError)
6878
+ fail(err.message, err.code, 2);
6879
+ fail(err instanceof Error ? err.message : String(err), "BAD_FLAG", 2);
6652
6880
  }
6653
- try {
6654
- let result;
6655
- switch (verb) {
6656
- case "spawn-task":
6657
- result = await spawnTask(client, parsed);
6658
- break;
6659
- case "fan-out":
6660
- result = await fanOut(client, parsed);
6661
- break;
6662
- case "send":
6663
- result = await send(client, parsed);
6664
- break;
6665
- case "get-task":
6666
- result = await getTask(client, parsed);
6667
- break;
6668
- case "collect":
6669
- result = await collect(client, parsed);
6670
- break;
6671
- case "list":
6672
- result = await list(client);
6673
- break;
6881
+ let client = null;
6882
+ if (!verb.offline) {
6883
+ try {
6884
+ client = await connectOrStartDaemon();
6885
+ } catch (err) {
6886
+ fail(`could not reach or start the kobe daemon: ${err instanceof Error ? err.message : String(err)}`, "BAD_DAEMON", 2);
6674
6887
  }
6888
+ }
6889
+ try {
6890
+ const result = await verb.handler(client, parsed);
6675
6891
  emit(result, parsed.pretty);
6676
6892
  } catch (err) {
6677
6893
  if (err instanceof ApiError)
6678
6894
  fail(err.message, err.code, 1);
6679
6895
  fail(err instanceof Error ? err.message : String(err), "RPC_ERROR", 1);
6680
6896
  } finally {
6681
- client.close();
6897
+ client?.close();
6682
6898
  }
6683
6899
  }
6684
- var API_VERBS, FANOUT_CAP = 10, ApiError;
6900
+ var API_SCHEMA_VERSION = 2, FANOUT_CAP = 10, TASK_STATUSES, ApiError, F, VERB_ALIASES, VERB_GROUPS, VERBS, API_VERBS, GLOBAL_FLAGS;
6685
6901
  var init_api_cmd = __esm(() => {
6686
6902
  init_daemon_process();
6687
6903
  init_interactive_command();
6688
6904
  init_client2();
6689
6905
  init_prompt_delivery();
6690
6906
  init_vendor();
6691
- API_VERBS = ["spawn-task", "fan-out", "send", "get-task", "collect", "list"];
6907
+ init_version();
6908
+ TASK_STATUSES = ["backlog", "in_progress", "in_review", "done", "canceled", "error"];
6692
6909
  ApiError = class ApiError extends Error {
6693
6910
  code;
6694
6911
  constructor(message, code) {
@@ -6696,6 +6913,241 @@ var init_api_cmd = __esm(() => {
6696
6913
  this.code = code;
6697
6914
  }
6698
6915
  };
6916
+ F = {
6917
+ repo: (required = true) => ({
6918
+ name: "repo",
6919
+ type: "string",
6920
+ required,
6921
+ placeholder: "PATH",
6922
+ description: "Repo root (git toplevel). Relative paths resolve against $PWD."
6923
+ }),
6924
+ taskId: (required = true) => ({
6925
+ name: "task-id",
6926
+ type: "string",
6927
+ required,
6928
+ placeholder: "ID",
6929
+ description: "Target task id (from `list` / `add`)."
6930
+ }),
6931
+ vendor: () => ({
6932
+ name: "vendor",
6933
+ type: "enum",
6934
+ values: ALL_VENDORS,
6935
+ placeholder: "V",
6936
+ description: "Engine vendor for the task."
6937
+ }),
6938
+ title: () => ({ name: "title", type: "string", placeholder: "T", description: "Human task title." }),
6939
+ prompt: (required, desc) => ({
6940
+ name: "prompt",
6941
+ type: "string",
6942
+ required,
6943
+ placeholder: "TEXT",
6944
+ description: desc
6945
+ })
6946
+ };
6947
+ VERB_ALIASES = { "spawn-task": "add" };
6948
+ VERB_GROUPS = {
6949
+ discover: ["schema"],
6950
+ read: ["list", "get-task", "collect"],
6951
+ create: ["add", "fan-out"],
6952
+ drive: ["send", "set-active"],
6953
+ edit: ["rename", "set-branch", "set-vendor", "set-status"],
6954
+ lifecycle: ["archive", "pin", "delete"],
6955
+ worktree: ["ensure-worktree", "adopt", "discover-adoptable"]
6956
+ };
6957
+ VERBS = [
6958
+ {
6959
+ name: "schema",
6960
+ summary: "Explore the API. Default = a COMPACT index (groups + verb summaries, no flags). Drill in with --verb / --group; --all for the full spec.",
6961
+ flags: [
6962
+ { name: "verb", type: "string", placeholder: "NAME", description: "Full flag detail for ONE verb." },
6963
+ { name: "group", type: "string", placeholder: "G", description: "List the verbs in one group (compact)." },
6964
+ {
6965
+ name: "all",
6966
+ type: "bool",
6967
+ description: "The COMPLETE spec \u2014 every verb AND every flag (large; avoid by default)."
6968
+ }
6969
+ ],
6970
+ offline: true,
6971
+ handler: handleSchema
6972
+ },
6973
+ { name: "list", summary: "List all tasks (incl. archived). Returns { tasks }.", flags: [], handler: list },
6974
+ {
6975
+ name: "get-task",
6976
+ summary: "Read one task's metadata. `.running` = its tmux session is live.",
6977
+ flags: [F.taskId()],
6978
+ handler: getTask
6979
+ },
6980
+ {
6981
+ name: "add",
6982
+ summary: "Create a task (shows in the sidebar immediately). With --prompt it also starts the engine and delivers it. Alias: spawn-task.",
6983
+ flags: [
6984
+ F.repo(),
6985
+ F.title(),
6986
+ {
6987
+ name: "branch",
6988
+ type: "string",
6989
+ placeholder: "B",
6990
+ description: "Explicit branch name (else auto kobe/<slug>-<id>)."
6991
+ },
6992
+ { name: "base-branch", type: "string", placeholder: "B", description: "Base ref the worktree branches from." },
6993
+ F.vendor(),
6994
+ {
6995
+ name: "status",
6996
+ type: "enum",
6997
+ values: TASK_STATUSES,
6998
+ default: "backlog",
6999
+ description: "Initial lifecycle status."
7000
+ },
7001
+ { name: "pin", type: "bool", description: "Pin the task to the top of the sidebar." },
7002
+ F.prompt(false, "Optional first message \u2014 when set, materializes the worktree, starts the engine, and pastes it.")
7003
+ ],
7004
+ handler: add
7005
+ },
7006
+ {
7007
+ name: "fan-out",
7008
+ summary: `Spawn N tasks of ONE prompt in a single call (parallel attempts). Capped at ${FANOUT_CAP}.`,
7009
+ flags: [
7010
+ F.repo(),
7011
+ F.prompt(true, "Shared prompt delivered to every spawned task."),
7012
+ { name: "count", type: "int", placeholder: "N", description: "Number of tasks of one vendor (with --vendor)." },
7013
+ {
7014
+ name: "agents",
7015
+ type: "string",
7016
+ placeholder: "claude:2,codex:1",
7017
+ description: "Per-vendor counts (alternative to --count)."
7018
+ },
7019
+ F.vendor(),
7020
+ F.title(),
7021
+ { name: "base-branch", type: "string", placeholder: "B", description: "Base ref for every worktree." }
7022
+ ],
7023
+ handler: fanOut
7024
+ },
7025
+ {
7026
+ name: "send",
7027
+ summary: "Paste a follow-up prompt into a task's running engine (one full turn). Defaults to the active task.",
7028
+ flags: [F.taskId(false), F.prompt(true, "Text pasted + submitted into the engine pane.")],
7029
+ handler: send
7030
+ },
7031
+ {
7032
+ name: "collect",
7033
+ summary: "Read-only comparison snapshot of several tasks (identity, branch, .running, uncommitted .changes).",
7034
+ flags: [
7035
+ { name: "task-ids", type: "csv", placeholder: "a,b,c", description: "Comma-separated task ids." },
7036
+ F.repo(false)
7037
+ ],
7038
+ handler: collect
7039
+ },
7040
+ {
7041
+ name: "rename",
7042
+ summary: "Set a task's title.",
7043
+ flags: [F.taskId(), { name: "title", type: "string", required: true, placeholder: "T", description: "New title." }],
7044
+ handler: (c, p) => simpleRpc(c, "task.rename", { taskId: required(p.flags, "task-id"), title: required(p.flags, "title") })
7045
+ },
7046
+ {
7047
+ name: "set-branch",
7048
+ summary: "Rename a task's branch (git branch -m if materialized, else recorded).",
7049
+ flags: [
7050
+ F.taskId(),
7051
+ { name: "branch", type: "string", required: true, placeholder: "B", description: "New branch name." }
7052
+ ],
7053
+ handler: (c, p) => simpleRpc(c, "task.setBranch", { taskId: required(p.flags, "task-id"), branch: required(p.flags, "branch") })
7054
+ },
7055
+ {
7056
+ name: "set-vendor",
7057
+ summary: "Change a task's engine vendor (takes effect on next session rebuild).",
7058
+ flags: [F.taskId(), { ...F.vendor(), required: true }],
7059
+ handler: (c, p) => simpleRpc(c, "task.setVendor", {
7060
+ taskId: required(p.flags, "task-id"),
7061
+ vendor: requireEnum(p.flags, "vendor", ALL_VENDORS)
7062
+ })
7063
+ },
7064
+ {
7065
+ name: "set-status",
7066
+ summary: "Set a task's lifecycle status.",
7067
+ flags: [
7068
+ F.taskId(),
7069
+ { name: "status", type: "enum", required: true, values: TASK_STATUSES, description: "New status." }
7070
+ ],
7071
+ handler: (c, p) => simpleRpc(c, "task.status", {
7072
+ taskId: required(p.flags, "task-id"),
7073
+ status: requireEnum(p.flags, "status", TASK_STATUSES)
7074
+ })
7075
+ },
7076
+ {
7077
+ name: "archive",
7078
+ summary: "Archive (or with --archived=false, unarchive) a task. Non-destructive: worktree/branch/history stay.",
7079
+ flags: [
7080
+ F.taskId(),
7081
+ { name: "archived", type: "bool", default: "true", description: "true to archive, false to unarchive." }
7082
+ ],
7083
+ handler: (c, p) => simpleRpc(c, "task.archive", {
7084
+ taskId: required(p.flags, "task-id"),
7085
+ archived: optionalBool(p.flags, "archived") ?? true
7086
+ })
7087
+ },
7088
+ {
7089
+ name: "pin",
7090
+ summary: "Pin (or with --pinned=false, unpin) a task to the top of the sidebar.",
7091
+ flags: [F.taskId(), { name: "pinned", type: "bool", default: "true", description: "true to pin, false to unpin." }],
7092
+ handler: (c, p) => simpleRpc(c, "task.pin", {
7093
+ taskId: required(p.flags, "task-id"),
7094
+ pinned: optionalBool(p.flags, "pinned") ?? true
7095
+ })
7096
+ },
7097
+ {
7098
+ name: "set-active",
7099
+ summary: "Set the shared active task (the focus every Tasks pane highlights). Pass --none to clear.",
7100
+ flags: [
7101
+ F.taskId(false),
7102
+ { name: "none", type: "bool", description: "Clear the active task instead of setting one." }
7103
+ ],
7104
+ handler: setActive
7105
+ },
7106
+ {
7107
+ name: "ensure-worktree",
7108
+ summary: "Materialize a task's git worktree on disk now (without starting an engine). Returns { worktreePath }.",
7109
+ flags: [F.taskId()],
7110
+ handler: (c, p) => simpleRpc(c, "task.ensureWorktree", { taskId: required(p.flags, "task-id") })
7111
+ },
7112
+ {
7113
+ name: "delete",
7114
+ summary: "Permanently remove a task (and its worktree). DESTRUCTIVE \u2014 prefer `archive`. Needs --force on a dirty worktree.",
7115
+ flags: [F.taskId(), { name: "force", type: "bool", description: "Delete even with uncommitted changes." }],
7116
+ handler: (c, p) => simpleRpc(c, "task.delete", {
7117
+ taskId: required(p.flags, "task-id"),
7118
+ force: optionalBool(p.flags, "force") ?? false
7119
+ })
7120
+ },
7121
+ {
7122
+ name: "discover-adoptable",
7123
+ summary: "List existing git worktrees in a repo not yet tracked as kobe tasks. Returns { worktrees }.",
7124
+ flags: [F.repo()],
7125
+ handler: (c, p) => simpleRpc(c, "worktree.discoverAdoptable", { repo: resolveRepoFlag(required(p.flags, "repo")) })
7126
+ },
7127
+ {
7128
+ name: "adopt",
7129
+ summary: "Import an existing git worktree as a kobe task. Returns { task }.",
7130
+ flags: [
7131
+ F.repo(),
7132
+ {
7133
+ name: "worktree",
7134
+ type: "string",
7135
+ required: true,
7136
+ placeholder: "PATH",
7137
+ description: "Path of the worktree to adopt."
7138
+ },
7139
+ { name: "branch", type: "string", placeholder: "B", description: "Branch override (else the worktree's own)." },
7140
+ F.vendor(),
7141
+ F.title()
7142
+ ],
7143
+ handler: adopt
7144
+ }
7145
+ ];
7146
+ API_VERBS = VERBS.map((v) => v.name);
7147
+ GLOBAL_FLAGS = [
7148
+ { name: "pretty", type: "bool", description: "Pretty-print stdout JSON." },
7149
+ { name: "help", type: "bool", description: "Show usage for the verb and exit." }
7150
+ ];
6699
7151
  });
6700
7152
 
6701
7153
  // src/cli/update.ts
@@ -7206,31 +7658,66 @@ var init_daemon_cmd = __esm(() => {
7206
7658
  });
7207
7659
 
7208
7660
  // src/lib/skill-install.ts
7209
- import { existsSync as existsSync4 } from "fs";
7661
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
7210
7662
  import { homedir as homedir9 } from "os";
7211
7663
  import { join as join7 } from "path";
7664
+ function npxSkillsArgv(opts = {}) {
7665
+ return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
7666
+ }
7667
+ function npxSkillsCommand(opts = {}) {
7668
+ return `npx ${npxSkillsArgv(opts).join(" ")}`;
7669
+ }
7212
7670
  function kobeSkillPaths(opts = {}) {
7213
7671
  const home = opts.home ?? homedir9();
7214
7672
  const cwd = opts.cwd ?? process.cwd();
7215
7673
  return [join7(home, SKILL_REL_PATH), join7(cwd, SKILL_REL_PATH)];
7216
7674
  }
7217
- function isKobeSkillInstalled(opts) {
7218
- return kobeSkillPaths(opts).some((p) => existsSync4(p));
7675
+ function parseSkillVersion(content) {
7676
+ const m = content.match(/kobe-skill-version:\s*(\d+)/);
7677
+ return m ? Number.parseInt(m[1], 10) : null;
7678
+ }
7679
+ function kobeSkillState(opts) {
7680
+ const path6 = kobeSkillPaths(opts).find((p) => existsSync4(p));
7681
+ if (!path6) {
7682
+ return { installed: false, installedVersion: null, currentVersion: KOBE_SKILL_VERSION, stale: false };
7683
+ }
7684
+ let installedVersion = null;
7685
+ try {
7686
+ installedVersion = parseSkillVersion(readFileSync6(path6, "utf8"));
7687
+ } catch {
7688
+ installedVersion = null;
7689
+ }
7690
+ const stale = installedVersion === null || installedVersion < KOBE_SKILL_VERSION;
7691
+ return { installed: true, installedVersion, currentVersion: KOBE_SKILL_VERSION, stale };
7219
7692
  }
7220
7693
  function maybeHintSkillInstall() {
7221
- if (isKobeSkillInstalled())
7222
- return;
7223
- if (getPersistedString(HINT_SEEN_KEY) === "1")
7694
+ const state = kobeSkillState();
7695
+ if (!state.installed) {
7696
+ if (getPersistedString(HINT_SEEN_KEY) === "1")
7697
+ return;
7698
+ setPersistedString(HINT_SEEN_KEY, "1");
7699
+ process.stderr.write(`
7700
+ kobe: the kobe agent skill isn't installed \u2014 install it so your coding agent can drive kobe via \`kobe api\`:
7701
+ ${SKILL_INSTALL_COMMAND}
7702
+ (wraps \`${npxSkillsCommand()}\`; check anytime with \`kobe doctor\`)
7703
+
7704
+ `);
7224
7705
  return;
7225
- setPersistedString(HINT_SEEN_KEY, "1");
7226
- process.stderr.write(`
7227
- kobe: the kobe agent skill isn't installed \u2014 install it so Claude Code can fan out parallel tasks via \`kobe api\`:
7706
+ }
7707
+ if (state.stale) {
7708
+ const key = `${HINT_SEEN_KEY}:v${state.currentVersion}`;
7709
+ if (getPersistedString(key) === "1")
7710
+ return;
7711
+ setPersistedString(key, "1");
7712
+ const was = state.installedVersion === null ? "an older" : `v${state.installedVersion}`;
7713
+ process.stderr.write(`
7714
+ kobe: your kobe agent skill is out of date (${was}; this kobe wants v${state.currentVersion}) \u2014 refresh it so \`kobe api\` guidance matches:
7228
7715
  ${SKILL_INSTALL_COMMAND}
7229
- (check anytime with \`kobe doctor\`)
7230
7716
 
7231
7717
  `);
7718
+ }
7232
7719
  }
7233
- var SKILL_REL_PATH = ".claude/skills/kobe/SKILL.md", SKILL_INSTALL_COMMAND = "npx skills add Sma1lboy/kobe --skill kobe --agent claude-code", HINT_SEEN_KEY = "skillHintSeen";
7720
+ var KOBE_SKILL_VERSION = 1, SKILL_REL_PATH = ".claude/skills/kobe/SKILL.md", SKILL_INSTALL_COMMAND = "kobe skill install", SKILL_SOURCE_SLUG = "Sma1lboy/kobe", DEFAULT_SKILL_AGENT = "claude-code", HINT_SEEN_KEY = "skillHintSeen";
7234
7721
  var init_skill_install = __esm(() => {
7235
7722
  init_repos();
7236
7723
  });
@@ -7242,7 +7729,7 @@ __export(exports_maintenance, {
7242
7729
  runReloadSubcommand: () => runReloadSubcommand,
7243
7730
  runDoctorSubcommand: () => runDoctorSubcommand
7244
7731
  });
7245
- import { existsSync as existsSync5, readFileSync as readFileSync6, statSync } from "fs";
7732
+ import { existsSync as existsSync5, readFileSync as readFileSync7, statSync } from "fs";
7246
7733
  import { unlink as unlink6 } from "fs/promises";
7247
7734
  import { join as join8 } from "path";
7248
7735
  import { createInterface } from "readline";
@@ -7291,7 +7778,7 @@ function describeFile(path6) {
7291
7778
  }
7292
7779
  function taskCount(tasksPath) {
7293
7780
  try {
7294
- const parsed = JSON.parse(readFileSync6(tasksPath, "utf8"));
7781
+ const parsed = JSON.parse(readFileSync7(tasksPath, "utf8"));
7295
7782
  return Array.isArray(parsed.tasks) ? parsed.tasks.length : null;
7296
7783
  } catch {
7297
7784
  return null;
@@ -7299,7 +7786,7 @@ function taskCount(tasksPath) {
7299
7786
  }
7300
7787
  function tailFile(path6, n) {
7301
7788
  try {
7302
- const lines = readFileSync6(path6, "utf8").split(`
7789
+ const lines = readFileSync7(path6, "utf8").split(`
7303
7790
  `).filter((l) => l.trim().length > 0);
7304
7791
  return lines.slice(-n).join(`
7305
7792
  `);
@@ -7379,11 +7866,16 @@ async function runDoctorSubcommand(argv = []) {
7379
7866
  out.push("tmux: \u2717 not found on PATH (task sessions need tmux)");
7380
7867
  }
7381
7868
  out.push("");
7382
- if (isKobeSkillInstalled()) {
7383
- out.push("skill: \u2713 kobe agent skill installed");
7384
- } else {
7385
- out.push("skill: \u2717 kobe agent skill not installed (optional \u2014 lets Claude Code drive `kobe api`)");
7869
+ const skill = kobeSkillState();
7870
+ if (!skill.installed) {
7871
+ out.push("skill: \u2717 kobe agent skill not installed (optional \u2014 lets a coding agent drive `kobe api`)");
7386
7872
  out.push(` \u2192 ${SKILL_INSTALL_COMMAND}`);
7873
+ } else if (skill.stale) {
7874
+ const was = skill.installedVersion === null ? "unstamped" : `v${skill.installedVersion}`;
7875
+ out.push(`skill: \u26A0 kobe agent skill out of date (${was}; this kobe wants v${skill.currentVersion})`);
7876
+ out.push(` \u2192 ${SKILL_INSTALL_COMMAND}`);
7877
+ } else {
7878
+ out.push(`skill: \u2713 kobe agent skill installed (v${skill.installedVersion})`);
7387
7879
  }
7388
7880
  out.push("");
7389
7881
  const count = taskCount(tasksPath);
@@ -7551,6 +8043,106 @@ var init_maintenance = __esm(() => {
7551
8043
  init_client2();
7552
8044
  });
7553
8045
 
8046
+ // src/cli/skill-cmd.ts
8047
+ var exports_skill_cmd = {};
8048
+ __export(exports_skill_cmd, {
8049
+ runSkillSubcommand: () => runSkillSubcommand
8050
+ });
8051
+ function skillUsage() {
8052
+ return [
8053
+ "usage: kobe skill <verb>",
8054
+ "",
8055
+ "verbs:",
8056
+ " install [--agent NAME] Install the kobe agent skill (wraps `npx skills add`)",
8057
+ " status Show whether the skill is installed",
8058
+ " command [--agent NAME] Print the underlying npx command without running it",
8059
+ "",
8060
+ `The skill teaches a coding agent how to drive \`kobe api\`. Default agent: ${DEFAULT_SKILL_AGENT}.`
8061
+ ].join(`
8062
+ `);
8063
+ }
8064
+ function parseAgent(rest) {
8065
+ let agent = DEFAULT_SKILL_AGENT;
8066
+ for (let i = 0;i < rest.length; i++) {
8067
+ const arg = rest[i];
8068
+ if (arg === "--agent") {
8069
+ const v = rest[i + 1];
8070
+ if (!v || v.startsWith("--")) {
8071
+ process.stderr.write(`kobe skill: --agent requires a value
8072
+ `);
8073
+ process.exit(2);
8074
+ }
8075
+ agent = v;
8076
+ i++;
8077
+ } else if (arg.startsWith("--agent=")) {
8078
+ agent = arg.slice("--agent=".length);
8079
+ } else {
8080
+ process.stderr.write(`kobe skill: unknown flag "${arg}"
8081
+
8082
+ ${skillUsage()}
8083
+ `);
8084
+ process.exit(2);
8085
+ }
8086
+ }
8087
+ return agent;
8088
+ }
8089
+ async function runSkillSubcommand(argv) {
8090
+ const [verb, ...rest] = argv;
8091
+ if (!verb || verb === "--help" || verb === "-h" || verb === "help") {
8092
+ process.stdout.write(`${skillUsage()}
8093
+ `);
8094
+ if (!verb)
8095
+ process.exitCode = 2;
8096
+ return;
8097
+ }
8098
+ if (!SKILL_VERBS.includes(verb)) {
8099
+ process.stderr.write(`kobe skill: unknown verb "${verb}"
8100
+
8101
+ ${skillUsage()}
8102
+ `);
8103
+ process.exit(2);
8104
+ }
8105
+ if (verb === "status") {
8106
+ const state = kobeSkillState();
8107
+ const [userPath, projectPath] = kobeSkillPaths();
8108
+ const head = !state.installed ? "\u2717 not installed" : state.stale ? `\u26A0 out of date (installed ${state.installedVersion === null ? "unstamped" : `v${state.installedVersion}`}, this kobe wants v${state.currentVersion})` : `\u2713 installed (v${state.installedVersion})`;
8109
+ process.stdout.write([
8110
+ `kobe skill: ${head}`,
8111
+ ` looked in: ${userPath}`,
8112
+ ` ${projectPath}`,
8113
+ state.installed && !state.stale ? "" : " \u2192 run `kobe skill install` to install / refresh",
8114
+ ""
8115
+ ].join(`
8116
+ `));
8117
+ return;
8118
+ }
8119
+ if (verb === "command") {
8120
+ process.stdout.write(`${npxSkillsCommand({ agent: parseAgent(rest) })}
8121
+ `);
8122
+ return;
8123
+ }
8124
+ const agent = parseAgent(rest);
8125
+ const args = npxSkillsArgv({ agent });
8126
+ process.stdout.write(`kobe skill: running \`npx ${args.join(" ")}\`
8127
+ `);
8128
+ const proc = Bun.spawn(["npx", ...args], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
8129
+ const code = await proc.exited;
8130
+ if (code !== 0) {
8131
+ process.stderr.write(`
8132
+ kobe skill install failed (npx exited ${code}). Is \`npx\` on PATH and are you online?
8133
+ ` + `You can run it yourself: ${npxSkillsCommand({ agent })}
8134
+ `);
8135
+ process.exit(code || 1);
8136
+ }
8137
+ process.stdout.write(`kobe skill: installed.
8138
+ `);
8139
+ }
8140
+ var SKILL_VERBS;
8141
+ var init_skill_cmd = __esm(() => {
8142
+ init_skill_install();
8143
+ SKILL_VERBS = ["install", "status", "command"];
8144
+ });
8145
+
7554
8146
  // ../../node_modules/.bun/entities@7.0.1/node_modules/entities/dist/esm/decode-codepoint.js
7555
8147
  function replaceCodePoint(codePoint) {
7556
8148
  var _a2;
@@ -13021,7 +13613,7 @@ var init_binary3 = __esm(() => {
13021
13613
  });
13022
13614
 
13023
13615
  // src/engine/account-detect.ts
13024
- import { readFileSync as readFileSync7, statSync as statSync6 } from "fs";
13616
+ import { readFileSync as readFileSync8, statSync as statSync6 } from "fs";
13025
13617
  import { homedir as homedir14 } from "os";
13026
13618
  import path10 from "path";
13027
13619
  function claudeGlobalConfigPath(env, home) {
@@ -13234,7 +13826,7 @@ var init_account_detect = __esm(() => {
13234
13826
  return null;
13235
13827
  throw err;
13236
13828
  }
13237
- return readFileSync7(p, "utf8");
13829
+ return readFileSync8(p, "utf8");
13238
13830
  },
13239
13831
  env(name) {
13240
13832
  return process.env[name];
@@ -14785,12 +15377,12 @@ var init_focus = __esm(() => {
14785
15377
  });
14786
15378
 
14787
15379
  // src/tui/context/kv.tsx
14788
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
15380
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
14789
15381
  import { dirname as dirname6 } from "path";
14790
15382
  function loadInitial() {
14791
15383
  const statePath2 = kvStatePath();
14792
15384
  try {
14793
- const text = readFileSync8(statePath2, "utf8");
15385
+ const text = readFileSync9(statePath2, "utf8");
14794
15386
  const parsed = JSON.parse(text);
14795
15387
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
14796
15388
  return parsed;
@@ -14886,10 +15478,10 @@ var init_kv = __esm(() => {
14886
15478
  });
14887
15479
 
14888
15480
  // src/tui/lib/persisted-ui-prefs.ts
14889
- import { readFileSync as readFileSync9 } from "fs";
15481
+ import { readFileSync as readFileSync10 } from "fs";
14890
15482
  function readPersistedUiPrefs(fallbackTheme) {
14891
15483
  try {
14892
- const parsed = JSON.parse(readFileSync9(kvStatePath(), "utf8"));
15484
+ const parsed = JSON.parse(readFileSync10(kvStatePath(), "utf8"));
14893
15485
  const theme = typeof parsed.activeTheme === "string" && hasTheme(parsed.activeTheme) ? parsed.activeTheme : fallbackTheme;
14894
15486
  const transparent = parsed.transparentBackground === true;
14895
15487
  const focusAccent = typeof parsed.focusAccent === "string" && FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent) ? parsed.focusAccent : null;
@@ -22260,6 +22852,7 @@ function topLevelUsage() {
22260
22852
  " api <verb> Scriptable RPC surface for agents (see `kobe api --help`)",
22261
22853
  " daemon <verb> Manage the daemon (start|stop|status|restart)",
22262
22854
  " theme <verb> Manage user themes (list|add|remove)",
22855
+ " skill <verb> Install the kobe agent skill (install|status|command)",
22263
22856
  " update [target] Self-update kobe",
22264
22857
  " doctor Diagnose daemon / tmux / state (read-only)",
22265
22858
  " reset [--hard] Recover a wedged install",
@@ -22521,6 +23114,11 @@ async function main() {
22521
23114
  await runReloadSubcommand2(rest);
22522
23115
  return;
22523
23116
  }
23117
+ if (subcommand === "skill") {
23118
+ const { runSkillSubcommand: runSkillSubcommand2 } = await Promise.resolve().then(() => (init_skill_cmd(), exports_skill_cmd));
23119
+ await runSkillSubcommand2(rest);
23120
+ return;
23121
+ }
22524
23122
  if (subcommand === "new-chattab") {
22525
23123
  const flags = parseOpsFlags(rest);
22526
23124
  const session = flags.session;
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.7.2",
4
+ "version": "0.7.3",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",