wildcrane-runner 0.6.12 → 0.6.14

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // ai4kanban 0.11.9 — built by runner/scripts/build.mjs. Do not edit.
2
+ // ai4kanban 0.11.11 — built by runner/scripts/build.mjs. Do not edit.
3
3
  import { createRequire as __ai4kanbanRequire } from 'node:module'
4
4
  const require = __ai4kanbanRequire(import.meta.url)
5
5
  var __create = Object.create;
@@ -1167,8 +1167,8 @@ var require_command = __commonJS({
1167
1167
  "../cli/node_modules/commander/lib/command.js"(exports) {
1168
1168
  var EventEmitter = __require("node:events").EventEmitter;
1169
1169
  var childProcess = __require("node:child_process");
1170
- var path85 = __require("node:path");
1171
- var fs86 = __require("node:fs");
1170
+ var path87 = __require("node:path");
1171
+ var fs87 = __require("node:fs");
1172
1172
  var process2 = __require("node:process");
1173
1173
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1174
1174
  var { CommanderError: CommanderError2 } = require_error();
@@ -2149,7 +2149,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2149
2149
  * @param {string} subcommandName
2150
2150
  */
2151
2151
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2152
- if (fs86.existsSync(executableFile)) return;
2152
+ if (fs87.existsSync(executableFile)) return;
2153
2153
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2154
2154
  const executableMissing = `'${executableFile}' does not exist
2155
2155
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2167,11 +2167,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2167
2167
  let launchWithNode = false;
2168
2168
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2169
2169
  function findFile(baseDir, baseName) {
2170
- const localBin = path85.resolve(baseDir, baseName);
2171
- if (fs86.existsSync(localBin)) return localBin;
2172
- if (sourceExt.includes(path85.extname(baseName))) return void 0;
2170
+ const localBin = path87.resolve(baseDir, baseName);
2171
+ if (fs87.existsSync(localBin)) return localBin;
2172
+ if (sourceExt.includes(path87.extname(baseName))) return void 0;
2173
2173
  const foundExt = sourceExt.find(
2174
- (ext) => fs86.existsSync(`${localBin}${ext}`)
2174
+ (ext) => fs87.existsSync(`${localBin}${ext}`)
2175
2175
  );
2176
2176
  if (foundExt) return `${localBin}${foundExt}`;
2177
2177
  return void 0;
@@ -2183,21 +2183,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2183
2183
  if (this._scriptPath) {
2184
2184
  let resolvedScriptPath;
2185
2185
  try {
2186
- resolvedScriptPath = fs86.realpathSync(this._scriptPath);
2186
+ resolvedScriptPath = fs87.realpathSync(this._scriptPath);
2187
2187
  } catch {
2188
2188
  resolvedScriptPath = this._scriptPath;
2189
2189
  }
2190
- executableDir = path85.resolve(
2191
- path85.dirname(resolvedScriptPath),
2190
+ executableDir = path87.resolve(
2191
+ path87.dirname(resolvedScriptPath),
2192
2192
  executableDir
2193
2193
  );
2194
2194
  }
2195
2195
  if (executableDir) {
2196
2196
  let localFile = findFile(executableDir, executableFile);
2197
2197
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2198
- const legacyName = path85.basename(
2198
+ const legacyName = path87.basename(
2199
2199
  this._scriptPath,
2200
- path85.extname(this._scriptPath)
2200
+ path87.extname(this._scriptPath)
2201
2201
  );
2202
2202
  if (legacyName !== this._name) {
2203
2203
  localFile = findFile(
@@ -2208,7 +2208,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2208
2208
  }
2209
2209
  executableFile = localFile || executableFile;
2210
2210
  }
2211
- launchWithNode = sourceExt.includes(path85.extname(executableFile));
2211
+ launchWithNode = sourceExt.includes(path87.extname(executableFile));
2212
2212
  let proc;
2213
2213
  if (process2.platform !== "win32") {
2214
2214
  if (launchWithNode) {
@@ -3055,7 +3055,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3055
3055
  * @return {Command}
3056
3056
  */
3057
3057
  nameFromFilename(filename) {
3058
- this._name = path85.basename(filename, path85.extname(filename));
3058
+ this._name = path87.basename(filename, path87.extname(filename));
3059
3059
  return this;
3060
3060
  }
3061
3061
  /**
@@ -3069,9 +3069,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3069
3069
  * @param {string} [path]
3070
3070
  * @return {(string|null|Command)}
3071
3071
  */
3072
- executableDir(path86) {
3073
- if (path86 === void 0) return this._executableDir;
3074
- this._executableDir = path86;
3072
+ executableDir(path88) {
3073
+ if (path88 === void 0) return this._executableDir;
3074
+ this._executableDir = path88;
3075
3075
  return this;
3076
3076
  }
3077
3077
  /**
@@ -5477,8 +5477,8 @@ var init_package = __esm({
5477
5477
  "../cli/package.json"() {
5478
5478
  package_default = {
5479
5479
  name: "wildcrane",
5480
- version: "0.11.9",
5481
- akbContract: 4,
5480
+ version: "0.11.11",
5481
+ akbContract: 5,
5482
5482
  description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
5483
5483
  keywords: [
5484
5484
  "kanban",
@@ -8316,10 +8316,10 @@ function findRollout(threadId) {
8316
8316
  }
8317
8317
  for (const name of names4) {
8318
8318
  if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
8319
- const path85 = join(dir, name);
8319
+ const path87 = join(dir, name);
8320
8320
  try {
8321
- const at = statSync(path85).mtimeMs;
8322
- if (!newest2 || at > newest2.at) newest2 = { path: path85, at };
8321
+ const at = statSync(path87).mtimeMs;
8322
+ if (!newest2 || at > newest2.at) newest2 = { path: path87, at };
8323
8323
  } catch {
8324
8324
  }
8325
8325
  }
@@ -8327,11 +8327,11 @@ function findRollout(threadId) {
8327
8327
  return newest2?.path;
8328
8328
  }
8329
8329
  function readCodexRunFacts(threadId) {
8330
- const path85 = findRollout(threadId);
8331
- if (!path85) return void 0;
8330
+ const path87 = findRollout(threadId);
8331
+ if (!path87) return void 0;
8332
8332
  let raw;
8333
8333
  try {
8334
- raw = readFileSync(path85, "utf8");
8334
+ raw = readFileSync(path87, "utf8");
8335
8335
  } catch {
8336
8336
  return void 0;
8337
8337
  }
@@ -8459,8 +8459,8 @@ function renderCompleted(item) {
8459
8459
  const changes = Array.isArray(item.changes) ? item.changes : [];
8460
8460
  return changes.map((raw) => {
8461
8461
  const change2 = obj(raw);
8462
- const path85 = str2(change2.path);
8463
- return path85 ? `\u23FA ${str2(change2.kind) || "change"}(${path85})
8462
+ const path87 = str2(change2.path);
8463
+ return path87 ? `\u23FA ${str2(change2.kind) || "change"}(${path87})
8464
8464
  ` : "";
8465
8465
  }).join("");
8466
8466
  }
