@sma1lboy/kobe 0.7.23 → 0.7.25

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/dist/cli/index.js CHANGED
@@ -90,7 +90,7 @@ var init_package = __esm(() => {
90
90
  package_default = {
91
91
  $schema: "https://json.schemastore.org/package.json",
92
92
  name: "@sma1lboy/kobe",
93
- version: "0.7.23",
93
+ version: "0.7.25",
94
94
  description: "TUI orchestrator for Claude Code (codename)",
95
95
  type: "module",
96
96
  packageManager: "bun@1.3.13",
@@ -1192,6 +1192,37 @@ class TaskIndexStore {
1192
1192
  this.notifyListeners();
1193
1193
  return next;
1194
1194
  }
1195
+ async reorder(moves) {
1196
+ this.assertLoaded();
1197
+ const resolved = moves.map((move) => {
1198
+ const idx = this.cache.tasks.findIndex((t) => t.id === move.id);
1199
+ const existing = idx >= 0 ? this.cache.tasks[idx] : undefined;
1200
+ if (!existing)
1201
+ throw new Error(`task not found: ${move.id}`);
1202
+ return { idx, position: move.position };
1203
+ });
1204
+ let dirty = false;
1205
+ const before = new Map;
1206
+ for (const { idx, position } of resolved) {
1207
+ const existing = this.cache.tasks[idx];
1208
+ if (!existing || existing.position === position)
1209
+ continue;
1210
+ if (!before.has(idx))
1211
+ before.set(idx, existing);
1212
+ this.cache.tasks[idx] = { ...existing, position };
1213
+ dirty = true;
1214
+ }
1215
+ if (!dirty)
1216
+ return;
1217
+ try {
1218
+ await this.save();
1219
+ } catch (err) {
1220
+ for (const [idx, task] of before)
1221
+ this.cache.tasks[idx] = task;
1222
+ throw err;
1223
+ }
1224
+ this.notifyListeners();
1225
+ }
1195
1226
  async archive(id, status = "done") {
1196
1227
  return this.update(id, { status });
1197
1228
  }
@@ -1291,6 +1322,7 @@ function coerceTask(value) {
1291
1322
  kind,
1292
1323
  vendor: isVendorId(v.vendor) ? v.vendor : DEFAULT_TASK_VENDOR,
1293
1324
  prStatus: coercePRStatus(v.prStatus),
1325
+ ...typeof v.position === "number" && Number.isFinite(v.position) ? { position: v.position } : {},
1294
1326
  createdAt: v.createdAt,
1295
1327
  updatedAt: v.updatedAt
1296
1328
  };
@@ -3770,6 +3802,18 @@ class Orchestrator {
3770
3802
  return;
3771
3803
  await this.store.update(task.id, { archived: next });
3772
3804
  }
3805
+ async reorderTasks(moves) {
3806
+ if (moves.length === 0)
3807
+ return;
3808
+ for (const move of moves) {
3809
+ const task = this.requireTask(move.taskId);
3810
+ if (task.kind === "main")
3811
+ throw new Error(`cannot reorder a main task: ${move.taskId}`);
3812
+ if (!Number.isFinite(move.position))
3813
+ throw new Error(`position must be a finite number: ${move.taskId}`);
3814
+ }
3815
+ await this.store.reorder(moves.map((move) => ({ id: move.taskId, position: move.position })));
3816
+ }
3773
3817
  async setStatus(id, status) {
3774
3818
  const task = this.requireTask(id);
3775
3819
  if (task.status === status)
@@ -3916,6 +3960,7 @@ function serializeTask(task) {
3916
3960
  pinned: task.pinned ?? false,
3917
3961
  vendor: task.vendor,
3918
3962
  prStatus: task.prStatus,
3963
+ position: task.position,
3919
3964
  createdAt: task.createdAt,
3920
3965
  updatedAt: task.updatedAt
3921
3966
  };
@@ -3934,7 +3979,9 @@ var init_protocol = __esm(() => {
3934
3979
  "ui-prefs",
3935
3980
  "keybindings",
3936
3981
  "task.jobs",
3937
- "worktree.changes"
3982
+ "worktree.changes",
3983
+ "task.conflicts",
3984
+ "session.deliver"
3938
3985
  ];
3939
3986
  CHANNEL_NAME_SET = new Set(CHANNEL_NAMES);
3940
3987
  });
@@ -4217,9 +4264,12 @@ class KobeDaemonClient {
4217
4264
  if (!pending)
4218
4265
  return;
4219
4266
  this.pending.delete(frame.id);
4220
- if (frame.error)
4221
- pending.reject(new Error(frame.error.message));
4222
- else
4267
+ if (frame.error) {
4268
+ const err = new Error(frame.error.message);
4269
+ if (frame.error.name)
4270
+ err.name = frame.error.name;
4271
+ pending.reject(err);
4272
+ } else
4223
4273
  pending.resolve(frame.payload);
4224
4274
  }
4225
4275
  emit(frame) {
@@ -6843,6 +6893,347 @@ var init_auto_title_poller = __esm(() => {
6843
6893
  init_chat_tab_naming();
6844
6894
  });
6845
6895
 
6896
+ // src/lib/poll-scheduling.ts
6897
+ import { spawn as spawn2 } from "child_process";
6898
+ function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
6899
+ if (timedOut)
6900
+ return startedAt + cfg.slowRetryMs;
6901
+ return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
6902
+ }
6903
+ function shouldPoll(state, now) {
6904
+ return !state.inFlight && now >= state.nextAllowedAt;
6905
+ }
6906
+ function maybeStartScheduledRun(state, cfg, run, onValue) {
6907
+ const startedAt = Date.now();
6908
+ if (!shouldPoll(state, startedAt))
6909
+ return false;
6910
+ state.inFlight = true;
6911
+ const controller = new AbortController;
6912
+ const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
6913
+ (async () => {
6914
+ let value;
6915
+ let ok = false;
6916
+ try {
6917
+ value = await run(controller.signal);
6918
+ ok = true;
6919
+ } catch {}
6920
+ clearTimeout(timer);
6921
+ const timedOut = controller.signal.aborted;
6922
+ state.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
6923
+ state.inFlight = false;
6924
+ if (ok && !timedOut)
6925
+ onValue(value);
6926
+ })();
6927
+ return true;
6928
+ }
6929
+ function spawnCapture(cmd, args, opts) {
6930
+ return new Promise((resolve2) => {
6931
+ let out = "";
6932
+ let settled = false;
6933
+ const finish = (status) => {
6934
+ if (settled)
6935
+ return;
6936
+ settled = true;
6937
+ resolve2({ status, stdout: out });
6938
+ };
6939
+ const child = spawn2(cmd, args.slice(), {
6940
+ cwd: opts.cwd,
6941
+ stdio: ["ignore", "pipe", "ignore"],
6942
+ env: opts.env,
6943
+ signal: opts.signal,
6944
+ killSignal: "SIGKILL"
6945
+ });
6946
+ child.stdout?.on("data", (chunk) => {
6947
+ out += String(chunk);
6948
+ });
6949
+ child.on("error", () => finish(null));
6950
+ child.on("close", (code) => finish(code));
6951
+ });
6952
+ }
6953
+ var init_poll_scheduling = () => {};
6954
+
6955
+ // ../kobe-daemon/src/daemon/conflict-collector.ts
6956
+ class GitGate {
6957
+ limit;
6958
+ active = 0;
6959
+ waiters = [];
6960
+ constructor(limit = MAX_CONCURRENT_GIT) {
6961
+ this.limit = limit;
6962
+ }
6963
+ async run(fn) {
6964
+ if (this.active >= this.limit) {
6965
+ await new Promise((resolve2) => this.waiters.push(resolve2));
6966
+ }
6967
+ this.active += 1;
6968
+ try {
6969
+ return await fn();
6970
+ } finally {
6971
+ this.active -= 1;
6972
+ this.waiters.shift()?.();
6973
+ }
6974
+ }
6975
+ }
6976
+ async function git(cwd, args, signal, gate) {
6977
+ return gate.run(() => spawnCapture("git", args, { cwd, env: { ...process.env, ...LOCK_FREE_ENV }, signal }));
6978
+ }
6979
+ function parsePorcelainPaths(stdout) {
6980
+ const paths = [];
6981
+ for (const line of stdout.split(`
6982
+ `)) {
6983
+ if (line.length < 4)
6984
+ continue;
6985
+ const rest = line.slice(3);
6986
+ const arrow = rest.indexOf(" -> ");
6987
+ if (arrow >= 0) {
6988
+ paths.push(rest.slice(0, arrow), rest.slice(arrow + 4));
6989
+ } else {
6990
+ paths.push(rest);
6991
+ }
6992
+ }
6993
+ return paths;
6994
+ }
6995
+ async function resolveBaseRef(worktree, signal, gate) {
6996
+ for (const ref of BASE_REF_CANDIDATES) {
6997
+ const res = await git(worktree, ["rev-parse", "--verify", "--quiet", ref], signal, gate);
6998
+ if (res.status === 0)
6999
+ return ref;
7000
+ }
7001
+ return null;
7002
+ }
7003
+ async function collectFootprint(worktree, repo, baseRef, signal, gate) {
7004
+ const head = await git(worktree, ["rev-parse", "HEAD"], signal, gate);
7005
+ if (head.status !== 0)
7006
+ throw new Error("rev-parse HEAD failed");
7007
+ const files = new Set;
7008
+ const status = await git(worktree, ["status", "--porcelain=v1"], signal, gate);
7009
+ if (status.status !== 0)
7010
+ throw new Error("git status failed");
7011
+ for (const p of parsePorcelainPaths(status.stdout))
7012
+ files.add(p);
7013
+ if (baseRef) {
7014
+ const diff = await git(worktree, ["diff", "--name-only", `${baseRef}...HEAD`], signal, gate);
7015
+ if (diff.status === 0) {
7016
+ for (const p of diff.stdout.split(`
7017
+ `))
7018
+ if (p)
7019
+ files.add(p);
7020
+ }
7021
+ }
7022
+ return { repo, head: head.stdout.trim(), files };
7023
+ }
7024
+ function sameFootprint(a, b) {
7025
+ if (a.head !== b.head || a.files.size !== b.files.size)
7026
+ return false;
7027
+ for (const f of a.files)
7028
+ if (!b.files.has(f))
7029
+ return false;
7030
+ return true;
7031
+ }
7032
+ function trackedConflictTasks(tasks) {
7033
+ return tasks.filter((t) => !t.archived && (t.kind ?? "task") !== "main" && !!t.worktreePath && !isRemoteRepoKey(t.repo) && !isRemoteRepoKey(t.worktreePath));
7034
+ }
7035
+ function overlapPairs(cards) {
7036
+ const ids = [...cards.keys()].sort();
7037
+ const pairs = [];
7038
+ for (let i = 0;i < ids.length; i++) {
7039
+ for (let j = i + 1;j < ids.length; j++) {
7040
+ const a = cards.get(ids[i]);
7041
+ const b = cards.get(ids[j]);
7042
+ if (a.repo !== b.repo)
7043
+ continue;
7044
+ const files = [...a.files].filter((f) => b.files.has(f)).sort();
7045
+ if (files.length === 0)
7046
+ continue;
7047
+ pairs.push({ a: ids[i], b: ids[j], files, level: "overlap" });
7048
+ }
7049
+ }
7050
+ return pairs;
7051
+ }
7052
+ function parseMergeTreeNames(stdout) {
7053
+ const lines = stdout.split(`
7054
+ `);
7055
+ const names = [];
7056
+ for (const line of lines.slice(1)) {
7057
+ if (!line)
7058
+ break;
7059
+ names.push(line);
7060
+ }
7061
+ return names;
7062
+ }
7063
+ function samePairs(a, b) {
7064
+ return JSON.stringify(a) === JSON.stringify(b);
7065
+ }
7066
+
7067
+ class ConflictCollector {
7068
+ orch;
7069
+ bus;
7070
+ options;
7071
+ entries = new Map;
7072
+ baseRefs = new Map;
7073
+ mergeProbes = new Map;
7074
+ gate = new GitGate;
7075
+ mergeTreeUnsupported = false;
7076
+ lastPublished = [];
7077
+ stopped = false;
7078
+ constructor(orch, bus, options = {}) {
7079
+ this.orch = orch;
7080
+ this.bus = bus;
7081
+ this.options = options;
7082
+ }
7083
+ tick() {
7084
+ if (this.stopped)
7085
+ return;
7086
+ if (this.options.hasSubscribers && !this.options.hasSubscribers())
7087
+ return;
7088
+ try {
7089
+ const tracked = trackedConflictTasks(this.orch.listTasks());
7090
+ const trackedIds = new Set(tracked.map((t) => t.id));
7091
+ let pruned = false;
7092
+ for (const id of this.entries.keys()) {
7093
+ if (trackedIds.has(id))
7094
+ continue;
7095
+ if (this.entries.get(id)?.value)
7096
+ pruned = true;
7097
+ this.entries.delete(id);
7098
+ }
7099
+ if (pruned)
7100
+ this.recompute();
7101
+ for (const task of tracked)
7102
+ this.maybeCollect(task);
7103
+ } catch (err) {
7104
+ logDaemonError("conflict-radar", err);
7105
+ }
7106
+ }
7107
+ stop() {
7108
+ this.stopped = true;
7109
+ }
7110
+ maybeCollect(task) {
7111
+ const id = task.id;
7112
+ let entry = this.entries.get(id);
7113
+ if (!entry) {
7114
+ entry = { inFlight: false, nextAllowedAt: 0 };
7115
+ this.entries.set(id, entry);
7116
+ }
7117
+ const cadence = this.options.cadence ?? {
7118
+ timeoutMs: CONFLICTS_TIMEOUT_MS,
7119
+ slowRetryMs: CONFLICTS_SLOW_RETRY_MS,
7120
+ minIntervalMs: CONFLICTS_MIN_INTERVAL_MS
7121
+ };
7122
+ const run = this.options.footprint ?? (async (t, signal) => {
7123
+ const baseRef = await this.baseRefFor(t.worktreePath, signal);
7124
+ return collectFootprint(t.worktreePath, t.repo, baseRef, signal, this.gate);
7125
+ });
7126
+ maybeStartScheduledRun(entry, cadence, (signal) => run(task, signal), (value) => {
7127
+ if (this.stopped)
7128
+ return;
7129
+ if (this.entries.get(id) !== entry)
7130
+ return;
7131
+ if (entry.value && sameFootprint(entry.value, value))
7132
+ return;
7133
+ entry.value = value;
7134
+ this.recompute();
7135
+ });
7136
+ }
7137
+ baseRefFor(worktree, signal) {
7138
+ const cached = this.baseRefs.get(worktree);
7139
+ if (cached)
7140
+ return cached;
7141
+ const promise = resolveBaseRef(worktree, signal, this.gate).catch(() => null);
7142
+ this.baseRefs.set(worktree, promise);
7143
+ return promise;
7144
+ }
7145
+ recompute() {
7146
+ const cards = new Map;
7147
+ for (const [id, entry] of this.entries) {
7148
+ if (entry.value)
7149
+ cards.set(id, entry.value);
7150
+ }
7151
+ const pairs = overlapPairs(cards);
7152
+ const resolved = [];
7153
+ for (const pair of pairs) {
7154
+ const a = cards.get(pair.a);
7155
+ const b = cards.get(pair.b);
7156
+ const key = [a.repo, ...[a.head, b.head].sort()].join("\x00");
7157
+ const probe2 = this.mergeProbes.get(key);
7158
+ if (probe2?.state === "conflict") {
7159
+ resolved.push({
7160
+ ...pair,
7161
+ level: "conflict",
7162
+ files: probe2.files.length > 0 ? probe2.files : pair.files
7163
+ });
7164
+ continue;
7165
+ }
7166
+ resolved.push(pair);
7167
+ if (!probe2 && !this.mergeTreeUnsupported && a.head !== b.head) {
7168
+ this.scheduleMergeProbe(key, cards, pair);
7169
+ }
7170
+ }
7171
+ if (samePairs(this.lastPublished, resolved))
7172
+ return;
7173
+ this.lastPublished = resolved;
7174
+ const payload = { pairs: resolved };
7175
+ this.bus.publish("task.conflicts", payload);
7176
+ }
7177
+ scheduleMergeProbe(key, cards, pair) {
7178
+ const a = cards.get(pair.a);
7179
+ const b = cards.get(pair.b);
7180
+ this.mergeProbes.set(key, { state: "pending" });
7181
+ const worktree = this.worktreeOf(pair.a);
7182
+ const probe2 = this.options.probeMerge ?? (async (wt, headA, headB) => {
7183
+ const res = await git(wt, ["merge-tree", "--write-tree", "--name-only", headA, headB], AbortSignal.timeout(MERGE_TREE_TIMEOUT_MS), this.gate);
7184
+ if (res.status === 0)
7185
+ return { conflict: false, files: [] };
7186
+ if (res.status === 1)
7187
+ return { conflict: true, files: parseMergeTreeNames(res.stdout) };
7188
+ return null;
7189
+ });
7190
+ if (!worktree) {
7191
+ this.mergeProbes.delete(key);
7192
+ return;
7193
+ }
7194
+ probe2(worktree, a.head, b.head).then((result) => {
7195
+ if (this.stopped)
7196
+ return;
7197
+ if (result === null) {
7198
+ if (!this.mergeTreeUnsupported) {
7199
+ this.mergeTreeUnsupported = true;
7200
+ console.log("[conflict-radar] merge-tree dry-run unavailable (git < 2.38 or no merge base) \u2014 radar degrades to file-overlap only");
7201
+ }
7202
+ this.mergeProbes.delete(key);
7203
+ return;
7204
+ }
7205
+ this.mergeProbes.set(key, result.conflict ? { state: "conflict", files: result.files } : { state: "clean" });
7206
+ this.recompute();
7207
+ }).catch((err) => {
7208
+ this.mergeProbes.delete(key);
7209
+ logDaemonError("conflict-radar", err);
7210
+ });
7211
+ }
7212
+ worktreeOf(taskId) {
7213
+ const task = this.orch.listTasks().find((t) => t.id === taskId);
7214
+ return task?.worktreePath || undefined;
7215
+ }
7216
+ }
7217
+ function startConflictCollector(orch, bus, tickMs = DEFAULT_CONFLICTS_TICK_MS, hasSubscribers) {
7218
+ if (tickMs <= 0)
7219
+ return () => {};
7220
+ const collector = new ConflictCollector(orch, bus, { hasSubscribers });
7221
+ collector.tick();
7222
+ const timer = setInterval(() => collector.tick(), tickMs);
7223
+ timer.unref?.();
7224
+ return () => {
7225
+ clearInterval(timer);
7226
+ collector.stop();
7227
+ };
7228
+ }
7229
+ var DEFAULT_CONFLICTS_TICK_MS = 5000, CONFLICTS_TIMEOUT_MS = 5000, CONFLICTS_SLOW_RETRY_MS = 60000, CONFLICTS_MIN_INTERVAL_MS = 1e4, MAX_CONCURRENT_GIT = 3, MERGE_TREE_TIMEOUT_MS = 8000, LOCK_FREE_ENV, BASE_REF_CANDIDATES;
7230
+ var init_conflict_collector = __esm(() => {
7231
+ init_poll_scheduling();
7232
+ init_repos();
7233
+ LOCK_FREE_ENV = { GIT_OPTIONAL_LOCKS: "0" };
7234
+ BASE_REF_CANDIDATES = ["origin/HEAD", "origin/main", "origin/master", "main", "master"];
7235
+ });
7236
+
6846
7237
  // ../kobe-daemon/src/daemon/event-bus.ts
6847
7238
  class DaemonEventBus {
6848
7239
  last = new Map;
@@ -6864,6 +7255,33 @@ class DaemonEventBus {
6864
7255
  }
6865
7256
  }
6866
7257
 
7258
+ // src/state/auto-status.ts
7259
+ function autoStatusEnabled() {
7260
+ return loadStateFile()[AUTO_STATUS_KEY] === true;
7261
+ }
7262
+ var AUTO_STATUS_KEY = "experimental.autoStatus";
7263
+ var init_auto_status = __esm(() => {
7264
+ init_store();
7265
+ });
7266
+
7267
+ // src/monitor/status-rules.ts
7268
+ async function maybeAutoStart(orch, taskId, enabled = autoStatusEnabled) {
7269
+ if (!enabled())
7270
+ return "skipped";
7271
+ const task = orch.getTask(taskId);
7272
+ if (!task)
7273
+ return "skipped";
7274
+ if ((task.kind ?? "task") === "main" || task.archived)
7275
+ return "skipped";
7276
+ if (task.status !== "backlog")
7277
+ return "skipped";
7278
+ await orch.setStatus(taskId, "in_progress");
7279
+ return "moved";
7280
+ }
7281
+ var init_status_rules = __esm(() => {
7282
+ init_auto_status();
7283
+ });
7284
+
6867
7285
  // ../kobe-daemon/src/daemon/cwd-task.ts
6868
7286
  import { createHash as createHash4 } from "crypto";
6869
7287
  import { homedir as homedir12 } from "os";
@@ -7128,6 +7546,29 @@ function createDaemonHandlerRegistry() {
7128
7546
  return {};
7129
7547
  }
7130
7548
  },
7549
+ {
7550
+ name: "task.reorder",
7551
+ async handle(payload, ctx) {
7552
+ const moves = payload.moves;
7553
+ if (!Array.isArray(moves) || moves.length === 0)
7554
+ throw new Error("moves must be a non-empty array");
7555
+ if (moves.length > 500)
7556
+ throw new Error("too many moves in one task.reorder batch (max 500)");
7557
+ const parsed = moves.map((move) => {
7558
+ if (typeof move !== "object" || move === null)
7559
+ throw new Error("each move needs taskId and position");
7560
+ const entry = move;
7561
+ const taskId = requireString(entry, "taskId");
7562
+ const position = entry.position;
7563
+ if (typeof position !== "number" || !Number.isFinite(position)) {
7564
+ throw new Error("position must be a finite number");
7565
+ }
7566
+ return { taskId, position };
7567
+ });
7568
+ await ctx.orch.reorderTasks(parsed);
7569
+ return {};
7570
+ }
7571
+ },
7131
7572
  {
7132
7573
  name: "task.ensureMain",
7133
7574
  async handle(payload, ctx) {
@@ -7204,6 +7645,47 @@ function createDaemonHandlerRegistry() {
7204
7645
  return {};
7205
7646
  }
7206
7647
  },
7648
+ {
7649
+ name: "session.deliver",
7650
+ async handle(payload, ctx) {
7651
+ const taskId = requireString(payload, "taskId");
7652
+ const text = requireString(payload, "text");
7653
+ const source = optionalString(payload, "source");
7654
+ if (source !== undefined && source !== "note" && source !== "dispatcher") {
7655
+ throw new Error('source must be "note" or "dispatcher"');
7656
+ }
7657
+ if (!ctx.orch.getTask(taskId))
7658
+ throw new Error(`task not found: ${taskId}`);
7659
+ ctx.bus.publish("session.deliver", {
7660
+ taskId,
7661
+ text,
7662
+ at: Date.now(),
7663
+ source: source ?? "dispatcher"
7664
+ });
7665
+ return { ok: true };
7666
+ }
7667
+ },
7668
+ {
7669
+ name: "note.file",
7670
+ async handle(payload, ctx) {
7671
+ const taskId = requireString(payload, "taskId");
7672
+ const text = requireString(payload, "text");
7673
+ const author = ctx.orch.getTask(taskId);
7674
+ if (!author)
7675
+ throw new Error(`task not found: ${taskId}`);
7676
+ const main = ctx.orch.listTasks().find((t) => (t.kind ?? "task") === "main" && t.repo === author.repo && !t.archived);
7677
+ if (!main || main.id === author.id)
7678
+ return { ok: true, routed: false };
7679
+ const label = author.title || author.branch || taskId;
7680
+ ctx.bus.publish("session.deliver", {
7681
+ taskId: main.id,
7682
+ text: `[KOBE FIELD NOTE] from "${label}" (task ${taskId}): ${text}`,
7683
+ at: Date.now(),
7684
+ source: "note"
7685
+ });
7686
+ return { ok: true, routed: true };
7687
+ }
7688
+ },
7207
7689
  {
7208
7690
  name: "engine.reportEvent",
7209
7691
  async handle(payload, ctx) {
@@ -7227,6 +7709,13 @@ function createDaemonHandlerRegistry() {
7227
7709
  return {};
7228
7710
  const detail = optionalActivityDetail(payload);
7229
7711
  ctx.activity.report(taskId, kind, detail);
7712
+ if (kind === "turn-start") {
7713
+ maybeAutoStart(ctx.orch, taskId).then((result) => {
7714
+ if (result === "moved") {
7715
+ console.log(`[status-rules] task ${taskId} auto-moved backlog \u2192 in_progress`);
7716
+ }
7717
+ }).catch((err) => logDaemonError("status-rules", err));
7718
+ }
7230
7719
  return {};
7231
7720
  }
7232
7721
  }
@@ -7280,6 +7769,7 @@ function optionalActivityDetail(payload) {
7280
7769
  }
7281
7770
  var init_handlers = __esm(() => {
7282
7771
  init_hook_events();
7772
+ init_status_rules();
7283
7773
  init_version();
7284
7774
  init_cwd_task();
7285
7775
  init_protocol();
@@ -7414,65 +7904,6 @@ var init_ui_prefs_watcher = __esm(() => {
7414
7904
  FOCUS_ACCENT_SLOT_NAMES = ["primary", "success", "info"];
7415
7905
  });
7416
7906
 
7417
- // src/lib/poll-scheduling.ts
7418
- import { spawn as spawn2 } from "child_process";
7419
- function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
7420
- if (timedOut)
7421
- return startedAt + cfg.slowRetryMs;
7422
- return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
7423
- }
7424
- function shouldPoll(state, now) {
7425
- return !state.inFlight && now >= state.nextAllowedAt;
7426
- }
7427
- function maybeStartScheduledRun(state, cfg, run, onValue) {
7428
- const startedAt = Date.now();
7429
- if (!shouldPoll(state, startedAt))
7430
- return false;
7431
- state.inFlight = true;
7432
- const controller = new AbortController;
7433
- const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
7434
- (async () => {
7435
- let value;
7436
- let ok = false;
7437
- try {
7438
- value = await run(controller.signal);
7439
- ok = true;
7440
- } catch {}
7441
- clearTimeout(timer);
7442
- const timedOut = controller.signal.aborted;
7443
- state.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
7444
- state.inFlight = false;
7445
- if (ok && !timedOut)
7446
- onValue(value);
7447
- })();
7448
- return true;
7449
- }
7450
- function spawnCapture(cmd, args, opts) {
7451
- return new Promise((resolve2) => {
7452
- let out = "";
7453
- let settled = false;
7454
- const finish = (status) => {
7455
- if (settled)
7456
- return;
7457
- settled = true;
7458
- resolve2({ status, stdout: out });
7459
- };
7460
- const child = spawn2(cmd, args.slice(), {
7461
- cwd: opts.cwd,
7462
- stdio: ["ignore", "pipe", "ignore"],
7463
- env: opts.env,
7464
- signal: opts.signal,
7465
- killSignal: "SIGKILL"
7466
- });
7467
- child.stdout?.on("data", (chunk) => {
7468
- out += String(chunk);
7469
- });
7470
- child.on("error", () => finish(null));
7471
- child.on("close", (code) => finish(code));
7472
- });
7473
- }
7474
- var init_poll_scheduling = () => {};
7475
-
7476
7907
  // src/tui/panes/sidebar/worktree-changes.ts
7477
7908
  var exports_worktree_changes = {};
7478
7909
  __export(exports_worktree_changes, {
@@ -7753,6 +8184,7 @@ async function startDaemonServer(orch, options = {}) {
7753
8184
  debounceMs: options.keybindingsDebounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS
7754
8185
  });
7755
8186
  const stopWorktreeChangesCollector = startWorktreeChangesCollector(orch, bus, options.worktreeChangesTickMs ?? DEFAULT_WORKTREE_CHANGES_TICK_MS, hasSubscribers);
8187
+ const stopConflictCollector = startConflictCollector(orch, bus, options.conflictsTickMs ?? DEFAULT_CONFLICTS_TICK_MS, hasSubscribers);
7756
8188
  const serverApi = {
7757
8189
  socketPath,
7758
8190
  pidPath,
@@ -7768,6 +8200,7 @@ async function startDaemonServer(orch, options = {}) {
7768
8200
  stopUiPrefsWatcher();
7769
8201
  stopKeybindingsWatcher();
7770
8202
  stopWorktreeChangesCollector();
8203
+ stopConflictCollector();
7771
8204
  activity.close();
7772
8205
  broadcast(clients, { type: "event", name: "daemon.stopping", payload: {} });
7773
8206
  for (const client of Array.from(clients)) {
@@ -7898,6 +8331,7 @@ var init_server = __esm(() => {
7898
8331
  init_version();
7899
8332
  init_activity_registry();
7900
8333
  init_auto_title_poller();
8334
+ init_conflict_collector();
7901
8335
  init_handlers();
7902
8336
  init_keybindings_watcher();
7903
8337
  init_paths2();
@@ -8218,6 +8652,15 @@ var init_repo_cmd = __esm(() => {
8218
8652
  `);
8219
8653
  });
8220
8654
 
8655
+ // src/state/dispatcher.ts
8656
+ function dispatcherEnabled() {
8657
+ return loadStateFile()[DISPATCHER_KEY] === true;
8658
+ }
8659
+ var DISPATCHER_KEY = "experimental.dispatcher";
8660
+ var init_dispatcher = __esm(() => {
8661
+ init_store();
8662
+ });
8663
+
8221
8664
  // src/engine/interactive-command.ts
8222
8665
  import { randomUUID } from "crypto";
8223
8666
  function engineCommandKey(vendor) {
@@ -8259,9 +8702,88 @@ function withClaudeSessionId(argv, vendor) {
8259
8702
  const sessionId = randomUUID();
8260
8703
  return { argv: [...argv, "--session-id", sessionId], sessionId };
8261
8704
  }
8705
+ function kobeApiInvocation() {
8706
+ const quote = (a) => /^[A-Za-z0-9_/.:=-]+$/.test(a) ? a : `'${a.replace(/'/g, "'\\''")}'`;
8707
+ try {
8708
+ return [...kobeCliInvocation(), "api"].map(quote).join(" ");
8709
+ } catch {
8710
+ return "kobe api";
8711
+ }
8712
+ }
8713
+ function statusReportProtocol(taskId, api = kobeApiInvocation()) {
8714
+ return [
8715
+ `You are running inside kobe (a local multi-session task manager) as task ${taskId}.`,
8716
+ "kobe tracks a lifecycle status for this task on a board.",
8717
+ "When you have COMPLETED the work requested in this session and verified it, report it by running:",
8718
+ ` ${api} set-status --task-id ${taskId} --status in_review`,
8719
+ "Run it only when the work is genuinely done \u2014 never while you are asking the user a question, waiting for input, or mid-task.",
8720
+ "Never set any other status value; everything beyond in_review is the user's decision."
8721
+ ].join(`
8722
+ `);
8723
+ }
8724
+ function noteFilingProtocol(taskId, api = kobeApiInvocation()) {
8725
+ return [
8726
+ "kobe shares hard-won discoveries between its parallel sessions as one-line field notes.",
8727
+ "When you RESOLVE a non-obvious, repo-level gotcha (a build flag, a flaky test, an environment quirk, an API trap), file it:",
8728
+ ` ${api} note --task-id ${taskId} --text "<one line: the verified conclusion>"`,
8729
+ "File only verified conclusions another session could act on \u2014 never progress logs, opinions, or details specific to your own task. A handful per session at most."
8730
+ ].join(`
8731
+ `);
8732
+ }
8733
+ function worktreeProtocol(taskId, api = kobeApiInvocation(), gates = {}) {
8734
+ const parts = [];
8735
+ if ((gates.status ?? autoStatusEnabled)())
8736
+ parts.push(statusReportProtocol(taskId, api));
8737
+ if ((gates.notes ?? dispatcherEnabled)())
8738
+ parts.push(noteFilingProtocol(taskId, api));
8739
+ return parts.length > 0 ? parts.join(`
8740
+
8741
+ `) : null;
8742
+ }
8743
+ function withWorktreeProtocol(argv, vendor, taskId, gates = {}) {
8744
+ if (!taskId)
8745
+ return argv;
8746
+ if ((vendor ?? "claude") !== "claude")
8747
+ return argv;
8748
+ if (argv.includes("--append-system-prompt") || argv.includes("--append-system-prompt-file")) {
8749
+ return argv;
8750
+ }
8751
+ const text = worktreeProtocol(taskId, kobeApiInvocation(), gates);
8752
+ if (!text)
8753
+ return argv;
8754
+ return [...argv, "--append-system-prompt", text];
8755
+ }
8756
+ function dispatcherProtocol(taskId, api = kobeApiInvocation()) {
8757
+ return [
8758
+ `You are running inside kobe (a local multi-session task manager) as this repository's DISPATCHER (task ${taskId}, the repo's main session).`,
8759
+ "kobe runs multiple worktree task sessions on this repo in parallel. When one of them resolves a non-obvious gotcha, it files a one-line field note; kobe forwards each note to you as a user message prefixed with [KOBE FIELD NOTE].",
8760
+ "Your job is routing that knowledge, fully autonomously \u2014 never ask the user for permission:",
8761
+ ` - See the fleet: \`${api} collect --repo .\` (status, running, change counts per task), or \`--task-ids id1,id2\` for specific tasks.`,
8762
+ ` - Relay a note to a task that would benefit: \`${api} dispatch --task-id <id> --prompt "[dispatcher] FYI from <author task>: <note verbatim>"\`.`,
8763
+ " - Relay to the in-flight tasks whose work plausibly touches the same area \u2014 and to nobody else. If no task benefits, do nothing.",
8764
+ " - Never relay a note back to its author, never relay the same note to the same task twice, and keep relays verbatim with provenance \u2014 no summarizing, no embellishment.",
8765
+ "Use ONLY the dispatch verb to message sessions \u2014 the `send` verb pastes via tmux and would spawn a DUPLICATE engine for web-hosted sessions. If dispatch fails, report the error in your own session and stop; do not fall back.",
8766
+ "Take no action on merge conflicts between tasks \u2014 the board's conflict radar is display-only by design, and resolution timing belongs to the humans and the tasks themselves.",
8767
+ "Never run git commands inside other tasks' worktrees."
8768
+ ].join(`
8769
+ `);
8770
+ }
8771
+ function withDispatcherProtocol(argv, vendor, taskId, enabled = dispatcherEnabled) {
8772
+ if (!taskId || !enabled())
8773
+ return argv;
8774
+ if ((vendor ?? "claude") !== "claude")
8775
+ return argv;
8776
+ if (argv.includes("--append-system-prompt") || argv.includes("--append-system-prompt-file")) {
8777
+ return argv;
8778
+ }
8779
+ return [...argv, "--append-system-prompt", dispatcherProtocol(taskId)];
8780
+ }
8262
8781
  var VENDOR_LABEL, CLAUDE_SESSION_CONTROL_FLAGS;
8263
8782
  var init_interactive_command = __esm(() => {
8783
+ init_invocation();
8264
8784
  init_registry();
8785
+ init_auto_status();
8786
+ init_dispatcher();
8265
8787
  init_repos();
8266
8788
  init_vendor();
8267
8789
  VENDOR_LABEL = Object.fromEntries(BUILTIN_VENDORS.map((v) => [v, engineEntry(v).displayName]));
@@ -11062,7 +11584,11 @@ async function ensureSessionImpl(opts) {
11062
11584
  }
11063
11585
  const inv = kobeCliInvocation();
11064
11586
  const launch = withClaudeSessionId(opts.command, opts.vendor);
11065
- const engineCmd = wrapEngineLaunch(shellQuoteArgv(launch.argv), remoteKey, opts.cwd);
11587
+ const isMainSession = opts.repo !== undefined && opts.cwd === opts.repo;
11588
+ const protocolTaskId = isMainSession || remoteKey ? undefined : opts.taskId;
11589
+ const dispatcherTaskId = isMainSession && !remoteKey ? opts.taskId : undefined;
11590
+ const launchArgv = withDispatcherProtocol(withWorktreeProtocol(launch.argv, opts.vendor, protocolTaskId), opts.vendor, dispatcherTaskId);
11591
+ const engineCmd = wrapEngineLaunch(shellQuoteArgv(launchArgv), remoteKey, opts.cwd);
11066
11592
  const r0 = await runTmuxCapturing([
11067
11593
  "new-session",
11068
11594
  "-d",
@@ -11688,6 +12214,19 @@ async function send(ctx) {
11688
12214
  engineReady: delivered.engineReady
11689
12215
  };
11690
12216
  }
12217
+ async function dispatch(ctx) {
12218
+ const daemon = daemonOf(ctx);
12219
+ const taskId = ctx.args.require("task-id");
12220
+ const text = ctx.args.require("prompt");
12221
+ await daemon.request("session.deliver", { taskId, text, source: "dispatcher" });
12222
+ return { ok: true, taskId, routed: "session.deliver" };
12223
+ }
12224
+ async function note(ctx) {
12225
+ const daemon = daemonOf(ctx);
12226
+ const taskId = ctx.args.require("task-id");
12227
+ const text = ctx.args.require("text");
12228
+ return await daemon.request("note.file", { taskId, text });
12229
+ }
11691
12230
  async function getTask(ctx) {
11692
12231
  const daemon = daemonOf(ctx);
11693
12232
  const taskId = ctx.args.require("task-id");
@@ -11936,7 +12475,7 @@ var init_api_cmd = __esm(() => {
11936
12475
  discover: ["schema"],
11937
12476
  read: ["list", "get-task", "collect"],
11938
12477
  create: ["add", "fan-out"],
11939
- drive: ["send", "set-active"],
12478
+ drive: ["send", "dispatch", "note", "set-active"],
11940
12479
  edit: ["rename", "set-branch", "set-vendor", "set-status"],
11941
12480
  lifecycle: ["archive", "pin", "delete"],
11942
12481
  worktree: ["ensure-worktree", "adopt", "discover-adoptable"],
@@ -12016,6 +12555,27 @@ var init_api_cmd = __esm(() => {
12016
12555
  flags: [F.taskId(false), F.prompt(true, "Text pasted + submitted into the engine pane.")],
12017
12556
  handler: send
12018
12557
  },
12558
+ {
12559
+ name: "dispatch",
12560
+ summary: "Route text into a task's live session via the daemon's session.deliver channel \u2014 the front-end hosting the session pastes it. The dispatcher's messenger (docs/design/dispatcher.md); unlike `send`, it never spawns or touches tmux itself.",
12561
+ flags: [F.taskId(true), F.prompt(true, "Text delivered into the task's engine session.")],
12562
+ handler: dispatch
12563
+ },
12564
+ {
12565
+ name: "note",
12566
+ summary: "File a one-line field note \u2014 a resolved, repo-level gotcha worth sharing. kobe forwards it to the repo's dispatcher session (the main session), which relays it to the in-flight tasks that benefit (docs/design/dispatcher.md).",
12567
+ flags: [
12568
+ F.taskId(true),
12569
+ {
12570
+ name: "text",
12571
+ type: "string",
12572
+ required: true,
12573
+ placeholder: "TEXT",
12574
+ description: "One line: the verified conclusion another session could act on."
12575
+ }
12576
+ ],
12577
+ handler: note
12578
+ },
12019
12579
  {
12020
12580
  name: "feedback",
12021
12581
  summary: "Create a GitHub Discussion in the kobe repo's Feedback category through `gh`.",
@@ -13470,6 +14030,8 @@ var init_spa_channels = __esm(() => {
13470
14030
  "update",
13471
14031
  "task.jobs",
13472
14032
  "worktree.changes",
14033
+ "task.conflicts",
14034
+ "session.deliver",
13473
14035
  "ui-prefs"
13474
14036
  ];
13475
14037
  SPA_CHANNEL_SET = new Set(SPA_CHANNELS);
@@ -13493,6 +14055,8 @@ class DaemonLink {
13493
14055
  update = null;
13494
14056
  jobs = {};
13495
14057
  worktreeChanges = {};
14058
+ conflicts = [];
14059
+ deliver = null;
13496
14060
  uiPrefs = null;
13497
14061
  async start() {
13498
14062
  await this.connectOnce(true);
@@ -13505,6 +14069,8 @@ class DaemonLink {
13505
14069
  update: this.update,
13506
14070
  jobs: this.jobs,
13507
14071
  worktreeChanges: this.worktreeChanges,
14072
+ conflicts: this.conflicts,
14073
+ deliver: this.deliver,
13508
14074
  uiPrefs: this.uiPrefs,
13509
14075
  connected: this.connected
13510
14076
  };
@@ -13615,6 +14181,12 @@ class DaemonLink {
13615
14181
  case "worktree.changes":
13616
14182
  this.worktreeChanges = payload.changes;
13617
14183
  break;
14184
+ case "task.conflicts":
14185
+ this.conflicts = payload.pairs;
14186
+ break;
14187
+ case "session.deliver":
14188
+ this.deliver = payload;
14189
+ break;
13618
14190
  case "ui-prefs":
13619
14191
  this.uiPrefs = payload;
13620
14192
  break;
@@ -13662,6 +14234,7 @@ var init_rpc_allowlist = __esm(() => {
13662
14234
  "task.pin",
13663
14235
  "task.move",
13664
14236
  "task.status",
14237
+ "task.reorder",
13665
14238
  "task.ensureMain",
13666
14239
  "task.ensureWorktree",
13667
14240
  "task.setActive",
@@ -13708,7 +14281,11 @@ function shellQuote2(argv) {
13708
14281
  }
13709
14282
  async function engineSpec(link, taskId) {
13710
14283
  const { task, worktreePath } = await ensureTaskWorktree(link, taskId);
13711
- const argv = [...interactiveEngineCommand(task.vendor)];
14284
+ const protocolTaskId = task.kind === "main" ? undefined : taskId;
14285
+ const dispatcherTaskId = task.kind === "main" ? taskId : undefined;
14286
+ const argv = [
14287
+ ...withDispatcherProtocol(withWorktreeProtocol(interactiveEngineCommand(task.vendor), task.vendor, protocolTaskId), task.vendor, dispatcherTaskId)
14288
+ ];
13712
14289
  const init = resolveRepoInit(task.repo ?? "", worktreePath);
13713
14290
  const quoted = shellQuote2(argv);
13714
14291
  const script = init.initScript?.trim() ? `${init.initScript}
@@ -13790,7 +14367,8 @@ async function rpcResponse(req, link, tearDown) {
13790
14367
  }
13791
14368
  return Response.json({ result });
13792
14369
  } catch (err) {
13793
- return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
14370
+ const name = err instanceof Error && err.name !== "Error" ? err.name : undefined;
14371
+ return Response.json({ error: err instanceof Error ? err.message : String(err), ...name ? { name } : {} }, { status: 500 });
13794
14372
  }
13795
14373
  }
13796
14374
  async function enginesResponse() {
@@ -13848,6 +14426,10 @@ function createRequestHandler(deps) {
13848
14426
  return specResponse(url, link, terminalSpec);
13849
14427
  if (url.pathname === "/api/engines" && req.method === "GET")
13850
14428
  return enginesResponse();
14429
+ if (url.pathname === "/api/quick-prompts" && req.method === "GET")
14430
+ return quickPromptsGet();
14431
+ if (url.pathname === "/api/quick-prompts" && req.method === "PUT")
14432
+ return quickPromptsPut(req);
13851
14433
  const notes = await handleNotesRequest(req, url);
13852
14434
  if (notes)
13853
14435
  return notes;
@@ -13865,6 +14447,24 @@ function createRequestHandler(deps) {
13865
14447
  return new Response("not found", { status: 404 });
13866
14448
  };
13867
14449
  }
14450
+ function quickPromptsGet() {
14451
+ return Response.json({
14452
+ review: getPersistedString(QUICK_PROMPT_KEYS.review) ?? null,
14453
+ pr: getPersistedString(QUICK_PROMPT_KEYS.pr) ?? null
14454
+ });
14455
+ }
14456
+ async function quickPromptsPut(req) {
14457
+ try {
14458
+ const body = await req.json();
14459
+ if (typeof body.review === "string")
14460
+ setPersistedString(QUICK_PROMPT_KEYS.review, body.review);
14461
+ if (typeof body.pr === "string")
14462
+ setPersistedString(QUICK_PROMPT_KEYS.pr, body.pr);
14463
+ return quickPromptsGet();
14464
+ } catch (err) {
14465
+ return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });
14466
+ }
14467
+ }
13868
14468
  async function staticResponse(pathname, staticDir) {
13869
14469
  const rel = pathname === "/" ? "/index.html" : pathname;
13870
14470
  const resolved = normalize2(join12(staticDir, rel));
@@ -13942,16 +14542,21 @@ async function createBridgeServer(opts = {}) {
13942
14542
  }
13943
14543
  };
13944
14544
  }
13945
- var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web";
14545
+ var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web", QUICK_PROMPT_KEYS;
13946
14546
  var init_bridge = __esm(() => {
13947
14547
  init_account_detect();
13948
14548
  init_interactive_command();
14549
+ init_repos();
13949
14550
  init_history4();
13950
14551
  init_notes();
13951
14552
  init_themes();
13952
14553
  init_daemon_link();
13953
14554
  init_rpc_allowlist();
13954
14555
  init_session();
14556
+ QUICK_PROMPT_KEYS = {
14557
+ review: "boardPrompt.review",
14558
+ pr: "boardPrompt.pr"
14559
+ };
13955
14560
  });
13956
14561
 
13957
14562
  // ../kobe-web/server/index.ts
@@ -17267,7 +17872,7 @@ function clampCursor(cursor, listLength) {
17267
17872
  return 0;
17268
17873
  return Math.max(0, Math.min(listLength - 1, cursor));
17269
17874
  }
17270
- function resolveBaseRef(typed, filteredBranches, cursor) {
17875
+ function resolveBaseRef2(typed, filteredBranches, cursor) {
17271
17876
  const picked = filteredBranches[cursor];
17272
17877
  if (picked)
17273
17878
  return picked;
@@ -17841,7 +18446,7 @@ function NewTaskDialogView(props) {
17841
18446
  setBaseRef(stripNewlines(v));
17842
18447
  });
17843
18448
  setProp(_el$28, "onSubmit", () => {
17844
- setBaseRef(resolveBaseRef(baseRef(), branchFiltered(), branchCursor()));
18449
+ setBaseRef(resolveBaseRef2(baseRef(), branchFiltered(), branchCursor()));
17845
18450
  setBaseRefTouched(true);
17846
18451
  setField("confirm");
17847
18452
  });
@@ -20661,7 +21266,9 @@ function devRows(hasDaemon) {
20661
21266
  return [
20662
21267
  { id: "dev-reset", kind: "devReset" },
20663
21268
  ...hasDaemon ? [{ id: "dev-restart", kind: "devRestartDaemon" }] : [],
20664
- { id: "remote-projects", kind: "devRemoteProjects" }
21269
+ { id: "remote-projects", kind: "devRemoteProjects" },
21270
+ { id: "auto-status", kind: "devAutoStatus" },
21271
+ { id: "dispatcher", kind: "devDispatcher" }
20665
21272
  ];
20666
21273
  }
20667
21274
  function sectionRows(section, input) {
@@ -21527,8 +22134,12 @@ function DevSettingsSection(props) {
21527
22134
  const restartIsCursor = () => props.level() === "body" && props.bodyRow() === 1;
21528
22135
  const experimentalRow = () => rowIndex(devRows(props.hasDaemon), "remote-projects");
21529
22136
  const remoteIsCursor = () => props.level() === "body" && props.bodyRow() === experimentalRow();
22137
+ const autoStatusRow = () => rowIndex(devRows(props.hasDaemon), "auto-status");
22138
+ const autoStatusIsCursor = () => props.level() === "body" && props.bodyRow() === autoStatusRow();
22139
+ const dispatcherRow = () => rowIndex(devRows(props.hasDaemon), "dispatcher");
22140
+ const dispatcherIsCursor = () => props.level() === "body" && props.bodyRow() === dispatcherRow();
21530
22141
  return (() => {
21531
- var _el$128 = createElement("box"), _el$129 = createElement("text"), _el$131 = createElement("text"), _el$133 = createElement("box"), _el$134 = createElement("text"), _el$144 = createElement("text"), _el$146 = createElement("box"), _el$147 = createElement("text"), _el$149 = createElement("text"), _el$151 = createElement("box"), _el$152 = createElement("text");
22142
+ var _el$128 = createElement("box"), _el$129 = createElement("text"), _el$131 = createElement("text"), _el$133 = createElement("box"), _el$134 = createElement("text"), _el$144 = createElement("text"), _el$146 = createElement("box"), _el$147 = createElement("text"), _el$149 = createElement("text"), _el$151 = createElement("box"), _el$152 = createElement("text"), _el$153 = createElement("text"), _el$155 = createElement("box"), _el$156 = createElement("text"), _el$157 = createElement("text"), _el$159 = createElement("box"), _el$160 = createElement("text");
21532
22143
  insertNode(_el$128, _el$129);
21533
22144
  insertNode(_el$128, _el$131);
21534
22145
  insertNode(_el$128, _el$133);
@@ -21599,6 +22210,10 @@ function DevSettingsSection(props) {
21599
22210
  insertNode(_el$146, _el$147);
21600
22211
  insertNode(_el$146, _el$149);
21601
22212
  insertNode(_el$146, _el$151);
22213
+ insertNode(_el$146, _el$153);
22214
+ insertNode(_el$146, _el$155);
22215
+ insertNode(_el$146, _el$157);
22216
+ insertNode(_el$146, _el$159);
21602
22217
  setProp(_el$146, "flexDirection", "column");
21603
22218
  setProp(_el$146, "gap", 0);
21604
22219
  setProp(_el$146, "paddingTop", 1);
@@ -21615,8 +22230,32 @@ function DevSettingsSection(props) {
21615
22230
  props.toggleRemoteProjects();
21616
22231
  });
21617
22232
  insert(_el$152, () => props.remoteProjectsEnabled() ? "[x] Remote projects (on)" : "[ ] Remote projects (off)");
22233
+ insertNode(_el$153, createTextNode(`Auto status flow: a backlog task moves to in_progress when its engine starts a turn, and new claude sessions get a system-prompt note telling the agent to set in_review itself when the work is done. Never touches done/canceled.`));
22234
+ setProp(_el$153, "wrapMode", "word");
22235
+ insertNode(_el$155, _el$156);
22236
+ setProp(_el$155, "flexDirection", "row");
22237
+ setProp(_el$155, "paddingLeft", 1);
22238
+ setProp(_el$155, "paddingRight", 1);
22239
+ setProp(_el$155, "onMouseUp", () => {
22240
+ props.setLevel("body");
22241
+ props.setBodyRow(autoStatusRow());
22242
+ props.toggleAutoStatus();
22243
+ });
22244
+ insert(_el$156, () => props.autoStatusEnabled() ? "[x] Auto status flow (on)" : "[ ] Auto status flow (off)");
22245
+ insertNode(_el$157, createTextNode(`Field-notes dispatcher: task sessions file one-line gotchas (\`kobe api note\`), the daemon forwards each to the repo's main session, and that session relays them to the in-flight tasks that benefit (\`kobe api dispatch\`). Web-hosted sessions receive the relays today.`));
22246
+ setProp(_el$157, "wrapMode", "word");
22247
+ insertNode(_el$159, _el$160);
22248
+ setProp(_el$159, "flexDirection", "row");
22249
+ setProp(_el$159, "paddingLeft", 1);
22250
+ setProp(_el$159, "paddingRight", 1);
22251
+ setProp(_el$159, "onMouseUp", () => {
22252
+ props.setLevel("body");
22253
+ props.setBodyRow(dispatcherRow());
22254
+ props.toggleDispatcher();
22255
+ });
22256
+ insert(_el$160, () => props.dispatcherEnabled() ? "[x] Field-notes dispatcher (on)" : "[ ] Field-notes dispatcher (off)");
21618
22257
  effect((_p$) => {
21619
- var _v$86 = theme.text, _v$87 = TextAttributes7.BOLD, _v$88 = theme.textMuted, _v$89 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$90 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$91 = TextAttributes7.BOLD, _v$92 = theme.textMuted, _v$93 = theme.text, _v$94 = TextAttributes7.BOLD, _v$95 = theme.textMuted, _v$96 = remoteIsCursor() ? theme.primary : theme.backgroundElement, _v$97 = remoteIsCursor() ? theme.selectedListItemText : theme.text, _v$98 = props.remoteProjectsEnabled() ? TextAttributes7.BOLD : undefined;
22258
+ var _v$86 = theme.text, _v$87 = TextAttributes7.BOLD, _v$88 = theme.textMuted, _v$89 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$90 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$91 = TextAttributes7.BOLD, _v$92 = theme.textMuted, _v$93 = theme.text, _v$94 = TextAttributes7.BOLD, _v$95 = theme.textMuted, _v$96 = remoteIsCursor() ? theme.primary : theme.backgroundElement, _v$97 = remoteIsCursor() ? theme.selectedListItemText : theme.text, _v$98 = props.remoteProjectsEnabled() ? TextAttributes7.BOLD : undefined, _v$99 = theme.textMuted, _v$100 = autoStatusIsCursor() ? theme.primary : theme.backgroundElement, _v$101 = autoStatusIsCursor() ? theme.selectedListItemText : theme.text, _v$102 = props.autoStatusEnabled() ? TextAttributes7.BOLD : undefined, _v$103 = theme.textMuted, _v$104 = dispatcherIsCursor() ? theme.primary : theme.backgroundElement, _v$105 = dispatcherIsCursor() ? theme.selectedListItemText : theme.text, _v$106 = props.dispatcherEnabled() ? TextAttributes7.BOLD : undefined;
21620
22259
  _v$86 !== _p$.e && (_p$.e = setProp(_el$129, "fg", _v$86, _p$.e));
21621
22260
  _v$87 !== _p$.t && (_p$.t = setProp(_el$129, "attributes", _v$87, _p$.t));
21622
22261
  _v$88 !== _p$.a && (_p$.a = setProp(_el$131, "fg", _v$88, _p$.a));
@@ -21630,6 +22269,14 @@ function DevSettingsSection(props) {
21630
22269
  _v$96 !== _p$.l && (_p$.l = setProp(_el$151, "backgroundColor", _v$96, _p$.l));
21631
22270
  _v$97 !== _p$.u && (_p$.u = setProp(_el$152, "fg", _v$97, _p$.u));
21632
22271
  _v$98 !== _p$.c && (_p$.c = setProp(_el$152, "attributes", _v$98, _p$.c));
22272
+ _v$99 !== _p$.w && (_p$.w = setProp(_el$153, "fg", _v$99, _p$.w));
22273
+ _v$100 !== _p$.m && (_p$.m = setProp(_el$155, "backgroundColor", _v$100, _p$.m));
22274
+ _v$101 !== _p$.f && (_p$.f = setProp(_el$156, "fg", _v$101, _p$.f));
22275
+ _v$102 !== _p$.y && (_p$.y = setProp(_el$156, "attributes", _v$102, _p$.y));
22276
+ _v$103 !== _p$.g && (_p$.g = setProp(_el$157, "fg", _v$103, _p$.g));
22277
+ _v$104 !== _p$.p && (_p$.p = setProp(_el$159, "backgroundColor", _v$104, _p$.p));
22278
+ _v$105 !== _p$.b && (_p$.b = setProp(_el$160, "fg", _v$105, _p$.b));
22279
+ _v$106 !== _p$.T && (_p$.T = setProp(_el$160, "attributes", _v$106, _p$.T));
21633
22280
  return _p$;
21634
22281
  }, {
21635
22282
  e: undefined,
@@ -21644,7 +22291,15 @@ function DevSettingsSection(props) {
21644
22291
  d: undefined,
21645
22292
  l: undefined,
21646
22293
  u: undefined,
21647
- c: undefined
22294
+ c: undefined,
22295
+ w: undefined,
22296
+ m: undefined,
22297
+ f: undefined,
22298
+ y: undefined,
22299
+ g: undefined,
22300
+ p: undefined,
22301
+ b: undefined,
22302
+ T: undefined
21648
22303
  });
21649
22304
  return _el$128;
21650
22305
  })();
@@ -21656,63 +22311,63 @@ function KeybindingsSettingsSection() {
21656
22311
  const report = userKeybindingsReport();
21657
22312
  const fixedIds = Object.keys(FIXED_BINDING_IDS).sort();
21658
22313
  return (() => {
21659
- var _el$153 = createElement("box"), _el$154 = createElement("text"), _el$156 = createElement("text"), _el$158 = createElement("box"), _el$159 = createElement("text"), _el$161 = createElement("text"), _el$189 = createElement("text"), _el$191 = createElement("text");
21660
- insertNode(_el$153, _el$154);
21661
- insertNode(_el$153, _el$156);
21662
- insertNode(_el$153, _el$158);
21663
- insertNode(_el$153, _el$189);
21664
- insertNode(_el$153, _el$191);
21665
- setProp(_el$153, "flexDirection", "column");
21666
- setProp(_el$153, "gap", 1);
21667
- insertNode(_el$154, createTextNode(`Keybindings`));
21668
- insertNode(_el$156, createTextNode(`Rebind chords by editing the YAML below, then restart kobe (or respawn the pane). Press F1 anywhere for the live keymap with every binding id.`));
21669
- setProp(_el$156, "wrapMode", "word");
21670
- insertNode(_el$158, _el$159);
21671
- insertNode(_el$158, _el$161);
21672
- setProp(_el$158, "flexDirection", "column");
21673
- setProp(_el$158, "gap", 0);
21674
- insertNode(_el$159, createTextNode(`Config file`));
21675
- setProp(_el$161, "wrapMode", "word");
21676
- insert(_el$161, () => report.path, null);
21677
- insert(_el$161, () => report.exists ? "" : " (not created yet)", null);
21678
- insert(_el$153, createComponent2(Show, {
22314
+ var _el$161 = createElement("box"), _el$162 = createElement("text"), _el$164 = createElement("text"), _el$166 = createElement("box"), _el$167 = createElement("text"), _el$169 = createElement("text"), _el$197 = createElement("text"), _el$199 = createElement("text");
22315
+ insertNode(_el$161, _el$162);
22316
+ insertNode(_el$161, _el$164);
22317
+ insertNode(_el$161, _el$166);
22318
+ insertNode(_el$161, _el$197);
22319
+ insertNode(_el$161, _el$199);
22320
+ setProp(_el$161, "flexDirection", "column");
22321
+ setProp(_el$161, "gap", 1);
22322
+ insertNode(_el$162, createTextNode(`Keybindings`));
22323
+ insertNode(_el$164, createTextNode(`Rebind chords by editing the YAML below, then restart kobe (or respawn the pane). Press F1 anywhere for the live keymap with every binding id.`));
22324
+ setProp(_el$164, "wrapMode", "word");
22325
+ insertNode(_el$166, _el$167);
22326
+ insertNode(_el$166, _el$169);
22327
+ setProp(_el$166, "flexDirection", "column");
22328
+ setProp(_el$166, "gap", 0);
22329
+ insertNode(_el$167, createTextNode(`Config file`));
22330
+ setProp(_el$169, "wrapMode", "word");
22331
+ insert(_el$169, () => report.path, null);
22332
+ insert(_el$169, () => report.exists ? "" : " (not created yet)", null);
22333
+ insert(_el$161, createComponent2(Show, {
21679
22334
  get when() {
21680
22335
  return !report.exists;
21681
22336
  },
21682
22337
  get children() {
21683
- var _el$162 = createElement("box"), _el$163 = createElement("text"), _el$165 = createElement("text"), _el$167 = createElement("text"), _el$169 = createElement("text"), _el$171 = createElement("text"), _el$173 = createElement("text"), _el$175 = createElement("text"), _el$177 = createElement("text"), _el$179 = createElement("text");
21684
- insertNode(_el$162, _el$163);
21685
- insertNode(_el$162, _el$165);
21686
- insertNode(_el$162, _el$167);
21687
- insertNode(_el$162, _el$169);
21688
- insertNode(_el$162, _el$171);
21689
- insertNode(_el$162, _el$173);
21690
- insertNode(_el$162, _el$175);
21691
- insertNode(_el$162, _el$177);
21692
- insertNode(_el$162, _el$179);
21693
- setProp(_el$162, "flexDirection", "column");
21694
- setProp(_el$162, "gap", 0);
21695
- insertNode(_el$163, createTextNode(`Example`));
21696
- insertNode(_el$165, createTextNode(`bindings:`));
21697
- insertNode(_el$167, createTextNode(` chat.fork.new: ctrl+g # string = one chord`));
21698
- insertNode(_el$169, createTextNode(` sidebar.select: [enter] # list = several chords`));
21699
- insertNode(_el$171, createTextNode(` files.createPR: null # null = unbind`));
21700
- insertNode(_el$173, createTextNode(` tmux.tab.new: ctrl+y # tmux session key (see below)`));
21701
- insertNode(_el$175, createTextNode(`darwin: # platform overlay (also: linux)`));
21702
- insertNode(_el$177, createTextNode(` bindings:`));
21703
- insertNode(_el$179, createTextNode(` palette.open: [cmd+p, ctrl+p]`));
22338
+ var _el$170 = createElement("box"), _el$171 = createElement("text"), _el$173 = createElement("text"), _el$175 = createElement("text"), _el$177 = createElement("text"), _el$179 = createElement("text"), _el$181 = createElement("text"), _el$183 = createElement("text"), _el$185 = createElement("text"), _el$187 = createElement("text");
22339
+ insertNode(_el$170, _el$171);
22340
+ insertNode(_el$170, _el$173);
22341
+ insertNode(_el$170, _el$175);
22342
+ insertNode(_el$170, _el$177);
22343
+ insertNode(_el$170, _el$179);
22344
+ insertNode(_el$170, _el$181);
22345
+ insertNode(_el$170, _el$183);
22346
+ insertNode(_el$170, _el$185);
22347
+ insertNode(_el$170, _el$187);
22348
+ setProp(_el$170, "flexDirection", "column");
22349
+ setProp(_el$170, "gap", 0);
22350
+ insertNode(_el$171, createTextNode(`Example`));
22351
+ insertNode(_el$173, createTextNode(`bindings:`));
22352
+ insertNode(_el$175, createTextNode(` chat.fork.new: ctrl+g # string = one chord`));
22353
+ insertNode(_el$177, createTextNode(` sidebar.select: [enter] # list = several chords`));
22354
+ insertNode(_el$179, createTextNode(` files.createPR: null # null = unbind`));
22355
+ insertNode(_el$181, createTextNode(` tmux.tab.new: ctrl+y # tmux session key (see below)`));
22356
+ insertNode(_el$183, createTextNode(`darwin: # platform overlay (also: linux)`));
22357
+ insertNode(_el$185, createTextNode(` bindings:`));
22358
+ insertNode(_el$187, createTextNode(` palette.open: [cmd+p, ctrl+p]`));
21704
22359
  effect((_p$) => {
21705
- var _v$99 = theme.text, _v$100 = TextAttributes7.BOLD, _v$101 = theme.textMuted, _v$102 = theme.textMuted, _v$103 = theme.textMuted, _v$104 = theme.textMuted, _v$105 = theme.textMuted, _v$106 = theme.textMuted, _v$107 = theme.textMuted, _v$108 = theme.textMuted;
21706
- _v$99 !== _p$.e && (_p$.e = setProp(_el$163, "fg", _v$99, _p$.e));
21707
- _v$100 !== _p$.t && (_p$.t = setProp(_el$163, "attributes", _v$100, _p$.t));
21708
- _v$101 !== _p$.a && (_p$.a = setProp(_el$165, "fg", _v$101, _p$.a));
21709
- _v$102 !== _p$.o && (_p$.o = setProp(_el$167, "fg", _v$102, _p$.o));
21710
- _v$103 !== _p$.i && (_p$.i = setProp(_el$169, "fg", _v$103, _p$.i));
21711
- _v$104 !== _p$.n && (_p$.n = setProp(_el$171, "fg", _v$104, _p$.n));
21712
- _v$105 !== _p$.s && (_p$.s = setProp(_el$173, "fg", _v$105, _p$.s));
21713
- _v$106 !== _p$.h && (_p$.h = setProp(_el$175, "fg", _v$106, _p$.h));
21714
- _v$107 !== _p$.r && (_p$.r = setProp(_el$177, "fg", _v$107, _p$.r));
21715
- _v$108 !== _p$.d && (_p$.d = setProp(_el$179, "fg", _v$108, _p$.d));
22360
+ var _v$107 = theme.text, _v$108 = TextAttributes7.BOLD, _v$109 = theme.textMuted, _v$110 = theme.textMuted, _v$111 = theme.textMuted, _v$112 = theme.textMuted, _v$113 = theme.textMuted, _v$114 = theme.textMuted, _v$115 = theme.textMuted, _v$116 = theme.textMuted;
22361
+ _v$107 !== _p$.e && (_p$.e = setProp(_el$171, "fg", _v$107, _p$.e));
22362
+ _v$108 !== _p$.t && (_p$.t = setProp(_el$171, "attributes", _v$108, _p$.t));
22363
+ _v$109 !== _p$.a && (_p$.a = setProp(_el$173, "fg", _v$109, _p$.a));
22364
+ _v$110 !== _p$.o && (_p$.o = setProp(_el$175, "fg", _v$110, _p$.o));
22365
+ _v$111 !== _p$.i && (_p$.i = setProp(_el$177, "fg", _v$111, _p$.i));
22366
+ _v$112 !== _p$.n && (_p$.n = setProp(_el$179, "fg", _v$112, _p$.n));
22367
+ _v$113 !== _p$.s && (_p$.s = setProp(_el$181, "fg", _v$113, _p$.s));
22368
+ _v$114 !== _p$.h && (_p$.h = setProp(_el$183, "fg", _v$114, _p$.h));
22369
+ _v$115 !== _p$.r && (_p$.r = setProp(_el$185, "fg", _v$115, _p$.r));
22370
+ _v$116 !== _p$.d && (_p$.d = setProp(_el$187, "fg", _v$116, _p$.d));
21716
22371
  return _p$;
21717
22372
  }, {
21718
22373
  e: undefined,
@@ -21726,102 +22381,102 @@ function KeybindingsSettingsSection() {
21726
22381
  r: undefined,
21727
22382
  d: undefined
21728
22383
  });
21729
- return _el$162;
22384
+ return _el$170;
21730
22385
  }
21731
- }), _el$189);
21732
- insert(_el$153, createComponent2(Show, {
22386
+ }), _el$197);
22387
+ insert(_el$161, createComponent2(Show, {
21733
22388
  get when() {
21734
22389
  return report.exists;
21735
22390
  },
21736
22391
  get children() {
21737
- var _el$181 = createElement("box"), _el$182 = createElement("text");
21738
- insertNode(_el$181, _el$182);
21739
- setProp(_el$181, "flexDirection", "column");
21740
- setProp(_el$181, "gap", 0);
21741
- insertNode(_el$182, createTextNode(`Overrides applied`));
21742
- insert(_el$181, createComponent2(Show, {
22392
+ var _el$189 = createElement("box"), _el$190 = createElement("text");
22393
+ insertNode(_el$189, _el$190);
22394
+ setProp(_el$189, "flexDirection", "column");
22395
+ setProp(_el$189, "gap", 0);
22396
+ insertNode(_el$190, createTextNode(`Overrides applied`));
22397
+ insert(_el$189, createComponent2(Show, {
21743
22398
  get when() {
21744
22399
  return report.applied.length === 0;
21745
22400
  },
21746
22401
  get children() {
21747
- var _el$184 = createElement("text");
21748
- insertNode(_el$184, createTextNode(`none`));
21749
- effect((_$p) => setProp(_el$184, "fg", theme.textMuted, _$p));
21750
- return _el$184;
22402
+ var _el$192 = createElement("text");
22403
+ insertNode(_el$192, createTextNode(`none`));
22404
+ effect((_$p) => setProp(_el$192, "fg", theme.textMuted, _$p));
22405
+ return _el$192;
21751
22406
  }
21752
22407
  }), null);
21753
- insert(_el$181, createComponent2(For, {
22408
+ insert(_el$189, createComponent2(For, {
21754
22409
  get each() {
21755
22410
  return report.applied;
21756
22411
  },
21757
22412
  children: (o) => (() => {
21758
- var _el$192 = createElement("text");
21759
- setProp(_el$192, "wrapMode", "word");
21760
- insert(_el$192, () => `${o.id} \u2192 ${o.keys.length > 0 ? o.keys.join(" / ") : "(unbound)"} (default: ${o.defaultKeys.join(" / ")})`);
21761
- effect((_$p) => setProp(_el$192, "fg", theme.text, _$p));
21762
- return _el$192;
22413
+ var _el$200 = createElement("text");
22414
+ setProp(_el$200, "wrapMode", "word");
22415
+ insert(_el$200, () => `${o.id} \u2192 ${o.keys.length > 0 ? o.keys.join(" / ") : "(unbound)"} (default: ${o.defaultKeys.join(" / ")})`);
22416
+ effect((_$p) => setProp(_el$200, "fg", theme.text, _$p));
22417
+ return _el$200;
21763
22418
  })()
21764
22419
  }), null);
21765
22420
  effect((_p$) => {
21766
- var _v$109 = theme.text, _v$110 = TextAttributes7.BOLD;
21767
- _v$109 !== _p$.e && (_p$.e = setProp(_el$182, "fg", _v$109, _p$.e));
21768
- _v$110 !== _p$.t && (_p$.t = setProp(_el$182, "attributes", _v$110, _p$.t));
22421
+ var _v$117 = theme.text, _v$118 = TextAttributes7.BOLD;
22422
+ _v$117 !== _p$.e && (_p$.e = setProp(_el$190, "fg", _v$117, _p$.e));
22423
+ _v$118 !== _p$.t && (_p$.t = setProp(_el$190, "attributes", _v$118, _p$.t));
21769
22424
  return _p$;
21770
22425
  }, {
21771
22426
  e: undefined,
21772
22427
  t: undefined
21773
22428
  });
21774
- return _el$181;
22429
+ return _el$189;
21775
22430
  }
21776
- }), _el$189);
21777
- insert(_el$153, createComponent2(Show, {
22431
+ }), _el$197);
22432
+ insert(_el$161, createComponent2(Show, {
21778
22433
  get when() {
21779
22434
  return report.warnings.length > 0;
21780
22435
  },
21781
22436
  get children() {
21782
- var _el$186 = createElement("box"), _el$187 = createElement("text");
21783
- insertNode(_el$186, _el$187);
21784
- setProp(_el$186, "flexDirection", "column");
21785
- setProp(_el$186, "gap", 0);
21786
- insertNode(_el$187, createTextNode(`Warnings`));
21787
- insert(_el$186, createComponent2(For, {
22437
+ var _el$194 = createElement("box"), _el$195 = createElement("text");
22438
+ insertNode(_el$194, _el$195);
22439
+ setProp(_el$194, "flexDirection", "column");
22440
+ setProp(_el$194, "gap", 0);
22441
+ insertNode(_el$195, createTextNode(`Warnings`));
22442
+ insert(_el$194, createComponent2(For, {
21788
22443
  get each() {
21789
22444
  return report.warnings;
21790
22445
  },
21791
22446
  children: (w) => (() => {
21792
- var _el$193 = createElement("text");
21793
- setProp(_el$193, "wrapMode", "word");
21794
- insert(_el$193, `! ${w}`);
21795
- effect((_$p) => setProp(_el$193, "fg", theme.warning, _$p));
21796
- return _el$193;
22447
+ var _el$201 = createElement("text");
22448
+ setProp(_el$201, "wrapMode", "word");
22449
+ insert(_el$201, `! ${w}`);
22450
+ effect((_$p) => setProp(_el$201, "fg", theme.warning, _$p));
22451
+ return _el$201;
21797
22452
  })()
21798
22453
  }), null);
21799
22454
  effect((_p$) => {
21800
- var _v$111 = theme.warning, _v$112 = TextAttributes7.BOLD;
21801
- _v$111 !== _p$.e && (_p$.e = setProp(_el$187, "fg", _v$111, _p$.e));
21802
- _v$112 !== _p$.t && (_p$.t = setProp(_el$187, "attributes", _v$112, _p$.t));
22455
+ var _v$119 = theme.warning, _v$120 = TextAttributes7.BOLD;
22456
+ _v$119 !== _p$.e && (_p$.e = setProp(_el$195, "fg", _v$119, _p$.e));
22457
+ _v$120 !== _p$.t && (_p$.t = setProp(_el$195, "attributes", _v$120, _p$.t));
21803
22458
  return _p$;
21804
22459
  }, {
21805
22460
  e: undefined,
21806
22461
  t: undefined
21807
22462
  });
21808
- return _el$186;
22463
+ return _el$194;
21809
22464
  }
21810
- }), _el$189);
21811
- insertNode(_el$189, createTextNode(`tmux session keys use the same file: tmux.tab.new (ctrl+t), tmux.tab.prev/next (ctrl+[/]), tmux.tab.close (ctrl+w), tmux.tab.rename (f2), tmux.tab.chooseEngine (ctrl+shift+t), tmux.detach (ctrl+q), tmux.focus (4 chords, left/down/up/right). They apply when a session is (re)built.`));
21812
- setProp(_el$189, "wrapMode", "word");
21813
- setProp(_el$191, "wrapMode", "word");
21814
- insert(_el$191, () => `Fixed (not rebindable): ${fixedIds.join(", ")}.`);
22465
+ }), _el$197);
22466
+ insertNode(_el$197, createTextNode(`tmux session keys use the same file: tmux.tab.new (ctrl+t), tmux.tab.prev/next (ctrl+[/]), tmux.tab.close (ctrl+w), tmux.tab.rename (f2), tmux.tab.chooseEngine (ctrl+shift+t), tmux.detach (ctrl+q), tmux.focus (4 chords, left/down/up/right). They apply when a session is (re)built.`));
22467
+ setProp(_el$197, "wrapMode", "word");
22468
+ setProp(_el$199, "wrapMode", "word");
22469
+ insert(_el$199, () => `Fixed (not rebindable): ${fixedIds.join(", ")}.`);
21815
22470
  effect((_p$) => {
21816
- var _v$113 = theme.text, _v$114 = TextAttributes7.BOLD, _v$115 = theme.textMuted, _v$116 = theme.text, _v$117 = TextAttributes7.BOLD, _v$118 = theme.textMuted, _v$119 = theme.textMuted, _v$120 = theme.textMuted;
21817
- _v$113 !== _p$.e && (_p$.e = setProp(_el$154, "fg", _v$113, _p$.e));
21818
- _v$114 !== _p$.t && (_p$.t = setProp(_el$154, "attributes", _v$114, _p$.t));
21819
- _v$115 !== _p$.a && (_p$.a = setProp(_el$156, "fg", _v$115, _p$.a));
21820
- _v$116 !== _p$.o && (_p$.o = setProp(_el$159, "fg", _v$116, _p$.o));
21821
- _v$117 !== _p$.i && (_p$.i = setProp(_el$159, "attributes", _v$117, _p$.i));
21822
- _v$118 !== _p$.n && (_p$.n = setProp(_el$161, "fg", _v$118, _p$.n));
21823
- _v$119 !== _p$.s && (_p$.s = setProp(_el$189, "fg", _v$119, _p$.s));
21824
- _v$120 !== _p$.h && (_p$.h = setProp(_el$191, "fg", _v$120, _p$.h));
22471
+ var _v$121 = theme.text, _v$122 = TextAttributes7.BOLD, _v$123 = theme.textMuted, _v$124 = theme.text, _v$125 = TextAttributes7.BOLD, _v$126 = theme.textMuted, _v$127 = theme.textMuted, _v$128 = theme.textMuted;
22472
+ _v$121 !== _p$.e && (_p$.e = setProp(_el$162, "fg", _v$121, _p$.e));
22473
+ _v$122 !== _p$.t && (_p$.t = setProp(_el$162, "attributes", _v$122, _p$.t));
22474
+ _v$123 !== _p$.a && (_p$.a = setProp(_el$164, "fg", _v$123, _p$.a));
22475
+ _v$124 !== _p$.o && (_p$.o = setProp(_el$167, "fg", _v$124, _p$.o));
22476
+ _v$125 !== _p$.i && (_p$.i = setProp(_el$167, "attributes", _v$125, _p$.i));
22477
+ _v$126 !== _p$.n && (_p$.n = setProp(_el$169, "fg", _v$126, _p$.n));
22478
+ _v$127 !== _p$.s && (_p$.s = setProp(_el$197, "fg", _v$127, _p$.s));
22479
+ _v$128 !== _p$.h && (_p$.h = setProp(_el$199, "fg", _v$128, _p$.h));
21825
22480
  return _p$;
21826
22481
  }, {
21827
22482
  e: undefined,
@@ -21833,7 +22488,7 @@ function KeybindingsSettingsSection() {
21833
22488
  s: undefined,
21834
22489
  h: undefined
21835
22490
  });
21836
- return _el$153;
22491
+ return _el$161;
21837
22492
  })();
21838
22493
  }
21839
22494
  var init_sections = __esm(() => {
@@ -21946,6 +22601,18 @@ function SettingsDialog(props) {
21946
22601
  function toggleRemoteProjects() {
21947
22602
  props.kv.set("experimental.remoteProjects", !remoteProjectsEnabled());
21948
22603
  }
22604
+ function autoStatusOn() {
22605
+ return props.kv.get(AUTO_STATUS_KEY, false) === true;
22606
+ }
22607
+ function toggleAutoStatus() {
22608
+ props.kv.set(AUTO_STATUS_KEY, !autoStatusOn());
22609
+ }
22610
+ function dispatcherOn() {
22611
+ return props.kv.get(DISPATCHER_KEY, false) === true;
22612
+ }
22613
+ function toggleDispatcher() {
22614
+ props.kv.set(DISPATCHER_KEY, !dispatcherOn());
22615
+ }
21949
22616
  function customEngines() {
21950
22617
  const raw = props.kv.get("customEngineIds", []);
21951
22618
  return Array.isArray(raw) ? raw.filter((s) => typeof s === "string" && s.trim().length > 0) : [];
@@ -22163,7 +22830,9 @@ function SettingsDialog(props) {
22163
22830
  feedbackSend: () => void sendFeedback(),
22164
22831
  devReset: () => void confirmResetState(dialog, props.kv, renderer),
22165
22832
  devRestartDaemon: () => void confirmRestartDaemon(dialog, props.orchestrator, renderer),
22166
- devRemoteProjects: () => toggleRemoteProjects()
22833
+ devRemoteProjects: () => toggleRemoteProjects(),
22834
+ devAutoStatus: () => toggleAutoStatus(),
22835
+ devDispatcher: () => toggleDispatcher()
22167
22836
  };
22168
22837
  function activateBodyRow() {
22169
22838
  const row = rowAt(bodyRows(), bodyRow());
@@ -22369,7 +23038,11 @@ function SettingsDialog(props) {
22369
23038
  confirmReset: () => void confirmResetState(dialog, props.kv, renderer),
22370
23039
  confirmRestartDaemon: () => void confirmRestartDaemon(dialog, props.orchestrator, renderer),
22371
23040
  remoteProjectsEnabled,
22372
- toggleRemoteProjects
23041
+ toggleRemoteProjects,
23042
+ autoStatusEnabled: autoStatusOn,
23043
+ toggleAutoStatus,
23044
+ dispatcherEnabled: dispatcherOn,
23045
+ toggleDispatcher
22373
23046
  });
22374
23047
  }
22375
23048
  }), null);
@@ -22405,6 +23078,8 @@ var init_settings_dialog = __esm(() => {
22405
23078
  init_account_detect();
22406
23079
  init_interactive_command();
22407
23080
  init_feedback();
23081
+ init_auto_status();
23082
+ init_dispatcher();
22408
23083
  init_repos();
22409
23084
  init_vendor();
22410
23085
  init_theme2();
@@ -26800,7 +27475,7 @@ var init_filetree = __esm(() => {
26800
27475
  // src/tui/ops/pr-prompt.ts
26801
27476
  import { promises as fs6 } from "fs";
26802
27477
  import path12 from "path";
26803
- async function git(cwd, args2) {
27478
+ async function git2(cwd, args2) {
26804
27479
  const controller = new AbortController;
26805
27480
  const timer = setTimeout(() => controller.abort(), GIT_TIMEOUT_MS2);
26806
27481
  try {
@@ -26819,20 +27494,20 @@ async function git(cwd, args2) {
26819
27494
  }
26820
27495
  }
26821
27496
  async function currentBranch2(cwd) {
26822
- return await git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) || "HEAD";
27497
+ return await git2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) || "HEAD";
26823
27498
  }
26824
27499
  async function targetBranch(cwd) {
26825
- const out = await git(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]);
27500
+ const out = await git2(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]);
26826
27501
  if (!out)
26827
27502
  return "main";
26828
27503
  return out.startsWith("origin/") ? out.slice("origin/".length) : out;
26829
27504
  }
26830
27505
  async function hasUpstream(cwd) {
26831
- const out = await git(cwd, ["rev-parse", "--abbrev-ref", "@{u}"]);
27506
+ const out = await git2(cwd, ["rev-parse", "--abbrev-ref", "@{u}"]);
26832
27507
  return out !== null && out.length > 0;
26833
27508
  }
26834
27509
  async function dirtyCount(cwd) {
26835
- const out = await git(cwd, ["status", "--porcelain"]);
27510
+ const out = await git2(cwd, ["status", "--porcelain"]);
26836
27511
  if (!out)
26837
27512
  return 0;
26838
27513
  return out.split(`