@@ -8719,10 +8719,10 @@ function readKimiRunFacts(sessionId) {
8719
8719
  const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
8720
8720
  let model;
8721
8721
  let holding = 0;
8722
- for (const { path: path85, main } of wireFiles(entry.sessionDir)) {
8722
+ for (const { path: path87, main } of wireFiles(entry.sessionDir)) {
8723
8723
  let raw;
8724
8724
  try {
8725
- raw = readFileSync2(path85, "utf8");
8725
+ raw = readFileSync2(path87, "utf8");
8726
8726
  } catch {
8727
8727
  continue;
8728
8728
  }
@@ -9003,8 +9003,8 @@ var init_tail = __esm({
9003
9003
  import fs24 from "node:fs";
9004
9004
  function whereHint(update2, title) {
9005
9005
  const locations = Array.isArray(update2.locations) ? update2.locations : [];
9006
- const path85 = str2(obj(locations[0]).path);
9007
- return path85 && !title.includes(path85) ? hint(path85) : "";
9006
+ const path87 = str2(obj(locations[0]).path);
9007
+ return path87 && !title.includes(path87) ? hint(path87) : "";
9008
9008
  }
9009
9009
  function saidBy(update2) {
9010
9010
  const content = Array.isArray(update2.content) ? update2.content : [];
@@ -9947,27 +9947,40 @@ import crypto3 from "node:crypto";
9947
9947
  import fs25 from "node:fs";
9948
9948
  import os4 from "node:os";
9949
9949
  import path28 from "node:path";
9950
+ function spawnGit(args, options) {
9951
+ const out = spawnSync3("git", args, {
9952
+ ...options,
9953
+ encoding: "utf8",
9954
+ stdio: ["ignore", "pipe", "pipe"],
9955
+ timeout: gitLimits.timeoutMs,
9956
+ killSignal: "SIGKILL"
9957
+ });
9958
+ if (out.error?.code === "ETIMEDOUT") throw new GitTimeoutError(`Git ${args[0]} timed out after ${gitLimits.timeoutMs / 1e3}s; the command was killed.`);
9959
+ return out;
9960
+ }
9950
9961
  function git(args, cwd = REPO_ROOT) {
9951
9962
  try {
9952
- const out = spawnSync3("git", args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
9963
+ const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
9953
9964
  if (out.error || out.status !== 0) return null;
9954
9965
  return out.stdout;
9955
- } catch {
9966
+ } catch (error) {
9967
+ if (error instanceof GitTimeoutError) throw error;
9956
9968
  return null;
9957
9969
  }
9958
9970
  }
9959
9971
  function gitDiff(args, cwd = REPO_ROOT) {
9960
9972
  try {
9961
- const out = spawnSync3("git", args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
9973
+ const out = spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true });
9962
9974
  if (out.error || out.status !== 0 && out.status !== 1) return null;
9963
9975
  return out.stdout;
9964
- } catch {
9976
+ } catch (error) {
9977
+ if (error instanceof GitTimeoutError) throw error;
9965
9978
  return null;
9966
9979
  }
9967
9980
  }
9968
9981
  function tryGit(args, cwd = REPO_ROOT, env2) {
9969
9982
  try {
9970
- const out = spawnSync3("git", args, {
9983
+ const out = spawnGit(args, {
9971
9984
  cwd,
9972
9985
  encoding: "utf8",
9973
9986
  maxBuffer: MAX_OUTPUT,
@@ -9978,6 +9991,7 @@ function tryGit(args, cwd = REPO_ROOT, env2) {
9978
9991
  if (out.status !== 0) return { ok: false, why: (out.stderr || out.stdout || "").trim() };
9979
9992
  return { ok: true };
9980
9993
  } catch (e) {
9994
+ if (e instanceof GitTimeoutError) throw e;
9981
9995
  return { ok: false, why: e instanceof Error ? e.message : String(e) };
9982
9996
  }
9983
9997
  }
@@ -9986,7 +10000,7 @@ function boardManagedPaths() {
9986
10000
  }
9987
10001
  function outsideBoardForAdd(cwd = REPO_ROOT) {
9988
10002
  const kept2 = boardManagedPaths().filter(
9989
- (p) => spawnSync3("git", ["check-ignore", "-q", p], { cwd, windowsHide: true }).status !== 0
10003
+ (p) => spawnGit(["check-ignore", "-q", p], { cwd, windowsHide: true }).status !== 0
9990
10004
  );
9991
10005
  return ["--", ".", ...kept2.map((p) => `:(exclude)${p}`)];
9992
10006
  }
@@ -10106,10 +10120,10 @@ function treeMark(base, cwd = REPO_ROOT) {
10106
10120
  const index = path28.join(ensureAkbDir(), `index-${process.pid}-${Date.now()}`);
10107
10121
  try {
10108
10122
  const env2 = { ...process.env, GIT_INDEX_FILE: index };
10109
- const run2 = (args) => spawnSync3("git", args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, env: env2 }).status === 0;
10123
+ const run2 = (args) => spawnGit(args, { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, env: env2 }).status === 0;
10110
10124
  if (!run2(["read-tree", base])) return null;
10111
10125
  if (!run2(["add", "-A", ...outsideBoardForAdd(cwd)])) return null;
10112
- const tree = spawnSync3("git", ["write-tree"], {
10126
+ const tree = spawnGit(["write-tree"], {
10113
10127
  cwd,
10114
10128
  encoding: "utf8",
10115
10129
  maxBuffer: MAX_OUTPUT,
@@ -10117,7 +10131,8 @@ function treeMark(base, cwd = REPO_ROOT) {
10117
10131
  env: env2
10118
10132
  });
10119
10133
  return tree.status === 0 ? tree.stdout.trim() || null : null;
10120
- } catch {
10134
+ } catch (error) {
10135
+ if (error instanceof GitTimeoutError) throw error;
10121
10136
  return null;
10122
10137
  } finally {
10123
10138
  try {
@@ -10128,23 +10143,26 @@ function treeMark(base, cwd = REPO_ROOT) {
10128
10143
  }
10129
10144
  function reverseApplies(patch2, ref, cwd = REPO_ROOT) {
10130
10145
  if (!patch2.trim()) return false;
10131
- const index = path28.join(os4.tmpdir(), `akb-apply-${process.pid}-${Date.now()}.index`);
10146
+ const folder = fs25.mkdtempSync(path28.join(os4.tmpdir(), `akb-apply-${process.pid}-`));
10147
+ const index = path28.join(folder, "index");
10148
+ const patchFile = path28.join(folder, "patch");
10132
10149
  const env2 = { GIT_INDEX_FILE: index };
10133
10150
  try {
10151
+ fs25.writeFileSync(patchFile, patch2);
10134
10152
  if (!tryGit(["read-tree", ref], cwd, env2).ok) return false;
10135
- const out = spawnSync3("git", ["apply", "--cached", "--reverse", "--check", "-"], {
10153
+ const out = spawnGit(["apply", "--cached", "--reverse", "--check", patchFile], {
10136
10154
  cwd,
10137
- input: patch2,
10138
10155
  encoding: "utf8",
10139
10156
  maxBuffer: MAX_OUTPUT,
10140
10157
  windowsHide: true,
10141
10158
  env: { ...process.env, ...env2 }
10142
10159
  });
10143
10160
  return !out.error && out.status === 0;
10144
- } catch {
10161
+ } catch (error) {
10162
+ if (error instanceof GitTimeoutError) throw error;
10145
10163
  return false;
10146
10164
  } finally {
10147
- fs25.rmSync(index, { force: true });
10165
+ fs25.rmSync(folder, { recursive: true, force: true });
10148
10166
  }
10149
10167
  }
10150
10168
  function squashOnto(dir, base, message) {
@@ -10226,12 +10244,15 @@ function moveBranchRef(branch, commit, expect) {
10226
10244
  if (branchTip(branch) !== expect) return { moved: true };
10227
10245
  return { ok: false, error: `couldn't move ${branch} to ${commit.slice(0, 12)}: ${done2.why ?? "git refused"}` };
10228
10246
  }
10229
- var MAX_OUTPUT, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
10247
+ var MAX_OUTPUT, gitLimits, GitTimeoutError, scripted, posix, outsideBoard, deliveryBranch, deliveryWorktree, worktreeDir, inGitRepo, headCommit, worktreeExists, branchExists, pruneWorktreeMetadata, branchTip, isAncestor, changedPaths, branchPatch, lastCommitTouching, continueRebase, abortRebase, conflictedPaths, headingKind;
10230
10248
  var init_worktree = __esm({
10231
10249
  "../cli/src/lib/agent/worktree.ts"() {
10232
10250
  "use strict";
10233
10251
  init_paths();
10234
10252
  MAX_OUTPUT = 64 * 1024 * 1024;
10253
+ gitLimits = { timeoutMs: 3e4 };
10254
+ GitTimeoutError = class extends Error {
10255
+ };
10235
10256
  scripted = (args, cwd) => tryGit(["-c", "core.hooksPath=/dev/null", ...args], cwd, { GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" });
10236
10257
  posix = (p) => p.split(path28.sep).join("/");
10237
10258
  outsideBoard = () => [
@@ -10248,7 +10269,7 @@ var init_worktree = __esm({
10248
10269
  branchExists = (branch) => !!branch && git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]) !== null;
10249
10270
  pruneWorktreeMetadata = () => void git(["worktree", "prune"]);
10250
10271
  branchTip = (branch) => git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`])?.trim() || null;
10251
- isAncestor = (older, newer, cwd = REPO_ROOT) => spawnSync3("git", ["merge-base", "--is-ancestor", older, newer], { cwd, windowsHide: true }).status === 0;
10272
+ isAncestor = (older, newer, cwd = REPO_ROOT) => spawnGit(["merge-base", "--is-ancestor", older, newer], { cwd, windowsHide: true }).status === 0;
10252
10273
  changedPaths = (base, branch, cwd = REPO_ROOT) => {
10253
10274
  const out = git(["diff", "--name-only", base, branch, ...outsideBoard()], cwd);
10254
10275
  return out === null ? null : out.split("\n").filter(Boolean);
@@ -15546,6 +15567,252 @@ var init_complete = __esm({
15546
15567
  }
15547
15568
  });
15548
15569
 
15570
+ // ../cli/src/lib/agent/stop.ts
15571
+ import { spawnSync as spawnSync4 } from "node:child_process";
15572
+ import { randomUUID as randomUUID5 } from "node:crypto";
15573
+ import fs39 from "node:fs";
15574
+ import os6 from "node:os";
15575
+ import path43 from "node:path";
15576
+ function markedInProc(entry, strict = false) {
15577
+ const pids = [];
15578
+ for (const name of fs39.readdirSync("/proc")) {
15579
+ if (!/^\d+$/.test(name)) continue;
15580
+ try {
15581
+ if (fs39.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
15582
+ } catch (error) {
15583
+ if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
15584
+ const stat = fs39.statSync(`/proc/${name}`, { throwIfNoEntry: false });
15585
+ if (stat?.uid === process.getuid?.()) throw error;
15586
+ }
15587
+ }
15588
+ }
15589
+ return pids;
15590
+ }
15591
+ function markedInPs(entry, roots) {
15592
+ const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
15593
+ encoding: "utf8",
15594
+ maxBuffer: 64 * 1024 * 1024,
15595
+ timeout: 5e3
15596
+ });
15597
+ if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
15598
+ const children = /* @__PURE__ */ new Map();
15599
+ const listed2 = /* @__PURE__ */ new Set();
15600
+ const found2 = /* @__PURE__ */ new Set();
15601
+ for (const line of ps.stdout.split("\n")) {
15602
+ const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
15603
+ if (!m) continue;
15604
+ const pid = Number(m[1]);
15605
+ const ppid = Number(m[2]);
15606
+ listed2.add(pid);
15607
+ children.set(ppid, [...children.get(ppid) ?? [], pid]);
15608
+ if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
15609
+ }
15610
+ const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
15611
+ for (const pid of queue) {
15612
+ found2.add(pid);
15613
+ for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
15614
+ }
15615
+ return [...found2];
15616
+ }
15617
+ function marked(mark, roots = []) {
15618
+ if (WINDOWS3 || !mark.value) return [];
15619
+ const entry = `${mark.name}=${mark.value}`;
15620
+ try {
15621
+ const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
15622
+ return pids.filter((pid) => pid !== process.pid);
15623
+ } catch {
15624
+ return [];
15625
+ }
15626
+ }
15627
+ function signal(pids, sig) {
15628
+ for (const pid of pids) {
15629
+ try {
15630
+ process.kill(pid, sig);
15631
+ } catch {
15632
+ }
15633
+ }
15634
+ }
15635
+ function stopWatchers(sessions, onlyPid, stillOwns = () => true, startedBefore) {
15636
+ if (sessions ? !sessions.length : onlyPid === void 0) return [];
15637
+ const command = WINDOWS3 ? "powershell.exe" : "ps";
15638
+ const args = WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine,@{Name='StartedAt';Expression={([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds()}} | ConvertTo-Json -Compress"] : ["-A", "-ww", "-o", "pid=,lstart=,command="];
15639
+ const out = spawnSync4(command, args, { encoding: "utf8", timeout: 5e3, maxBuffer: 16 * 1024 * 1024, windowsHide: true, env: { ...process.env, LC_ALL: "C" } });
15640
+ if (out.status !== 0) return [];
15641
+ let procs;
15642
+ try {
15643
+ procs = WINDOWS3 ? [].concat(JSON.parse(out.stdout)).map((p) => ({ pid: p.ProcessId, command: p.CommandLine ?? "", startedAt: p.StartedAt })) : out.stdout.split("\n").flatMap((line) => {
15644
+ const m = /^\s*(\d+)\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d{4})\s+(.*)$/.exec(line);
15645
+ return m ? [{ pid: Number(m[1]), command: m[3], startedAt: Date.parse(m[2]) }] : [];
15646
+ });
15647
+ } catch {
15648
+ return [];
15649
+ }
15650
+ const known2 = sessions && new Set(sessions);
15651
+ const stopped2 = [];
15652
+ for (const proc of procs) {
15653
+ if (proc.pid === process.pid || onlyPid !== void 0 && proc.pid !== onlyPid) continue;
15654
+ if (startedBefore !== void 0 && (!Number.isFinite(proc.startedAt) || proc.startedAt > startedBefore)) continue;
15655
+ const match = /^"?[^\n]*?\bnode(?:\.exe)?"?\s+(?:"[^\n"]+\.mjs"|\S+\.mjs)\s+__watch\s+(\S+)(?:\s|$)/.exec(proc.command);
15656
+ const args2 = `${proc.command} `;
15657
+ const names4 = (flag, value) => args2.includes(` ${flag} ${value} `) || args2.includes(` ${flag} "${value}" `);
15658
+ const board2 = names4("--board", KANBAN) || KANBAN === path43.join(REPO_ROOT, "docs", "kanban") && names4("--dir", REPO_ROOT);
15659
+ if (!match || known2 && !known2.has(match[1]) || !board2 || !stillOwns()) continue;
15660
+ if (WINDOWS3) {
15661
+ if (killTreeOnWindows(proc.pid)) stopped2.push(proc.pid);
15662
+ } else {
15663
+ try {
15664
+ process.kill(-proc.pid, "SIGKILL");
15665
+ stopped2.push(proc.pid);
15666
+ } catch {
15667
+ }
15668
+ }
15669
+ }
15670
+ return stopped2;
15671
+ }
15672
+ function startedUnder(list, root, since) {
15673
+ const self = list.procs.find((p) => p.pid === root);
15674
+ const from = since ?? self?.startedAt;
15675
+ if (from === void 0 || list.bootedAt > from) return [];
15676
+ if (self && self.startedAt > from) return [];
15677
+ const found2 = self ? [root] : [];
15678
+ const seen = /* @__PURE__ */ new Set([root]);
15679
+ const queue = [{ pid: root, startedAt: from }];
15680
+ for (const parent of queue) {
15681
+ for (const p of list.procs) {
15682
+ if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
15683
+ seen.add(p.pid);
15684
+ found2.push(p.pid);
15685
+ queue.push(p);
15686
+ }
15687
+ }
15688
+ return found2.reverse();
15689
+ }
15690
+ function readProcList(out) {
15691
+ const procs = [];
15692
+ let bootedAt;
15693
+ for (const line of out.split(/\r?\n/)) {
15694
+ const boot = /^boot (\d+)$/.exec(line.trim());
15695
+ if (boot) bootedAt = Number(boot[1]);
15696
+ const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
15697
+ if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
15698
+ }
15699
+ return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
15700
+ }
15701
+ function procsOnWindows() {
15702
+ const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
15703
+ encoding: "utf8",
15704
+ maxBuffer: 64 * 1024 * 1024,
15705
+ timeout: 5e3,
15706
+ windowsHide: true
15707
+ });
15708
+ return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
15709
+ }
15710
+ function killUnderOnWindows(pid, since) {
15711
+ if (!WINDOWS3 || !pid) return;
15712
+ const list = procsOnWindows();
15713
+ if (!list) return;
15714
+ const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
15715
+ if (!pids.length) return;
15716
+ spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
15717
+ }
15718
+ function killMarked(mark, agent) {
15719
+ if (WINDOWS3) {
15720
+ if (agent) killUnderOnWindows(agent.pid, agent.since);
15721
+ return;
15722
+ }
15723
+ signal(marked(mark), "SIGKILL");
15724
+ }
15725
+ function killTreeOnWindows(pid) {
15726
+ if (!WINDOWS3 || !pid) return false;
15727
+ const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
15728
+ return out.status === 0;
15729
+ }
15730
+ function later(ms, fn) {
15731
+ const t = setTimeout(fn, ms);
15732
+ if (typeof t.unref === "function") t.unref();
15733
+ }
15734
+ function endAgent(child, mark, graceMs) {
15735
+ const term = (sig) => {
15736
+ try {
15737
+ child.kill(sig);
15738
+ } catch {
15739
+ }
15740
+ };
15741
+ if (killTreeOnWindows(child.pid)) return;
15742
+ killUnderOnWindows(child.pid);
15743
+ const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
15744
+ term("SIGTERM");
15745
+ signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
15746
+ const round = (left) => {
15747
+ const pids = marked(mark, [...seen]);
15748
+ signal(pids, "SIGKILL");
15749
+ for (const pid of pids) seen.add(pid);
15750
+ if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
15751
+ };
15752
+ later(graceMs, () => {
15753
+ term("SIGKILL");
15754
+ round(KILL_ROUNDS);
15755
+ });
15756
+ }
15757
+ function killRunning() {
15758
+ for (const [child, mark] of running) {
15759
+ if (killTreeOnWindows(child.pid)) continue;
15760
+ killUnderOnWindows(child.pid);
15761
+ signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
15762
+ try {
15763
+ child.kill("SIGKILL");
15764
+ } catch {
15765
+ }
15766
+ }
15767
+ running.clear();
15768
+ }
15769
+ function unlisten() {
15770
+ for (const [sig, fn] of listening) process.removeListener(sig, fn);
15771
+ listening.clear();
15772
+ }
15773
+ function onQuit(sig) {
15774
+ killRunning();
15775
+ unlisten();
15776
+ if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
15777
+ }
15778
+ function trackAgent(child, mark) {
15779
+ if (!child.pid) return;
15780
+ running.set(child, mark);
15781
+ child.once("close", () => {
15782
+ running.delete(child);
15783
+ if (!running.size) unlisten();
15784
+ });
15785
+ for (const sig of QUIT_SIGNALS) {
15786
+ if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
15787
+ const fn = () => onQuit(sig);
15788
+ listening.set(sig, fn);
15789
+ process.on(sig, fn);
15790
+ }
15791
+ }
15792
+ var runMark, stopMark, markEnv, WINDOWS3, KILL_ROUND_MS, KILL_ROUNDS, LIST_PROCS, running, QUIT_SIGNALS, listening, interruptIsTheCallers;
15793
+ var init_stop = __esm({
15794
+ "../cli/src/lib/agent/stop.ts"() {
15795
+ "use strict";
15796
+ init_env();
15797
+ init_paths();
15798
+ runMark = (sessionId) => ({ name: RUN_ENV, value: sessionId });
15799
+ stopMark = () => ({ name: STOP_ENV, value: randomUUID5() });
15800
+ markEnv = (env2, mark) => ({ ...env2, [mark.name]: mark.value });
15801
+ WINDOWS3 = process.platform === "win32";
15802
+ KILL_ROUND_MS = 300;
15803
+ KILL_ROUNDS = 3;
15804
+ LIST_PROCS = [
15805
+ "$ErrorActionPreference = 'Stop'",
15806
+ "Get-CimInstance Win32_Process | ForEach-Object { if ($_.CreationDate) { '{0} {1} {2}' -f $_.ProcessId, $_.ParentProcessId, ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() } }",
15807
+ "'boot {0}' -f ([DateTimeOffset](Get-CimInstance Win32_OperatingSystem).LastBootUpTime).ToUnixTimeMilliseconds()"
15808
+ ].join("; ");
15809
+ running = /* @__PURE__ */ new Map();
15810
+ QUIT_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
15811
+ listening = /* @__PURE__ */ new Map();
15812
+ interruptIsTheCallers = false;
15813
+ }
15814
+ });
15815
+
15549
15816
  // ../cli/src/lib/agent/pause.ts
15550
15817
  function deliveryState(delivery, questions) {
15551
15818
  const landing = delivery.landing;
@@ -15734,12 +16001,12 @@ var init_runner = __esm({
15734
16001
  });
15735
16002
 
15736
16003
  // ../cli/src/lib/agent/rules.ts
15737
- import fs39 from "node:fs";
15738
- import path43 from "node:path";
16004
+ import fs40 from "node:fs";
16005
+ import path44 from "node:path";
15739
16006
  function ruleFile(agent) {
15740
16007
  if (!agent || hiddenRole(agent)) return "";
15741
16008
  try {
15742
- return fs39.readFileSync(rulePath(agent), "utf8").trim();
16009
+ return fs40.readFileSync(rulePath(agent), "utf8").trim();
15743
16010
  } catch {
15744
16011
  return "";
15745
16012
  }
@@ -15754,11 +16021,11 @@ function setAgentRule(agent, text5) {
15754
16021
  const rule = String(text5 ?? "").trim();
15755
16022
  try {
15756
16023
  if (!rule) {
15757
- fs39.rmSync(rulePath(agent), { force: true });
16024
+ fs40.rmSync(rulePath(agent), { force: true });
15758
16025
  return { ok: true };
15759
16026
  }
15760
- fs39.mkdirSync(RULES, { recursive: true });
15761
- fs39.writeFileSync(rulePath(agent), `${rule}
16027
+ fs40.mkdirSync(RULES, { recursive: true });
16028
+ fs40.writeFileSync(rulePath(agent), `${rule}
15762
16029
  `);
15763
16030
  return { ok: true };
15764
16031
  } catch (err) {
@@ -15806,7 +16073,7 @@ function migrateFlowRules() {
15806
16073
  adoptRenamedRules();
15807
16074
  let here;
15808
16075
  try {
15809
- here = new Set(fs39.readdirSync(RULES));
16076
+ here = new Set(fs40.readdirSync(RULES));
15810
16077
  } catch {
15811
16078
  return [];
15812
16079
  }
@@ -15817,9 +16084,9 @@ function migrateFlowRules() {
15817
16084
  if (!from.length) continue;
15818
16085
  const parts = [ruleFile(owner.name), ...from.map(ruleFile)].filter(Boolean);
15819
16086
  try {
15820
- if (parts.length) fs39.writeFileSync(rulePath(owner.name), `${parts.join("\n\n")}
16087
+ if (parts.length) fs40.writeFileSync(rulePath(owner.name), `${parts.join("\n\n")}
15821
16088
  `);
15822
- for (const flow of from) fs39.rmSync(rulePath(flow), { force: true });
16089
+ for (const flow of from) fs40.rmSync(rulePath(flow), { force: true });
15823
16090
  } catch (err) {
15824
16091
  notes.push(`could not move the ${from.join(", ")} rule(s) onto \`${owner.name}\`: ${String(err)}`);
15825
16092
  continue;
@@ -15833,7 +16100,7 @@ function migrateFlowRules() {
15833
16100
  function adoptRenamedRules() {
15834
16101
  let here;
15835
16102
  try {
15836
- here = fs39.readdirSync(RULES);
16103
+ here = fs40.readdirSync(RULES);
15837
16104
  } catch {
15838
16105
  return;
15839
16106
  }
@@ -15843,13 +16110,13 @@ function adoptRenamedRules() {
15843
16110
  const now = RENAMED_ROLES[was] ?? canonicalSpecAgent(was);
15844
16111
  if (now === was) continue;
15845
16112
  try {
15846
- if (!fs39.existsSync(rulePath(now))) fs39.renameSync(rulePath(was), rulePath(now));
16113
+ if (!fs40.existsSync(rulePath(now))) fs40.renameSync(rulePath(was), rulePath(now));
15847
16114
  else if (FOLDED_SPEC_AGENTS.has(was)) {
15848
16115
  const kept2 = ruleFile(now);
15849
16116
  const carried = ruleFile(was);
15850
- if (carried && !kept2.includes(carried)) fs39.writeFileSync(rulePath(now), `${[kept2, carried].filter(Boolean).join("\n\n")}
16117
+ if (carried && !kept2.includes(carried)) fs40.writeFileSync(rulePath(now), `${[kept2, carried].filter(Boolean).join("\n\n")}
15851
16118
  `);
15852
- fs39.rmSync(rulePath(was), { force: true });
16119
+ fs40.rmSync(rulePath(was), { force: true });
15853
16120
  }
15854
16121
  } catch {
15855
16122
  }
@@ -15866,7 +16133,7 @@ var init_rules2 = __esm({
15866
16133
  init_runner();
15867
16134
  init_workflows();
15868
16135
  init_types3();
15869
- rulePath = (agent) => path43.join(RULES, `${agent}.md`);
16136
+ rulePath = (agent) => path44.join(RULES, `${agent}.md`);
15870
16137
  notOnTheRoster = (name) => `"${name}" is not an agent on this board. It has: ${agentNames().join(", ")}.`;
15871
16138
  flowForRequest = (req) => req.refineRound !== void 0 && REFINE_ACTIONS.has(req.action) ? flowByCommand("refine") : flowByAction(req.action);
15872
16139
  ruleOwnerSays = (owner) => `\`${owner.name}\` agent carries one rule of its own${owner.role ? ", on every flow it runs" : ""}`;
@@ -15951,8 +16218,8 @@ var init_source = __esm({
15951
16218
  });
15952
16219
 
15953
16220
  // ../cli/src/lib/card-sources.ts
15954
- import fs40 from "node:fs";
15955
- import path44 from "node:path";
16221
+ import fs41 from "node:fs";
16222
+ import path45 from "node:path";
15956
16223
  function planById(id) {
15957
16224
  const found2 = readPlan(`plans/${id}-.md`);
15958
16225
  return found2?.text.trim() ? found2 : null;
@@ -15962,8 +16229,8 @@ function cardLink(id) {
15962
16229
  const found2 = open3 ?? locateArchived(id);
15963
16230
  if (!found2) return null;
15964
16231
  try {
15965
- const file = found2.kind === "group" ? path44.join(found2.target, "root.md") : found2.target;
15966
- const title = parseFrontmatter(fs40.readFileSync(file, "utf8")).meta?.title ?? "";
16232
+ const file = found2.kind === "group" ? path45.join(found2.target, "root.md") : found2.target;
16233
+ const title = parseFrontmatter(fs41.readFileSync(file, "utf8")).meta?.title ?? "";
15967
16234
  return { kind: "card", ref: String(id), title, ...open3 ? {} : { archived: true } };
15968
16235
  } catch {
15969
16236
  return null;
@@ -15988,7 +16255,7 @@ function link(ref, items) {
15988
16255
  if (ref.kind === "plan") return planLink(ref.id);
15989
16256
  if (ref.kind === "card") return cardLink(ref.id);
15990
16257
  if (ref.kind === "url") return urlLink(ref.url);
15991
- const item = items().find((signal2) => path44.basename(signal2.relPath) === ref.file);
16258
+ const item = items().find((signal2) => path45.basename(signal2.relPath) === ref.file);
15992
16259
  return item ? itemLink(item) : null;
15993
16260
  }
15994
16261
  function cardSources(meta, body) {
@@ -16029,12 +16296,12 @@ var init_card_sources = __esm({
16029
16296
  });
16030
16297
 
16031
16298
  // ../cli/src/lib/view/read.ts
16032
- import fs41 from "node:fs";
16033
- import path45 from "node:path";
16299
+ import fs42 from "node:fs";
16300
+ import path46 from "node:path";
16034
16301
  function readCard(file, relFromTodo) {
16035
- const id = idPrefix(path45.basename(relFromTodo));
16302
+ const id = idPrefix(path46.basename(relFromTodo));
16036
16303
  if (id === null) {
16037
- const parts = relFromTodo.split(path45.sep);
16304
+ const parts = relFromTodo.split(path46.sep);
16038
16305
  const folderId = idPrefix(parts[parts.length - 2] || "");
16039
16306
  if (folderId === null) return null;
16040
16307
  return buildCard(folderId, file, relFromTodo);
@@ -16042,10 +16309,10 @@ function readCard(file, relFromTodo) {
16042
16309
  return buildCard(id, file, relFromTodo);
16043
16310
  }
16044
16311
  function buildCard(id, file, relFromTodo) {
16045
- const text5 = fs41.readFileSync(file, "utf8");
16312
+ const text5 = fs42.readFileSync(file, "utf8");
16046
16313
  const { meta, body } = parseFrontmatter(text5);
16047
16314
  if (!meta) return null;
16048
- const relPath = relFromTodo.split(path45.sep).join("/");
16315
+ const relPath = relFromTodo.split(path46.sep).join("/");
16049
16316
  const flow = workflowFor(meta.workflow);
16050
16317
  const plan = !!meta.workflow && archivesOnApproval(flow);
16051
16318
  const stages = cardStages(meta, flow).length;
@@ -16054,7 +16321,7 @@ function buildCard(id, file, relFromTodo) {
16054
16321
  // Derived from the file exactly as it is on disk (lib/board/revision.ts), so a card
16055
16322
  // read here and a card read by the contract agree on what version this is.
16056
16323
  revision: revisionOf(text5),
16057
- updatedAt: fs41.statSync(file).mtime.toISOString(),
16324
+ updatedAt: fs42.statSync(file).mtime.toISOString(),
16058
16325
  relPath,
16059
16326
  title: meta.title,
16060
16327
  priority: meta.priority,
@@ -16075,22 +16342,22 @@ function buildCard(id, file, relFromTodo) {
16075
16342
  };
16076
16343
  }
16077
16344
  function readGroup2(folderName) {
16078
- const groupDir = path45.join(TODO, folderName);
16079
- const rootFile = path45.join(groupDir, "root.md");
16080
- if (!fs41.existsSync(rootFile)) return null;
16081
- const root = readCard(rootFile, path45.join(folderName, "root.md"));
16345
+ const groupDir = path46.join(TODO, folderName);
16346
+ const rootFile = path46.join(groupDir, "root.md");
16347
+ if (!fs42.existsSync(rootFile)) return null;
16348
+ const root = readCard(rootFile, path46.join(folderName, "root.md"));
16082
16349
  if (!root) return null;
16083
16350
  root.isGroup = true;
16084
16351
  const { total, resolved: resolved2 } = subtaskLines(root.body);
16085
16352
  root.subtaskLines = { total, resolved: resolved2 };
16086
16353
  const subCards = [];
16087
16354
  const recurse = (dir, relDir) => {
16088
- for (const entry of fs41.readdirSync(dir, { withFileTypes: true })) {
16089
- const childRel = path45.join(relDir, entry.name);
16355
+ for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
16356
+ const childRel = path46.join(relDir, entry.name);
16090
16357
  if (entry.isDirectory()) {
16091
- recurse(path45.join(dir, entry.name), childRel);
16358
+ recurse(path46.join(dir, entry.name), childRel);
16092
16359
  } else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "root.md") {
16093
- const c = readCard(path45.join(dir, entry.name), childRel);
16360
+ const c = readCard(path46.join(dir, entry.name), childRel);
16094
16361
  if (c) {
16095
16362
  c.parent = { id: root.id, title: root.title };
16096
16363
  subCards.push(c);
@@ -16111,12 +16378,12 @@ function readGroup2(folderName) {
16111
16378
  return { root, subCards };
16112
16379
  }
16113
16380
  function standaloneCards(dirRel) {
16114
- const dir = dirRel ? path45.join(TODO, dirRel) : TODO;
16115
- if (!fs41.existsSync(dir)) return [];
16381
+ const dir = dirRel ? path46.join(TODO, dirRel) : TODO;
16382
+ if (!fs42.existsSync(dir)) return [];
16116
16383
  const cards = [];
16117
- for (const entry of fs41.readdirSync(dir, { withFileTypes: true })) {
16384
+ for (const entry of fs42.readdirSync(dir, { withFileTypes: true })) {
16118
16385
  if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
16119
- const c = readCard(path45.join(dir, entry.name), path45.join(dirRel, entry.name));
16386
+ const c = readCard(path46.join(dir, entry.name), path46.join(dirRel, entry.name));
16120
16387
  if (c) cards.push(c);
16121
16388
  }
16122
16389
  }
@@ -16128,7 +16395,7 @@ function collectCards() {
16128
16395
  const top = standaloneCards("");
16129
16396
  board2.push(...top);
16130
16397
  every.push(...top);
16131
- for (const entry of fs41.readdirSync(TODO, { withFileTypes: true })) {
16398
+ for (const entry of fs42.readdirSync(TODO, { withFileTypes: true })) {
16132
16399
  if (!entry.isDirectory()) continue;
16133
16400
  if (isGroupFolder(entry.name)) {
16134
16401
  const g = readGroup2(entry.name);
@@ -16160,7 +16427,7 @@ function findCard(id) {
16160
16427
  }
16161
16428
  function attachSources(card) {
16162
16429
  try {
16163
- const { meta, body } = parseFrontmatter(fs41.readFileSync(path45.join(TODO, card.relPath), "utf8"));
16430
+ const { meta, body } = parseFrontmatter(fs42.readFileSync(path46.join(TODO, card.relPath), "utf8"));
16164
16431
  const sources = meta ? cardSources(meta, body) : [];
16165
16432
  if (sources.length) card.sources = sources;
16166
16433
  } catch {
@@ -16348,8 +16615,8 @@ var init_review = __esm({
16348
16615
  });
16349
16616
 
16350
16617
  // ../cli/src/lib/agent/deliveries.ts
16351
- import fs42 from "node:fs";
16352
- import path46 from "node:path";
16618
+ import fs43 from "node:fs";
16619
+ import path47 from "node:path";
16353
16620
  function writeAudit(delivery, runs) {
16354
16621
  const known2 = new Map(readAudit(delivery.deliveryId)?.sessions.map((s) => [s.sessionId, s]) ?? []);
16355
16622
  const sessions = delivery.sessions.flatMap((id) => {
@@ -16377,10 +16644,10 @@ function writeAudit(delivery, runs) {
16377
16644
  }
16378
16645
  function writeAuditFile(record) {
16379
16646
  try {
16380
- fs42.mkdirSync(DELIVERIES, { recursive: true });
16647
+ fs43.mkdirSync(DELIVERIES, { recursive: true });
16381
16648
  const tmp = `${auditPath(record.deliveryId)}.tmp`;
16382
- fs42.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n");
16383
- fs42.renameSync(tmp, auditPath(record.deliveryId));
16649
+ fs43.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n");
16650
+ fs43.renameSync(tmp, auditPath(record.deliveryId));
16384
16651
  return true;
16385
16652
  } catch {
16386
16653
  return false;
@@ -16388,7 +16655,7 @@ function writeAuditFile(record) {
16388
16655
  }
16389
16656
  function readAudit(deliveryId) {
16390
16657
  try {
16391
- const data = JSON.parse(fs42.readFileSync(auditPath(deliveryId), "utf8"));
16658
+ const data = JSON.parse(fs43.readFileSync(auditPath(deliveryId), "utf8"));
16392
16659
  return { sessions: Array.isArray(data?.sessions) ? data.sessions : [] };
16393
16660
  } catch {
16394
16661
  return null;
@@ -16403,7 +16670,7 @@ function syncAudit(deliveryId, just) {
16403
16670
  }
16404
16671
  function wholeAudit(file) {
16405
16672
  try {
16406
- const data = JSON.parse(fs42.readFileSync(file, "utf8"));
16673
+ const data = JSON.parse(fs43.readFileSync(file, "utf8"));
16407
16674
  return typeof data?.deliveryId === "string" && Array.isArray(data.sessions) ? data : null;
16408
16675
  } catch {
16409
16676
  return null;
@@ -16416,14 +16683,14 @@ function activeAudit(file) {
16416
16683
  function allAudits() {
16417
16684
  let names4;
16418
16685
  try {
16419
- names4 = fs42.readdirSync(DELIVERIES);
16686
+ names4 = fs43.readdirSync(DELIVERIES);
16420
16687
  } catch {
16421
16688
  return [];
16422
16689
  }
16423
16690
  const out = [];
16424
16691
  for (const name of names4) {
16425
16692
  if (!name.endsWith(".json")) continue;
16426
- const audit = wholeAudit(path46.join(DELIVERIES, name));
16693
+ const audit = wholeAudit(path47.join(DELIVERIES, name));
16427
16694
  if (audit) out.push(audit);
16428
16695
  }
16429
16696
  return out;
@@ -16534,11 +16801,11 @@ function resumeRefusal(delivery, store) {
16534
16801
  );
16535
16802
  }
16536
16803
  if (!worktreeExists(delivery.worktree)) {
16537
- const path85 = delivery.worktree ?? "";
16804
+ const path87 = delivery.worktree ?? "";
16538
16805
  return refusal(
16539
16806
  "deliveryWorktreeGone",
16540
- `delivery ${id}'s worktree ${path85} is gone, so there is nothing to carry on. ${rebuild}`,
16541
- { id, task, path: path85, command: discard }
16807
+ `delivery ${id}'s worktree ${path87} is gone, so there is nothing to carry on. ${rebuild}`,
16808
+ { id, task, path: path87, command: discard }
16542
16809
  );
16543
16810
  }
16544
16811
  if (!branchExists(delivery.branch)) {
@@ -16591,11 +16858,12 @@ function tidyCheckout(deliveryId) {
16591
16858
  const store = readStore();
16592
16859
  const row = store.deliveries.find((d) => d.deliveryId === deliveryId) ?? auditOnly(deliveryId);
16593
16860
  if (!row || row.status === "active") return NOTHING;
16594
- if (!row.worktree && !row.branch) return NOTHING;
16595
- if (isResumable(row)) return { removed: false, kept: true };
16596
16861
  if (store.runs.some((r) => r.deliveryId === deliveryId && runIsLive(r))) {
16597
16862
  return { removed: false, kept: false, error: `a run of delivery ${deliveryId} is still going`, reason: "deliveryRunGoing", args: { id: deliveryId } };
16598
16863
  }
16864
+ if (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) stopWatchers(row.sessions);
16865
+ if (!row.worktree && !row.branch) return NOTHING;
16866
+ if (isResumable(row)) return { removed: false, kept: true };
16599
16867
  const removed = removeWorktree(row.worktree, row.branch, true);
16600
16868
  if (!removed.ok) return { removed: false, kept: false, error: removed.error };
16601
16869
  forgetCheckout(deliveryId);
@@ -16618,7 +16886,10 @@ function forgetCheckout(deliveryId) {
16618
16886
  function sweepCheckouts() {
16619
16887
  const seen = /* @__PURE__ */ new Set();
16620
16888
  const complaints = [];
16621
- const rows = [...readStore().deliveries, ...allAudits().flatMap((a) => auditRow(a) ?? [])];
16889
+ const store = readStore();
16890
+ const rows = [...store.deliveries, ...allAudits().flatMap((a) => auditRow(a) ?? [])];
16891
+ const ended = rows.filter((row) => row.status !== "active" && (row.status !== "finished" || Date.now() - (row.endedAt ?? 0) > 3e4) && !store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r)));
16892
+ stopWatchers([...new Set(ended.flatMap((row) => row.sessions))]);
16622
16893
  for (const row of rows) {
16623
16894
  if (seen.has(row.deliveryId) || row.status === "active" || !row.worktree) continue;
16624
16895
  seen.add(row.deliveryId);
@@ -16671,10 +16942,10 @@ function carryOnFrom(deliveryId) {
16671
16942
  function approvedRequirements(cardId2) {
16672
16943
  const found2 = locate(cardId2);
16673
16944
  if (!found2) return "";
16674
- const file = found2.kind === "group" ? path46.join(found2.target, "root.md") : found2.target;
16945
+ const file = found2.kind === "group" ? path47.join(found2.target, "root.md") : found2.target;
16675
16946
  let text5;
16676
16947
  try {
16677
- text5 = fs42.readFileSync(file, "utf8");
16948
+ text5 = fs43.readFileSync(file, "utf8");
16678
16949
  } catch {
16679
16950
  return "";
16680
16951
  }
@@ -16704,8 +16975,8 @@ function cardMeta2(cardId2) {
16704
16975
  try {
16705
16976
  const found2 = locate(cardId2);
16706
16977
  if (!found2) return null;
16707
- const file = found2.kind === "group" ? path46.join(found2.target, "root.md") : found2.target;
16708
- return parseFrontmatter(fs42.readFileSync(file, "utf8")).meta;
16978
+ const file = found2.kind === "group" ? path47.join(found2.target, "root.md") : found2.target;
16979
+ return parseFrontmatter(fs43.readFileSync(file, "utf8")).meta;
16709
16980
  } catch {
16710
16981
  return null;
16711
16982
  }
@@ -16954,6 +17225,7 @@ var init_deliveries = __esm({
16954
17225
  init_complete();
16955
17226
  init_outputs();
16956
17227
  init_env();
17228
+ init_stop();
16957
17229
  init_flows();
16958
17230
  init_pause();
16959
17231
  init_rules2();
@@ -16963,7 +17235,7 @@ var init_deliveries = __esm({
16963
17235
  init_store();
16964
17236
  init_types3();
16965
17237
  init_worktree();
16966
- auditPath = (deliveryId) => path46.join(DELIVERIES, `${deliveryId}.json`);
17238
+ auditPath = (deliveryId) => path47.join(DELIVERIES, `${deliveryId}.json`);
16967
17239
  activeAudits = () => allAudits().filter((a) => a.status === "active");
16968
17240
  auditRow = (audit) => readDeliveryRow({ ...audit, sessions: audit.sessions.map((s) => s.sessionId) });
16969
17241
  lastSessionRunning = (audit) => audit.sessions[audit.sessions.length - 1]?.status === "running";
@@ -16991,9 +17263,44 @@ var init_deliveries = __esm({
16991
17263
  }
16992
17264
  });
16993
17265
 
16994
- // ../cli/src/lib/agent/answers.ts
16995
- function answerOutcome(delivery) {
16996
- return pendingAnswers(delivery).some((a) => a.outcome === "changed") ? "changed" : "unchanged";
17266
+ // ../cli/src/lib/agent/landing-lock.ts
17267
+ import fs44 from "node:fs";
17268
+ import path48 from "node:path";
17269
+ function lockLanding() {
17270
+ const dir = path48.join(SESSIONS_DIR, ".landing.lock");
17271
+ try {
17272
+ const entries = fs44.readdirSync(dir);
17273
+ const token3 = entries.length === 1 ? entries[0] : "";
17274
+ const match = /^(\d+)-[a-f0-9-]{36}$/.exec(token3);
17275
+ const stat = fs44.statSync(dir);
17276
+ if (match && Date.now() - stat.mtimeMs > landingLimits.staleMs) {
17277
+ stopWatchers(void 0, Number(match[1]), () => {
17278
+ const now = fs44.readdirSync(dir);
17279
+ return now.length === 1 && now[0] === token3 && Date.now() - fs44.statSync(dir).mtimeMs > landingLimits.staleMs;
17280
+ }, stat.birthtimeMs || stat.mtimeMs);
17281
+ }
17282
+ } catch {
17283
+ }
17284
+ return tryLock(dir);
17285
+ }
17286
+ function renewLandingLock() {
17287
+ const now = /* @__PURE__ */ new Date();
17288
+ fs44.utimesSync(path48.join(SESSIONS_DIR, ".landing.lock"), now, now);
17289
+ }
17290
+ var landingLimits;
17291
+ var init_landing_lock = __esm({
17292
+ "../cli/src/lib/agent/landing-lock.ts"() {
17293
+ "use strict";
17294
+ init_lock();
17295
+ init_paths();
17296
+ init_stop();
17297
+ landingLimits = { staleMs: 5 * 6e4 };
17298
+ }
17299
+ });
17300
+
17301
+ // ../cli/src/lib/agent/answers.ts
17302
+ function answerOutcome(delivery) {
17303
+ return pendingAnswers(delivery).some((a) => a.outcome === "changed") ? "changed" : "unchanged";
16997
17304
  }
16998
17305
  function takeUnchanged(delivery) {
16999
17306
  if (!pendingAnswers(delivery).some((a) => a.outcome === "unchanged")) return;
@@ -17055,8 +17362,8 @@ var init_retry = __esm({
17055
17362
  });
17056
17363
 
17057
17364
  // ../cli/src/lib/agent/landing.ts
17058
- import fs43 from "node:fs";
17059
- import path47 from "node:path";
17365
+ import fs45 from "node:fs";
17366
+ import path49 from "node:path";
17060
17367
  function waitingOut(delivery, now = Date.now()) {
17061
17368
  const landing = delivery.landing;
17062
17369
  if (!landing) return false;
@@ -17192,8 +17499,9 @@ function noteQueue(held4) {
17192
17499
  }
17193
17500
  async function advanceLanding() {
17194
17501
  let release;
17502
+ let current;
17195
17503
  try {
17196
- release = tryLock(path47.join(SESSIONS_DIR, ".landing.lock"));
17504
+ release = lockLanding();
17197
17505
  if (!release) return null;
17198
17506
  const owed = await owedRestart();
17199
17507
  if (owed) return owed;
@@ -17208,11 +17516,19 @@ async function advanceLanding() {
17208
17516
  return null;
17209
17517
  }
17210
17518
  tried.add(picked.deliveryId);
17519
+ current = picked;
17520
+ renewLandingLock();
17211
17521
  const step = await landStep(picked);
17212
17522
  if (step.start) return step.start;
17213
17523
  if (!step.done) return null;
17214
17524
  }
17215
- } catch {
17525
+ } catch (error) {
17526
+ if (current && error instanceof GitTimeoutError) {
17527
+ giveUpSlot(current, error.message);
17528
+ patchLanding(current.deliveryId, (landing) => {
17529
+ landing.retryAt = Date.now() + 3e4;
17530
+ });
17531
+ }
17216
17532
  return null;
17217
17533
  } finally {
17218
17534
  release?.();
@@ -17529,7 +17845,7 @@ async function settleAlreadyLanded(delivery) {
17529
17845
  return true;
17530
17846
  }
17531
17847
  function repairLanding() {
17532
- const release = tryLock(path47.join(SESSIONS_DIR, ".landing.lock"));
17848
+ const release = lockLanding();
17533
17849
  if (!release) return [];
17534
17850
  try {
17535
17851
  return repairIdleLanding();
@@ -17562,7 +17878,7 @@ var init_landing = __esm({
17562
17878
  init_execution();
17563
17879
  init_board();
17564
17880
  init_io();
17565
- init_lock();
17881
+ init_landing_lock();
17566
17882
  init_paths();
17567
17883
  init_answers();
17568
17884
  init_commit_mode();
@@ -17603,9 +17919,9 @@ var init_landing = __esm({
17603
17919
  sameDir = (a, b) => {
17604
17920
  const real2 = (dir) => {
17605
17921
  try {
17606
- return fs43.realpathSync(dir);
17922
+ return fs45.realpathSync(dir);
17607
17923
  } catch {
17608
- return path47.resolve(dir);
17924
+ return path49.resolve(dir);
17609
17925
  }
17610
17926
  };
17611
17927
  return real2(a) === real2(b);
@@ -17614,35 +17930,35 @@ var init_landing = __esm({
17614
17930
  });
17615
17931
 
17616
17932
  // ../cli/src/lib/agent/pictures.ts
17617
- import fs44 from "node:fs";
17618
- import path48 from "node:path";
17933
+ import fs46 from "node:fs";
17934
+ import path50 from "node:path";
17619
17935
  function claimRunPictures(box2, sessionId, names4 = []) {
17620
17936
  if (!box2 || !freeBox(box2) || names4.length === 0) return [];
17621
17937
  const from = pictureBox(box2);
17622
17938
  const to = pictureBox(sessionId);
17623
17939
  try {
17624
- fs44.mkdirSync(SESSIONS_DIR, { recursive: true });
17625
- fs44.renameSync(from, to);
17940
+ fs46.mkdirSync(SESSIONS_DIR, { recursive: true });
17941
+ fs46.renameSync(from, to);
17626
17942
  } catch {
17627
17943
  return [];
17628
17944
  }
17629
- return names4.filter(pictureName).map((name) => path48.join(to, name)).filter((file) => fs44.existsSync(file));
17945
+ return names4.filter(pictureName).map((name) => path50.join(to, name)).filter((file) => fs46.existsSync(file));
17630
17946
  }
17631
17947
  function returnRunPictures(sessionId, box2) {
17632
17948
  if (!box2 || !boxId(box2)) return;
17633
17949
  try {
17634
- fs44.renameSync(pictureBox(sessionId), pictureBox(box2));
17950
+ fs46.renameSync(pictureBox(sessionId), pictureBox(box2));
17635
17951
  } catch {
17636
17952
  }
17637
17953
  }
17638
17954
  function moveRunPictures(from, to, files2 = []) {
17639
17955
  if (files2.length === 0) return [];
17640
17956
  try {
17641
- fs44.renameSync(pictureBox(from), pictureBox(to));
17957
+ fs46.renameSync(pictureBox(from), pictureBox(to));
17642
17958
  } catch {
17643
17959
  return files2;
17644
17960
  }
17645
- return files2.map((file) => path48.join(pictureBox(to), path48.basename(file)));
17961
+ return files2.map((file) => path50.join(pictureBox(to), path50.basename(file)));
17646
17962
  }
17647
17963
  var pictureName, boxId, freeBox, pictureBox;
17648
17964
  var init_pictures = __esm({
@@ -17651,14 +17967,14 @@ var init_pictures = __esm({
17651
17967
  init_paths();
17652
17968
  pictureName = (name) => /^[0-9a-f-]{36}\.[a-z0-9]{2,5}$/.test(name);
17653
17969
  boxId = (box2) => /^[0-9a-f-]{36}$/.test(box2);
17654
- freeBox = (box2) => boxId(box2) && !fs44.existsSync(path48.join(SESSIONS_DIR, `${box2}.log`));
17655
- pictureBox = (box2) => path48.join(SESSIONS_DIR, `${box2}.images`);
17970
+ freeBox = (box2) => boxId(box2) && !fs46.existsSync(path50.join(SESSIONS_DIR, `${box2}.log`));
17971
+ pictureBox = (box2) => path50.join(SESSIONS_DIR, `${box2}.images`);
17656
17972
  }
17657
17973
  });
17658
17974
 
17659
17975
  // ../cli/src/lib/memories.ts
17660
- import fs45 from "node:fs";
17661
- import path49 from "node:path";
17976
+ import fs47 from "node:fs";
17977
+ import path51 from "node:path";
17662
17978
  function readMemory(text5, builtIn, folder) {
17663
17979
  const { meta, body } = splitFrontmatter(text5);
17664
17980
  if (meta === null) return null;
@@ -17678,7 +17994,7 @@ function readMemory(text5, builtIn, folder) {
17678
17994
  rules: body.trim(),
17679
17995
  text: text5,
17680
17996
  builtIn,
17681
- dir: path49.join(MEMORY, folder)
17997
+ dir: path51.join(MEMORY, folder)
17682
17998
  };
17683
17999
  }
17684
18000
  function globalMemories() {
@@ -17689,14 +18005,14 @@ function globalMemories() {
17689
18005
  }
17690
18006
  let folders2 = [];
17691
18007
  try {
17692
- folders2 = fs45.readdirSync(MEMORY, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
18008
+ folders2 = fs47.readdirSync(MEMORY, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
17693
18009
  } catch {
17694
18010
  }
17695
18011
  for (const name of folders2) {
17696
18012
  if (name in BUNDLED_MEMORY_FILES || name === RESERVED_MEMORY_DIR) continue;
17697
- const file = path49.join(MEMORY, name, GLOBAL_MEMORY_FILE);
17698
- if (!fs45.existsSync(file)) continue;
17699
- const memory = readMemory(fs45.readFileSync(file, "utf8"), false, name);
18013
+ const file = path51.join(MEMORY, name, GLOBAL_MEMORY_FILE);
18014
+ if (!fs47.existsSync(file)) continue;
18015
+ const memory = readMemory(fs47.readFileSync(file, "utf8"), false, name);
17700
18016
  if (memory) out.push(memory);
17701
18017
  }
17702
18018
  return out;
@@ -17752,16 +18068,16 @@ var init_facts = __esm({
17752
18068
  });
17753
18069
 
17754
18070
  // ../cli/src/lib/view/archive.ts
17755
- import fs46 from "node:fs";
17756
- import path50 from "node:path";
18071
+ import fs48 from "node:fs";
18072
+ import path52 from "node:path";
17757
18073
  function archivedId(file) {
17758
- const own = idPrefix(path50.basename(file));
17759
- return own !== null ? own : idPrefix(path50.basename(path50.dirname(file)));
18074
+ const own = idPrefix(path52.basename(file));
18075
+ return own !== null ? own : idPrefix(path52.basename(path52.dirname(file)));
17760
18076
  }
17761
18077
  function readFile(file) {
17762
18078
  const id = archivedId(file);
17763
18079
  if (id === null) return null;
17764
- const { meta, body } = parseFrontmatter(fs46.readFileSync(file, "utf8"));
18080
+ const { meta, body } = parseFrontmatter(fs48.readFileSync(file, "utf8"));
17765
18081
  if (!meta) return null;
17766
18082
  return {
17767
18083
  row: {
@@ -17769,15 +18085,15 @@ function readFile(file) {
17769
18085
  title: meta.title,
17770
18086
  archived: meta.archived,
17771
18087
  rejected: meta.rejected,
17772
- relPath: rel(file).split(path50.sep).join("/")
18088
+ relPath: rel(file).split(path52.sep).join("/")
17773
18089
  },
17774
18090
  body: body.replace(/^\n+/, "").replace(/\s+$/, ""),
17775
18091
  meta
17776
18092
  };
17777
18093
  }
17778
18094
  function readArchive() {
17779
- const relPath = rel(ARCHIVE).split(path50.sep).join("/");
17780
- if (!fs46.existsSync(ARCHIVE)) return { relPath, cards: [] };
18095
+ const relPath = rel(ARCHIVE).split(path52.sep).join("/");
18096
+ if (!fs48.existsSync(ARCHIVE)) return { relPath, cards: [] };
17781
18097
  const cards = walkMd(ARCHIVE).map(readFile).filter((found2) => found2 !== null).map((found2) => found2.row);
17782
18098
  cards.sort((a, b) => b.id - a.id);
17783
18099
  return { relPath, cards };
@@ -17792,7 +18108,7 @@ var init_archive = __esm({
17792
18108
  });
17793
18109
 
17794
18110
  // ../cli/src/lib/agent/prompts.ts
17795
- import path51 from "node:path";
18111
+ import path53 from "node:path";
17796
18112
  function resumePrompt(deliveryId, cardId2, action) {
17797
18113
  if (!deliveryId) return RESUME_PROMPT;
17798
18114
  if (action === "conflict") {
@@ -17836,7 +18152,7 @@ function buildAsk(rawReq, notes = []) {
17836
18152
  }
17837
18153
  function cardFileOf(id) {
17838
18154
  const found2 = id === void 0 ? null : locate(id);
17839
- return found2 ? found2.kind === "group" ? path51.join(found2.target, "root.md") : found2.target : "";
18155
+ return found2 ? found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target : "";
17840
18156
  }
17841
18157
  function realPaths(text5, req) {
17842
18158
  if (req.action !== "scheduled" && req.action !== "sub") return text5;
@@ -17929,7 +18245,7 @@ function frozenRules(req) {
17929
18245
  function archivedCardFile(id) {
17930
18246
  const found2 = locateArchived(id);
17931
18247
  if (!found2) return `${rel(ARCHIVE)}/`;
17932
- return rel(found2.kind === "group" ? path51.join(found2.target, "root.md") : found2.target);
18248
+ return rel(found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target);
17933
18249
  }
17934
18250
  function shippedLines(cardId2) {
17935
18251
  const delivery = endedDelivery(`${cardId2}`);
@@ -18100,7 +18416,7 @@ function actionPrompt(req, command, notes) {
18100
18416
  case "spec": {
18101
18417
  const agent = findSpecAgent(req.specAgent ?? "");
18102
18418
  const found2 = req.id === void 0 ? null : locate(req.id);
18103
- const cardFile3 = found2 ? rel(found2.kind === "group" ? path51.join(found2.target, "root.md") : found2.target) : `task #${req.id}`;
18419
+ const cardFile3 = found2 ? rel(found2.kind === "group" ? path53.join(found2.target, "root.md") : found2.target) : `task #${req.id}`;
18104
18420
  const own = agent ? specAgentInstructions(agent) : null;
18105
18421
  if (own) notes.push(...own.notes);
18106
18422
  const memory = agent ? agentMemoryBlock(agent) : "";
@@ -18213,288 +18529,81 @@ ${shared}` : ""
18213
18529
  req.notes ? `Extra notes: ${req.notes}` : "",
18214
18530
  `Don't ask me questions with human-in-the-loop. Leave any questions as open questions.`
18215
18531
  ].filter(Boolean).join(" ");
18216
- case "resolve":
18217
- return [
18218
- `${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
18219
- carriedNote(req),
18220
- answeredNote(req.id, command),
18221
- req.notes ? `Extra notes: ${req.notes}` : ""
18222
- ].filter(Boolean).join(" ");
18223
- }
18224
- }
18225
- function discardedCardsPrompt(cards = []) {
18226
- if (!cards.length) return "";
18227
- return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
18228
- }
18229
- var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
18230
- var init_prompts = __esm({
18231
- "../cli/src/lib/agent/prompts.ts"() {
18232
- "use strict";
18233
- init_cards();
18234
- init_memory();
18235
- init_memories();
18236
- init_paths();
18237
- init_agents();
18238
- init_command();
18239
- init_deliveries();
18240
- init_env();
18241
- init_facts();
18242
- init_outputs();
18243
- init_archive();
18244
- init_flows();
18245
- init_resolve();
18246
- init_runner();
18247
- init_workflows();
18248
- init_catalog2();
18249
- init_roles();
18250
- init_rules2();
18251
- init_settings2();
18252
- init_types3();
18253
- RESUME_PROMPT = [
18254
- `Continue. The previous run ended before it finished the task.`,
18255
- `Pick up where you left off and carry it through.`
18256
- ].join(" ");
18257
- DELIVERY_RESUME = [
18258
- `Continue delivery %s. The previous run ended before the delivery finished.`,
18259
- `Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
18260
- ].join(" ");
18261
- DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
18262
- RESTARTABLE = /* @__PURE__ */ new Set([
18263
- "implement",
18264
- "clarify",
18265
- "resolve",
18266
- "approve",
18267
- "archive",
18268
- "spec",
18269
- "conflict",
18270
- "scheduled"
18271
- ]);
18272
- RESTART_LEAD = [
18273
- `The session you were continuing is gone, and this one holds nothing of it.`,
18274
- `Do the task below from the top, checking each step's precondition before you do it \u2014 work an earlier run already finished is done, so don't repeat it.`
18275
- ].join(" ");
18276
- restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
18277
- NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
18278
- BOARD_FOLDER = "<board folder>";
18279
- CARD_FILE = "<card file>";
18280
- PROJECT_FOLDER = "<project folder>";
18281
- PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
18282
- SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
18283
- reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
18284
- }
18285
- });
18286
-
18287
- // ../cli/src/lib/agent/stop.ts
18288
- import { spawnSync as spawnSync4 } from "node:child_process";
18289
- import { randomUUID as randomUUID5 } from "node:crypto";
18290
- import fs47 from "node:fs";
18291
- import os6 from "node:os";
18292
- function markedInProc(entry, strict = false) {
18293
- const pids = [];
18294
- for (const name of fs47.readdirSync("/proc")) {
18295
- if (!/^\d+$/.test(name)) continue;
18296
- try {
18297
- if (fs47.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
18298
- } catch (error) {
18299
- if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
18300
- const stat = fs47.statSync(`/proc/${name}`, { throwIfNoEntry: false });
18301
- if (stat?.uid === process.getuid?.()) throw error;
18302
- }
18303
- }
18304
- }
18305
- return pids;
18306
- }
18307
- function markedInPs(entry, roots) {
18308
- const ps = spawnSync4("ps", ["-A", "-E", "-ww", "-o", "pid=,ppid=,command="], {
18309
- encoding: "utf8",
18310
- maxBuffer: 64 * 1024 * 1024,
18311
- timeout: 5e3
18312
- });
18313
- if (ps.status !== 0 || !ps.stdout) throw new Error("\u65E0\u6CD5\u67E5\u8BE2\u8FDB\u7A0B\u6811\u3002");
18314
- const children = /* @__PURE__ */ new Map();
18315
- const listed2 = /* @__PURE__ */ new Set();
18316
- const found2 = /* @__PURE__ */ new Set();
18317
- for (const line of ps.stdout.split("\n")) {
18318
- const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
18319
- if (!m) continue;
18320
- const pid = Number(m[1]);
18321
- const ppid = Number(m[2]);
18322
- listed2.add(pid);
18323
- children.set(ppid, [...children.get(ppid) ?? [], pid]);
18324
- if (`${m[3]} `.includes(` ${entry} `)) found2.add(pid);
18325
- }
18326
- const queue = [...found2, ...roots.filter((pid) => listed2.has(pid))];
18327
- for (const pid of queue) {
18328
- found2.add(pid);
18329
- for (const child of children.get(pid) ?? []) if (!found2.has(child)) queue.push(child);
18330
- }
18331
- return [...found2];
18332
- }
18333
- function marked(mark, roots = []) {
18334
- if (WINDOWS3 || !mark.value) return [];
18335
- const entry = `${mark.name}=${mark.value}`;
18336
- try {
18337
- const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
18338
- return pids.filter((pid) => pid !== process.pid);
18339
- } catch {
18340
- return [];
18341
- }
18342
- }
18343
- function signal(pids, sig) {
18344
- for (const pid of pids) {
18345
- try {
18346
- process.kill(pid, sig);
18347
- } catch {
18348
- }
18349
- }
18350
- }
18351
- function startedUnder(list, root, since) {
18352
- const self = list.procs.find((p) => p.pid === root);
18353
- const from = since ?? self?.startedAt;
18354
- if (from === void 0 || list.bootedAt > from) return [];
18355
- if (self && self.startedAt > from) return [];
18356
- const found2 = self ? [root] : [];
18357
- const seen = /* @__PURE__ */ new Set([root]);
18358
- const queue = [{ pid: root, startedAt: from }];
18359
- for (const parent of queue) {
18360
- for (const p of list.procs) {
18361
- if (p.ppid !== parent.pid || seen.has(p.pid) || p.startedAt < parent.startedAt) continue;
18362
- seen.add(p.pid);
18363
- found2.push(p.pid);
18364
- queue.push(p);
18365
- }
18366
- }
18367
- return found2.reverse();
18368
- }
18369
- function readProcList(out) {
18370
- const procs = [];
18371
- let bootedAt;
18372
- for (const line of out.split(/\r?\n/)) {
18373
- const boot = /^boot (\d+)$/.exec(line.trim());
18374
- if (boot) bootedAt = Number(boot[1]);
18375
- const m = /^(\d+) (\d+) (\d+)$/.exec(line.trim());
18376
- if (m) procs.push({ pid: Number(m[1]), ppid: Number(m[2]), startedAt: Number(m[3]) });
18377
- }
18378
- return bootedAt !== void 0 && procs.length ? { procs, bootedAt } : void 0;
18379
- }
18380
- function procsOnWindows() {
18381
- const ps = spawnSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", LIST_PROCS], {
18382
- encoding: "utf8",
18383
- maxBuffer: 64 * 1024 * 1024,
18384
- timeout: 5e3,
18385
- windowsHide: true
18386
- });
18387
- return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
18388
- }
18389
- function killUnderOnWindows(pid, since) {
18390
- if (!WINDOWS3 || !pid) return;
18391
- const list = procsOnWindows();
18392
- if (!list) return;
18393
- const pids = startedUnder(list, pid, since).filter((p) => p !== process.pid);
18394
- if (!pids.length) return;
18395
- spawnSync4("taskkill", ["/F", ...pids.flatMap((p) => ["/PID", String(p)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
18396
- }
18397
- function killMarked(mark, agent) {
18398
- if (WINDOWS3) {
18399
- if (agent) killUnderOnWindows(agent.pid, agent.since);
18400
- return;
18401
- }
18402
- signal(marked(mark), "SIGKILL");
18403
- }
18404
- function killTreeOnWindows(pid) {
18405
- if (!WINDOWS3 || !pid) return false;
18406
- const out = spawnSync4("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
18407
- return out.status === 0;
18408
- }
18409
- function later(ms, fn) {
18410
- const t = setTimeout(fn, ms);
18411
- if (typeof t.unref === "function") t.unref();
18412
- }
18413
- function endAgent(child, mark, graceMs) {
18414
- const term = (sig) => {
18415
- try {
18416
- child.kill(sig);
18417
- } catch {
18418
- }
18419
- };
18420
- if (killTreeOnWindows(child.pid)) return;
18421
- killUnderOnWindows(child.pid);
18422
- const seen = new Set(marked(mark, child.pid ? [child.pid] : []));
18423
- term("SIGTERM");
18424
- signal([...seen].filter((pid) => pid !== child.pid), "SIGTERM");
18425
- const round = (left) => {
18426
- const pids = marked(mark, [...seen]);
18427
- signal(pids, "SIGKILL");
18428
- for (const pid of pids) seen.add(pid);
18429
- if (pids.length && left > 1) later(KILL_ROUND_MS, () => round(left - 1));
18430
- };
18431
- later(graceMs, () => {
18432
- term("SIGKILL");
18433
- round(KILL_ROUNDS);
18434
- });
18435
- }
18436
- function killRunning() {
18437
- for (const [child, mark] of running) {
18438
- if (killTreeOnWindows(child.pid)) continue;
18439
- killUnderOnWindows(child.pid);
18440
- signal(marked(mark, child.pid ? [child.pid] : []), "SIGKILL");
18441
- try {
18442
- child.kill("SIGKILL");
18443
- } catch {
18444
- }
18532
+ case "resolve":
18533
+ return [
18534
+ `${kb}. Apply my answers to the open questions on task ${req.id} ${named2} following \`akb guide resolve\`.`,
18535
+ carriedNote(req),
18536
+ answeredNote(req.id, command),
18537
+ req.notes ? `Extra notes: ${req.notes}` : ""
18538
+ ].filter(Boolean).join(" ");
18445
18539
  }
18446
- running.clear();
18447
- }
18448
- function unlisten() {
18449
- for (const [sig, fn] of listening) process.removeListener(sig, fn);
18450
- listening.clear();
18451
- }
18452
- function onQuit(sig) {
18453
- killRunning();
18454
- unlisten();
18455
- if (process.listenerCount(sig) === 0) process.exit(128 + os6.constants.signals[sig]);
18456
18540
  }
18457
- function trackAgent(child, mark) {
18458
- if (!child.pid) return;
18459
- running.set(child, mark);
18460
- child.once("close", () => {
18461
- running.delete(child);
18462
- if (!running.size) unlisten();
18463
- });
18464
- for (const sig of QUIT_SIGNALS) {
18465
- if (listening.has(sig) || sig === "SIGINT" && interruptIsTheCallers) continue;
18466
- const fn = () => onQuit(sig);
18467
- listening.set(sig, fn);
18468
- process.on(sig, fn);
18469
- }
18541
+ function discardedCardsPrompt(cards = []) {
18542
+ if (!cards.length) return "";
18543
+ return "The user discarded the cards listed below. Their absence is intentional. Do not restore or recreate them, including under new IDs or filenames. Continue the remaining work from the current board, preserving the other cards.\n\n" + cards.map((c) => `- #${c.id}: ${c.path}`).join("\n");
18470
18544
  }
18471
- var runMark, stopMark, markEnv, WINDOWS3, KILL_ROUND_MS, KILL_ROUNDS, LIST_PROCS, running, QUIT_SIGNALS, listening, interruptIsTheCallers;
18472
- var init_stop = __esm({
18473
- "../cli/src/lib/agent/stop.ts"() {
18545
+ var RESUME_PROMPT, DELIVERY_RESUME, DELIVERY_RESUME_CARD, RESTARTABLE, RESTART_LEAD, restartable, NO_IMPLEMENT, BOARD_FOLDER, CARD_FILE, PROJECT_FOLDER, PLANNING_FOR, SPEC_SELECTOR_FOR, reflectedCards;
18546
+ var init_prompts = __esm({
18547
+ "../cli/src/lib/agent/prompts.ts"() {
18474
18548
  "use strict";
18549
+ init_cards();
18550
+ init_memory();
18551
+ init_memories();
18552
+ init_paths();
18553
+ init_agents();
18554
+ init_command();
18555
+ init_deliveries();
18475
18556
  init_env();
18476
- runMark = (sessionId) => ({ name: RUN_ENV, value: sessionId });
18477
- stopMark = () => ({ name: STOP_ENV, value: randomUUID5() });
18478
- markEnv = (env2, mark) => ({ ...env2, [mark.name]: mark.value });
18479
- WINDOWS3 = process.platform === "win32";
18480
- KILL_ROUND_MS = 300;
18481
- KILL_ROUNDS = 3;
18482
- LIST_PROCS = [
18483
- "$ErrorActionPreference = 'Stop'",
18484
- "Get-CimInstance Win32_Process | ForEach-Object { if ($_.CreationDate) { '{0} {1} {2}' -f $_.ProcessId, $_.ParentProcessId, ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() } }",
18485
- "'boot {0}' -f ([DateTimeOffset](Get-CimInstance Win32_OperatingSystem).LastBootUpTime).ToUnixTimeMilliseconds()"
18486
- ].join("; ");
18487
- running = /* @__PURE__ */ new Map();
18488
- QUIT_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
18489
- listening = /* @__PURE__ */ new Map();
18490
- interruptIsTheCallers = false;
18557
+ init_facts();
18558
+ init_outputs();
18559
+ init_archive();
18560
+ init_flows();
18561
+ init_resolve();
18562
+ init_runner();
18563
+ init_workflows();
18564
+ init_catalog2();
18565
+ init_roles();
18566
+ init_rules2();
18567
+ init_settings2();
18568
+ init_types3();
18569
+ RESUME_PROMPT = [
18570
+ `Continue. The previous run ended before it finished the task.`,
18571
+ `Pick up where you left off and carry it through.`
18572
+ ].join(" ");
18573
+ DELIVERY_RESUME = [
18574
+ `Continue delivery %s. The previous run ended before the delivery finished.`,
18575
+ `Re-enter the flow from the top and check each step's precondition before you do it \u2014 work that is already done is done, so do not repeat it.`
18576
+ ].join(" ");
18577
+ DELIVERY_RESUME_CARD = `Build the card as the delivery holds it, not as the file reads now: \`%c\` prints the approved copy.`;
18578
+ RESTARTABLE = /* @__PURE__ */ new Set([
18579
+ "implement",
18580
+ "clarify",
18581
+ "resolve",
18582
+ "approve",
18583
+ "archive",
18584
+ "spec",
18585
+ "conflict",
18586
+ "scheduled"
18587
+ ]);
18588
+ RESTART_LEAD = [
18589
+ `The session you were continuing is gone, and this one holds nothing of it.`,
18590
+ `Do the task below from the top, checking each step's precondition before you do it \u2014 work an earlier run already finished is done, so don't repeat it.`
18591
+ ].join(" ");
18592
+ restartable = (run2) => (run2.cardId !== null || !!run2.deliveryId) && RESTARTABLE.has(run2.action);
18593
+ NO_IMPLEMENT = `(Unless the request explicitly asks for implementation, don't implement it; revising the card's drafts is not implementing.)`;
18594
+ BOARD_FOLDER = "<board folder>";
18595
+ CARD_FILE = "<card file>";
18596
+ PROJECT_FOLDER = "<project folder>";
18597
+ PLANNING_FOR = /* @__PURE__ */ new Set(["create", "clarify", "resolve", "approve", "edit"]);
18598
+ SPEC_SELECTOR_FOR = /* @__PURE__ */ new Set(["clarify", "resolve", "approve", "edit"]);
18599
+ reflectedCards = (req) => req.cards ?? (req.id !== void 0 ? [req.id] : []);
18491
18600
  }
18492
18601
  });
18493
18602
 
18494
18603
  // ../cli/src/lib/agent/sessions.ts
18495
18604
  import { randomUUID as randomUUID6 } from "node:crypto";
18496
- import fs48 from "node:fs";
18497
- import path52 from "node:path";
18605
+ import fs49 from "node:fs";
18606
+ import path54 from "node:path";
18498
18607
  function reap(runs, reaped = [], restore = []) {
18499
18608
  let changed = false;
18500
18609
  const now = Date.now();
@@ -18514,7 +18623,7 @@ function reap(runs, reaped = [], restore = []) {
18514
18623
  }
18515
18624
  function stampDuration(run2, endedAt) {
18516
18625
  try {
18517
- fs48.appendFileSync(run2.logPath, durationLine(endedAt - run2.startedAt));
18626
+ fs49.appendFileSync(run2.logPath, durationLine(endedAt - run2.startedAt));
18518
18627
  } catch {
18519
18628
  }
18520
18629
  }
@@ -18528,7 +18637,7 @@ function cardNow(cardId2) {
18528
18637
  try {
18529
18638
  const file = cardFile(cardId2);
18530
18639
  if (!file) return null;
18531
- const { meta } = parseFrontmatter(fs48.readFileSync(file, "utf8"));
18640
+ const { meta } = parseFrontmatter(fs49.readFileSync(file, "utf8"));
18532
18641
  if (!meta) return null;
18533
18642
  return { status: meta.status || "todo", questions: openOf(meta.questions).length, title: meta.title };
18534
18643
  } catch {
@@ -18665,9 +18774,9 @@ function leftBoardOnLanding(id) {
18665
18774
  function archivedRejected(id) {
18666
18775
  const filed = locateArchived(id);
18667
18776
  if (!filed) return null;
18668
- const file = filed.kind === "group" ? path52.join(filed.target, "root.md") : filed.target;
18777
+ const file = filed.kind === "group" ? path54.join(filed.target, "root.md") : filed.target;
18669
18778
  try {
18670
- return !!parseFrontmatter(fs48.readFileSync(file, "utf8")).meta?.rejected;
18779
+ return !!parseFrontmatter(fs49.readFileSync(file, "utf8")).meta?.rejected;
18671
18780
  } catch {
18672
18781
  return false;
18673
18782
  }
@@ -18924,7 +19033,7 @@ function openRun(req, prompt, notes = [], sessionId = randomUUID6(), said3) {
18924
19033
  return { run: record, spec };
18925
19034
  }
18926
19035
  function planningSession(cardId2, cwd = REPO_ROOT) {
18927
- const here = (dir) => !dir || path52.resolve(dir) === cwd;
19036
+ const here = (dir) => !dir || path54.resolve(dir) === cwd;
18928
19037
  const ended = readRuns().filter((r) => r.status === "done").sort((a, b) => b.startedAt - a.startedAt);
18929
19038
  const of = (run3, fork) => {
18930
19039
  if (!here(run3.cwd)) return { none: "elsewhere" };
@@ -18993,7 +19102,7 @@ function requestOf(record) {
18993
19102
  pictures: record.pictures
18994
19103
  };
18995
19104
  }
18996
- async function openResume(id) {
19105
+ async function openResume(id, requireSession = false) {
18997
19106
  const restore = [];
18998
19107
  const runs = withRuns((all2) => {
18999
19108
  reap(all2, [], restore);
@@ -19013,6 +19122,9 @@ async function openResume(id) {
19013
19122
  if (refused) return refused;
19014
19123
  }
19015
19124
  const retrying = lacksSession(prev);
19125
+ if (requireSession && !resumeSessionId(prev)) {
19126
+ return refusal("runNoSession", "the original conversation cannot be resumed; no new conversation was started");
19127
+ }
19016
19128
  const ask2 = retrying && prev.status !== "stopped" ? retryAsk(prev) : void 0;
19017
19129
  if (retrying && !ask2) return refusal("runNoSession", "that run never reported a session id to continue by");
19018
19130
  const ended = endedDeliveryRefusal(prev);
@@ -19143,8 +19255,8 @@ async function resumeHeld(prev, plan, sessionId, resuming, fresh) {
19143
19255
  clearAsks(prev.sessionId);
19144
19256
  try {
19145
19257
  if (prev.action !== "create") {
19146
- fs48.unlinkSync(prev.logPath);
19147
- fs48.rmSync(eventsPathOf(prev.sessionId), { force: true });
19258
+ fs49.unlinkSync(prev.logPath);
19259
+ fs49.rmSync(eventsPathOf(prev.sessionId), { force: true });
19148
19260
  }
19149
19261
  } catch {
19150
19262
  }
@@ -19168,8 +19280,8 @@ function forgetRun(sessionId) {
19168
19280
  if (!gone) return;
19169
19281
  clearAsks(sessionId);
19170
19282
  try {
19171
- fs48.unlinkSync(gone.logPath);
19172
- fs48.rmSync(eventsPathOf(gone.sessionId), { force: true });
19283
+ fs49.unlinkSync(gone.logPath);
19284
+ fs49.rmSync(eventsPathOf(gone.sessionId), { force: true });
19173
19285
  } catch {
19174
19286
  }
19175
19287
  }
@@ -19198,14 +19310,14 @@ function noteSpecRefusals(sessionId, refused) {
19198
19310
  }
19199
19311
  function clearAsks(sessionId) {
19200
19312
  try {
19201
- fs48.unlinkSync(asksPathOf(sessionId));
19313
+ fs49.unlinkSync(asksPathOf(sessionId));
19202
19314
  } catch {
19203
19315
  }
19204
19316
  }
19205
19317
  function readAsks(sessionId) {
19206
19318
  let data;
19207
19319
  try {
19208
- data = JSON.parse(fs48.readFileSync(asksPathOf(sessionId), "utf8"));
19320
+ data = JSON.parse(fs49.readFileSync(asksPathOf(sessionId), "utf8"));
19209
19321
  } catch {
19210
19322
  return { asks: [], refines: [], refused: [] };
19211
19323
  }
@@ -19232,10 +19344,10 @@ function readAsks(sessionId) {
19232
19344
  return { asks: asks.filter((a) => !discarded.has(a.cardId)), refines: refines.filter((a) => !discarded.has(a.cardId)), refused };
19233
19345
  }
19234
19346
  function writeAsks(sessionId, file) {
19235
- fs48.mkdirSync(SESSIONS_DIR, { recursive: true });
19347
+ fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
19236
19348
  const tmp = `${asksPathOf(sessionId)}.tmp`;
19237
- fs48.writeFileSync(tmp, JSON.stringify(file, null, 2) + "\n");
19238
- fs48.renameSync(tmp, asksPathOf(sessionId));
19349
+ fs49.writeFileSync(tmp, JSON.stringify(file, null, 2) + "\n");
19350
+ fs49.renameSync(tmp, asksPathOf(sessionId));
19239
19351
  }
19240
19352
  function markSpawned(sessionId, pid) {
19241
19353
  patch(sessionId, (r) => {
@@ -19323,7 +19435,10 @@ async function cancelDelivery(id) {
19323
19435
  if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to cancel", { action: "cancel" }) };
19324
19436
  const delivery = namedDelivery(id);
19325
19437
  if (!delivery) return { ok: false, ...unknownDelivery(id) };
19326
- if (delivery.status !== "active") return { ok: true, deliveryId: delivery.deliveryId };
19438
+ if (delivery.status !== "active") {
19439
+ tidyCheckout(delivery.deliveryId);
19440
+ return { ok: true, deliveryId: delivery.deliveryId };
19441
+ }
19327
19442
  endDelivery(delivery.deliveryId, "cancelled");
19328
19443
  const live = readRuns().find((r) => r.status === "running" && r.deliveryId === delivery.deliveryId && r.action !== "sub");
19329
19444
  if (live) await stopRun(live.sessionId);
@@ -19389,12 +19504,12 @@ async function discardDelivery(id) {
19389
19504
  return { ok: true, deliveryId: delivery.deliveryId };
19390
19505
  }
19391
19506
  function writeSpec(spec) {
19392
- fs48.mkdirSync(SESSIONS_DIR, { recursive: true });
19393
- fs48.writeFileSync(specPathOf(spec.sessionId), JSON.stringify(spec, null, 2) + "\n");
19507
+ fs49.mkdirSync(SESSIONS_DIR, { recursive: true });
19508
+ fs49.writeFileSync(specPathOf(spec.sessionId), JSON.stringify(spec, null, 2) + "\n");
19394
19509
  }
19395
19510
  function readSpec(sessionId) {
19396
19511
  try {
19397
- const spec = JSON.parse(fs48.readFileSync(specPathOf(sessionId), "utf8"));
19512
+ const spec = JSON.parse(fs49.readFileSync(specPathOf(sessionId), "utf8"));
19398
19513
  return spec && Array.isArray(spec.plan?.argv) ? spec : null;
19399
19514
  } catch {
19400
19515
  return null;
@@ -19402,28 +19517,28 @@ function readSpec(sessionId) {
19402
19517
  }
19403
19518
  function dropSpec(sessionId) {
19404
19519
  try {
19405
- fs48.unlinkSync(specPathOf(sessionId));
19520
+ fs49.unlinkSync(specPathOf(sessionId));
19406
19521
  } catch {
19407
19522
  }
19408
19523
  }
19409
19524
  async function acquireIndexLock(giveUp) {
19410
19525
  const until = Date.now() + INDEX_WAIT_MS;
19411
- const holder = path52.join(INDEX_LOCK2, "holder");
19526
+ const holder = path54.join(INDEX_LOCK2, "holder");
19412
19527
  for (; ; ) {
19413
19528
  if (giveUp()) return null;
19414
19529
  try {
19415
- fs48.mkdirSync(INDEX_LOCK2, { recursive: false });
19416
- fs48.writeFileSync(holder, String(process.pid));
19417
- return () => fs48.rmSync(INDEX_LOCK2, { recursive: true, force: true });
19530
+ fs49.mkdirSync(INDEX_LOCK2, { recursive: false });
19531
+ fs49.writeFileSync(holder, String(process.pid));
19532
+ return () => fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
19418
19533
  } catch (err) {
19419
19534
  const code = err.code;
19420
19535
  if (code === "ENOENT") {
19421
- fs48.mkdirSync(path52.dirname(INDEX_LOCK2), { recursive: true });
19536
+ fs49.mkdirSync(path54.dirname(INDEX_LOCK2), { recursive: true });
19422
19537
  continue;
19423
19538
  }
19424
19539
  if (code !== "EEXIST") throw err;
19425
19540
  if (!pidAlive(Number(readHolder(holder)))) {
19426
- fs48.rmSync(INDEX_LOCK2, { recursive: true, force: true });
19541
+ fs49.rmSync(INDEX_LOCK2, { recursive: true, force: true });
19427
19542
  continue;
19428
19543
  }
19429
19544
  if (Date.now() > until) return null;
@@ -19433,7 +19548,7 @@ async function acquireIndexLock(giveUp) {
19433
19548
  }
19434
19549
  function readHolder(file) {
19435
19550
  try {
19436
- return fs48.readFileSync(file, "utf8").trim();
19551
+ return fs49.readFileSync(file, "utf8").trim();
19437
19552
  } catch {
19438
19553
  return String(process.pid);
19439
19554
  }
@@ -19524,8 +19639,8 @@ var init_sessions = __esm({
19524
19639
  conflict: "implementing",
19525
19640
  hook: "implementing"
19526
19641
  };
19527
- specPathOf = (sessionId) => path52.join(SESSIONS_DIR, `${sessionId}.plan.json`);
19528
- asksPathOf = (sessionId) => path52.join(SESSIONS_DIR, `${sessionId}.asks.json`);
19642
+ specPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.plan.json`);
19643
+ asksPathOf = (sessionId) => path54.join(SESSIONS_DIR, `${sessionId}.asks.json`);
19529
19644
  carriesSession = (r) => !!(r.resumedFrom || r.continues && !r.continues.fork);
19530
19645
  canPickUp = (r) => (r.status === "error" || r.status === "interrupted" || r.status === "stopped") && !(r.action === "create" && readRuns().some((next) => next.resumedFrom === r.sessionId));
19531
19646
  neverStarted = (r) => r.unspawned === true || !!r.errorWhy?.some((why) => why.kind === "notInstalled");
@@ -19659,8 +19774,8 @@ var init_questions = __esm({
19659
19774
  });
19660
19775
 
19661
19776
  // ../cli/src/lib/agent/workflow-cards.ts
19662
- import fs49 from "node:fs";
19663
- import path53 from "node:path";
19777
+ import fs50 from "node:fs";
19778
+ import path55 from "node:path";
19664
19779
  function cardsOnWorkflow(id) {
19665
19780
  const wanted = id || DEFAULT_WORKFLOW;
19666
19781
  const ids = [];
@@ -19671,12 +19786,12 @@ function cardsOnWorkflow(id) {
19671
19786
  return [];
19672
19787
  }
19673
19788
  for (const file of files2) {
19674
- const base = path53.basename(file);
19675
- const card = base === "root.md" ? idPrefix(path53.basename(path53.dirname(file))) : idPrefix(base);
19789
+ const base = path55.basename(file);
19790
+ const card = base === "root.md" ? idPrefix(path55.basename(path55.dirname(file))) : idPrefix(base);
19676
19791
  if (card === null) continue;
19677
19792
  let meta;
19678
19793
  try {
19679
- meta = parseFrontmatter(fs49.readFileSync(file, "utf8")).meta;
19794
+ meta = parseFrontmatter(fs50.readFileSync(file, "utf8")).meta;
19680
19795
  } catch {
19681
19796
  continue;
19682
19797
  }
@@ -19734,12 +19849,12 @@ var init_workflow_cards = __esm({
19734
19849
  });
19735
19850
 
19736
19851
  // ../cli/src/lib/readme.ts
19737
- import fs50 from "node:fs";
19738
- import path54 from "node:path";
19852
+ import fs51 from "node:fs";
19853
+ import path56 from "node:path";
19739
19854
  function stripReadmeRefs(target) {
19740
- if (!fs50.existsSync(README)) return [];
19855
+ if (!fs51.existsSync(README)) return [];
19741
19856
  const needle = target.kind === "group" ? `](${target.rel}/` : `](${target.rel})`;
19742
- const lines = fs50.readFileSync(README, "utf8").split("\n");
19857
+ const lines = fs51.readFileSync(README, "utf8").split("\n");
19743
19858
  const out = [];
19744
19859
  const removed = [];
19745
19860
  let i = 0;
@@ -19761,20 +19876,20 @@ function stripReadmeRefs(target) {
19761
19876
  out.push(line);
19762
19877
  i++;
19763
19878
  }
19764
- if (removed.length) fs50.writeFileSync(README, out.join("\n"));
19879
+ if (removed.length) fs51.writeFileSync(README, out.join("\n"));
19765
19880
  return removed;
19766
19881
  }
19767
19882
  function addReadmeRef(id, title, relPath) {
19768
- if (!fs50.existsSync(README)) return false;
19769
- const link2 = relPath.split(path54.sep).join("/");
19883
+ if (!fs51.existsSync(README)) return false;
19884
+ const link2 = relPath.split(path56.sep).join("/");
19770
19885
  const bullet = `- [#${id} ${title}](${link2})`;
19771
19886
  const heading = `## ${TASKS_HEADING}`;
19772
- let lines = fs50.readFileSync(README, "utf8").split("\n");
19887
+ let lines = fs51.readFileSync(README, "utf8").split("\n");
19773
19888
  const hi = lines.findIndex((l) => l.trim().toLowerCase() === heading.toLowerCase());
19774
19889
  if (hi === -1) {
19775
19890
  while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
19776
19891
  lines.push("", heading, "", bullet);
19777
- fs50.writeFileSync(README, lines.join("\n") + "\n");
19892
+ fs51.writeFileSync(README, lines.join("\n") + "\n");
19778
19893
  return true;
19779
19894
  }
19780
19895
  let end2 = hi + 1;
@@ -19788,15 +19903,15 @@ function addReadmeRef(id, title, relPath) {
19788
19903
  const at = lastBullet !== -1 ? lastBullet + 1 : lines[hi + 1] === "" ? hi + 2 : hi + 1;
19789
19904
  lines.splice(at, 0, bullet);
19790
19905
  }
19791
- fs50.writeFileSync(README, lines.join("\n"));
19906
+ fs51.writeFileSync(README, lines.join("\n"));
19792
19907
  return true;
19793
19908
  }
19794
19909
  function repointReadmeLink(id, oldRel, newRel, title) {
19795
- if (!fs50.existsSync(README)) return false;
19796
- const oldLink = oldRel.split(path54.sep).join("/");
19797
- const newLink = newRel.split(path54.sep).join("/");
19910
+ if (!fs51.existsSync(README)) return false;
19911
+ const oldLink = oldRel.split(path56.sep).join("/");
19912
+ const newLink = newRel.split(path56.sep).join("/");
19798
19913
  const linkRe = new RegExp(`\\[#${id}\\b[^\\]]*\\]\\(${escapeRegex(oldLink)}\\)`);
19799
- const lines = fs50.readFileSync(README, "utf8").split("\n");
19914
+ const lines = fs51.readFileSync(README, "utf8").split("\n");
19800
19915
  let changed = false;
19801
19916
  for (let i = 0; i < lines.length; i++) {
19802
19917
  if (!lines[i].includes(`](${oldLink})`)) continue;
@@ -19806,7 +19921,7 @@ function repointReadmeLink(id, oldRel, newRel, title) {
19806
19921
  changed = true;
19807
19922
  }
19808
19923
  }
19809
- if (changed) fs50.writeFileSync(README, lines.join("\n"));
19924
+ if (changed) fs51.writeFileSync(README, lines.join("\n"));
19810
19925
  return changed;
19811
19926
  }
19812
19927
  var isTableRow, isBulletStart, bulletIndent, TASKS_HEADING, escapeRegex;
@@ -19823,14 +19938,14 @@ var init_readme = __esm({
19823
19938
  });
19824
19939
 
19825
19940
  // ../cli/src/lib/reconcile.ts
19826
- import fs51 from "node:fs";
19827
- import path55 from "node:path";
19941
+ import fs52 from "node:fs";
19942
+ import path57 from "node:path";
19828
19943
  function liveIds() {
19829
19944
  const ids = /* @__PURE__ */ new Set();
19830
19945
  for (const file of walkMd(TODO)) {
19831
- const base = path55.basename(file);
19946
+ const base = path57.basename(file);
19832
19947
  if (base === "README.md") continue;
19833
- const id = base === "root.md" ? idPrefix(path55.basename(path55.dirname(file))) : idPrefix(base);
19948
+ const id = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
19834
19949
  if (id != null) ids.add(id);
19835
19950
  }
19836
19951
  return ids;
@@ -19838,16 +19953,16 @@ function liveIds() {
19838
19953
  function indexableCards() {
19839
19954
  const out = [];
19840
19955
  for (const dir of walkDirs(TODO)) {
19841
- const id = idPrefix(path55.basename(dir));
19842
- if (id != null && fs51.existsSync(path55.join(dir, "root.md"))) {
19843
- out.push({ id, rel: path55.join(path55.relative(TODO, dir), "root.md").split(path55.sep).join("/") });
19956
+ const id = idPrefix(path57.basename(dir));
19957
+ if (id != null && fs52.existsSync(path57.join(dir, "root.md"))) {
19958
+ out.push({ id, rel: path57.join(path57.relative(TODO, dir), "root.md").split(path57.sep).join("/") });
19844
19959
  }
19845
19960
  }
19846
19961
  for (const file of walkMd(TODO)) {
19847
- const base = path55.basename(file);
19962
+ const base = path57.basename(file);
19848
19963
  if (base === "README.md" || base === "root.md") continue;
19849
- const relTODO = path55.relative(TODO, file);
19850
- const segs = relTODO.split(path55.sep);
19964
+ const relTODO = path57.relative(TODO, file);
19965
+ const segs = relTODO.split(path57.sep);
19851
19966
  if (segs.length !== 1) continue;
19852
19967
  const id = idPrefix(base);
19853
19968
  if (id != null) out.push({ id, rel: segs.join("/") });
@@ -19856,7 +19971,7 @@ function indexableCards() {
19856
19971
  }
19857
19972
  function readmeComplaints(fix) {
19858
19973
  const said3 = [];
19859
- const lines = fs51.readFileSync(README, "utf8").split("\n");
19974
+ const lines = fs52.readFileSync(README, "utf8").split("\n");
19860
19975
  const indexed = /* @__PURE__ */ new Set();
19861
19976
  let fixed = 0;
19862
19977
  for (let i = 0; i < lines.length; i++) {
@@ -19865,13 +19980,13 @@ function readmeComplaints(fix) {
19865
19980
  const id = Number(m[1]);
19866
19981
  const linkPath = m[2];
19867
19982
  indexed.add(id);
19868
- if (fs51.existsSync(path55.join(TODO, linkPath))) continue;
19983
+ if (fs52.existsSync(path57.join(TODO, linkPath))) continue;
19869
19984
  const found2 = locate(id);
19870
19985
  if (!found2) {
19871
19986
  said3.push(`README links #${id} \u2192 ${linkPath}, but no card with that id exists (removed by hand?). Drop the entry or restore the file.`);
19872
19987
  continue;
19873
19988
  }
19874
- const want = (found2.kind === "group" ? path55.join(found2.rel, "root.md") : found2.rel).split(path55.sep).join("/");
19989
+ const want = (found2.kind === "group" ? path57.join(found2.rel, "root.md") : found2.rel).split(path57.sep).join("/");
19875
19990
  if (want === linkPath) continue;
19876
19991
  if (!fix) {
19877
19992
  said3.push(`README links #${id} \u2192 ${linkPath}, which isn't there any more; the card is at ${want}.`);
@@ -19881,7 +19996,7 @@ function readmeComplaints(fix) {
19881
19996
  said3.push(`README link #${id} pointed at missing ${linkPath} \u2192 repointed to ${want}.`);
19882
19997
  fixed++;
19883
19998
  }
19884
- if (fixed) fs51.writeFileSync(README, lines.join("\n"));
19999
+ if (fixed) fs52.writeFileSync(README, lines.join("\n"));
19885
20000
  for (const c of indexableCards()) {
19886
20001
  if (!indexed.has(c.id)) {
19887
20002
  said3.push(`card ${c.rel} (#${c.id}) is not in the README index. Add it under "## ${TASKS_HEADING}".`);
@@ -19893,11 +20008,11 @@ function crossRefComplaints() {
19893
20008
  const said3 = [];
19894
20009
  const live = liveIds();
19895
20010
  for (const file of walkMd(TODO)) {
19896
- const base = path55.basename(file);
20011
+ const base = path57.basename(file);
19897
20012
  if (base === "README.md") continue;
19898
- const ownerId = base === "root.md" ? idPrefix(path55.basename(path55.dirname(file))) : idPrefix(base);
20013
+ const ownerId = base === "root.md" ? idPrefix(path57.basename(path57.dirname(file))) : idPrefix(base);
19899
20014
  if (ownerId == null) continue;
19900
- const { meta } = parseFrontmatter(fs51.readFileSync(file, "utf8"));
20015
+ const { meta } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
19901
20016
  if (!meta) continue;
19902
20017
  for (const field2 of ["blocked_by", "related"]) {
19903
20018
  for (const ref of meta[field2] || []) {
@@ -19910,11 +20025,11 @@ function crossRefComplaints() {
19910
20025
  return said3;
19911
20026
  }
19912
20027
  function reconcileBoard() {
19913
- if (!fs51.existsSync(README)) return;
20028
+ if (!fs52.existsSync(README)) return;
19914
20029
  for (const line of [...readmeComplaints(true), ...crossRefComplaints()]) warn2(line);
19915
20030
  }
19916
20031
  function boardComplaints() {
19917
- if (!fs51.existsSync(README)) return [];
20032
+ if (!fs52.existsSync(README)) return [];
19918
20033
  try {
19919
20034
  return [...readmeComplaints(false), ...crossRefComplaints()];
19920
20035
  } catch {
@@ -19934,8 +20049,8 @@ var init_reconcile = __esm({
19934
20049
  });
19935
20050
 
19936
20051
  // ../cli/src/commands/card.ts
19937
- import fs52 from "node:fs";
19938
- import path56 from "node:path";
20052
+ import fs53 from "node:fs";
20053
+ import path58 from "node:path";
19939
20054
  function defaultBody() {
19940
20055
  return [
19941
20056
  "<\u7528\u4E00\u5C0F\u6BB5\u8BDD\u5148\u8BB2\u53D1\u751F\u4E86\u4EC0\u4E48\u3001\u7528\u6237\u9047\u5230\u4EC0\u4E48\uFF0C\u518D\u8BB2\u8FD9\u5F20\u5361\u8981\u8FBE\u5230\u7684\u7ED3\u679C\u3002>",
@@ -19961,7 +20076,7 @@ function bodyFromFile(opts) {
19961
20076
  if (opts.body === false) die("pass --body-file or --no-body, not both");
19962
20077
  let text5;
19963
20078
  try {
19964
- text5 = fs52.readFileSync(opts.bodyFile, "utf8");
20079
+ text5 = fs53.readFileSync(opts.bodyFile, "utf8");
19965
20080
  } catch {
19966
20081
  die(`can't read ${opts.bodyFile} \u2014 write the body to a file, then pass its path`, { kind: "needs-input" });
19967
20082
  }
@@ -19993,20 +20108,20 @@ function cmdCreate(opts) {
19993
20108
  const start = opts.id;
19994
20109
  const slug2 = slugify(opts.slug !== void 0 ? opts.slug : title);
19995
20110
  const fileRel = `${start}-${slug2}.md`;
19996
- const file = path56.join(TODO, fileRel);
20111
+ const file = path58.join(TODO, fileRel);
19997
20112
  const held4 = locate(start) ?? locateArchived(start);
19998
- if (held4 && path56.basename(held4.target) === `${start}-${slug2}.md`) {
20113
+ if (held4 && path58.basename(held4.target) === `${start}-${slug2}.md`) {
19999
20114
  say(start);
20000
20115
  say(` ${rel(held4.target)} was already written`);
20001
20116
  return { id: start, ids: [start], title, file: rel(held4.target), indexed: false };
20002
20117
  }
20003
20118
  if (held4) die(`#${start} is already a card here (${rel(held4.target)})`, { kind: "id-taken", id: start });
20004
- if (fs52.existsSync(file)) die(`${rel(file)} already exists \u2014 pick a different --slug`);
20119
+ if (fs53.existsSync(file)) die(`${rel(file)} already exists \u2014 pick a different --slug`);
20005
20120
  countBoardEvent(triage ? "cards_created_proposed" : "cards_created_asked");
20006
20121
  const meta = { title, priority, status: "todo", blocked_by, related, modules, workflow, triage, source, questions };
20007
20122
  const scaffolded = !written && opts.body !== false;
20008
20123
  const body = written ?? (scaffolded ? defaultBody() : "");
20009
- fs52.writeFileSync(file, serializeFrontmatter(meta) + "\n\n" + body);
20124
+ fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n\n" + body);
20010
20125
  const indexed = addReadmeRef(start, title, fileRel);
20011
20126
  say(start);
20012
20127
  say(
@@ -20044,8 +20159,8 @@ function cmdUpdate(id, flags) {
20044
20159
  if (creating) die(creating, { kind: "card-being-created", id });
20045
20160
  const found2 = locate(id);
20046
20161
  if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
20047
- const file = found2.kind === "group" ? path56.join(found2.target, "root.md") : found2.target;
20048
- const { meta, body } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
20162
+ const file = found2.kind === "group" ? path58.join(found2.target, "root.md") : found2.target;
20163
+ const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
20049
20164
  if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
20050
20165
  if (flags.blockedBy !== void 0 && flags.addBlockedBy !== void 0) die("--blocked-by replaces the list and --add-blocked-by appends to it \u2014 pass one");
20051
20166
  if (flags.related !== void 0 && flags.addRelated !== void 0) die("--related replaces the list and --add-related appends to it \u2014 pass one");
@@ -20125,26 +20240,26 @@ function cmdUpdate(id, flags) {
20125
20240
  meta.status = "todo";
20126
20241
  changes.push("status\u2192todo (open questions)");
20127
20242
  }
20128
- const curRel = path56.relative(TODO, file);
20243
+ const curRel = path58.relative(TODO, file);
20129
20244
  const isSubtask = found2.kind === "file" && enclosingGroupRoot(file) !== null;
20130
- let base = path56.basename(file);
20245
+ let base = path58.basename(file);
20131
20246
  if (flags.slug !== void 0) {
20132
20247
  if (found2.kind === "group") die("renaming a group root by script is not supported");
20133
20248
  base = `${id}-${slugify(flags.slug)}.md`;
20134
20249
  }
20135
- const destRel = path56.join(path56.dirname(curRel), base);
20136
- const dest = path56.join(TODO, destRel);
20250
+ const destRel = path58.join(path58.dirname(curRel), base);
20251
+ const dest = path58.join(TODO, destRel);
20137
20252
  const moving = dest !== file;
20138
- if (moving && fs52.existsSync(dest)) die(`${rel(dest)} already exists`);
20139
- fs52.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
20140
- if (moving) fs52.renameSync(file, dest);
20253
+ if (moving && fs53.existsSync(dest)) die(`${rel(dest)} already exists`);
20254
+ fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
20255
+ if (moving) fs53.renameSync(file, dest);
20141
20256
  if (isSubtask) {
20142
20257
  if (moving || changes.includes("title")) repointReadmeLink(id, curRel, destRel, meta.title);
20143
- if (moving) changes.push(`renamed \u2192 ${destRel.split(path56.sep).join("/")}`);
20258
+ if (moving) changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
20144
20259
  } else if (moving) {
20145
20260
  stripReadmeRefs({ kind: "file", rel: curRel });
20146
20261
  addReadmeRef(id, meta.title, destRel);
20147
- changes.push(`renamed \u2192 ${destRel.split(path56.sep).join("/")}`);
20262
+ changes.push(`renamed \u2192 ${destRel.split(path58.sep).join("/")}`);
20148
20263
  } else if (changes.includes("title")) {
20149
20264
  stripReadmeRefs({ kind: "file", rel: curRel });
20150
20265
  addReadmeRef(id, meta.title, curRel);
@@ -20156,8 +20271,8 @@ function cmdUpdateQuestions(id, input) {
20156
20271
  const ops = readQuestionOps(input.ops ?? []);
20157
20272
  const found2 = locate(id);
20158
20273
  if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
20159
- const file = found2.kind === "group" ? path56.join(found2.target, "root.md") : found2.target;
20160
- const { meta, body } = parseFrontmatter(fs52.readFileSync(file, "utf8"));
20274
+ const file = found2.kind === "group" ? path58.join(found2.target, "root.md") : found2.target;
20275
+ const { meta, body } = parseFrontmatter(fs53.readFileSync(file, "utf8"));
20161
20276
  if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
20162
20277
  if (insideRun() && ops.some((op) => op.kind === "skip" || op.kind === "unskip")) {
20163
20278
  die("only the user skips or reopens a question \u2014 a run leaves questions for them to answer");
@@ -20230,7 +20345,7 @@ function cmdUpdateQuestions(id, input) {
20230
20345
  meta.status = "todo";
20231
20346
  changes.push("status\u2192todo (open questions)");
20232
20347
  }
20233
- fs52.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
20348
+ fs53.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
20234
20349
  for (const kind of closed2) countBoardEvent(kind);
20235
20350
  settleDelivery2(id, skipped, unskipped);
20236
20351
  say(
@@ -20304,13 +20419,13 @@ var init_config_template = __esm({
20304
20419
  });
20305
20420
 
20306
20421
  // ../cli/src/lib/agent/local.ts
20307
- import fs53 from "node:fs";
20308
- import path57 from "node:path";
20422
+ import fs54 from "node:fs";
20423
+ import path59 from "node:path";
20309
20424
  function takeLocalModels(board2) {
20310
- const file = path57.join(board2, ".local.json");
20425
+ const file = path59.join(board2, ".local.json");
20311
20426
  let held4;
20312
20427
  try {
20313
- held4 = configBlock(JSON.parse(fs53.readFileSync(file, "utf8")));
20428
+ held4 = configBlock(JSON.parse(fs54.readFileSync(file, "utf8")));
20314
20429
  } catch {
20315
20430
  return {};
20316
20431
  }
@@ -20328,7 +20443,7 @@ function takeLocalModels(board2) {
20328
20443
  }
20329
20444
  if (!Object.keys(agents).length) return out;
20330
20445
  delete held4.agents;
20331
- fs53.writeFileSync(file, JSON.stringify(held4, null, 2) + "\n");
20446
+ fs54.writeFileSync(file, JSON.stringify(held4, null, 2) + "\n");
20332
20447
  return out;
20333
20448
  }
20334
20449
  var LOCAL_IGNORE_LINE;
@@ -20389,12 +20504,12 @@ var init_identity2 = __esm({
20389
20504
  });
20390
20505
 
20391
20506
  // ../cli/src/lib/signals/migrate.ts
20392
- import fs54 from "node:fs";
20393
- import path58 from "node:path";
20507
+ import fs55 from "node:fs";
20508
+ import path60 from "node:path";
20394
20509
  function readHandled() {
20395
20510
  let text5;
20396
20511
  try {
20397
- text5 = fs54.readFileSync(SIGNALS_OLD_HANDLED, "utf8");
20512
+ text5 = fs55.readFileSync(SIGNALS_OLD_HANDLED, "utf8");
20398
20513
  } catch {
20399
20514
  return [];
20400
20515
  }
@@ -20411,57 +20526,57 @@ function readHandled() {
20411
20526
  return held4;
20412
20527
  }
20413
20528
  function lift(from, into) {
20414
- fs54.mkdirSync(into, { recursive: true });
20415
- fs54.renameSync(from, path58.join(into, freeName(into, path58.basename(from))));
20529
+ fs55.mkdirSync(into, { recursive: true });
20530
+ fs55.renameSync(from, path60.join(into, freeName(into, path60.basename(from))));
20416
20531
  }
20417
20532
  function repoint(dir, from, into) {
20418
20533
  let entries;
20419
20534
  try {
20420
- entries = fs54.readdirSync(dir, { withFileTypes: true });
20535
+ entries = fs55.readdirSync(dir, { withFileTypes: true });
20421
20536
  } catch {
20422
20537
  return;
20423
20538
  }
20424
20539
  for (const entry of entries) {
20425
20540
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
20426
- const file = path58.join(dir, entry.name);
20427
- const text5 = fs54.readFileSync(file, "utf8");
20428
- if (text5.includes(from)) fs54.writeFileSync(file, text5.split(from).join(into));
20541
+ const file = path60.join(dir, entry.name);
20542
+ const text5 = fs55.readFileSync(file, "utf8");
20543
+ if (text5.includes(from)) fs55.writeFileSync(file, text5.split(from).join(into));
20429
20544
  }
20430
20545
  }
20431
20546
  function dropIfEmpty2(dir) {
20432
20547
  try {
20433
- fs54.rmdirSync(dir);
20548
+ fs55.rmdirSync(dir);
20434
20549
  } catch {
20435
20550
  }
20436
20551
  }
20437
20552
  function liftInbox() {
20438
- if (!fs54.existsSync(SIGNALS_OLD_INBOX)) return;
20439
- const oldFiles = path58.join(SIGNALS_OLD_INBOX, "files");
20440
- if (fs54.existsSync(oldFiles)) {
20441
- for (const entry of fs54.readdirSync(oldFiles, { withFileTypes: true })) {
20442
- if (entry.isFile()) lift(path58.join(oldFiles, entry.name), SIGNALS_FILES);
20553
+ if (!fs55.existsSync(SIGNALS_OLD_INBOX)) return;
20554
+ const oldFiles = path60.join(SIGNALS_OLD_INBOX, "files");
20555
+ if (fs55.existsSync(oldFiles)) {
20556
+ for (const entry of fs55.readdirSync(oldFiles, { withFileTypes: true })) {
20557
+ if (entry.isFile()) lift(path60.join(oldFiles, entry.name), SIGNALS_FILES);
20443
20558
  }
20444
20559
  dropIfEmpty2(oldFiles);
20445
20560
  }
20446
20561
  const held4 = new Set(readFolder(TRIAGE).map((signal2) => signal2.sourceId));
20447
- for (const entry of fs54.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
20562
+ for (const entry of fs55.readdirSync(SIGNALS_OLD_INBOX, { withFileTypes: true })) {
20448
20563
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
20449
- const from = path58.join(SIGNALS_OLD_INBOX, entry.name);
20564
+ const from = path60.join(SIGNALS_OLD_INBOX, entry.name);
20450
20565
  const item = parse(from);
20451
20566
  if (item && held4.has(item.sourceId)) {
20452
- fs54.rmSync(from, { force: true });
20567
+ fs55.rmSync(from, { force: true });
20453
20568
  continue;
20454
20569
  }
20455
20570
  if (item) held4.add(item.sourceId);
20456
20571
  lift(from, TRIAGE);
20457
20572
  }
20458
- const wasAt = asWritten(path58.join(SIGNALS_OLD_INBOX, "files"));
20573
+ const wasAt = asWritten(path60.join(SIGNALS_OLD_INBOX, "files"));
20459
20574
  const nowAt = asWritten(SIGNALS_FILES);
20460
20575
  for (const dir of [TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED]) repoint(dir, wasAt, nowAt);
20461
20576
  dropIfEmpty2(SIGNALS_OLD_INBOX);
20462
20577
  }
20463
20578
  function liftHandled() {
20464
- if (!fs54.existsSync(SIGNALS_OLD_HANDLED)) return;
20579
+ if (!fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
20465
20580
  const lines = readHandled();
20466
20581
  const held4 = /* @__PURE__ */ new Set([
20467
20582
  ...readFolder(TRIAGE).map((signal2) => signal2.sourceId),
@@ -20471,7 +20586,7 @@ function liftHandled() {
20471
20586
  for (const { sourceId, when } of lines) {
20472
20587
  if (held4.has(sourceId)) continue;
20473
20588
  held4.add(sourceId);
20474
- fs54.mkdirSync(SIGNALS_DISMISSED, { recursive: true });
20589
+ fs55.mkdirSync(SIGNALS_DISMISSED, { recursive: true });
20475
20590
  const name = freeName(SIGNALS_DISMISSED, fileName("", sourceId));
20476
20591
  const front = [
20477
20592
  "---",
@@ -20481,13 +20596,13 @@ function liftHandled() {
20481
20596
  "---",
20482
20597
  ""
20483
20598
  ];
20484
- fs54.writeFileSync(path58.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
20599
+ fs55.writeFileSync(path60.join(SIGNALS_DISMISSED, name), `${front.join("\n")}
20485
20600
  `);
20486
20601
  }
20487
- fs54.rmSync(SIGNALS_OLD_HANDLED, { force: true });
20602
+ fs55.rmSync(SIGNALS_OLD_HANDLED, { force: true });
20488
20603
  }
20489
20604
  function migrateTriage() {
20490
- if (!fs54.existsSync(SIGNALS_OLD_INBOX) && !fs54.existsSync(SIGNALS_OLD_HANDLED)) return;
20605
+ if (!fs55.existsSync(SIGNALS_OLD_INBOX) && !fs55.existsSync(SIGNALS_OLD_HANDLED)) return;
20491
20606
  try {
20492
20607
  liftInbox();
20493
20608
  liftHandled();
@@ -20502,13 +20617,13 @@ var init_migrate = __esm({
20502
20617
  init_yaml();
20503
20618
  init_inbox();
20504
20619
  HANDLED_LINE = /^-\s+(.+?)\s+—\s+(.+)$/;
20505
- asWritten = (dir) => `${rel(dir).split(path58.sep).join("/")}/`;
20620
+ asWritten = (dir) => `${rel(dir).split(path60.sep).join("/")}/`;
20506
20621
  }
20507
20622
  });
20508
20623
 
20509
20624
  // ../cli/src/lib/signals/add.ts
20510
- import fs55 from "node:fs";
20511
- import path59 from "node:path";
20625
+ import fs56 from "node:fs";
20626
+ import path61 from "node:path";
20512
20627
  import { createHash as createHash3 } from "node:crypto";
20513
20628
  function linkTitle(url) {
20514
20629
  let at;
@@ -20521,7 +20636,7 @@ function linkTitle(url) {
20521
20636
  return oneLine(where) || url.slice(0, TITLE_MAX);
20522
20637
  }
20523
20638
  function asText(file) {
20524
- const ext = path59.extname(file.name).toLowerCase();
20639
+ const ext = path61.extname(file.name).toLowerCase();
20525
20640
  if (!file.type.startsWith("text/") && file.type !== "application/json" && !TEXT_EXTENSIONS.has(ext)) return null;
20526
20641
  try {
20527
20642
  const text5 = new TextDecoder("utf-8", { fatal: true }).decode(file.data);
@@ -20531,9 +20646,9 @@ function asText(file) {
20531
20646
  }
20532
20647
  }
20533
20648
  function fileName2(name, data) {
20534
- const base = path59.basename(name);
20535
- const ext = path59.extname(base).toLowerCase().slice(0, 12);
20536
- const word = path59.basename(base, path59.extname(base)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40) || "file";
20649
+ const base = path61.basename(name);
20650
+ const ext = path61.extname(base).toLowerCase().slice(0, 12);
20651
+ const word = path61.basename(base, path61.extname(base)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40) || "file";
20537
20652
  const hash2 = createHash3("sha256").update(data).digest("hex").slice(0, 8);
20538
20653
  return `${word}-${hash2}${ext}`;
20539
20654
  }
@@ -20542,9 +20657,9 @@ function addToInbox(drop) {
20542
20657
  const typed = (drop.text ?? "").trim();
20543
20658
  const file = drop.file;
20544
20659
  if (!typed && !file) return { ok: false, error: "nothing to add \u2014 paste a link or some text, or drop a file." };
20545
- if (file && file.data.length === 0) return { ok: false, error: `${path59.basename(file.name)} is empty.` };
20660
+ if (file && file.data.length === 0) return { ok: false, error: `${path61.basename(file.name)} is empty.` };
20546
20661
  if (file && file.data.length > MAX_BYTES) {
20547
- return { ok: false, error: `${path59.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
20662
+ return { ok: false, error: `${path61.basename(file.name)} is over ${MAX_BYTES / 1024 / 1024} MB \u2014 too big for the board.` };
20548
20663
  }
20549
20664
  const read4 = describe(typed, file);
20550
20665
  const keep2 = read4.keep;
@@ -20566,8 +20681,8 @@ ${incoming.summary}`);
20566
20681
  if (seen.status === "pending" || seen.status === "archived") return { ok: false, error: `${sayHit(seen)}.` };
20567
20682
  try {
20568
20683
  if (keep2) {
20569
- fs55.mkdirSync(SIGNALS_FILES, { recursive: true });
20570
- fs55.writeFileSync(keep2.at, keep2.data);
20684
+ fs56.mkdirSync(SIGNALS_FILES, { recursive: true });
20685
+ fs56.writeFileSync(keep2.at, keep2.data);
20571
20686
  }
20572
20687
  return { ok: true, signal: writeSignal({ ...incoming, sourceId }, formatStamp(/* @__PURE__ */ new Date()), drop.slug) };
20573
20688
  } catch (e) {
@@ -20581,7 +20696,7 @@ function describe(typed, file) {
20581
20696
  return only ? [only] : [];
20582
20697
  };
20583
20698
  if (file) {
20584
- const name = path59.basename(file.name);
20699
+ const name = path61.basename(file.name);
20585
20700
  const text5 = asText(file);
20586
20701
  const note = typed ? `
20587
20702
 
@@ -20593,7 +20708,7 @@ ${typed}` : "";
20593
20708
  const at = keepAt(file);
20594
20709
  return {
20595
20710
  incoming: {
20596
- title: path59.basename(name, path59.extname(name)) || name,
20711
+ title: path61.basename(name, path61.extname(name)) || name,
20597
20712
  summary: `${name} \u2014 ${boardRel2(at)}${note}`,
20598
20713
  ...named2
20599
20714
  },
@@ -20631,73 +20746,73 @@ var init_add = __esm({
20631
20746
  MAX_BYTES = 25 * 1024 * 1024;
20632
20747
  TITLE_MAX = 120;
20633
20748
  TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".csv", ".json", ".yml", ".yaml", ".log"]);
20634
- boardRel2 = (file) => rel(file).split(path59.sep).join("/");
20749
+ boardRel2 = (file) => rel(file).split(path61.sep).join("/");
20635
20750
  oneLine = (text5) => {
20636
20751
  const first = text5.split("\n").find((line) => line.trim()) ?? "";
20637
20752
  const clean = first.replace(/^#+\s*/, "").trim();
20638
20753
  return clean.length > TITLE_MAX ? `${clean.slice(0, TITLE_MAX - 1).trimEnd()}\u2026` : clean;
20639
20754
  };
20640
- keepAt = (file) => path59.join(SIGNALS_FILES, fileName2(file.name, file.data));
20755
+ keepAt = (file) => path61.join(SIGNALS_FILES, fileName2(file.name, file.data));
20641
20756
  }
20642
20757
  });
20643
20758
 
20644
20759
  // ../cli/src/lib/recurring.ts
20645
- import fs56 from "node:fs";
20646
- import path60 from "node:path";
20760
+ import fs57 from "node:fs";
20761
+ import path62 from "node:path";
20647
20762
  function migratePruneMemoryCard() {
20648
20763
  const dir = recurringDir();
20649
20764
  let names4;
20650
20765
  try {
20651
- names4 = fs56.readdirSync(dir);
20766
+ names4 = fs57.readdirSync(dir);
20652
20767
  } catch {
20653
20768
  return null;
20654
20769
  }
20655
20770
  const name = names4.find((entry) => entry.endsWith(`-${PRUNE_SLUG}.md`));
20656
20771
  if (!name) return null;
20657
- const file = path60.join(dir, name);
20772
+ const file = path62.join(dir, name);
20658
20773
  try {
20659
- const { meta } = parseFrontmatter(fs56.readFileSync(file, "utf8"));
20774
+ const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
20660
20775
  const cadence = meta?.cadence;
20661
20776
  if (typeof cadence === "string") adoptMemoryPruneCadence(cadence);
20662
20777
  } catch {
20663
20778
  }
20664
- fs56.rmSync(file, { force: true });
20779
+ fs57.rmSync(file, { force: true });
20665
20780
  return rel(file);
20666
20781
  }
20667
20782
  function legacyCards() {
20668
20783
  const dir = recurringDir();
20669
- if (!fs56.existsSync(dir)) return [];
20784
+ if (!fs57.existsSync(dir)) return [];
20670
20785
  const cards = [];
20671
- for (const entry of fs56.readdirSync(dir, { withFileTypes: true })) {
20786
+ for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
20672
20787
  const id = idPrefix(entry.name);
20673
20788
  if (id === null) continue;
20674
- const full = path60.join(dir, entry.name);
20789
+ const full = path62.join(dir, entry.name);
20675
20790
  const slug2 = entry.name.replace(/\.md$/, "").replace(/^\d+-?/, "");
20676
20791
  if (entry.isFile() && entry.name.endsWith(".md")) cards.push({ id, slug: slug2, file: full });
20677
- else if (entry.isDirectory() && fs56.existsSync(path60.join(full, "root.md"))) {
20678
- cards.push({ id, slug: slug2, file: path60.join(full, "root.md"), folder: full });
20792
+ else if (entry.isDirectory() && fs57.existsSync(path62.join(full, "root.md"))) {
20793
+ cards.push({ id, slug: slug2, file: path62.join(full, "root.md"), folder: full });
20679
20794
  }
20680
20795
  }
20681
20796
  return cards.sort((a, b) => a.id - b.id);
20682
20797
  }
20683
20798
  function mergeInto2(from, to, skip = []) {
20684
- fs56.mkdirSync(to, { recursive: true });
20685
- for (const entry of fs56.readdirSync(from, { withFileTypes: true })) {
20799
+ fs57.mkdirSync(to, { recursive: true });
20800
+ for (const entry of fs57.readdirSync(from, { withFileTypes: true })) {
20686
20801
  if (skip.includes(entry.name)) continue;
20687
- const source = path60.join(from, entry.name);
20688
- const target = path60.join(to, entry.name);
20689
- if (!fs56.existsSync(target)) fs56.renameSync(source, target);
20690
- else if (entry.isDirectory() && fs56.statSync(target).isDirectory()) mergeInto2(source, target);
20802
+ const source = path62.join(from, entry.name);
20803
+ const target = path62.join(to, entry.name);
20804
+ if (!fs57.existsSync(target)) fs57.renameSync(source, target);
20805
+ else if (entry.isDirectory() && fs57.statSync(target).isDirectory()) mergeInto2(source, target);
20691
20806
  }
20692
- if (isEmpty(from)) fs56.rmSync(from, { recursive: true, force: true });
20807
+ if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
20693
20808
  }
20694
20809
  function moveSideFolders() {
20695
20810
  const dir = recurringDir();
20696
- for (const entry of fs56.readdirSync(dir, { withFileTypes: true })) {
20811
+ for (const entry of fs57.readdirSync(dir, { withFileTypes: true })) {
20697
20812
  if (!entry.isDirectory() || idPrefix(entry.name) !== null) continue;
20698
- const from = path60.join(dir, entry.name);
20699
- if (isEmpty(from)) fs56.rmSync(from, { recursive: true, force: true });
20700
- else mergeInto2(from, path60.join(AGENTS, entry.name));
20813
+ const from = path62.join(dir, entry.name);
20814
+ if (isEmpty(from)) fs57.rmSync(from, { recursive: true, force: true });
20815
+ else mergeInto2(from, path62.join(AGENTS, entry.name));
20701
20816
  }
20702
20817
  }
20703
20818
  function sections(body) {
@@ -20747,8 +20862,8 @@ function agentName(card, text5) {
20747
20862
  const known2 = /* @__PURE__ */ new Set([...specAgentCatalog().agents.map((a) => a.name), ...ROLE_NAMES]);
20748
20863
  for (const name of [slug2, `${slug2}-${card.id}`]) {
20749
20864
  if (!AGENT_NAME.test(name)) continue;
20750
- const file = path60.join(AGENTS, name, AGENT_FILE3);
20751
- const written = fs56.existsSync(file) ? fs56.readFileSync(file, "utf8") : null;
20865
+ const file = path62.join(AGENTS, name, AGENT_FILE3);
20866
+ const written = fs57.existsSync(file) ? fs57.readFileSync(file, "utf8") : null;
20752
20867
  if (written === text5(name)) return name;
20753
20868
  if (written === null && !known2.has(name)) return name;
20754
20869
  }
@@ -20765,11 +20880,11 @@ ${q.options.map((o) => `- ${o}`).join("\n")}` : "";
20765
20880
  function removeCard(card) {
20766
20881
  dropCrossRefs(card.id);
20767
20882
  const target = card.folder ?? card.file;
20768
- stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path60.relative(TODO, target).split(path60.sep).join("/") });
20769
- fs56.rmSync(target, { recursive: true, force: true });
20883
+ stripReadmeRefs({ kind: card.folder ? "group" : "file", rel: path62.relative(TODO, target).split(path62.sep).join("/") });
20884
+ fs57.rmSync(target, { recursive: true, force: true });
20770
20885
  }
20771
20886
  function migrateCard(card) {
20772
- const { meta, body } = parseFrontmatter(fs56.readFileSync(card.file, "utf8"));
20887
+ const { meta, body } = parseFrontmatter(fs57.readFileSync(card.file, "utf8"));
20773
20888
  if (!meta) throw new Error("its frontmatter cannot be read");
20774
20889
  const raw = meta;
20775
20890
  const said3 = (key) => typeof raw[key] === "string" ? raw[key].trim() : "";
@@ -20779,19 +20894,19 @@ function migrateCard(card) {
20779
20894
  if (!flow) throw new Error("this board has no workflow to put it in");
20780
20895
  const text5 = (name2) => agentText(name2, meta.title || card.slug, body);
20781
20896
  const name = agentName(card, text5);
20782
- const dir = path60.join(AGENTS, name);
20783
- const file = path60.join(dir, AGENT_FILE3);
20784
- const madeDir = !fs56.existsSync(dir);
20785
- const wrote = !fs56.existsSync(file);
20897
+ const dir = path62.join(AGENTS, name);
20898
+ const file = path62.join(dir, AGENT_FILE3);
20899
+ const madeDir = !fs57.existsSync(dir);
20900
+ const wrote = !fs57.existsSync(file);
20786
20901
  try {
20787
- fs56.mkdirSync(dir, { recursive: true });
20788
- if (wrote) fs56.writeFileSync(file, text5(name));
20902
+ fs57.mkdirSync(dir, { recursive: true });
20903
+ if (wrote) fs57.writeFileSync(file, text5(name));
20789
20904
  const adopted = adoptWorkflowScheduled(flow.id, name, { cadence, lastRun });
20790
20905
  if (!adopted.ok) throw new Error(adopted.error);
20791
20906
  questionsToTriage(meta, name);
20792
20907
  } catch (e) {
20793
- if (madeDir) fs56.rmSync(dir, { recursive: true, force: true });
20794
- else if (wrote) fs56.rmSync(file, { force: true });
20908
+ if (madeDir) fs57.rmSync(dir, { recursive: true, force: true });
20909
+ else if (wrote) fs57.rmSync(file, { force: true });
20795
20910
  throw e;
20796
20911
  }
20797
20912
  if (card.folder) mergeInto2(card.folder, dir, ["root.md"]);
@@ -20801,7 +20916,7 @@ function migrateCard(card) {
20801
20916
  function migrateRecurringCards() {
20802
20917
  const done2 = { agents: [], removed: [], failed: [] };
20803
20918
  const dir = recurringDir();
20804
- if (!fs56.existsSync(dir)) return done2;
20919
+ if (!fs57.existsSync(dir)) return done2;
20805
20920
  migratePruneMemoryCard();
20806
20921
  moveSideFolders();
20807
20922
  for (const card of legacyCards()) {
@@ -20814,7 +20929,7 @@ function migrateRecurringCards() {
20814
20929
  done2.failed.push({ card: rel(card.file), why: e instanceof Error ? e.message : String(e) });
20815
20930
  }
20816
20931
  }
20817
- if (isEmpty(dir)) fs56.rmSync(dir, { recursive: true, force: true });
20932
+ if (isEmpty(dir)) fs57.rmSync(dir, { recursive: true, force: true });
20818
20933
  return done2;
20819
20934
  }
20820
20935
  function recurringMigrationLines(done2) {
@@ -20847,14 +20962,14 @@ var init_recurring = __esm({
20847
20962
  PRUNE_SLUG = "prune-the-memory";
20848
20963
  FETCH_SLUG = "fetch-triage-items";
20849
20964
  AGENT_FILE3 = "AGENT.md";
20850
- recurringDir = () => path60.join(TODO, LEGACY_RECURRING);
20851
- isEmpty = (dir) => fs56.readdirSync(dir).every((name) => name === ".DS_Store");
20965
+ recurringDir = () => path62.join(TODO, LEGACY_RECURRING);
20966
+ isEmpty = (dir) => fs57.readdirSync(dir).every((name) => name === ".DS_Store");
20852
20967
  SECTION = /^##\s+(.+?)\s*$/;
20853
20968
  }
20854
20969
  });
20855
20970
 
20856
20971
  // ../cli/src/commands/init.ts
20857
- import fs57 from "node:fs";
20972
+ import fs58 from "node:fs";
20858
20973
  function boardReadme() {
20859
20974
  return `# Board
20860
20975
 
@@ -20867,20 +20982,20 @@ _(none)_
20867
20982
  `;
20868
20983
  }
20869
20984
  function writeConfigIfMissing() {
20870
- if (fs57.existsSync(CONFIG)) return false;
20871
- fs57.mkdirSync(KANBAN, { recursive: true });
20872
- fs57.writeFileSync(CONFIG, CONFIG_TEMPLATE);
20985
+ if (fs58.existsSync(CONFIG)) return false;
20986
+ fs58.mkdirSync(KANBAN, { recursive: true });
20987
+ fs58.writeFileSync(CONFIG, CONFIG_TEMPLATE);
20873
20988
  return true;
20874
20989
  }
20875
20990
  function writeModulesIfMissing() {
20876
- if (fs57.existsSync(MODULES_MD)) return false;
20877
- fs57.mkdirSync(KANBAN, { recursive: true });
20878
- fs57.writeFileSync(MODULES_MD, MODULES_TEMPLATE);
20991
+ if (fs58.existsSync(MODULES_MD)) return false;
20992
+ fs58.mkdirSync(KANBAN, { recursive: true });
20993
+ fs58.writeFileSync(MODULES_MD, MODULES_TEMPLATE);
20879
20994
  return true;
20880
20995
  }
20881
20996
  function writeGitignoreIfMissing() {
20882
- fs57.mkdirSync(KANBAN, { recursive: true });
20883
- const text5 = fs57.existsSync(KANBAN_GITIGNORE) ? fs57.readFileSync(KANBAN_GITIGNORE, "utf8") : "";
20997
+ fs58.mkdirSync(KANBAN, { recursive: true });
20998
+ const text5 = fs58.existsSync(KANBAN_GITIGNORE) ? fs58.readFileSync(KANBAN_GITIGNORE, "utf8") : "";
20884
20999
  const has = (rule) => text5.split("\n").some((line) => line.trim() === rule.line);
20885
21000
  const missing = IGNORE_RULES.filter((rule) => !has(rule));
20886
21001
  if (!missing.length) return false;
@@ -20888,37 +21003,37 @@ function writeGitignoreIfMissing() {
20888
21003
  ${rule.line}
20889
21004
  `).join("");
20890
21005
  const separator = !text5 || text5.endsWith("\n") ? "" : "\n";
20891
- fs57.writeFileSync(KANBAN_GITIGNORE, `${text5}${separator}${block}`);
21006
+ fs58.writeFileSync(KANBAN_GITIGNORE, `${text5}${separator}${block}`);
20892
21007
  return true;
20893
21008
  }
20894
21009
  function writeSkeletonIfMissing() {
20895
21010
  const added = [];
20896
- if (!fs57.existsSync(TODO)) {
20897
- fs57.mkdirSync(TODO, { recursive: true });
21011
+ if (!fs58.existsSync(TODO)) {
21012
+ fs58.mkdirSync(TODO, { recursive: true });
20898
21013
  added.push(`${rel(TODO)}/`);
20899
21014
  }
20900
- if (!fs57.existsSync(README)) {
20901
- fs57.writeFileSync(README, boardReadme());
21015
+ if (!fs58.existsSync(README)) {
21016
+ fs58.writeFileSync(README, boardReadme());
20902
21017
  added.push(rel(README));
20903
21018
  }
20904
21019
  return added;
20905
21020
  }
20906
21021
  function retireReleases() {
20907
21022
  const done2 = [];
20908
- if (fs57.existsSync(RELEASES)) {
20909
- fs57.rmSync(RELEASES);
21023
+ if (fs58.existsSync(RELEASES)) {
21024
+ fs58.rmSync(RELEASES);
20910
21025
  done2.push(`deleted ${rel(RELEASES)}`);
20911
21026
  }
20912
21027
  let cards = 0;
20913
21028
  for (const dir of [TODO, ARCHIVE]) {
20914
- if (!fs57.existsSync(dir)) continue;
21029
+ if (!fs58.existsSync(dir)) continue;
20915
21030
  for (const file of walkMd(dir)) {
20916
- const text5 = fs57.readFileSync(file, "utf8");
21031
+ const text5 = fs58.readFileSync(file, "utf8");
20917
21032
  const end2 = text5.startsWith("---\n") ? text5.indexOf("\n---", 3) : -1;
20918
21033
  if (end2 < 0) continue;
20919
21034
  const head = text5.slice(0, end2).replace(/\nrelease:[^\n]*/, "");
20920
21035
  if (head.length === end2) continue;
20921
- fs57.writeFileSync(file, head + text5.slice(end2));
21036
+ fs58.writeFileSync(file, head + text5.slice(end2));
20922
21037
  cards++;
20923
21038
  }
20924
21039
  }
@@ -20926,7 +21041,7 @@ function retireReleases() {
20926
21041
  return done2.join(", ");
20927
21042
  }
20928
21043
  function cmdInit() {
20929
- if (fs57.existsSync(KANBAN)) {
21044
+ if (fs58.existsSync(KANBAN)) {
20930
21045
  const added = [
20931
21046
  ...writeSkeletonIfMissing(),
20932
21047
  writeConfigIfMissing() && rel(CONFIG),
@@ -20961,8 +21076,8 @@ function cmdInit() {
20961
21076
  }
20962
21077
  return { board: rel(KANBAN), created: false, added, memory_paths: scaffolded.map((s) => rel(s.dir)) };
20963
21078
  }
20964
- fs57.mkdirSync(TODO, { recursive: true });
20965
- fs57.writeFileSync(README, boardReadme());
21079
+ fs58.mkdirSync(TODO, { recursive: true });
21080
+ fs58.writeFileSync(README, boardReadme());
20966
21081
  writeConfigIfMissing();
20967
21082
  scaffoldProjectMemory();
20968
21083
  writeModulesIfMissing();
@@ -21051,25 +21166,25 @@ var init_scheduled = __esm({
21051
21166
  });
21052
21167
 
21053
21168
  // ../cli/src/lib/card-age.ts
21054
- import fs58 from "node:fs";
21055
- import path61 from "node:path";
21169
+ import fs59 from "node:fs";
21170
+ import path63 from "node:path";
21056
21171
  function staleAfterIn(config) {
21057
21172
  const days = Number(LINE.exec(config)?.[1]);
21058
21173
  return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
21059
21174
  }
21060
21175
  function staleAfter() {
21061
- if (!fs58.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
21062
- return staleAfterIn(fs58.readFileSync(CONFIG, "utf8"));
21176
+ if (!fs59.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
21177
+ return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
21063
21178
  }
21064
21179
  function todoPathspec(top) {
21065
- const from = path61.relative(real(top), real(TODO));
21066
- if (!from || from.startsWith("..") || path61.isAbsolute(from)) return null;
21067
- return from.split(path61.sep).join("/");
21180
+ const from = path63.relative(real(top), real(TODO));
21181
+ if (!from || from.startsWith("..") || path63.isAbsolute(from)) return null;
21182
+ return from.split(path63.sep).join("/");
21068
21183
  }
21069
21184
  function cardFile2(spec, printed) {
21070
21185
  const p = unquotePath(printed);
21071
21186
  if (!p.startsWith(`${spec}/`)) return null;
21072
- return path61.join(TODO, ...p.slice(spec.length + 1).split("/"));
21187
+ return path63.join(TODO, ...p.slice(spec.length + 1).split("/"));
21073
21188
  }
21074
21189
  function dirtyCards(top, spec) {
21075
21190
  const out = git(["status", "--porcelain", "--untracked-files=all", "--", spec], top);
@@ -21116,9 +21231,9 @@ var init_card_age = __esm({
21116
21231
  gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
21117
21232
  real = (p) => {
21118
21233
  try {
21119
- return fs58.realpathSync(p);
21234
+ return fs59.realpathSync(p);
21120
21235
  } catch {
21121
- return path61.resolve(p);
21236
+ return path63.resolve(p);
21122
21237
  }
21123
21238
  };
21124
21239
  unquotePath = (line) => {
@@ -21153,8 +21268,8 @@ var init_card_holds = __esm({
21153
21268
  });
21154
21269
 
21155
21270
  // ../cli/src/commands/list.ts
21156
- import fs59 from "node:fs";
21157
- import path62 from "node:path";
21271
+ import fs60 from "node:fs";
21272
+ import path64 from "node:path";
21158
21273
  function summaryLine(body) {
21159
21274
  const lines = body.split("\n");
21160
21275
  let i = 0;
@@ -21170,13 +21285,13 @@ function summaryLine(body) {
21170
21285
  function openRows() {
21171
21286
  const rows = [];
21172
21287
  for (const file of walkMd(TODO)) {
21173
- const base = path62.basename(file);
21288
+ const base = path64.basename(file);
21174
21289
  if (base === "README.md") continue;
21175
21290
  const isRoot = base === "root.md";
21176
- const id = isRoot ? idPrefix(path62.basename(path62.dirname(file))) : idPrefix(base);
21291
+ const id = isRoot ? idPrefix(path64.basename(path64.dirname(file))) : idPrefix(base);
21177
21292
  if (id == null) continue;
21178
- if (isLegacyRecurring(path62.relative(TODO, file))) continue;
21179
- const { meta, body } = parseFrontmatter(fs59.readFileSync(file, "utf8"));
21293
+ if (isLegacyRecurring(path64.relative(TODO, file))) continue;
21294
+ const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
21180
21295
  rows.push({
21181
21296
  id,
21182
21297
  file,
@@ -21218,15 +21333,15 @@ function cmdStale(rows, all2, scope, mod) {
21218
21333
  }
21219
21334
  function archivedFiles() {
21220
21335
  const files2 = /* @__PURE__ */ new Map();
21221
- if (!fs59.existsSync(ARCHIVE)) return files2;
21336
+ if (!fs60.existsSync(ARCHIVE)) return files2;
21222
21337
  for (const file of walkMd(ARCHIVE)) {
21223
- const id = idPrefix(path62.basename(file));
21338
+ const id = idPrefix(path64.basename(file));
21224
21339
  if (id !== null) files2.set(id, file);
21225
21340
  }
21226
21341
  for (const dir of walkDirs(ARCHIVE)) {
21227
- const id = idPrefix(path62.basename(dir));
21228
- const root = path62.join(dir, "root.md");
21229
- if (id !== null && fs59.existsSync(root)) files2.set(id, root);
21342
+ const id = idPrefix(path64.basename(dir));
21343
+ const root = path64.join(dir, "root.md");
21344
+ if (id !== null && fs60.existsSync(root)) files2.set(id, root);
21230
21345
  }
21231
21346
  return files2;
21232
21347
  }
@@ -21337,14 +21452,14 @@ var init_list = __esm({
21337
21452
  });
21338
21453
 
21339
21454
  // ../cli/src/commands/misc.ts
21340
- import fs60 from "node:fs";
21341
- import path63 from "node:path";
21455
+ import fs61 from "node:fs";
21456
+ import path65 from "node:path";
21342
21457
  function extractOldMeta(text5, file) {
21343
21458
  const grab = (re) => {
21344
21459
  const m = text5.match(re);
21345
21460
  return m ? m[1].trim() : null;
21346
21461
  };
21347
- const title = grab(/^#\s+(.+)$/m) || slugify(path63.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
21462
+ const title = grab(/^#\s+(.+)$/m) || slugify(path65.basename(file, ".md").replace(/^\d+-/, "")).replace(/-/g, " ");
21348
21463
  const norm = (raw) => {
21349
21464
  const v = (raw || "").toLowerCase();
21350
21465
  return LEVELS.includes(v) ? v : "med";
@@ -21371,11 +21486,11 @@ function stripOldHeader(text5) {
21371
21486
  }
21372
21487
  function cmdMigrate(opts) {
21373
21488
  const dry = opts.dryRun === true;
21374
- const files2 = walkMd(TODO).filter((f) => path63.basename(f) !== "README.md");
21489
+ const files2 = walkMd(TODO).filter((f) => path65.basename(f) !== "README.md");
21375
21490
  let changed = 0;
21376
21491
  let skipped = 0;
21377
21492
  for (const file of files2) {
21378
- const text5 = fs60.readFileSync(file, "utf8");
21493
+ const text5 = fs61.readFileSync(file, "utf8");
21379
21494
  if (text5.trimStart().startsWith("---")) {
21380
21495
  skipped++;
21381
21496
  continue;
@@ -21384,7 +21499,7 @@ function cmdMigrate(opts) {
21384
21499
  const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
21385
21500
  if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
21386
21501
  else {
21387
- fs60.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
21502
+ fs61.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
21388
21503
  say(`migrated ${rel(file)}`);
21389
21504
  }
21390
21505
  changed++;
@@ -21405,10 +21520,10 @@ var init_misc = __esm({
21405
21520
  });
21406
21521
 
21407
21522
  // ../cli/src/lib/group-close.ts
21408
- import fs61 from "node:fs";
21523
+ import fs62 from "node:fs";
21409
21524
  function groupCloses(rootFile) {
21410
21525
  try {
21411
- return groupCloseOf(fs61.readFileSync(rootFile, "utf8"));
21526
+ return groupCloseOf(fs62.readFileSync(rootFile, "utf8"));
21412
21527
  } catch {
21413
21528
  return false;
21414
21529
  }
@@ -21421,12 +21536,12 @@ var init_group_close = __esm({
21421
21536
  });
21422
21537
 
21423
21538
  // ../cli/src/commands/remove.ts
21424
- import fs62 from "node:fs";
21425
- import path64 from "node:path";
21539
+ import fs63 from "node:fs";
21540
+ import path66 from "node:path";
21426
21541
  function leavingIds(id, found2) {
21427
21542
  const ids = /* @__PURE__ */ new Set([id]);
21428
21543
  if (found2.kind === "group") {
21429
- const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path64.basename(f));
21544
+ const names4 = [...walkMd(found2.target), ...walkDirs(found2.target)].map((f) => path66.basename(f));
21430
21545
  for (const name of names4) {
21431
21546
  const n = idPrefix(name);
21432
21547
  if (n !== null) ids.add(n);
@@ -21438,10 +21553,10 @@ function findMentions(ids) {
21438
21553
  const hits = [];
21439
21554
  const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
21440
21555
  for (const dir of [TODO, MEMORY]) {
21441
- if (!fs62.existsSync(dir)) continue;
21556
+ if (!fs63.existsSync(dir)) continue;
21442
21557
  for (const file of walkMd(dir)) {
21443
- if (path64.basename(file) === "README.md") continue;
21444
- const lines = fs62.readFileSync(file, "utf8").split("\n");
21558
+ if (path66.basename(file) === "README.md") continue;
21559
+ const lines = fs63.readFileSync(file, "utf8").split("\n");
21445
21560
  const fmEnd = frontmatterEnd(lines);
21446
21561
  lines.forEach((line, i) => {
21447
21562
  if (!re.test(line)) return;
@@ -21457,12 +21572,12 @@ function findMentions(ids) {
21457
21572
  return hits;
21458
21573
  }
21459
21574
  function leavingCards(id, found2) {
21460
- const own = found2.kind === "group" ? path64.join(found2.target, "root.md") : found2.target;
21575
+ const own = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
21461
21576
  const cards = [{ id, file: own }];
21462
21577
  if (found2.kind !== "group") return cards;
21463
21578
  for (const file of walkMd(found2.target)) {
21464
21579
  if (file === own) continue;
21465
- const n = idPrefix(path64.basename(file));
21580
+ const n = idPrefix(path66.basename(file));
21466
21581
  if (n !== null) cards.push({ id: n, file });
21467
21582
  }
21468
21583
  return cards;
@@ -21471,10 +21586,10 @@ function stampLeaving(cards, rejection) {
21471
21586
  const day = formatDay();
21472
21587
  const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
21473
21588
  for (const card of cards) {
21474
- if (!fs62.existsSync(card.file)) continue;
21475
- const text5 = fs62.readFileSync(card.file, "utf8");
21589
+ if (!fs63.existsSync(card.file)) continue;
21590
+ const text5 = fs63.readFileSync(card.file, "utf8");
21476
21591
  const stamped = stampLeavingIn(text5, day, stamp4);
21477
- if (stamped !== text5) fs62.writeFileSync(card.file, stamped);
21592
+ if (stamped !== text5) fs63.writeFileSync(card.file, stamped);
21478
21593
  }
21479
21594
  }
21480
21595
  function unfinishedDelivery(id) {
@@ -21490,7 +21605,7 @@ function unfinishedDelivery(id) {
21490
21605
  }[gap];
21491
21606
  return `#${id} is not finished: ${what}.`;
21492
21607
  }
21493
- const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs62.existsSync(path64.join(ASSETS, src.slice(".assets/".length))));
21608
+ const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(path66.join(ASSETS, src.slice(".assets/".length))));
21494
21609
  return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
21495
21610
  }
21496
21611
  function agentDecisions(text5) {
@@ -21517,8 +21632,8 @@ function removeCard2(id, metric, options) {
21517
21632
  const unfinished = metric === "completed" ? unfinishedDelivery(id) : null;
21518
21633
  if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
21519
21634
  const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
21520
- const cardFile3 = found2.kind === "group" ? path64.join(found2.target, "root.md") : found2.target;
21521
- const cardText = fs62.existsSync(cardFile3) ? fs62.readFileSync(cardFile3, "utf8") : "";
21635
+ const cardFile3 = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
21636
+ const cardText = fs63.existsSync(cardFile3) ? fs63.readFileSync(cardFile3, "utf8") : "";
21522
21637
  const { meta: cardMeta3 } = parseFrontmatter(cardText);
21523
21638
  const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
21524
21639
  const leftIds = leavingIds(id, found2);
@@ -21549,10 +21664,10 @@ function removeCard2(id, metric, options) {
21549
21664
  const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
21550
21665
  if (dest) {
21551
21666
  stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
21552
- fs62.mkdirSync(ARCHIVE, { recursive: true });
21553
- fs62.renameSync(found2.target, dest);
21667
+ fs63.mkdirSync(ARCHIVE, { recursive: true });
21668
+ fs63.renameSync(found2.target, dest);
21554
21669
  } else {
21555
- fs62.rmSync(found2.target, { recursive: true, force: true });
21670
+ fs63.rmSync(found2.target, { recursive: true, force: true });
21556
21671
  }
21557
21672
  const removedRefs = stripReadmeRefs(found2);
21558
21673
  let marked2 = null;
@@ -21601,14 +21716,14 @@ function removeCard2(id, metric, options) {
21601
21716
  };
21602
21717
  }
21603
21718
  function closeGroup(rootFile) {
21604
- const rootId = idPrefix(path64.basename(path64.dirname(rootFile)));
21719
+ const rootId = idPrefix(path66.basename(path66.dirname(rootFile)));
21605
21720
  if (rootId === null) return null;
21606
21721
  if (runnerProject(KANBAN)) return null;
21607
21722
  if (!groupCloses(rootFile)) return null;
21608
21723
  say(`
21609
21724
  every subtask line on #${rootId} is resolved \u2014 closing the group:`);
21610
21725
  try {
21611
- const { meta, body } = parseFrontmatter(fs62.readFileSync(rootFile, "utf8"));
21726
+ const { meta, body } = parseFrontmatter(fs63.readFileSync(rootFile, "utf8"));
21612
21727
  const res = cmdRemove(rootId, "completed", { closing: true });
21613
21728
  return {
21614
21729
  id: rootId,
@@ -21665,7 +21780,7 @@ function cleanupDiscardedCards(sessionId) {
21665
21780
  if (card.pending) throw new Error(`Discarding #${card.id} did not finish. Retry the discard.`);
21666
21781
  while (locate(card.id)) cmdRemove(card.id, "rejected", { discard: true, cleanupDiscarded: true });
21667
21782
  dropCrossRefs(card.id);
21668
- stripReadmeRefs({ kind: "file", rel: path64.relative(TODO, path64.resolve(REPO_ROOT, card.path)).split(path64.sep).join("/") });
21783
+ stripReadmeRefs({ kind: "file", rel: path66.relative(TODO, path66.resolve(REPO_ROOT, card.path)).split(path66.sep).join("/") });
21669
21784
  }
21670
21785
  return discarded.map((c) => c.id);
21671
21786
  });
@@ -21751,8 +21866,8 @@ var init_memory_file = __esm({
21751
21866
  });
21752
21867
 
21753
21868
  // ../cli/src/commands/rule.ts
21754
- import fs63 from "node:fs";
21755
- import path65 from "node:path";
21869
+ import fs64 from "node:fs";
21870
+ import path67 from "node:path";
21756
21871
  function cmdRule(askedName, flags) {
21757
21872
  const agent = askedName.trim();
21758
21873
  if (!agentRoster().some((a) => a.name === agent)) {
@@ -21761,7 +21876,7 @@ function cmdRule(askedName, flags) {
21761
21876
  const rule = readRuleText(flags.file, flags.text);
21762
21877
  const res = setAgentRule(agent, rule);
21763
21878
  if (!res.ok) die(res.error);
21764
- const file = rel(path65.join(RULES, `${agent}.md`));
21879
+ const file = rel(path67.join(RULES, `${agent}.md`));
21765
21880
  say(rule ? `wrote the \`${agent}\` rule (${file})` : `cleared the \`${agent}\` rule`);
21766
21881
  return { agent, file, cleared: !rule };
21767
21882
  }
@@ -21769,7 +21884,7 @@ function readRuleText(file, text5) {
21769
21884
  if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
21770
21885
  if (file !== void 0) {
21771
21886
  try {
21772
- return fs63.readFileSync(file, "utf8").trim();
21887
+ return fs64.readFileSync(file, "utf8").trim();
21773
21888
  } catch {
21774
21889
  die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
21775
21890
  }
@@ -21896,22 +22011,22 @@ var init_storyboard = __esm({
21896
22011
  });
21897
22012
 
21898
22013
  // ../cli/src/lib/spec-contract.ts
21899
- import fs64 from "node:fs";
21900
- import path66 from "node:path";
22014
+ import fs65 from "node:fs";
22015
+ import path68 from "node:path";
21901
22016
  function snapshotSpecs() {
21902
22017
  const out = /* @__PURE__ */ new Map();
21903
- if (!fs64.existsSync(TODO)) return out;
22018
+ if (!fs65.existsSync(TODO)) return out;
21904
22019
  for (const file of walkMd(TODO)) {
21905
- if (isLegacyRecurring(path66.relative(TODO, file))) continue;
21906
- const id = idPrefix(path66.basename(file) === "root.md" ? path66.basename(path66.dirname(file)) : path66.basename(file));
21907
- if (id !== null) out.set(file, { id, text: fs64.readFileSync(file, "utf8") });
22020
+ if (isLegacyRecurring(path68.relative(TODO, file))) continue;
22021
+ const id = idPrefix(path68.basename(file) === "root.md" ? path68.basename(path68.dirname(file)) : path68.basename(file));
22022
+ if (id !== null) out.set(file, { id, text: fs65.readFileSync(file, "utf8") });
21908
22023
  }
21909
22024
  return out;
21910
22025
  }
21911
22026
  function enclosingGroups(file) {
21912
22027
  const ids = [];
21913
22028
  for (let root = enclosingGroupRoot(file); root; root = enclosingGroupRoot(root)) {
21914
- const id = idPrefix(path66.basename(path66.dirname(root)));
22029
+ const id = idPrefix(path68.basename(path68.dirname(root)));
21915
22030
  if (id !== null) ids.push(id);
21916
22031
  }
21917
22032
  return ids;
@@ -22080,9 +22195,9 @@ function validateSpec(file, text5, id) {
22080
22195
  }
22081
22196
  function inside(dir, name) {
22082
22197
  try {
22083
- const root = fs64.realpathSync(dir);
22084
- const real2 = fs64.realpathSync(path66.join(dir, name));
22085
- return real2.startsWith(root + path66.sep) && fs64.statSync(real2).isFile() ? real2 : null;
22198
+ const root = fs65.realpathSync(dir);
22199
+ const real2 = fs65.realpathSync(path68.join(dir, name));
22200
+ return real2.startsWith(root + path68.sep) && fs65.statSync(real2).isFile() ? real2 : null;
22086
22201
  } catch {
22087
22202
  return null;
22088
22203
  }
@@ -22100,7 +22215,7 @@ function variantsProblem(raw, src, id) {
22100
22215
  if (files2.length < 2 || !files2.every((f) => typeof f === "string" && f)) return `variants needs at least two labels, each naming a file. ${shape}`;
22101
22216
  if (!files2.includes(src)) return `src="${src}" is not one of the variants. Set src to the version shown first.`;
22102
22217
  if (id === void 0) return null;
22103
- const dir = path66.join(ASSETS, String(id));
22218
+ const dir = path68.join(ASSETS, String(id));
22104
22219
  for (const f of files2) {
22105
22220
  const parts = f.split("/");
22106
22221
  if (parts.length !== 3 || parts[0] !== ".assets" || parts[1] !== String(id) || !inside(dir, parts[2])) {
@@ -22110,7 +22225,7 @@ function variantsProblem(raw, src, id) {
22110
22225
  return null;
22111
22226
  }
22112
22227
  function compareProblem(attrs, id) {
22113
- const dir = path66.join(ASSETS, String(id));
22228
+ const dir = path68.join(ASSETS, String(id));
22114
22229
  const src = (message) => ({ rule: "compare-src", message });
22115
22230
  const shots = (message) => ({ rule: "compare-shots", message });
22116
22231
  for (const side of ["a", "b"]) {
@@ -22130,7 +22245,7 @@ function compareProblem(attrs, id) {
22130
22245
  const file = inside(dir, named2.name);
22131
22246
  if (!file) return { message: `Compare shots ${at} names no file in this card's asset folder. Write the JSON there or fix the path.` };
22132
22247
  try {
22133
- return { data: JSON.parse(fs64.readFileSync(file, "utf8")) };
22248
+ return { data: JSON.parse(fs65.readFileSync(file, "utf8")) };
22134
22249
  } catch {
22135
22250
  return { message: `${at} is not valid JSON.` };
22136
22251
  }
@@ -22150,7 +22265,7 @@ function compareProblem(attrs, id) {
22150
22265
  }
22151
22266
  function validateStoryboards(file, id, text5) {
22152
22267
  const errors = [];
22153
- const dir = path66.join(ASSETS, String(id));
22268
+ const dir = path68.join(ASSETS, String(id));
22154
22269
  for (const marker of storyboardMarkers(text5)) {
22155
22270
  const named2 = assetName(marker.src, id, ["json"]);
22156
22271
  const add = (message) => errors.push({ file: rel(file), line: marker.line, rule: "storyboard-src", message });
@@ -22299,8 +22414,8 @@ var init_mockups = __esm({
22299
22414
  });
22300
22415
 
22301
22416
  // ../cli/src/commands/spec-write.ts
22302
- import fs65 from "node:fs";
22303
- import path67 from "node:path";
22417
+ import fs66 from "node:fs";
22418
+ import path69 from "node:path";
22304
22419
  function cmdSpecWrite(id, askedName, flags) {
22305
22420
  const agent = findSpecAgent(askedName);
22306
22421
  if (!agent) die(notAnAgent(askedName), { kind: "no-such-spec-agent", specAgent: askedName });
@@ -22308,11 +22423,11 @@ function cmdSpecWrite(id, askedName, flags) {
22308
22423
  const section = readSection(flags.file, flags.text);
22309
22424
  const found2 = locate(id);
22310
22425
  if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
22311
- const file = found2.kind === "group" ? path67.join(found2.target, "root.md") : found2.target;
22312
- const { meta, body } = parseFrontmatter(fs65.readFileSync(file, "utf8"));
22426
+ const file = found2.kind === "group" ? path69.join(found2.target, "root.md") : found2.target;
22427
+ const { meta, body } = parseFrontmatter(fs66.readFileSync(file, "utf8"));
22313
22428
  if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
22314
22429
  const { body: next, replaced } = splice(body, name, section);
22315
- fs65.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
22430
+ fs66.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
22316
22431
  say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
22317
22432
  return { id, specAgent: name, replaced, file: rel(file) };
22318
22433
  }
@@ -22321,7 +22436,7 @@ function readSection(file, text5) {
22321
22436
  let raw;
22322
22437
  if (file !== void 0) {
22323
22438
  try {
22324
- raw = fs65.readFileSync(file, "utf8");
22439
+ raw = fs66.readFileSync(file, "utf8");
22325
22440
  } catch {
22326
22441
  die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
22327
22442
  }
@@ -22515,7 +22630,7 @@ function localBoard() {
22515
22630
  const no = checkWrite({ board: true }, env2, boardRevision());
22516
22631
  if (no) return no;
22517
22632
  const res = await resumeDelivery(deliveryId);
22518
- if (!res.ok) return opRefused(new Error(res.error || "the delivery could not be carried on"));
22633
+ if (!res.ok) return { ...opRefused(new Error(res.error || "the delivery could not be carried on")), reason: res.reason, args: res.args };
22519
22634
  return opOk(boardRevision(), { deliveryId: res.deliveryId, landed: res.landed, carryOn: res.carryOn });
22520
22635
  },
22521
22636
  async discardDelivery(deliveryId, env2) {
@@ -22787,11 +22902,11 @@ var init_shared = __esm({
22787
22902
  });
22788
22903
 
22789
22904
  // ../cli/src/commands/goal.ts
22790
- import fs66 from "node:fs";
22905
+ import fs67 from "node:fs";
22791
22906
  function load() {
22792
22907
  const file = goalsFile();
22793
22908
  if (!file) die("Goals exist only inside a WildCrane goal discussion.");
22794
- const held4 = JSON.parse(fs66.readFileSync(file, "utf8"));
22909
+ const held4 = JSON.parse(fs67.readFileSync(file, "utf8"));
22795
22910
  if (held4.goals !== void 0 && held4.ideas !== void 0 && JSON.stringify(held4.goals) !== JSON.stringify(held4.ideas)) die("goals and ideas do not match.");
22796
22911
  const goals = held4.goals ?? held4.ideas;
22797
22912
  if (!Array.isArray(goals)) die("The goals array is missing or invalid.");
@@ -22816,7 +22931,7 @@ function splitBody2(text5) {
22816
22931
  }
22817
22932
  function readBody(bodyFile) {
22818
22933
  if (!bodyFile) die("--body-file is required; write the goal in a writable temporary file first.");
22819
- const text5 = fs66.readFileSync(bodyFile, "utf8");
22934
+ const text5 = fs67.readFileSync(bodyFile, "utf8");
22820
22935
  if (!text5.trim()) die("The goal must not be empty.");
22821
22936
  return splitBody2(text5);
22822
22937
  }
@@ -22828,7 +22943,7 @@ function named(held4, id) {
22828
22943
  function cmdGoal(args, opts) {
22829
22944
  const sub = args[0] ?? "";
22830
22945
  const { file, held: held4 } = load();
22831
- const write2 = () => fs66.writeFileSync(file, `${JSON.stringify({ workflows: held4.workflows, ideas: held4.goals }, null, 2)}
22946
+ const write2 = () => fs67.writeFileSync(file, `${JSON.stringify({ workflows: held4.workflows, ideas: held4.goals }, null, 2)}
22832
22947
  `);
22833
22948
  if (sub === "new") {
22834
22949
  const title = opts.title?.trim();
@@ -22898,8 +23013,8 @@ __export(board_exports, {
22898
23013
  buildBoardProgram: () => buildBoardProgram
22899
23014
  });
22900
23015
  import crypto4 from "node:crypto";
22901
- import fs67 from "node:fs";
22902
- import path68 from "node:path";
23016
+ import fs68 from "node:fs";
23017
+ import path70 from "node:path";
22903
23018
  async function dispatch(move2, cmd, args, opts, cli) {
22904
23019
  const ctx = ctxOf(cmd, cli.program);
22905
23020
  const data = await runAction(ctx, {}, async () => {
@@ -22938,7 +23053,7 @@ async function cloudCard(task, opts) {
22938
23053
  let request;
22939
23054
  if (typeof opts.bodyFile === "string") {
22940
23055
  try {
22941
- request = fs67.readFileSync(opts.bodyFile, "utf8");
23056
+ request = fs68.readFileSync(opts.bodyFile, "utf8");
22942
23057
  } catch {
22943
23058
  }
22944
23059
  }
@@ -22968,7 +23083,7 @@ async function cloudCard(task, opts) {
22968
23083
  const file = creationReceipt(`${checked.value}:${requestId}`, slot);
22969
23084
  const journal = readCreation(file, said3);
22970
23085
  if (!journal.made) {
22971
- const key = `akb-${path68.basename(file, ".json").slice(0, 40)}`;
23086
+ const key = `akb-${path70.basename(file, ".json").slice(0, 40)}`;
22972
23087
  const made = await createProjectTask(checked.value, journal.said, key);
22973
23088
  if (!made.ok) throw new BoardError(creationError(`Cloud did not make the task: ${made.error}`, triage), { kind: "refused" });
22974
23089
  journal.made = made.value;
@@ -23113,16 +23228,16 @@ var init_board2 = __esm({
23113
23228
  });
23114
23229
 
23115
23230
  // ../cli/src/lib/board-cli.ts
23116
- import fs68 from "node:fs";
23117
- import path69 from "node:path";
23231
+ import fs69 from "node:fs";
23232
+ import path71 from "node:path";
23118
23233
  function useBoard(found2, dirNamed) {
23119
- if (!found2.named && found2.board === path69.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
23234
+ if (!found2.named && found2.board === path71.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
23120
23235
  else setBoardDir(found2.board, found2.root);
23121
23236
  useProjectState();
23122
23237
  }
23123
23238
  function whole(found2, move2) {
23124
23239
  if (move2 === MAKES_A_BOARD) return found2;
23125
- if (!fs68.existsSync(path69.join(found2.board, "todo"))) {
23240
+ if (!fs69.existsSync(path71.join(found2.board, "todo"))) {
23126
23241
  die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
23127
23242
  kind: "board-incomplete",
23128
23243
  dir: found2.root
@@ -23131,13 +23246,13 @@ function whole(found2, move2) {
23131
23246
  return found2;
23132
23247
  }
23133
23248
  function findBoardUpward(from, move2) {
23134
- let dir = path69.resolve(from);
23249
+ let dir = path71.resolve(from);
23135
23250
  for (; ; ) {
23136
- if (hasBoard(dir)) return { root: dir, board: path69.join(dir, "docs", "kanban") };
23251
+ if (hasBoard(dir)) return { root: dir, board: path71.join(dir, "docs", "kanban") };
23137
23252
  if (isBoardDir(dir)) return { root: projectRootOf(dir), board: dir };
23138
- const kept2 = path69.join(dir, ".akb", "kanban");
23139
- if (move2 !== MAKES_A_BOARD && fs68.existsSync(path69.join(kept2, "todo"))) return { root: dir, board: kept2 };
23140
- const up = path69.dirname(dir);
23253
+ const kept2 = path71.join(dir, ".akb", "kanban");
23254
+ if (move2 !== MAKES_A_BOARD && fs69.existsSync(path71.join(kept2, "todo"))) return { root: dir, board: kept2 };
23255
+ const up = path71.dirname(dir);
23141
23256
  if (up === dir) return null;
23142
23257
  dir = up;
23143
23258
  }
@@ -23145,11 +23260,11 @@ function findBoardUpward(from, move2) {
23145
23260
  function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
23146
23261
  const named2 = board2 ?? boardFromEnv();
23147
23262
  if (named2) {
23148
- const folder = path69.resolve(cwd, named2);
23149
- if (!fs68.existsSync(folder) && move2 !== MAKES_A_BOARD) {
23263
+ const folder = path71.resolve(cwd, named2);
23264
+ if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
23150
23265
  die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
23151
23266
  }
23152
- if (fs68.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
23267
+ if (fs69.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
23153
23268
  die(`${folder} is not a board \u2014 it has no todo/ and config.md. ${installHint}\u3002`, {
23154
23269
  kind: "no-board",
23155
23270
  dir: folder
@@ -23158,24 +23273,24 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
23158
23273
  return { root: projectRootOf(folder), board: folder, named: true };
23159
23274
  }
23160
23275
  if (dir !== null) {
23161
- const root = path69.resolve(dir);
23162
- if (!fs68.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
23163
- const kept2 = path69.join(root, ".akb", "kanban");
23164
- if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs68.existsSync(path69.join(kept2, "todo"))) return whole({ root, board: kept2 }, move2);
23276
+ const root = path71.resolve(dir);
23277
+ if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
23278
+ const kept2 = path71.join(root, ".akb", "kanban");
23279
+ if (!hasBoard(root) && move2 !== MAKES_A_BOARD && fs69.existsSync(path71.join(kept2, "todo"))) return whole({ root, board: kept2 }, move2);
23165
23280
  if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
23166
23281
  die(`no board in ${root} \u2014 it has no docs/kanban/. ${installHint}\u3002`, {
23167
23282
  kind: "no-board",
23168
23283
  dir: root
23169
23284
  });
23170
23285
  }
23171
- return whole({ root, board: path69.join(root, "docs", "kanban") }, move2);
23286
+ return whole({ root, board: path71.join(root, "docs", "kanban") }, move2);
23172
23287
  }
23173
23288
  const found2 = findBoardUpward(cwd, move2);
23174
23289
  if (found2) return whole(found2, move2);
23175
- if (move2 === MAKES_A_BOARD) return { root: path69.resolve(cwd), board: path69.join(path69.resolve(cwd), "docs", "kanban") };
23290
+ if (move2 === MAKES_A_BOARD) return { root: path71.resolve(cwd), board: path71.join(path71.resolve(cwd), "docs", "kanban") };
23176
23291
  die(
23177
- `no board here. Looked in ${path69.resolve(cwd)} and every folder above it for docs/kanban/. ${installHint}\uFF0C\u6216\u7528 --board \u6307\u5B9A\u5DF2\u6709\u770B\u677F\u3002`,
23178
- { kind: "no-board", dir: path69.resolve(cwd) }
23292
+ `no board here. Looked in ${path71.resolve(cwd)} and every folder above it for docs/kanban/. ${installHint}\uFF0C\u6216\u7528 --board \u6307\u5B9A\u5DF2\u6709\u770B\u677F\u3002`,
23293
+ { kind: "no-board", dir: path71.resolve(cwd) }
23179
23294
  );
23180
23295
  }
23181
23296
  function editDistance(a, b) {
@@ -23257,8 +23372,8 @@ var init_board_cli = __esm({
23257
23372
  init_io();
23258
23373
  init_pointer();
23259
23374
  MAKES_A_BOARD = "init";
23260
- hasBoard = (dir) => fs68.existsSync(path69.join(dir, "docs", "kanban"));
23261
- isBoardDir = (dir) => fs68.existsSync(path69.join(dir, "todo")) && fs68.existsSync(path69.join(dir, "config.md"));
23375
+ hasBoard = (dir) => fs69.existsSync(path71.join(dir, "docs", "kanban"));
23376
+ isBoardDir = (dir) => fs69.existsSync(path71.join(dir, "todo")) && fs69.existsSync(path71.join(dir, "config.md"));
23262
23377
  boardFromEnv = () => {
23263
23378
  const named2 = process.env.AI4KANBAN_BOARD?.trim();
23264
23379
  return named2 ? named2 : null;
@@ -23525,8 +23640,8 @@ var init_guide = __esm({
23525
23640
  });
23526
23641
 
23527
23642
  // ../cli/src/lib/agent/approve.ts
23528
- import fs74 from "node:fs";
23529
- import path75 from "node:path";
23643
+ import fs75 from "node:fs";
23644
+ import path77 from "node:path";
23530
23645
  function approvalRequest(req) {
23531
23646
  const id = req.id;
23532
23647
  const card = id === void 0 ? null : findCard(id);
@@ -23544,12 +23659,12 @@ function advancePhase(id) {
23544
23659
  const card = findCard(id);
23545
23660
  const found2 = locate(id);
23546
23661
  if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
23547
- const file = found2.kind === "group" ? path75.join(found2.target, "root.md") : found2.target;
23548
- const { meta, body } = parseFrontmatter(fs74.readFileSync(file, "utf8"));
23662
+ const file = found2.kind === "group" ? path77.join(found2.target, "root.md") : found2.target;
23663
+ const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
23549
23664
  if (!meta) return { error: `#${id} has no frontmatter` };
23550
23665
  meta.phase = card.stage + 1;
23551
23666
  meta.status = "todo";
23552
- fs74.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
23667
+ fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
23553
23668
  return null;
23554
23669
  }
23555
23670
  var init_approve = __esm({
@@ -23647,8 +23762,8 @@ function open2(req, sessionId) {
23647
23762
  markSpawned(run2.sessionId, pid);
23648
23763
  return { run: run2, spawned: pid !== void 0 };
23649
23764
  }
23650
- async function startResume(id) {
23651
- const opened = await openResume(id);
23765
+ async function startResume(id, requireSession = false) {
23766
+ const opened = await openResume(id, requireSession);
23652
23767
  if ("error" in opened) return opened;
23653
23768
  const pid = spawnWatcher(opened.run.sessionId);
23654
23769
  markSpawned(opened.run.sessionId, pid);
@@ -23673,16 +23788,16 @@ var init_start = __esm({
23673
23788
  });
23674
23789
 
23675
23790
  // ../cli/src/lib/agent/task-chats.ts
23676
- import fs75 from "node:fs";
23677
- import path76 from "node:path";
23791
+ import fs76 from "node:fs";
23792
+ import path78 from "node:path";
23678
23793
  function taskChatFile(task, home3) {
23679
23794
  if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
23680
- return path76.join(taskChatsDir(home3), `${task}.jsonl`);
23795
+ return path78.join(taskChatsDir(home3), `${task}.jsonl`);
23681
23796
  }
23682
23797
  function readTaskChat(task, home3) {
23683
23798
  let text5;
23684
23799
  try {
23685
- text5 = fs75.readFileSync(taskChatFile(task, home3), "utf8");
23800
+ text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
23686
23801
  } catch {
23687
23802
  return [];
23688
23803
  }
@@ -23703,13 +23818,13 @@ var init_task_chats = __esm({
23703
23818
  "use strict";
23704
23819
  init_home();
23705
23820
  TASK = /^[\w-]{1,100}$/;
23706
- taskChatsDir = (home3 = machineHome()) => path76.join(home3, "chats");
23821
+ taskChatsDir = (home3 = machineHome()) => path78.join(home3, "chats");
23707
23822
  }
23708
23823
  });
23709
23824
 
23710
23825
  // ../cli/src/lib/agent/memory-review.ts
23711
- import fs76 from "node:fs";
23712
- import path77 from "node:path";
23826
+ import fs77 from "node:fs";
23827
+ import path79 from "node:path";
23713
23828
  function taskChatsToReview(tasks) {
23714
23829
  const out = [];
23715
23830
  for (const ref of [...new Set(tasks)]) {
@@ -23731,8 +23846,8 @@ function readArchived2(id) {
23731
23846
  const found2 = Number.isInteger(id) && id > 0 ? locateArchived(id) : null;
23732
23847
  if (!found2) return null;
23733
23848
  try {
23734
- const file = found2.kind === "group" ? path77.join(found2.target, "root.md") : found2.target;
23735
- const { meta, body } = parseFrontmatter(fs76.readFileSync(file, "utf8"));
23849
+ const file = found2.kind === "group" ? path79.join(found2.target, "root.md") : found2.target;
23850
+ const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
23736
23851
  if (!meta) return null;
23737
23852
  return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
23738
23853
  } catch {
@@ -23801,17 +23916,17 @@ var init_memory_review = __esm({
23801
23916
  });
23802
23917
 
23803
23918
  // ../cli/src/lib/agent/dismissal-review.ts
23804
- import fs77 from "node:fs";
23805
- import path78 from "node:path";
23919
+ import fs78 from "node:fs";
23920
+ import path80 from "node:path";
23806
23921
  function rejectionsToReview() {
23807
- const dir = path78.join(ARCHIVE, REJECTIONS_TO_REVIEW);
23808
- if (!fs77.existsSync(dir)) return [];
23922
+ const dir = path80.join(ARCHIVE, REJECTIONS_TO_REVIEW);
23923
+ if (!fs78.existsSync(dir)) return [];
23809
23924
  const found2 = [];
23810
23925
  for (const file of walkMd(dir)) {
23811
23926
  const id = archivedId(file);
23812
23927
  if (id === null) continue;
23813
23928
  try {
23814
- const { meta } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
23929
+ const { meta } = parseFrontmatter(fs78.readFileSync(file, "utf8"));
23815
23930
  if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
23816
23931
  found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
23817
23932
  } catch {
@@ -23841,7 +23956,7 @@ function citedSources(text5) {
23841
23956
  function withdrawnSources() {
23842
23957
  const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
23843
23958
  try {
23844
- return citedSources(fs77.readFileSync(file, "utf8"));
23959
+ return citedSources(fs78.readFileSync(file, "utf8"));
23845
23960
  } catch {
23846
23961
  return [];
23847
23962
  }
@@ -23866,8 +23981,8 @@ var init_dismissal_review = __esm({
23866
23981
  });
23867
23982
 
23868
23983
  // ../cli/src/lib/agent/flow.ts
23869
- import fs78 from "node:fs";
23870
- import path79 from "node:path";
23984
+ import fs79 from "node:fs";
23985
+ import path81 from "node:path";
23871
23986
  function readCard2(id, home3 = "board") {
23872
23987
  const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
23873
23988
  if (!found2) {
@@ -23876,10 +23991,10 @@ function readCard2(id, home3 = "board") {
23876
23991
  { kind: "card-not-found", id }
23877
23992
  );
23878
23993
  }
23879
- const file = found2.kind === "group" ? path79.join(found2.target, "root.md") : found2.target;
23994
+ const file = found2.kind === "group" ? path81.join(found2.target, "root.md") : found2.target;
23880
23995
  let text5;
23881
23996
  try {
23882
- text5 = fs78.readFileSync(file, "utf8");
23997
+ text5 = fs79.readFileSync(file, "utf8");
23883
23998
  } catch {
23884
23999
  die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
23885
24000
  }
@@ -24003,7 +24118,7 @@ function workspaceField(delivery) {
24003
24118
  if (!delivery?.worktree) return [];
24004
24119
  return field("workspace", [
24005
24120
  `write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
24006
- path79.resolve(process.cwd()).startsWith(worktreeDir(delivery.worktree)) ? `it is your working folder already; the board's own files are NOT in it and never go on that branch.` : `cd into it first \u2014 you are in the project checkout, and code written here is not part of this build. The board's own files are NOT in it and never go on that branch.`,
24121
+ path81.resolve(process.cwd()).startsWith(worktreeDir(delivery.worktree)) ? `it is your working folder already; the board's own files are NOT in it and never go on that branch.` : `cd into it first \u2014 you are in the project checkout, and code written here is not part of this build. The board's own files are NOT in it and never go on that branch.`,
24007
24122
  `every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
24008
24123
  `${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
24009
24124
  ]);
@@ -24747,7 +24862,7 @@ __export(watch_exports, {
24747
24862
  watchRun: () => watchRun
24748
24863
  });
24749
24864
  import { spawn as spawn4 } from "node:child_process";
24750
- import fs79 from "node:fs";
24865
+ import fs80 from "node:fs";
24751
24866
  async function watchRun(sessionId, resume = startResume) {
24752
24867
  const spec = readSpec(sessionId);
24753
24868
  const run2 = peekRun(sessionId);
@@ -24783,11 +24898,11 @@ async function watchRun(sessionId, resume = startResume) {
24783
24898
  const record = claimed ?? run2;
24784
24899
  const sub = record.action === "sub";
24785
24900
  if (claim) await setCardStatus(claim.cardId, claim.status);
24786
- fs79.mkdirSync(SESSIONS_DIR, { recursive: true });
24787
- const logFile = fs79.createWriteStream(record.logPath, { flags: "a" });
24901
+ fs80.mkdirSync(SESSIONS_DIR, { recursive: true });
24902
+ const logFile = fs80.createWriteStream(record.logPath, { flags: "a" });
24788
24903
  logFile.on("error", () => {
24789
24904
  });
24790
- const eventsFile = fs79.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
24905
+ const eventsFile = fs80.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
24791
24906
  eventsFile.on("error", () => {
24792
24907
  });
24793
24908
  const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
@@ -25376,7 +25491,7 @@ var init_watch = __esm({
25376
25491
  });
25377
25492
 
25378
25493
  // ../cli/src/kanban.ts
25379
- import fs85 from "node:fs";
25494
+ import fs86 from "node:fs";
25380
25495
  import { fileURLToPath as fileURLToPath3 } from "node:url";
25381
25496
 
25382
25497
  // ../cli/src/lib/cli/agent.ts
@@ -25865,8 +25980,8 @@ async function testAgent(args) {
25865
25980
  }
25866
25981
 
25867
25982
  // ../cli/src/commands/cloud.ts
25868
- import fs70 from "node:fs";
25869
- import path71 from "node:path";
25983
+ import fs71 from "node:fs";
25984
+ import path73 from "node:path";
25870
25985
 
25871
25986
  // ../cli/src/lib/cloud/avatar.ts
25872
25987
  init_session();
@@ -25914,13 +26029,13 @@ init_config();
25914
26029
  init_home();
25915
26030
  init_config();
25916
26031
  init_session();
25917
- import fs69 from "node:fs";
25918
- import path70 from "node:path";
26032
+ import fs70 from "node:fs";
26033
+ import path72 from "node:path";
25919
26034
  var FRESH_MS = 10 * 6e4;
25920
26035
  var GRACE_MS = 7 * 24 * 60 * 6e4;
25921
- var heldFile = () => path70.join(machineHome(), "pro.json");
26036
+ var heldFile = () => path72.join(machineHome(), "pro.json");
25922
26037
  function forgetPro() {
25923
- fs69.rmSync(heldFile(), { force: true });
26038
+ fs70.rmSync(heldFile(), { force: true });
25924
26039
  }
25925
26040
 
25926
26041
  // ../cli/src/lib/cloud/account.ts
@@ -26097,10 +26212,10 @@ async function cloudImage(opts, program2) {
26097
26212
  const prompt = opts.prompt.trim();
26098
26213
  if (!prompt) usage("Missing the prompt.", line);
26099
26214
  const references = opts.refs.map((file) => {
26100
- const type = REF_TYPES[path71.extname(file).toLowerCase()];
26215
+ const type = REF_TYPES[path73.extname(file).toLowerCase()];
26101
26216
  if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
26102
26217
  try {
26103
- return `data:${type};base64,${fs70.readFileSync(file).toString("base64")}`;
26218
+ return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
26104
26219
  } catch (e) {
26105
26220
  usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
26106
26221
  }
@@ -26206,31 +26321,31 @@ async function generate(g) {
26206
26321
  refuse(g.failed, g.fallback);
26207
26322
  }
26208
26323
  const out = g.outFor?.(res, g.out) ?? g.out;
26209
- fs70.mkdirSync(path71.dirname(path71.resolve(out)), { recursive: true });
26210
- fs70.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
26324
+ fs71.mkdirSync(path73.dirname(path73.resolve(out)), { recursive: true });
26325
+ fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
26211
26326
  say(`${out} (${g.label(res)})`);
26212
26327
  return { out };
26213
26328
  }
26214
26329
 
26215
26330
  // ../cli/src/commands/cloud-import.ts
26216
26331
  import crypto5 from "node:crypto";
26217
- import fs72 from "node:fs";
26218
- import path73 from "node:path";
26332
+ import fs73 from "node:fs";
26333
+ import path75 from "node:path";
26219
26334
 
26220
26335
  // ../cloud/src/board-files.ts
26221
26336
  var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
26222
26337
  var SETTINGS = ["modules.md", "config.md"];
26223
- var isSettingsPath = (path85) => SETTINGS.includes(path85) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path85);
26338
+ var isSettingsPath = (path87) => SETTINGS.includes(path87) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path87);
26224
26339
  var BOARD_FILE_LIMIT = 256 * 1024;
26225
- function isBoardPath(path85) {
26226
- if (!path85 || path85.length > 500 || path85.startsWith("/") || path85.includes("\\") || path85.includes("\0")) return false;
26227
- if (path85.split("/").some((part) => !part || part === "." || part === "..")) return false;
26228
- if (path85 === "todo/README.md" || path85.split("/").includes(".sync")) return false;
26229
- if (path85.startsWith("workflows/")) return true;
26230
- if (SETTINGS.includes(path85)) return true;
26231
- if (path85.startsWith("rules/")) return isSettingsPath(path85);
26232
- if (path85.startsWith("assets/")) return path85.endsWith(".json");
26233
- return ROOTS.some((root) => path85.startsWith(root)) && path85.endsWith(".md");
26340
+ function isBoardPath(path87) {
26341
+ if (!path87 || path87.length > 500 || path87.startsWith("/") || path87.includes("\\") || path87.includes("\0")) return false;
26342
+ if (path87.split("/").some((part) => !part || part === "." || part === "..")) return false;
26343
+ if (path87 === "todo/README.md" || path87.split("/").includes(".sync")) return false;
26344
+ if (path87.startsWith("workflows/")) return true;
26345
+ if (SETTINGS.includes(path87)) return true;
26346
+ if (path87.startsWith("rules/")) return isSettingsPath(path87);
26347
+ if (path87.startsWith("assets/")) return path87.endsWith(".json");
26348
+ return ROOTS.some((root) => path87.startsWith(root)) && path87.endsWith(".md");
26234
26349
  }
26235
26350
  var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
26236
26351
 
@@ -26334,8 +26449,8 @@ init_bundled();
26334
26449
  // ../cli/src/lib/agents/shared-memory.ts
26335
26450
  init_bundled();
26336
26451
  init_parse();
26337
- import fs71 from "node:fs";
26338
- import path72 from "node:path";
26452
+ import fs72 from "node:fs";
26453
+ import path74 from "node:path";
26339
26454
  function sharedMemory(board2) {
26340
26455
  const declared = /* @__PURE__ */ new Map();
26341
26456
  const take = (folder, text5, builtIn) => {
@@ -26347,16 +26462,16 @@ function sharedMemory(board2) {
26347
26462
  const at = /^([^/]+)\/AGENT\.md$/.exec(key);
26348
26463
  if (at) take(at[1], text5, true);
26349
26464
  }
26350
- const root = path72.join(board2, "agents");
26465
+ const root = path74.join(board2, "agents");
26351
26466
  let folders2 = [];
26352
26467
  try {
26353
- folders2 = fs71.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
26468
+ folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
26354
26469
  } catch {
26355
26470
  }
26356
26471
  for (const folder of folders2.sort()) {
26357
26472
  let text5 = null;
26358
26473
  try {
26359
- text5 = fs71.readFileSync(path72.join(root, folder, "AGENT.md"), "utf8");
26474
+ text5 = fs72.readFileSync(path74.join(root, folder, "AGENT.md"), "utf8");
26360
26475
  } catch {
26361
26476
  }
26362
26477
  take(folder, text5, false);
@@ -26445,24 +26560,24 @@ async function findProject(asked) {
26445
26560
  }
26446
26561
  throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
26447
26562
  }
26448
- var read = (file) => fs72.readFileSync(file, "utf8");
26563
+ var read = (file) => fs73.readFileSync(file, "utf8");
26449
26564
  var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
26450
26565
  function files(dir) {
26451
- if (!fs72.existsSync(dir)) return [];
26566
+ if (!fs73.existsSync(dir)) return [];
26452
26567
  const out = [];
26453
- for (const entry of fs72.readdirSync(dir, { withFileTypes: true })) {
26568
+ for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
26454
26569
  if (entry.name.startsWith(".")) continue;
26455
- const full = path73.join(dir, entry.name);
26570
+ const full = path75.join(dir, entry.name);
26456
26571
  if (entry.isDirectory()) out.push(...files(full));
26457
26572
  else if (entry.isFile()) out.push(full);
26458
26573
  }
26459
26574
  return out;
26460
26575
  }
26461
- var posix2 = (p) => p.split(path73.sep).join("/");
26576
+ var posix2 = (p) => p.split(path75.sep).join("/");
26462
26577
  function openCards() {
26463
26578
  const out = [];
26464
26579
  for (const file of walkMd(TODO)) {
26465
- const rel2 = posix2(path73.relative(TODO, file));
26580
+ const rel2 = posix2(path75.relative(TODO, file));
26466
26581
  if (rel2 === "README.md") continue;
26467
26582
  const parts = rel2.split("/");
26468
26583
  const text5 = read(file);
@@ -26496,14 +26611,14 @@ function bodyProblems(text5) {
26496
26611
  return out;
26497
26612
  }
26498
26613
  function packWorkflow(flow) {
26499
- const own = path73.join(WORKFLOWS, flow.id);
26614
+ const own = path75.join(WORKFLOWS, flow.id);
26500
26615
  const out = {};
26501
- for (const file of files(own)) out[posix2(path73.relative(own, file))] = read(file);
26616
+ for (const file of files(own)) out[posix2(path75.relative(own, file))] = read(file);
26502
26617
  const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
26503
26618
  for (const agent of new Set(named2)) {
26504
- const dir = path73.join(AGENTS, agent);
26505
- if (fs72.existsSync(path73.join(dir, "AGENT.md"))) {
26506
- for (const file of files(dir)) out[`agents/${agent}/${posix2(path73.relative(dir, file))}`] = read(file);
26619
+ const dir = path75.join(AGENTS, agent);
26620
+ if (fs73.existsSync(path75.join(dir, "AGENT.md"))) {
26621
+ for (const file of files(dir)) out[`agents/${agent}/${posix2(path75.relative(dir, file))}`] = read(file);
26507
26622
  continue;
26508
26623
  }
26509
26624
  for (const [key, text5] of Object.entries(BUNDLED_AGENT_FILES)) {
@@ -26555,8 +26670,8 @@ function draftsOf(text5) {
26555
26670
  const at = /^\.(assets|mockups)\/(.+)$/.exec(name);
26556
26671
  if (!at) continue;
26557
26672
  const base = at[1] === "assets" ? ASSETS : MOCKUPS;
26558
- const file = path73.resolve(base, at[2]);
26559
- if (file.startsWith(base + path73.sep) && fs72.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
26673
+ const file = path75.resolve(base, at[2]);
26674
+ if (file.startsWith(base + path75.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
26560
26675
  }
26561
26676
  return [...out].map(([name, file]) => ({ name, file }));
26562
26677
  }
@@ -26590,9 +26705,9 @@ async function uploadDrafts(task, drafts) {
26590
26705
  const assets = {};
26591
26706
  for (const { name, file } of drafts) {
26592
26707
  if (!card.body.includes(name)) continue;
26593
- const ext = path73.extname(file).toLowerCase();
26708
+ const ext = path75.extname(file).toLowerCase();
26594
26709
  const type = TYPES[ext] ?? "application/octet-stream";
26595
- const data = fs72.readFileSync(file);
26710
+ const data = fs73.readFileSync(file);
26596
26711
  const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
26597
26712
  const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
26598
26713
  await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
@@ -26635,10 +26750,10 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
26635
26750
  async function run(asked, dryRun) {
26636
26751
  const project = await findProject(asked);
26637
26752
  const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
26638
- const board2 = crypto5.createHash("sha256").update(fs72.realpathSync(KANBAN)).digest("hex").slice(0, 16);
26753
+ const board2 = crypto5.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
26639
26754
  const cards = openCards();
26640
26755
  const problems = [];
26641
- const where = (file) => posix2(path73.relative(KANBAN, file));
26756
+ const where = (file) => posix2(path75.relative(KANBAN, file));
26642
26757
  const own = workflows().filter((w) => !w.builtIn);
26643
26758
  const packed = /* @__PURE__ */ new Map();
26644
26759
  for (const flow of own) {
@@ -26665,7 +26780,7 @@ async function run(asked, dryRun) {
26665
26780
  const root = `projects/${project.id}/`;
26666
26781
  const boardFiles = [];
26667
26782
  for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
26668
- if (!fs72.existsSync(file)) continue;
26783
+ if (!fs73.existsSync(file)) continue;
26669
26784
  const rel2 = where(file);
26670
26785
  if (!isBoardPath(rel2)) continue;
26671
26786
  const content = read(file);
@@ -26776,16 +26891,16 @@ async function run(asked, dryRun) {
26776
26891
  }
26777
26892
  function leftLocal(own) {
26778
26893
  const out = [];
26779
- const count4 = (dir) => fs72.existsSync(dir) ? walkMd(dir).length : 0;
26894
+ const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
26780
26895
  const archived = count4(ARCHIVE);
26781
26896
  if (archived) out.push(`.archive/ (${archived})`);
26782
26897
  const triage = count4(TRIAGE);
26783
26898
  if (triage) out.push(`triage/ (${triage})`);
26784
- if (fs72.existsSync(DELIVERIES)) out.push("deliveries/");
26785
- if (fs72.existsSync(path73.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
26899
+ if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
26900
+ if (fs73.existsSync(path75.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
26786
26901
  const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
26787
- if (fs72.existsSync(AGENTS)) {
26788
- for (const e of fs72.readdirSync(AGENTS, { withFileTypes: true })) {
26902
+ if (fs73.existsSync(AGENTS)) {
26903
+ for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
26789
26904
  if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
26790
26905
  }
26791
26906
  }
@@ -26803,8 +26918,8 @@ init_session();
26803
26918
  init_io();
26804
26919
  init_paths();
26805
26920
  import { createHash as createHash4 } from "node:crypto";
26806
- import fs73 from "node:fs";
26807
- import path74 from "node:path";
26921
+ import fs74 from "node:fs";
26922
+ import path76 from "node:path";
26808
26923
  var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
26809
26924
  var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
26810
26925
  var MAX_BYTES3 = 50 * 1024 * 1024;
@@ -26903,10 +27018,10 @@ This template has no preview.`);
26903
27018
  const written = [];
26904
27019
  for (const p of previews) {
26905
27020
  const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
26906
- const file = path74.basename(p.path);
26907
- const out = path74.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
26908
- fs73.mkdirSync(opts.to, { recursive: true });
26909
- fs73.writeFileSync(out, bytes3);
27021
+ const file = path76.basename(p.path);
27022
+ const out = path76.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
27023
+ fs74.mkdirSync(opts.to, { recursive: true });
27024
+ fs74.writeFileSync(out, bytes3);
26910
27025
  written.push(out);
26911
27026
  say(out);
26912
27027
  }
@@ -26919,15 +27034,15 @@ async function templateGet(name, opts, program2) {
26919
27034
  const { template } = await asJson(await ask(`/${at}`, {}, WITHOUT));
26920
27035
  const fetched = [];
26921
27036
  for (const f of template.files) {
26922
- const out = path74.resolve(to, f.path);
26923
- if (!out.startsWith(path74.resolve(to) + path74.sep)) refuse2(T.corrupt, WITHOUT);
27037
+ const out = path76.resolve(to, f.path);
27038
+ if (!out.startsWith(path76.resolve(to) + path76.sep)) refuse2(T.corrupt, WITHOUT);
26924
27039
  const bytes3 = Buffer.from(await (await ask(`/${at}/files/${route(f.path)}`, {}, WITHOUT)).arrayBuffer());
26925
27040
  if (bytes3.byteLength !== f.size || createHash4("sha256").update(bytes3).digest("hex") !== f.sha256) {
26926
27041
  refuse2(T.corrupt, WITHOUT);
26927
27042
  }
26928
27043
  fetched.push({ out, bytes: bytes3 });
26929
27044
  }
26930
- const taken = fetched.filter((f) => fs73.existsSync(f.out)).map((f) => path74.relative(to, f.out));
27045
+ const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path76.relative(to, f.out));
26931
27046
  if (taken.length > 0) {
26932
27047
  refuse2({
26933
27048
  zh: `\u76EE\u6807\u6587\u4EF6\u5939\u5DF2\u6709\u540C\u540D\u6587\u4EF6\uFF1A${taken.join("\u3001")}\u3002\u8BF7\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\uFF0C\u6216\u5148\u79FB\u8D70\u8FD9\u4E9B\u6587\u4EF6\u3002`,
@@ -26935,8 +27050,8 @@ async function templateGet(name, opts, program2) {
26935
27050
  });
26936
27051
  }
26937
27052
  for (const f of fetched) {
26938
- fs73.mkdirSync(path74.dirname(f.out), { recursive: true });
26939
- fs73.writeFileSync(f.out, f.bytes);
27053
+ fs74.mkdirSync(path76.dirname(f.out), { recursive: true });
27054
+ fs74.writeFileSync(f.out, f.bytes);
26940
27055
  }
26941
27056
  say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
26942
27057
  Fetched ${name}: ${fetched.length} files into ${to}`);
@@ -26954,25 +27069,25 @@ async function templateSource(name, opts) {
26954
27069
  say(`evidence: ${e.path}`);
26955
27070
  continue;
26956
27071
  }
26957
- const out = path74.join(opts.to, `${name}-${path74.basename(e.path)}`);
26958
- fs73.mkdirSync(opts.to, { recursive: true });
26959
- fs73.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
27072
+ const out = path76.join(opts.to, `${name}-${path76.basename(e.path)}`);
27073
+ fs74.mkdirSync(opts.to, { recursive: true });
27074
+ fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
26960
27075
  written.push(out);
26961
27076
  say(out);
26962
27077
  }
26963
27078
  return { name, source, evidence: written.length > 0 ? written : evidence.map((e) => e.path) };
26964
27079
  }
26965
27080
  async function templatePublish(dir, opts) {
26966
- const root = path74.resolve(dir);
26967
- const manifest = path74.join(root, "template.md");
26968
- if (!fs73.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
26969
- const meta = readTemplateMeta(fs73.readFileSync(manifest, "utf8"));
27081
+ const root = path76.resolve(dir);
27082
+ const manifest = path76.join(root, "template.md");
27083
+ if (!fs74.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
27084
+ const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
26970
27085
  const files2 = walk2(root);
26971
- const total = files2.reduce((sum, f) => sum + fs73.statSync(path74.join(root, f)).size, 0);
27086
+ const total = files2.reduce((sum, f) => sum + fs74.statSync(path76.join(root, f)).size, 0);
26972
27087
  if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
26973
27088
  for (const extra of [...opts.preview, ...opts.evidence]) {
26974
- if (!fs73.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
26975
- if (path74.resolve(extra).startsWith(root + path74.sep)) {
27089
+ if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
27090
+ if (path76.resolve(extra).startsWith(root + path76.sep)) {
26976
27091
  invalid(
26977
27092
  `${extra} \u5728\u6A21\u677F\u6587\u4EF6\u5939\u5185\uFF1B\u9884\u89C8\u56FE\u4E0E\u6765\u6E90\u51ED\u8BC1\u9700\u653E\u5728\u6A21\u677F\u6587\u4EF6\u5939\u4E4B\u5916\u3002`,
26978
27093
  `${extra} is inside the template folder; keep previews and evidence outside it.`
@@ -26984,10 +27099,10 @@ async function templatePublish(dir, opts) {
26984
27099
  }
26985
27100
  const form = new FormData();
26986
27101
  form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
26987
- const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs73.readFileSync(file)]), path74.basename(name));
26988
- for (const f of files2) add("files", f, path74.join(root, f));
26989
- for (const f of opts.preview) add("previews", path74.basename(f), f);
26990
- for (const f of opts.evidence) add("evidence", path74.basename(f), f);
27102
+ const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path76.basename(name));
27103
+ for (const f of files2) add("files", f, path76.join(root, f));
27104
+ for (const f of opts.preview) add("previews", path76.basename(f), f);
27105
+ for (const f of opts.evidence) add("evidence", path76.basename(f), f);
26991
27106
  const done2 = await asJson(await ask("", { method: "POST", body: form }));
26992
27107
  say(
26993
27108
  done2.replaced ? `\u5DF2\u66FF\u6362 ${done2.name}\uFF1A${files2.length} \u4E2A\u6587\u4EF6\u3002
@@ -27003,11 +27118,11 @@ Deleted ${name} from the catalog.`);
27003
27118
  return { name, deleted: true };
27004
27119
  }
27005
27120
  async function templateShowcase(name, dir) {
27006
- const file = path74.join(dir, "showcase.json");
27007
- if (!fs73.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
27121
+ const file = path76.join(dir, "showcase.json");
27122
+ if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
27008
27123
  let meta;
27009
27124
  try {
27010
- meta = JSON.parse(fs73.readFileSync(file, "utf8"));
27125
+ meta = JSON.parse(fs74.readFileSync(file, "utf8"));
27011
27126
  } catch {
27012
27127
  return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
27013
27128
  }
@@ -27019,9 +27134,9 @@ async function templateShowcase(name, dir) {
27019
27134
  const form = new FormData();
27020
27135
  form.set("meta", JSON.stringify(meta));
27021
27136
  for (const p of new Set(named2)) {
27022
- const at = path74.join(dir, p);
27023
- if (!fs73.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
27024
- form.append(`media:${p}`, new Blob([fs73.readFileSync(at)]), p);
27137
+ const at = path76.join(dir, p);
27138
+ if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
27139
+ form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
27025
27140
  }
27026
27141
  const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
27027
27142
  say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
@@ -27030,7 +27145,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
27030
27145
  }
27031
27146
  function walk2(root, sub = "") {
27032
27147
  const out = [];
27033
- for (const entry of fs73.readdirSync(path74.join(root, sub), { withFileTypes: true })) {
27148
+ for (const entry of fs74.readdirSync(path76.join(root, sub), { withFileTypes: true })) {
27034
27149
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
27035
27150
  const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
27036
27151
  if (entry.isDirectory()) out.push(...walk2(root, rel2));
@@ -27315,7 +27430,7 @@ init_opencode();
27315
27430
  init_locate();
27316
27431
  init_store();
27317
27432
  import { spawnSync as spawnSync5 } from "node:child_process";
27318
- import path80 from "node:path";
27433
+ import path82 from "node:path";
27319
27434
  var AGENTS2 = {
27320
27435
  claude: "claude-code",
27321
27436
  codex: "codex",
@@ -27330,10 +27445,10 @@ var AGENTS2 = {
27330
27445
  var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
27331
27446
  function agentOf2(args) {
27332
27447
  const words = args.trim().split(/\s+/);
27333
- const first = path80.basename(words[0] ?? "");
27448
+ const first = path82.basename(words[0] ?? "");
27334
27449
  const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
27335
27450
  if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
27336
- return AGENTS2[path80.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
27451
+ return AGENTS2[path82.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
27337
27452
  }
27338
27453
  function processChain() {
27339
27454
  if (process.platform === "win32") return [];
@@ -27397,8 +27512,8 @@ init_tasks();
27397
27512
  init_read();
27398
27513
  init_rules();
27399
27514
  init_board();
27400
- import fs80 from "node:fs";
27401
- import path81 from "node:path";
27515
+ import fs81 from "node:fs";
27516
+ import path83 from "node:path";
27402
27517
  var FOLLOW_MS = 400;
27403
27518
  async function cmdStartRun(action, args, opts, program2 = "akb") {
27404
27519
  const { req, follow: follow2, print } = readRequest(action, args, opts);
@@ -27588,13 +27703,13 @@ function readRequest(action, args, opts) {
27588
27703
  return { req, follow: follow2, print };
27589
27704
  }
27590
27705
  async function cmdResume(id, opts) {
27591
- const opened = await startResume(id ?? "last");
27706
+ const opened = await startResume(id ?? "last", opts.requireSession);
27592
27707
  if ("error" in opened) die(opened.error, { kind: "run-refused" });
27593
27708
  const { run: run2 } = opened;
27594
27709
  if (!opened.spawned) die(`couldn't start a process for run ${run2.sessionId}`, { kind: "spawn-failed" });
27595
27710
  const within = run2.deliveryId ? ` in delivery ${run2.deliveryId}` : "";
27596
27711
  say(run2.resumedFrom ? `continuing ${short(run2.resumedFrom)} \u2014 run ${run2.sessionId}${within}` : `trying again \u2014 run ${run2.sessionId}${within}`);
27597
- if (opts.follow === true) return await followStarted(run2.sessionId, "akb");
27712
+ if (opts.follow === true) return await followStarted(run2.sessionId, "akb", opts.events === true);
27598
27713
  return { sessionId: run2.sessionId, resumedFrom: run2.resumedFrom };
27599
27714
  }
27600
27715
  async function cmdCancel(named2) {
@@ -27608,7 +27723,10 @@ async function cmdCancel(named2) {
27608
27723
  }
27609
27724
  async function cmdResumeDelivery(named2) {
27610
27725
  const res = await resumeBoardDelivery(named2);
27611
- if (!res.ok) die(res.error ?? "that delivery could not be carried on", { kind: "run-refused" });
27726
+ if (!res.ok) die(res.error ?? "that delivery could not be carried on", {
27727
+ kind: "run-refused",
27728
+ ...res.kind === "refused" ? { reason: res.reason, args: res.args } : {}
27729
+ });
27612
27730
  if (res.landed) {
27613
27731
  say(`delivery ${res.deliveryId} is done \u2014 its changes were already on the target branch.`);
27614
27732
  return { deliveryId: res.deliveryId, landed: true };
@@ -27685,7 +27803,7 @@ async function cmdSubStart(words, opts) {
27685
27803
  let prompt = words.join(" ");
27686
27804
  if (opts.file) {
27687
27805
  try {
27688
- prompt = fs80.readFileSync(path81.resolve(opts.file), "utf8");
27806
+ prompt = fs81.readFileSync(path83.resolve(opts.file), "utf8");
27689
27807
  } catch {
27690
27808
  die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
27691
27809
  }
@@ -27976,7 +28094,7 @@ async function cmdWatch(sessionId) {
27976
28094
  // ../cli/src/commands/triage.ts
27977
28095
  init_scheduled();
27978
28096
  init_workflows();
27979
- import fs82 from "node:fs";
28097
+ import fs83 from "node:fs";
27980
28098
 
27981
28099
  // ../cli/src/lib/signals/index.ts
27982
28100
  init_cards();
@@ -27986,16 +28104,16 @@ init_yaml();
27986
28104
  // ../cli/src/lib/signals/config.ts
27987
28105
  init_settings2();
27988
28106
  init_paths();
27989
- import fs81 from "node:fs";
27990
- import path82 from "node:path";
28107
+ import fs82 from "node:fs";
28108
+ import path84 from "node:path";
27991
28109
  var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
27992
28110
  var ENDPOINT_SETTING = "Triage endpoint";
27993
28111
  var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
27994
- var boardRel3 = (file) => rel(file).split(path82.sep).join("/");
28112
+ var boardRel3 = (file) => rel(file).split(path84.sep).join("/");
27995
28113
  function signalEndpoint() {
27996
28114
  let text5;
27997
28115
  try {
27998
- text5 = fs81.readFileSync(CONFIG, "utf8");
28116
+ text5 = fs82.readFileSync(CONFIG, "utf8");
27999
28117
  } catch {
28000
28118
  return "";
28001
28119
  }
@@ -28171,7 +28289,7 @@ async function cmdTriageAdd(opts) {
28171
28289
  let body = opts.text;
28172
28290
  if (opts.file !== void 0) {
28173
28291
  try {
28174
- body = fs82.readFileSync(opts.file, "utf8");
28292
+ body = fs83.readFileSync(opts.file, "utf8");
28175
28293
  } catch {
28176
28294
  die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
28177
28295
  }
@@ -28603,7 +28721,7 @@ function declareRunning(run2, cli) {
28603
28721
  });
28604
28722
  verb("resume").argument("[id]", RUN_ID, String).summary("continue a run that failed, was cut off or was stopped").description(
28605
28723
  "The one command that reaches past the board: it picks the coding agent's own session back up \u2014 the conversation it was having, with everything it had already read \u2014 while the board counts the work as a fresh run. Before resuming a creation with no card, check sign-in, project access and connection. A refusal starts no run and leaves triage unchanged. Keep the triage source and request identity; reuse any task already created in Cloud. Repairing an existing card creates no other task. Example: akb run resume abc12345"
28606
- ).option("-f, --follow", "watch its log instead of returning").action(async function(...vals) {
28724
+ ).option("-f, --follow", "watch its log instead of returning").option("--events", "with --follow: print its structured events, one JSON object per line").option("--require-session", "refuse if the original conversation cannot be resumed").action(async function(...vals) {
28607
28725
  const [id] = positional(vals);
28608
28726
  await onBoard2(this, cli, () => cmdResume(id, this.opts()));
28609
28727
  });
@@ -28735,8 +28853,8 @@ function declareAgent(program2, cli) {
28735
28853
  }
28736
28854
 
28737
28855
  // ../cli/src/lib/cli/setup.ts
28738
- import fs84 from "node:fs";
28739
- import path84 from "node:path";
28856
+ import fs85 from "node:fs";
28857
+ import path86 from "node:path";
28740
28858
  import { fileURLToPath as fileURLToPath2 } from "node:url";
28741
28859
 
28742
28860
  // ../cli/src/commands/update.ts
@@ -28750,8 +28868,8 @@ init_paths();
28750
28868
  init_memory();
28751
28869
  init_yaml2();
28752
28870
  init_version();
28753
- import fs83 from "node:fs";
28754
- import path83 from "node:path";
28871
+ import fs84 from "node:fs";
28872
+ import path85 from "node:path";
28755
28873
  import { spawnSync as spawnSync6 } from "node:child_process";
28756
28874
  var NAME = "wildcrane";
28757
28875
  var REPO = "https://github.com/ai4kanban/ai4kanban";
@@ -28773,10 +28891,10 @@ var Report = class {
28773
28891
  for (const line of this.notes) say(` ! ${line}`);
28774
28892
  }
28775
28893
  };
28776
- var read3 = (file) => fs83.existsSync(file) ? fs83.readFileSync(file, "utf8") : null;
28894
+ var read3 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
28777
28895
  function statOf(p) {
28778
28896
  try {
28779
- return fs83.lstatSync(p);
28897
+ return fs84.lstatSync(p);
28780
28898
  } catch {
28781
28899
  return null;
28782
28900
  }
@@ -28801,10 +28919,10 @@ async function cmdUpdate2(ctx) {
28801
28919
  return { updated: ctx.dir, from, to: SKILL_VERSION };
28802
28920
  }
28803
28921
  async function repairBoard(root, report3, named2) {
28804
- const legacy = path83.join(root, "docs", "kanban");
28805
- const kept2 = path83.join(root, ".akb", "kanban");
28806
- const board2 = named2 ?? (fs83.existsSync(legacy) ? legacy : kept2);
28807
- if (!fs83.existsSync(board2)) {
28922
+ const legacy = path85.join(root, "docs", "kanban");
28923
+ const kept2 = path85.join(root, ".akb", "kanban");
28924
+ const board2 = named2 ?? (fs84.existsSync(legacy) ? legacy : kept2);
28925
+ if (!fs84.existsSync(board2)) {
28808
28926
  report3.notes.push("\u672A\u627E\u5230\u770B\u677F\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002");
28809
28927
  return false;
28810
28928
  }
@@ -28835,20 +28953,20 @@ function moveRuntimes(root, board2, report3) {
28835
28953
  }
28836
28954
  }
28837
28955
  function moveLegacyMemory(board2, report3) {
28838
- const memory = path83.join(board2, "memory");
28956
+ const memory = path85.join(board2, "memory");
28839
28957
  const moved = [];
28840
28958
  for (const name of MEMORY_FILES) {
28841
- const from = path83.join(board2, name);
28959
+ const from = path85.join(board2, name);
28842
28960
  const st = statOf(from);
28843
28961
  if (!st || !st.isFile()) continue;
28844
- fs83.mkdirSync(memory, { recursive: true });
28845
- const to = path83.join(memory, name);
28962
+ fs84.mkdirSync(memory, { recursive: true });
28963
+ const to = path85.join(memory, name);
28846
28964
  const there = read3(to);
28847
28965
  if (there === null) {
28848
- fs83.renameSync(from, to);
28966
+ fs84.renameSync(from, to);
28849
28967
  moved.push(name);
28850
28968
  } else if (there === read3(from)) {
28851
- fs83.rmSync(from);
28969
+ fs84.rmSync(from);
28852
28970
  moved.push(name);
28853
28971
  } else {
28854
28972
  report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
@@ -28857,23 +28975,23 @@ function moveLegacyMemory(board2, report3) {
28857
28975
  if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
28858
28976
  }
28859
28977
  function dropRecordFile(board2) {
28860
- const file = path83.join(board2, "record.csv");
28861
- if (!fs83.existsSync(file)) return null;
28862
- fs83.rmSync(file);
28978
+ const file = path85.join(board2, "record.csv");
28979
+ if (!fs84.existsSync(file)) return null;
28980
+ fs84.rmSync(file);
28863
28981
  return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
28864
28982
  }
28865
28983
  function dropModuleGoals(board2, report3) {
28866
- const memory = path83.join(board2, "memory");
28867
- if (!fs83.existsSync(memory)) return;
28868
- const root = read3(path83.join(memory, "goal.md"));
28984
+ const memory = path85.join(board2, "memory");
28985
+ if (!fs84.existsSync(memory)) return;
28986
+ const root = read3(path85.join(memory, "goal.md"));
28869
28987
  let dropped = 0;
28870
- for (const entry of fs83.readdirSync(memory, { withFileTypes: true })) {
28988
+ for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
28871
28989
  if (!entry.isDirectory()) continue;
28872
- const file = path83.join(memory, entry.name, "goal.md");
28990
+ const file = path85.join(memory, entry.name, "goal.md");
28873
28991
  const text5 = read3(file);
28874
28992
  if (text5 === null) continue;
28875
28993
  if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
28876
- fs83.rmSync(file);
28994
+ fs84.rmSync(file);
28877
28995
  dropped++;
28878
28996
  } else {
28879
28997
  report3.notes.push(
@@ -28895,10 +29013,10 @@ var PLAN_LEADS = {
28895
29013
  var GONE_LEADS = ["software-planner", "planner"];
28896
29014
  function planLeads(board2) {
28897
29015
  const leads = { ...PLAN_LEADS };
28898
- const root = path83.join(board2, "workflows");
28899
- for (const entry of fs83.existsSync(root) ? fs83.readdirSync(root, { withFileTypes: true }) : []) {
29016
+ const root = path85.join(board2, "workflows");
29017
+ for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
28900
29018
  if (!entry.isDirectory()) continue;
28901
- const meta = splitFrontmatter(read3(path83.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
29019
+ const meta = splitFrontmatter(read3(path85.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
28902
29020
  const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
28903
29021
  const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
28904
29022
  const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
@@ -28907,30 +29025,30 @@ function planLeads(board2) {
28907
29025
  return leads;
28908
29026
  }
28909
29027
  function movePlanRules(board2, report3) {
28910
- const rules = path83.join(board2, "rules");
29028
+ const rules = path85.join(board2, "rules");
28911
29029
  const moved = [];
28912
29030
  for (const [id, leads] of Object.entries(planLeads(board2))) {
28913
29031
  for (const lead of leads) {
28914
- const from = path83.join(rules, `${lead}.md`);
29032
+ const from = path85.join(rules, `${lead}.md`);
28915
29033
  const rule = read3(from)?.trim();
28916
29034
  if (!rule) continue;
28917
- const to = path83.join(rules, "workflows", `${id}.md`);
29035
+ const to = path85.join(rules, "workflows", `${id}.md`);
28918
29036
  const there = read3(to)?.trim() ?? "";
28919
29037
  if (!there.includes(rule)) {
28920
- fs83.mkdirSync(path83.dirname(to), { recursive: true });
28921
- fs83.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
29038
+ fs84.mkdirSync(path85.dirname(to), { recursive: true });
29039
+ fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
28922
29040
  `);
28923
29041
  moved.push(`${lead} \u2192 ${id}`);
28924
29042
  }
28925
29043
  }
28926
29044
  }
28927
- for (const lead of GONE_LEADS) fs83.rmSync(path83.join(rules, `${lead}.md`), { force: true });
29045
+ for (const lead of GONE_LEADS) fs84.rmSync(path85.join(rules, `${lead}.md`), { force: true });
28928
29046
  if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
28929
29047
  }
28930
29048
  function noteOldWorkflows(board2, report3) {
28931
29049
  const old = (dir, file, keys) => {
28932
- const root = path83.join(board2, dir);
28933
- return (fs83.existsSync(root) ? fs83.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(path83.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
29050
+ const root = path85.join(board2, dir);
29051
+ return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read3(path85.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
28934
29052
  };
28935
29053
  const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
28936
29054
  if (files2.length) {
@@ -28940,7 +29058,7 @@ function noteOldWorkflows(board2, report3) {
28940
29058
  }
28941
29059
  }
28942
29060
  function checkConfig(board2, report3) {
28943
- const current = read3(path83.join(board2, "config.md"));
29061
+ const current = read3(path85.join(board2, "config.md"));
28944
29062
  if (!current) return;
28945
29063
  const added = missingConfigKeys(current);
28946
29064
  if (added.length) {
@@ -28950,7 +29068,7 @@ function checkConfig(board2, report3) {
28950
29068
  }
28951
29069
  }
28952
29070
  function checkModules(board2, report3) {
28953
- const map2 = read3(path83.join(board2, "modules.md"));
29071
+ const map2 = read3(path85.join(board2, "modules.md"));
28954
29072
  if (map2 && map2.includes(UNFILLED)) {
28955
29073
  report3.notes.push(
28956
29074
  "docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
@@ -29004,14 +29122,14 @@ function declareSetup(program2, cli) {
29004
29122
  program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
29005
29123
  const dirOption = (cmd) => cmd.option("--dir <path>", "the project to work on (default: the current folder)").option("--json", "answer as one JSON object instead of prose");
29006
29124
  const where = (cmd) => {
29007
- const dir = path84.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
29008
- if (!fs84.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
29125
+ const dir = path86.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
29126
+ if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
29009
29127
  return dir;
29010
29128
  };
29011
29129
  dirOption(program2.command("update")).summary("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u68C0\u67E5 CLI \u65B0\u7248\u672C").description("\u4FEE\u590D\u5DF2\u6709\u770B\u677F\u5E76\u63D0\u793A CLI \u5347\u7EA7\u547D\u4EE4\u3002\u6CA1\u6709\u770B\u677F\u65F6\uFF0C\u8BF7\u901A\u8FC7\u63D2\u4EF6\u8FDE\u63A5\u4E91\u7AEF\u9879\u76EE\u3002").action(async function() {
29012
29130
  const dir = where(this);
29013
29131
  const named2 = this.optsWithGlobals().board ?? process.env.AI4KANBAN_BOARD?.trim();
29014
- const board2 = named2 ? path84.resolve(cli.cwd, named2) : null;
29132
+ const board2 = named2 ? path86.resolve(cli.cwd, named2) : null;
29015
29133
  await runAction(ctxOf(this, cli.program), {}, () => cmdUpdate2({ dir, program: cli.program, board: board2 }));
29016
29134
  });
29017
29135
  program2.command("__rules", { hidden: true }).action(function() {
@@ -29055,7 +29173,7 @@ function invokedDirectly() {
29055
29173
  const entry = process.argv[1];
29056
29174
  if (!entry) return false;
29057
29175
  try {
29058
- return fs85.realpathSync(entry) === fs85.realpathSync(SELF2);
29176
+ return fs86.realpathSync(entry) === fs86.realpathSync(SELF2);
29059
29177
  } catch {
29060
29178
  return false;
29061
29179
  }