wildcrane-runner 0.6.4 → 0.6.6

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.3 — built by runner/scripts/build.mjs. Do not edit.
2
+ // ai4kanban 0.11.5 — 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;
@@ -50,9 +50,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
50
50
  mod
51
51
  ));
52
52
 
53
- // ../cli/node_modules/commander/lib/error.js
53
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/lib/error.js
54
54
  var require_error = __commonJS({
55
- "../cli/node_modules/commander/lib/error.js"(exports) {
55
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/lib/error.js"(exports) {
56
56
  var CommanderError2 = class extends Error {
57
57
  /**
58
58
  * Constructs the CommanderError class
@@ -85,9 +85,9 @@ var require_error = __commonJS({
85
85
  }
86
86
  });
87
87
 
88
- // ../cli/node_modules/commander/lib/argument.js
88
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/lib/argument.js
89
89
  var require_argument = __commonJS({
90
- "../cli/node_modules/commander/lib/argument.js"(exports) {
90
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/lib/argument.js"(exports) {
91
91
  var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
92
92
  var Argument2 = class {
93
93
  /**
@@ -212,9 +212,9 @@ var require_argument = __commonJS({
212
212
  }
213
213
  });
214
214
 
215
- // ../cli/node_modules/commander/lib/help.js
215
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/lib/help.js
216
216
  var require_help = __commonJS({
217
- "../cli/node_modules/commander/lib/help.js"(exports) {
217
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/lib/help.js"(exports) {
218
218
  var { humanReadableArgName } = require_argument();
219
219
  var Help2 = class {
220
220
  constructor() {
@@ -781,9 +781,9 @@ ${itemIndentStr}`);
781
781
  }
782
782
  });
783
783
 
784
- // ../cli/node_modules/commander/lib/option.js
784
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/lib/option.js
785
785
  var require_option = __commonJS({
786
- "../cli/node_modules/commander/lib/option.js"(exports) {
786
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/lib/option.js"(exports) {
787
787
  var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
788
788
  var Option2 = class {
789
789
  /**
@@ -1082,9 +1082,9 @@ var require_option = __commonJS({
1082
1082
  }
1083
1083
  });
1084
1084
 
1085
- // ../cli/node_modules/commander/lib/suggestSimilar.js
1085
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/lib/suggestSimilar.js
1086
1086
  var require_suggestSimilar = __commonJS({
1087
- "../cli/node_modules/commander/lib/suggestSimilar.js"(exports) {
1087
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/lib/suggestSimilar.js"(exports) {
1088
1088
  var maxDistance = 3;
1089
1089
  function editDistance2(a, b) {
1090
1090
  if (Math.abs(a.length - b.length) > maxDistance)
@@ -1162,13 +1162,13 @@ var require_suggestSimilar = __commonJS({
1162
1162
  }
1163
1163
  });
1164
1164
 
1165
- // ../cli/node_modules/commander/lib/command.js
1165
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/lib/command.js
1166
1166
  var require_command = __commonJS({
1167
- "../cli/node_modules/commander/lib/command.js"(exports) {
1167
+ "../../../../../../git/ai4kanban/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 fs88 = __require("node:fs");
1170
+ var path86 = __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 (fs88.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 (fs88.existsSync(localBin)) return localBin;
2172
- if (sourceExt.includes(path85.extname(baseName))) return void 0;
2170
+ const localBin = path86.resolve(baseDir, baseName);
2171
+ if (fs87.existsSync(localBin)) return localBin;
2172
+ if (sourceExt.includes(path86.extname(baseName))) return void 0;
2173
2173
  const foundExt = sourceExt.find(
2174
- (ext) => fs88.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 = fs88.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 = path86.resolve(
2191
+ path86.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 = path86.basename(
2199
2199
  this._scriptPath,
2200
- path85.extname(this._scriptPath)
2200
+ path86.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(path86.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 = path86.basename(filename, path86.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(path87) {
3073
+ if (path87 === void 0) return this._executableDir;
3074
+ this._executableDir = path87;
3075
3075
  return this;
3076
3076
  }
3077
3077
  /**
@@ -3321,9 +3321,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3321
3321
  }
3322
3322
  });
3323
3323
 
3324
- // ../cli/node_modules/commander/index.js
3324
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/index.js
3325
3325
  var require_commander = __commonJS({
3326
- "../cli/node_modules/commander/index.js"(exports) {
3326
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/index.js"(exports) {
3327
3327
  var { Argument: Argument2 } = require_argument();
3328
3328
  var { Command: Command2 } = require_command();
3329
3329
  var { CommanderError: CommanderError2, InvalidArgumentError: InvalidArgumentError2 } = require_error();
@@ -3343,10 +3343,10 @@ var require_commander = __commonJS({
3343
3343
  }
3344
3344
  });
3345
3345
 
3346
- // ../cli/node_modules/commander/esm.mjs
3346
+ // ../../../../../../git/ai4kanban/cli/node_modules/commander/esm.mjs
3347
3347
  var import_index, program, createCommand, createArgument, createOption, CommanderError, InvalidArgumentError, InvalidOptionArgumentError, Command, Argument, Option, Help;
3348
3348
  var init_esm = __esm({
3349
- "../cli/node_modules/commander/esm.mjs"() {
3349
+ "../../../../../../git/ai4kanban/cli/node_modules/commander/esm.mjs"() {
3350
3350
  import_index = __toESM(require_commander(), 1);
3351
3351
  ({
3352
3352
  program,
@@ -3650,7 +3650,7 @@ function useProjectState() {
3650
3650
  ensureAkbDir();
3651
3651
  return ensureProjectState(KANBAN, REPO_ROOT);
3652
3652
  }
3653
- var REPO_ROOT, KANBAN, TODO, ARCHIVE, ARCHIVE_MD, README, MODULES_MD, CONFIG, KANBAN_GITIGNORE, RELEASES, RELEASE_SUMMARIES, AGENTS, WORKFLOWS, LEGACY_AGENTS, ASSETS, BOARD_STATE, MOCKUPS, MEMORY, AGENT_MEMORY, PROJECT_MD, LOCK, UI_CONFIG, LEGACY_UI_CONFIG, LOCAL_CONFIG, ENV_FILE, SESSIONS, SESSIONS_DIR, SESSIONS_LOCK2, INDEX_LOCK2, DELIVERIES, PLANS, PLANS_ARCHIVE, RULES, TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED, SIGNALS_FILES, SIGNALS_OLD_INBOX, SIGNALS_OLD_HANDLED, AKB_DIR, ROOT_GITIGNORE, AKB_IGNORE_LINE, BOARD_FLAG, rel, BOARD_PATH_IN_TEXT, ASSETS_PATH_IN_TEXT, MOCKUPS_PATH_IN_TEXT, BOARD_STATE_IN_TEXT;
3653
+ var REPO_ROOT, KANBAN, TODO, ARCHIVE, REJECTIONS_TO_REVIEW, ARCHIVE_MD, README, MODULES_MD, CONFIG, KANBAN_GITIGNORE, RELEASES, RELEASE_SUMMARIES, AGENTS, WORKFLOWS, LEGACY_AGENTS, ASSETS, BOARD_STATE, MOCKUPS, MEMORY, AGENT_MEMORY, PROJECT_MD, LOCK, UI_CONFIG, LEGACY_UI_CONFIG, LOCAL_CONFIG, ENV_FILE, SESSIONS, SESSIONS_DIR, SESSIONS_LOCK2, INDEX_LOCK2, DELIVERIES, PLANS, PLANS_ARCHIVE, RULES, TRIAGE, SIGNALS_ARCHIVED, SIGNALS_DISMISSED, SIGNALS_FILES, SIGNALS_OLD_INBOX, SIGNALS_OLD_HANDLED, AKB_DIR, ROOT_GITIGNORE, AKB_IGNORE_LINE, BOARD_FLAG, rel, BOARD_PATH_IN_TEXT, ASSETS_PATH_IN_TEXT, MOCKUPS_PATH_IN_TEXT, BOARD_STATE_IN_TEXT;
3654
3654
  var init_paths = __esm({
3655
3655
  "../cli/src/lib/paths.ts"() {
3656
3656
  "use strict";
@@ -3660,6 +3660,7 @@ var init_paths = __esm({
3660
3660
  KANBAN = "";
3661
3661
  TODO = "";
3662
3662
  ARCHIVE = "";
3663
+ REJECTIONS_TO_REVIEW = ".sync";
3663
3664
  ARCHIVE_MD = "";
3664
3665
  README = "";
3665
3666
  MODULES_MD = "";
@@ -4018,11 +4019,12 @@ function insideRun() {
4018
4019
  function runEnv(env2, value) {
4019
4020
  return { ...env2, [RUN_ENV]: value };
4020
4021
  }
4021
- var RUN_ENV, STOP_ENV, SESSION_VARS;
4022
+ var RUN_ENV, SCHEDULED_AGENTS_ENV, STOP_ENV, SESSION_VARS;
4022
4023
  var init_env = __esm({
4023
4024
  "../cli/src/lib/agent/env.ts"() {
4024
4025
  "use strict";
4025
4026
  RUN_ENV = "KANBAN_RUN";
4027
+ SCHEDULED_AGENTS_ENV = "AI4KANBAN_SCHEDULED_AGENTS";
4026
4028
  STOP_ENV = "KANBAN_STOP_ID";
4027
4029
  SESSION_VARS = {
4028
4030
  "claude-code": "CLAUDE_CODE_SESSION_ID",
@@ -4974,8 +4976,8 @@ var init_package = __esm({
4974
4976
  "../cli/package.json"() {
4975
4977
  package_default = {
4976
4978
  name: "wildcrane",
4977
- version: "0.11.3",
4978
- akbContract: 1,
4979
+ version: "0.11.5",
4980
+ akbContract: 2,
4979
4981
  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.",
4980
4982
  keywords: [
4981
4983
  "kanban",
@@ -7867,10 +7869,10 @@ function findRollout(threadId) {
7867
7869
  }
7868
7870
  for (const name of names4) {
7869
7871
  if (!name.startsWith("rollout-") || !name.endsWith(tail2)) continue;
7870
- const path85 = join(dir, name);
7872
+ const path86 = join(dir, name);
7871
7873
  try {
7872
- const at = statSync(path85).mtimeMs;
7873
- if (!newest2 || at > newest2.at) newest2 = { path: path85, at };
7874
+ const at = statSync(path86).mtimeMs;
7875
+ if (!newest2 || at > newest2.at) newest2 = { path: path86, at };
7874
7876
  } catch {
7875
7877
  }
7876
7878
  }
@@ -7878,11 +7880,11 @@ function findRollout(threadId) {
7878
7880
  return newest2?.path;
7879
7881
  }
7880
7882
  function readCodexRunFacts(threadId) {
7881
- const path85 = findRollout(threadId);
7882
- if (!path85) return void 0;
7883
+ const path86 = findRollout(threadId);
7884
+ if (!path86) return void 0;
7883
7885
  let raw;
7884
7886
  try {
7885
- raw = readFileSync(path85, "utf8");
7887
+ raw = readFileSync(path86, "utf8");
7886
7888
  } catch {
7887
7889
  return void 0;
7888
7890
  }
@@ -8010,8 +8012,8 @@ function renderCompleted(item) {
8010
8012
  const changes = Array.isArray(item.changes) ? item.changes : [];
8011
8013
  return changes.map((raw) => {
8012
8014
  const change2 = obj(raw);
8013
- const path85 = str2(change2.path);
8014
- return path85 ? `\u23FA ${str2(change2.kind) || "change"}(${path85})
8015
+ const path86 = str2(change2.path);
8016
+ return path86 ? `\u23FA ${str2(change2.kind) || "change"}(${path86})
8015
8017
  ` : "";
8016
8018
  }).join("");
8017
8019
  }
@@ -8270,10 +8272,10 @@ function readKimiRunFacts(sessionId) {
8270
8272
  const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
8271
8273
  let model;
8272
8274
  let holding = 0;
8273
- for (const { path: path85, main } of wireFiles(entry.sessionDir)) {
8275
+ for (const { path: path86, main } of wireFiles(entry.sessionDir)) {
8274
8276
  let raw;
8275
8277
  try {
8276
- raw = readFileSync2(path85, "utf8");
8278
+ raw = readFileSync2(path86, "utf8");
8277
8279
  } catch {
8278
8280
  continue;
8279
8281
  }
@@ -8554,8 +8556,8 @@ var init_tail = __esm({
8554
8556
  import fs22 from "node:fs";
8555
8557
  function whereHint(update2, title) {
8556
8558
  const locations = Array.isArray(update2.locations) ? update2.locations : [];
8557
- const path85 = str2(obj(locations[0]).path);
8558
- return path85 && !title.includes(path85) ? hint(path85) : "";
8559
+ const path86 = str2(obj(locations[0]).path);
8560
+ return path86 && !title.includes(path86) ? hint(path86) : "";
8559
8561
  }
8560
8562
  function saidBy(update2) {
8561
8563
  const content = Array.isArray(update2.content) ? update2.content : [];
@@ -11781,7 +11783,7 @@ var init_flows = __esm({
11781
11783
  argument: "<id> [note...]",
11782
11784
  argumentNote: NOTE,
11783
11785
  gloss: "build the card",
11784
- options: [RUNTIME_OPTION]
11786
+ options: [RUNTIME_OPTION, ...CARRY_OPTIONS]
11785
11787
  },
11786
11788
  {
11787
11789
  command: "conflict",
@@ -11860,9 +11862,8 @@ var init_flows = __esm({
11860
11862
  action: "review-dismissals",
11861
11863
  argument: "",
11862
11864
  gloss: "learn what the user does not want from their rejection and dismissal reasons",
11863
- options: [{ flags: "--all", description: "every reason on the board, not only those since the last review \u2014 a Runner's board" }],
11864
11865
  more: [
11865
- "Reads the reasons written since the last review that passed, and withdraws what rested on an item since restored."
11866
+ "Reads the reasons Cloud has not had learned, and withdraws what rested on an item since restored."
11866
11867
  ]
11867
11868
  },
11868
11869
  // The project writer's one flow (#1268). Typed bare: it acts on the project.
@@ -11957,21 +11958,10 @@ function parseStamp(raw) {
11957
11958
  const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), 0, 0);
11958
11959
  return Number.isNaN(d.getTime()) ? null : d;
11959
11960
  }
11960
- function nextDue(lastRun, cadence) {
11961
- const c = typeof cadence === "string" ? parseCadence(cadence) : cadence;
11962
- if (!c) return null;
11963
- const last = parseStamp(lastRun);
11964
- if (!last) return /* @__PURE__ */ new Date(0);
11965
- if (c.unit === "m") return new Date(last.getTime() + c.n * 6e4);
11966
- if (c.unit === "h") return new Date(last.getTime() + c.n * 36e5);
11967
- const [hour, minute] = c.at ? c.at.split(":").map(Number) : [last.getHours(), last.getMinutes()];
11968
- return new Date(last.getFullYear(), last.getMonth(), last.getDate() + c.n, hour, minute, 0, 0);
11969
- }
11970
- var CADENCE_FORMS, AUTO_CADENCE, isAuto, CADENCE_RE, pad;
11961
+ var AUTO_CADENCE, isAuto, CADENCE_RE, pad;
11971
11962
  var init_cadence = __esm({
11972
11963
  "../cli/src/lib/cadence.ts"() {
11973
11964
  "use strict";
11974
- CADENCE_FORMS = "<N>m (minutes), <N>h (hours), <N>d (days), or <N>d at HH:MM (whole days, at that time of day) \u2014 e.g. 30m, 6h, 1d at 09:30";
11975
11965
  AUTO_CADENCE = "auto";
11976
11966
  isAuto = (cadence) => cadence.trim().toLowerCase() === AUTO_CADENCE;
11977
11967
  CADENCE_RE = /^(\d+)\s*([mhd])(?:\s+at\s+(\d{1,2}):(\d{2}))?$/i;
@@ -11980,7 +11970,7 @@ var init_cadence = __esm({
11980
11970
  });
11981
11971
 
11982
11972
  // ../cli/src/lib/agent/types.ts
11983
- var SPECIALIST_ACTIONS, CARD_FREE_ACTIONS, holdsCard, REFINE_ACTIONS, RETIRED, isRetired, refusal, RETIRED_WORKFLOWS, workflowDeleted, SCHEDULED_CADENCE;
11973
+ var SPECIALIST_ACTIONS, CARD_FREE_ACTIONS, holdsCard, REFINE_ACTIONS, RETIRED, isRetired, refusal, RETIRED_WORKFLOWS, workflowDeleted;
11984
11974
  var init_types3 = __esm({
11985
11975
  "../cli/src/lib/agent/types.ts"() {
11986
11976
  "use strict";
@@ -11996,7 +11986,6 @@ var init_types3 = __esm({
11996
11986
  refusal = (reason, error, args) => args ? { error, reason, args } : { error, reason };
11997
11987
  RETIRED_WORKFLOWS = ["content"];
11998
11988
  workflowDeleted = (id, known2) => !!id && !known2.includes(id) && !RETIRED_WORKFLOWS.includes(id);
11999
- SCHEDULED_CADENCE = "1d";
12000
11989
  }
12001
11990
  });
12002
11991
 
@@ -12217,16 +12206,6 @@ function setSecret(name, value) {
12217
12206
  return { ok: false, ...refusal("fileWrite", `couldn't write ${ENV_FILE}: ${why}`, { path: ENV_FILE, details: why }) };
12218
12207
  }
12219
12208
  }
12220
- function readSchedule(key) {
12221
- const block = configBlock(safeConfig()[key]);
12222
- const saved = stringIn(block, "cadence");
12223
- return {
12224
- enabled: block.off !== true,
12225
- cadence: saved && parseCadence(saved) ? saved : AUTO_CADENCE,
12226
- lastRun: stringIn(block, "lastRun"),
12227
- ...stringIn(block, "since") ? { since: stringIn(block, "since") } : {}
12228
- };
12229
- }
12230
12209
  function stampSchedule(key, when) {
12231
12210
  return writeConfig((cfg) => {
12232
12211
  cfg[key] = { ...configBlock(cfg[key]), lastRun: formatStamp(when) };
@@ -12259,7 +12238,7 @@ function dropReflected(ids) {
12259
12238
  cfg.reflectQueue = queueIn(cfg).filter((id) => !ids.includes(id));
12260
12239
  });
12261
12240
  }
12262
- var SILENCE_MINUTES, BACKGROUND_WAIT, BOARD_SPEC_KEYS, IGNORE_BLOCK, stringIn, stampMemoryPrune, queueIn, reflectQueue, dismissalReview, stampDismissalReview, stampProjectDescription;
12241
+ var SILENCE_MINUTES, BACKGROUND_WAIT, BOARD_SPEC_KEYS, IGNORE_BLOCK, stringIn, stampMemoryPrune, queueIn, reflectQueue, stampProjectDescription;
12263
12242
  var init_settings2 = __esm({
12264
12243
  "../cli/src/lib/agent/settings.ts"() {
12265
12244
  "use strict";
@@ -12276,8 +12255,6 @@ var init_settings2 = __esm({
12276
12255
  stampMemoryPrune = (when = /* @__PURE__ */ new Date()) => void stampSchedule("memoryPrune", when);
12277
12256
  queueIn = (cfg) => Array.isArray(cfg.reflectQueue) ? cfg.reflectQueue.filter((id) => Number.isInteger(id) && id > 0) : [];
12278
12257
  reflectQueue = () => queueIn(safeConfig());
12279
- dismissalReview = () => readSchedule("dismissalReview");
12280
- stampDismissalReview = (when) => void stampSchedule("dismissalReview", when);
12281
12258
  stampProjectDescription = (when) => void stampSchedule("projectDescription", when);
12282
12259
  }
12283
12260
  });
@@ -13352,7 +13329,7 @@ ${rest}`);
13352
13329
  fs31.rmSync(goal, { force: true });
13353
13330
  return words ? `moved the goal to the top of ${rel(agentMemoryFile(PLANNER, "decisions.md"))}` : "removed the empty goal";
13354
13331
  }
13355
- var isGlobalMemory, PLANNER, PLANNER_MEMORY_FILES, PROPOSER, PROPOSER_MISSED, proposerMissedFile, BOARD_MEMORY_FILES, STARTERS, onlyStarter, setOf, RESERVED_MEMORY_DIR, KEPT_MEMORY_FOLDERS, agentMemoryDir, agentMemoryFile, writtenMemoryNames, memoryNamesOf, agentMemoryFiles, legacyAgentMemoryFile, HEADINGS, HEADING_RE, stripHeading, FOLDED_UNDER, moveMemory, RESERVED_MODULE_NAMES, isModule, moduleMemoryFile, plannerCopies, memoryFile, ENTRY_RE, entriesOf2, demote, promote, PRODUCT_STARTER, SEEDED_GOAL_PARAGRAPHS;
13332
+ var isGlobalMemory, PLANNER, PLANNER_MEMORY_FILES, BOARD_MEMORY_FILES, STARTERS, onlyStarter, setOf, RESERVED_MEMORY_DIR, KEPT_MEMORY_FOLDERS, agentMemoryDir, agentMemoryFile, writtenMemoryNames, memoryNamesOf, agentMemoryFiles, legacyAgentMemoryFile, HEADINGS, HEADING_RE, stripHeading, FOLDED_UNDER, moveMemory, RESERVED_MODULE_NAMES, isModule, moduleMemoryFile, plannerCopies, memoryFile, ENTRY_RE, entriesOf2, demote, promote, PRODUCT_STARTER, SEEDED_GOAL_PARAGRAPHS;
13356
13333
  var init_memory = __esm({
13357
13334
  "../cli/src/lib/memory.ts"() {
13358
13335
  "use strict";
@@ -13363,9 +13340,6 @@ var init_memory = __esm({
13363
13340
  isGlobalMemory = (name) => name in BUNDLED_MEMORY_FILES || fs31.existsSync(path32.join(MEMORY, name, "MEMORY.md"));
13364
13341
  PLANNER = "planner";
13365
13342
  PLANNER_MEMORY_FILES = ["decisions.md", "rejected.md", "redesign.md"];
13366
- PROPOSER = "proposer";
13367
- PROPOSER_MISSED = "missed.md";
13368
- proposerMissedFile = () => rel(agentMemoryFile(PROPOSER, PROPOSER_MISSED));
13369
13343
  BOARD_MEMORY_FILES = ["readme.md", "project.md"];
13370
13344
  STARTERS = {
13371
13345
  "readme.md": `# Shipped
@@ -14022,7 +13996,7 @@ function switchWorkflowAgent(id, agent, on) {
14022
13996
  if (!listedHere(flow, wanted)) {
14023
13997
  if (!onBoard().has(wanted)) return { ok: false, ...refusal("agentNotFound", `this board has no \`${wanted}\` agent`, { agent: wanted }) };
14024
13998
  if (agentWorkflow(wanted) === id && flow.scheduled.some((h) => h.agent === wanted)) {
14025
- return { ok: false, ...refusal("agentNotListed", `\`${wanted}\` is a scheduled agent of "${flow.name}" \u2014 switch it with \`akb workflow schedule\``, { agent: wanted, name: flow.name, workflow: flow.id }) };
13999
+ return { ok: false, ...refusal("agentNotListed", `\`${wanted}\` is a scheduled agent of "${flow.name}" \u2014 switch it on the project's Agents page`, { agent: wanted, name: flow.name, workflow: flow.id }) };
14026
14000
  }
14027
14001
  const refused = elsewhere(wanted, id);
14028
14002
  if (refused) return refused;
@@ -14079,6 +14053,11 @@ function scheduledMembers(flow) {
14079
14053
  const known2 = onBoard();
14080
14054
  return flow.scheduled.filter((h) => known2.has(h.agent));
14081
14055
  }
14056
+ function listedScheduled(flow) {
14057
+ const def = (flow.builtIn ? BUILTINS : boardWorkflowDefs().defs).find((d) => d.id === flow.id);
14058
+ const known2 = onBoard();
14059
+ return (def?.scheduled ?? []).map((s) => s.agent).filter((agent) => known2.has(agent));
14060
+ }
14082
14061
  function setScheduled(id, change2, claim = "") {
14083
14062
  const flow = workflowById(id);
14084
14063
  if (!flow) return notFound(id);
@@ -14091,48 +14070,6 @@ function setScheduled(id, change2, claim = "") {
14091
14070
  block.stages = stages;
14092
14071
  });
14093
14072
  }
14094
- function switchWorkflowScheduled(id, agent, on, now = /* @__PURE__ */ new Date()) {
14095
- const wanted = canonicalSpecAgent(agent.trim());
14096
- return setScheduled(id, (rows) => {
14097
- let one = agentWorkflow(wanted) === id ? rows.find((h) => h.agent === wanted) : void 0;
14098
- if (!one) {
14099
- if (!onBoard().has(wanted)) return { ok: false, ...refusal("agentNotFound", `this board has no \`${wanted}\` agent`, { agent: wanted }) };
14100
- const flow = workflowById(id);
14101
- if (listedHere(flow, wanted)) return notScheduled(wanted, flow);
14102
- const refused = elsewhere(wanted, id);
14103
- if (refused) return refused;
14104
- one = { agent: wanted, extra: "", off: true, cadence: AUTO_CADENCE, lastRun: "" };
14105
- rows.push(one);
14106
- }
14107
- if (!on) one.off = true;
14108
- else if (one.off) {
14109
- delete one.off;
14110
- one.since = formatStamp(now);
14111
- }
14112
- });
14113
- }
14114
- function setWorkflowScheduledCadence(id, agent, cadence) {
14115
- const wanted = canonicalSpecAgent(agent.trim());
14116
- const said3 = cadence.trim();
14117
- const next = !said3 || isAuto(said3) ? AUTO_CADENCE : said3;
14118
- if (!isAuto(next) && !parseCadence(next)) {
14119
- const formats = `${AUTO_CADENCE}, or ${CADENCE_FORMS}`;
14120
- return { ok: false, ...refusal("cadence", `"${next}" isn't a cadence \u2014 use ${formats}`, { cadence: next, formats }) };
14121
- }
14122
- return setScheduled(id, (rows) => {
14123
- const one = rows.find((h) => h.agent === wanted);
14124
- if (!one) return notScheduled(wanted, workflowById(id));
14125
- one.cadence = next;
14126
- });
14127
- }
14128
- function setWorkflowScheduledExtra(id, agent, extra) {
14129
- const wanted = canonicalSpecAgent(agent.trim());
14130
- return setScheduled(id, (rows) => {
14131
- const one = rows.find((h) => h.agent === wanted);
14132
- if (!one) return notScheduled(wanted, workflowById(id));
14133
- one.extra = extra;
14134
- });
14135
- }
14136
14073
  function stampScheduledRun(id, agent, startedAt) {
14137
14074
  return setScheduled(
14138
14075
  id,
@@ -14181,7 +14118,7 @@ function frozenWorkflow(id) {
14181
14118
  land: landAgents(flow).map(row)
14182
14119
  };
14183
14120
  }
14184
- var LAND_ROLES, DEFAULT_WORKFLOW, BUILTINS, BUILTIN_WORKFLOW_IDS, isBuiltinWorkflow, builtinDescription, workflowsBlock, storedStages, PREVIEWS, LAND, SCHEDULE, toRow, scheduledRow, OLD_STAGE_KEYS, savedOldStages, workflowFileProblems, allListed, resolved, agentAfter, agentKeyProblems, agentWorkflow, workflows, workflowById, onBoard, previewMembers, previewAgents, landAgents, archivesOnApproval, workflowRuleFile, save2, ownDef, notFound, trimmedName, numbered, freeId2, nameTaken, listedHere, scheduledAgent, scheduledClock, notScheduled, cardWorkflowId, cardWorkflow, cardStages, cardStage;
14121
+ var LAND_ROLES, DEFAULT_WORKFLOW, BUILTINS, BUILTIN_WORKFLOW_IDS, isBuiltinWorkflow, builtinDescription, workflowsBlock, storedStages, PREVIEWS, LAND, SCHEDULE, toRow, scheduledRow, OLD_STAGE_KEYS, savedOldStages, workflowFileProblems, allListed, resolved, agentAfter, agentKeyProblems, agentWorkflow, workflows, workflowById, onBoard, previewMembers, previewAgents, landAgents, archivesOnApproval, workflowRuleFile, save2, ownDef, notFound, trimmedName, numbered, freeId2, nameTaken, listedHere, scheduledAgent, cardWorkflowId, cardWorkflow, cardStages, cardStage;
14185
14122
  var init_workflows = __esm({
14186
14123
  "../cli/src/lib/agent/workflows.ts"() {
14187
14124
  "use strict";
@@ -14285,11 +14222,6 @@ var init_workflows = __esm({
14285
14222
  const flow = workflowById(id);
14286
14223
  return flow ? scheduledMembers(flow).find((h) => h.agent === canonicalSpecAgent(agent.trim())) : void 0;
14287
14224
  };
14288
- scheduledClock = (one) => [one.lastRun, one.since ?? ""].filter((stamp4) => parseStamp(stamp4)).sort().pop() ?? "";
14289
- notScheduled = (agent, flow) => ({
14290
- ok: false,
14291
- ...refusal("agentNotScheduled", `\`${agent}\` is not a scheduled agent of "${flow.name}"`, { agent, name: flow.name, workflow: flow.id })
14292
- });
14293
14225
  cardWorkflowId = (id) => cardMeta(id)?.workflow ?? "";
14294
14226
  cardWorkflow = (id) => workflowFor(typeof id === "number" ? cardWorkflowId(id) : "");
14295
14227
  cardStages = (meta, flow) => previewStages(flow, meta?.previews ?? []);
@@ -14614,7 +14546,7 @@ function agentRoster() {
14614
14546
  ...specialists.filter((a) => !a.builtIn)
14615
14547
  ];
14616
14548
  }
14617
- var PLANNER_DECISIONS, PLANNER_REJECTED, PLANNER_REDESIGN, DISCUSSION_HELPER, MEMORY_PRUNER, MEMORY_REVIEWER, DISMISSAL_REVIEWER, PROJECT_WRITER, SMALL_FIX, PROPOSER2, PLANNER_ROLE, BOARD_ROLES, DISCUSSION_ROLE, ROLE_NAMES, PLANNING_ROLE, roles, roleForFlow, roleNamed, hiddenRole, stageContractProblems, memoryOf, ownMemoryOf, agentNames;
14549
+ var PLANNER_DECISIONS, PLANNER_REJECTED, PLANNER_REDESIGN, DISCUSSION_HELPER, MEMORY_PRUNER, MEMORY_REVIEWER, DISMISSAL_REVIEWER, PROJECT_WRITER, SMALL_FIX, PROPOSER, PLANNER_ROLE, BOARD_ROLES, DISCUSSION_ROLE, ROLE_NAMES, PLANNING_ROLE, roles, roleForFlow, roleNamed, hiddenRole, stageContractProblems, memoryOf, ownMemoryOf, agentNames;
14618
14550
  var init_roles = __esm({
14619
14551
  "../cli/src/lib/agent/roles.ts"() {
14620
14552
  "use strict";
@@ -14653,10 +14585,10 @@ var init_roles = __esm({
14653
14585
  memory: ["memory/project.md"]
14654
14586
  };
14655
14587
  SMALL_FIX = "A small fix is a small change whose workflow needs none of its previews.";
14656
- PROPOSER2 = {
14588
+ PROPOSER = {
14657
14589
  name: "proposer",
14658
14590
  gloss: "proposes the work a finished card leaves behind",
14659
- memory: [`memory/agents/proposer/${PROPOSER_MISSED}`],
14591
+ memory: [],
14660
14592
  settings: [
14661
14593
  {
14662
14594
  key: "small-fixes",
@@ -14703,7 +14635,7 @@ var init_roles = __esm({
14703
14635
  MEMORY_REVIEWER,
14704
14636
  DISMISSAL_REVIEWER,
14705
14637
  PROJECT_WRITER,
14706
- PROPOSER2
14638
+ PROPOSER
14707
14639
  ];
14708
14640
  DISCUSSION_ROLE = DISCUSSION_HELPER.name;
14709
14641
  ROLE_NAMES = BOARD_ROLES.map((r) => r.name);
@@ -15597,11 +15529,6 @@ function parse(file, lenient = false) {
15597
15529
  cardId: /^\d+$/.test(held4.card_id ?? "") ? Number(held4.card_id) : null,
15598
15530
  archivedAt: held4.archived_at || "",
15599
15531
  contentKept: held4.content_kept !== "false",
15600
- verdict: VERDICTS.includes(held4.verdict) ? held4.verdict : "",
15601
- verdictReason: REASONS.includes(held4.verdict_reason) ? held4.verdict_reason : "",
15602
- verdictCard: /^\d+$/.test(held4.verdict_card ?? "") ? Number(held4.verdict_card) : null,
15603
- dropConfidence: confidenceOf(held4.drop_confidence),
15604
- doConfidence: confidenceOf(held4.do_confidence),
15605
15532
  relPath: boardRel(file)
15606
15533
  };
15607
15534
  }
@@ -15637,11 +15564,6 @@ function writeSignal(incoming, importedAt, slug2 = "") {
15637
15564
  cardId: null,
15638
15565
  archivedAt: "",
15639
15566
  contentKept: true,
15640
- verdict: "",
15641
- verdictReason: "",
15642
- verdictCard: null,
15643
- dropConfidence: null,
15644
- doConfidence: null,
15645
15567
  relPath: ""
15646
15568
  };
15647
15569
  const file = path41.join(TRIAGE, freeName2(TRIAGE, fileName(signal2.title, slug2)));
@@ -15726,7 +15648,7 @@ function restoreInboxItems(sourceIds) {
15726
15648
  function restoreInboxItem(sourceId) {
15727
15649
  return restoreInboxItems([sourceId])[0];
15728
15650
  }
15729
- var boardRel, VERDICTS, REASONS, FIELDS, META_LINE, confidenceOf, slugOf, fileName, readArchived, readAllDismissed, bySource;
15651
+ var boardRel, FIELDS, META_LINE, slugOf, fileName, readArchived, readAllDismissed, bySource;
15730
15652
  var init_inbox = __esm({
15731
15653
  "../cli/src/lib/signals/inbox.ts"() {
15732
15654
  "use strict";
@@ -15735,11 +15657,8 @@ var init_inbox = __esm({
15735
15657
  init_paths();
15736
15658
  init_yaml();
15737
15659
  boardRel = (file) => rel(file).split(path41.sep).join("/");
15738
- VERDICTS = ["plan", "plan-without-refine", "skip", "human-review"];
15739
- REASONS = ["supported", "rejected", "duplicate", "low-value", "needs-user", "unsure", "small", "plan", "no-workflow"];
15740
15660
  FIELDS = ["source_id", "title", "collected_at", "imported_at"];
15741
15661
  META_LINE = /^\s+("(?:[^"\\]|\\.)*"|[^:]+):\s*(.*)$/;
15742
- confidenceOf = (said3) => /^(0(\.\d+)?|1(\.0+)?)$/.test(said3 ?? "") ? Number(said3) : null;
15743
15662
  slugOf = (said3) => said3.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 56).replace(/-+$/, "");
15744
15663
  fileName = (title, slug2 = "") => `${slugOf(slug2) || slugOf(title) || "item"}.md`;
15745
15664
  readArchived = () => readFolder(SIGNALS_ARCHIVED);
@@ -16404,11 +16323,11 @@ function resumeRefusal(delivery, store) {
16404
16323
  );
16405
16324
  }
16406
16325
  if (!worktreeExists(delivery.worktree)) {
16407
- const path85 = delivery.worktree ?? "";
16326
+ const path86 = delivery.worktree ?? "";
16408
16327
  return refusal(
16409
16328
  "deliveryWorktreeGone",
16410
- `delivery ${id}'s worktree ${path85} is gone, so there is nothing to carry on. ${rebuild}`,
16411
- { id, task, path: path85, command: discard }
16329
+ `delivery ${id}'s worktree ${path86} is gone, so there is nothing to carry on. ${rebuild}`,
16330
+ { id, task, path: path86, command: discard }
16412
16331
  );
16413
16332
  }
16414
16333
  if (!branchExists(delivery.branch)) {
@@ -17812,7 +17731,8 @@ function reflectBlocks(ids) {
17812
17731
  ]);
17813
17732
  }
17814
17733
  function scheduledBlock() {
17815
- const on = new Set(workflows().flatMap((flow) => scheduledMembers(flow).filter((h) => !h.off).map((h) => h.agent)));
17734
+ const given = process.env[SCHEDULED_AGENTS_ENV];
17735
+ const on = new Set(given === void 0 ? workflows().flatMap((flow) => listedScheduled(flow)) : given.split(","));
17816
17736
  const lines = specAgentCatalog().agents.filter((a) => on.has(a.name)).map((a) => `${a.name} \u2014 ${a.description}`);
17817
17737
  return lines.length ? ["<scheduled-agents>", ...lines, "</scheduled-agents>"] : [];
17818
17738
  }
@@ -17840,10 +17760,10 @@ function answeredNote(cardId2, command) {
17840
17760
  }
17841
17761
  function carriedNote(req) {
17842
17762
  if (req.session !== "resume" && req.session !== "fork") {
17843
- return req.carry?.transcript ? `This card came out of a discussion. Read it for what the card leaves out: ${req.carry.transcript}` : "";
17763
+ return req.carry?.transcript ? `This card came out of a discussion. The card is the final word: the discussion only fills in the background, and what it dropped stays dropped. Read it for what the card leaves out: ${req.carry.transcript}` : "";
17844
17764
  }
17845
- const held4 = req.session === "resume" ? "This session planned this card before." : "This session is a copy of the one this card was created in.";
17846
- return `${held4} The card as it reads now outranks what you remember of it: read it again before you act.`;
17765
+ const held4 = req.session === "resume" ? "This session planned this card before." : req.carry?.resumeId ? "This session is a copy of the discussion this card came from, and that discussion's rules ended with it: follow this ask." : "This session is a copy of the one this card was created in.";
17766
+ return `${held4} The card as it reads now outranks what you remember of it, and what was dropped along the way stays dropped: read it again before you act.`;
17847
17767
  }
17848
17768
  function actionPrompt(req, command, notes) {
17849
17769
  const kb = skillCall(agentForRun(req), req.runtime);
@@ -17866,6 +17786,7 @@ function actionPrompt(req, command, notes) {
17866
17786
  }
17867
17787
  return [
17868
17788
  `${kb}. Implement task ${req.id} ${named2} following \`akb guide implement\`.`,
17789
+ carriedNote(req),
17869
17790
  req.notes ? `Extra notes: ${req.notes}` : ""
17870
17791
  ].filter(Boolean).join(" ");
17871
17792
  case "archive":
@@ -17933,10 +17854,7 @@ function actionPrompt(req, command, notes) {
17933
17854
  // The dismissal review (#929). The flow lists the dismissals and restored ids; the guide
17934
17855
  // holds every rule.
17935
17856
  case "review-dismissals":
17936
- return [
17937
- `${kb}. Learn what the user does not want from their rejections and dismissals, following \`akb guide review-rejections\`.`,
17938
- req.allReasons ? `The reasons are the ones \`${command} review-dismissals --all --print\` prints.` : ""
17939
- ].filter(Boolean).join(" ");
17857
+ return `${kb}. Learn what the user does not want from their rejections and dismissals, following \`akb guide review-rejections\`.`;
17940
17858
  case "describe-project":
17941
17859
  return `${kb}. Describe this project following \`akb guide describe-project\`.`;
17942
17860
  // Reflecting on the cards the board has just completed (#534, #1467). They are off the
@@ -17951,7 +17869,7 @@ function actionPrompt(req, command, notes) {
17951
17869
  `${kb}. These tasks have just been completed: ${titles.join(", ")}. Propose the work that should follow them, following \`akb guide reflect\`.`,
17952
17870
  `They have left the board \u2014 read each in its own <card> block below, with the discussion it came from when the block lists one.`,
17953
17871
  `Skip anything already on the board, already in the inbox, or turned down before.`,
17954
- `Write each survivor with \`${command} triage add\`, and revise an open card whose plan what shipped made untrue with \`${command} card revise\`: that is all you may do unless your settings below say otherwise \u2014 you edit and archive no card yourself, and finding nothing worth proposing is a complete result.`,
17872
+ `Write each survivor with \`${command} triage add\`, and revise an open card whose plan what shipped made untrue with \`${command} card revise\`: that is all you may do${settingsBlock(req) ? " unless your settings below say otherwise" : ""} \u2014 you edit and archive no card yourself, and finding nothing worth proposing is a complete result.`,
17955
17873
  `Don't ask me questions with human-in-the-loop.`
17956
17874
  ].join(" "),
17957
17875
  ...reflectBlocks(ids),
@@ -18099,6 +18017,7 @@ var init_prompts = __esm({
18099
18017
  init_agents();
18100
18018
  init_command();
18101
18019
  init_deliveries();
18020
+ init_env();
18102
18021
  init_facts();
18103
18022
  init_outputs();
18104
18023
  init_archive();
@@ -18456,13 +18375,6 @@ function recordReflection(run2) {
18456
18375
  } catch {
18457
18376
  }
18458
18377
  }
18459
- function recordDismissalReview(run2) {
18460
- if (run2.action !== "review-dismissals" || run2.status !== "done") return;
18461
- try {
18462
- stampDismissalReview(new Date(run2.startedAt));
18463
- } catch {
18464
- }
18465
- }
18466
18378
  function recordScheduledRun(run2) {
18467
18379
  if (run2.action !== "scheduled" || run2.status !== "done" || !run2.workflow || !run2.specAgent) return;
18468
18380
  try {
@@ -18822,7 +18734,8 @@ function planningSession(cardId2, cwd = REPO_ROOT) {
18822
18734
  return created ?? { none: "unrecorded" };
18823
18735
  }
18824
18736
  function carriedSession(req) {
18825
- if (!Number.isInteger(req.id) || !PLANNING.has(req.action)) return void 0;
18737
+ const carries = PLANNING.has(req.action) || req.action === "implement" && !!req.carry?.resumeId;
18738
+ if (!Number.isInteger(req.id) || !carries) return void 0;
18826
18739
  const fresh = (why) => ({ note: `new session \u2014 ${why}` });
18827
18740
  if (req.session === "new") return fresh("the earlier one is gone, so this step was started again");
18828
18741
  const agent = agentForRun(req);
@@ -19153,7 +19066,6 @@ async function closeRun(sessionId, res) {
19153
19066
  recordPrune(closed2);
19154
19067
  recordMemoryReview(closed2);
19155
19068
  recordReflection(closed2);
19156
- recordDismissalReview(closed2);
19157
19069
  recordProjectDescription(closed2);
19158
19070
  recordScheduledRun(closed2);
19159
19071
  dropSpec(sessionId);
@@ -19805,7 +19717,7 @@ import fs51 from "node:fs";
19805
19717
  import path55 from "node:path";
19806
19718
  function defaultBody() {
19807
19719
  return [
19808
- "<one short paragraph: the observable result and the current behavior or constraint it changes.>",
19720
+ "<\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>",
19809
19721
  "",
19810
19722
  "## Worth noting",
19811
19723
  "",
@@ -20863,145 +20775,6 @@ _(not filled in yet \u2014 build the map from the repo before tagging a card.)_
20863
20775
  }
20864
20776
  });
20865
20777
 
20866
- // ../cli/src/lib/agent/dismissal-review.ts
20867
- import fs57 from "node:fs";
20868
- function rejectionsToReview(since) {
20869
- if (!fs57.existsSync(ARCHIVE)) return [];
20870
- const found2 = [];
20871
- for (const file of walkMd(ARCHIVE)) {
20872
- const id = archivedId(file);
20873
- if (id === null) continue;
20874
- try {
20875
- const { meta } = parseFrontmatter(fs57.readFileSync(file, "utf8"));
20876
- if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
20877
- if ((parseStamp(meta.rejected_at)?.getTime() ?? 0) < since) continue;
20878
- found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
20879
- } catch {
20880
- continue;
20881
- }
20882
- }
20883
- return found2.sort((a, b) => a.rejectedAt.localeCompare(b.rejectedAt) || a.id - b.id);
20884
- }
20885
- function dismissalsToReview(since) {
20886
- return readAllDismissed().filter((s) => s.dismissedBy === "user" && s.dismissedReason.trim()).filter((s) => (parseStamp(s.dismissedAt)?.getTime() ?? 0) >= since).sort((a, b) => a.dismissedAt.localeCompare(b.dismissedAt)).map((s) => ({
20887
- sourceId: s.sourceId,
20888
- title: s.title,
20889
- file: s.relPath,
20890
- reason: s.dismissedReason.trim(),
20891
- dismissedAt: s.dismissedAt
20892
- }));
20893
- }
20894
- function citedSources(text5) {
20895
- const ids = /* @__PURE__ */ new Set();
20896
- for (const line of text5.split("\n")) {
20897
- if (!/^\s*[-*]\s/.test(line)) continue;
20898
- const m = line.match(SOURCES);
20899
- for (const id of m?.[1].split(",") ?? []) if (id.trim()) ids.add(id.trim());
20900
- }
20901
- return [...ids];
20902
- }
20903
- function withdrawnSources() {
20904
- const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
20905
- try {
20906
- return citedSources(fs57.readFileSync(file, "utf8"));
20907
- } catch {
20908
- return [];
20909
- }
20910
- }))];
20911
- if (cited.length === 0) return [];
20912
- const dismissed = new Set(readAllDismissed().map((s) => s.sourceId));
20913
- const restored = new Set([...readInbox(), ...readArchived()].map((s) => s.sourceId));
20914
- return cited.filter((id) => restored.has(id) && !dismissed.has(id));
20915
- }
20916
- function rejectionWorkWaiting(since) {
20917
- return rejectionsToReview(since).length > 0 || dismissalsToReview(since).length > 0 || withdrawnSources().length > 0;
20918
- }
20919
- var SOURCES;
20920
- var init_dismissal_review = __esm({
20921
- "../cli/src/lib/agent/dismissal-review.ts"() {
20922
- "use strict";
20923
- init_cadence();
20924
- init_cards();
20925
- init_frontmatter();
20926
- init_memory();
20927
- init_paths();
20928
- init_inbox();
20929
- init_archive();
20930
- SOURCES = /\(([^()]+)\)\s*$/;
20931
- }
20932
- });
20933
-
20934
- // ../cli/src/lib/agent/due.ts
20935
- import fs58 from "node:fs";
20936
- function scheduleDue(ask2, now = Date.now()) {
20937
- const gap = !isAuto(ask2.cadence) ? ask2.cadence : autoSchedule(ask2.reads, ask2.fallback).every;
20938
- const from = Math.max(ask2.from, ...ask2.attempts.map((a) => a.startedAt));
20939
- let next = ask2.goesOn ? /* @__PURE__ */ new Date(0) : from ? nextDue(formatStamp(new Date(from)), gap) ?? /* @__PURE__ */ new Date(0) : /* @__PURE__ */ new Date(0);
20940
- const failed = failures(ask2.attempts);
20941
- if (!ask2.goesOn && from && failed > 1) {
20942
- const base = next.getTime() - from;
20943
- next = new Date(from + Math.min(base * 2 ** (failed - 1), Math.max(base, DAY)));
20944
- }
20945
- const newest2 = ask2.attempts.reduce((a, r) => a && a.startedAt >= r.startedAt ? a : r, null);
20946
- const retrying = isAuto(ask2.cadence) && newest2?.status === "error";
20947
- const wait = next.getTime() > now ? retrying ? "retrying" : "tooSoon" : !ask2.newWork() ? "nothingNew" : ask2.building?.() ? "building" : null;
20948
- const reason = !wait || wait === "tooSoon" ? void 0 : wait === "nothingNew" ? ask2.reads : wait;
20949
- return { next, wait, ...reason ? { reason } : {} };
20950
- }
20951
- function readsNew(reads, since) {
20952
- switch (reads) {
20953
- case "archived-cards":
20954
- return archivedSince(since);
20955
- case "chats":
20956
- return false;
20957
- case "dismissals":
20958
- return rejectionWorkWaiting(since);
20959
- }
20960
- }
20961
- function archivedSince(since) {
20962
- if (!fs58.existsSync(ARCHIVE)) return false;
20963
- const day = formatDay(new Date(since));
20964
- for (const file of walkMd(ARCHIVE)) {
20965
- try {
20966
- if (fs58.statSync(file).mtimeMs <= since) continue;
20967
- const { meta } = parseFrontmatter(fs58.readFileSync(file, "utf8"));
20968
- if (meta && !meta.rejected && (meta.archived ?? "") >= day) return true;
20969
- } catch {
20970
- continue;
20971
- }
20972
- }
20973
- return false;
20974
- }
20975
- function building(store) {
20976
- const builds = new Set(store.deliveries.filter((d) => d.status === "active" && !d.scheduled).map((d) => d.deliveryId));
20977
- if (store.deliveries.some((d) => builds.has(d.deliveryId) && d.landing)) return true;
20978
- return store.runs.some((r) => r.status === "running" && r.deliveryId !== void 0 && builds.has(r.deliveryId));
20979
- }
20980
- var AUTO_GAP, DAY, autoSchedule, failures, stampMs;
20981
- var init_due = __esm({
20982
- "../cli/src/lib/agent/due.ts"() {
20983
- "use strict";
20984
- init_cadence();
20985
- init_cards();
20986
- init_frontmatter();
20987
- init_paths();
20988
- init_dismissal_review();
20989
- AUTO_GAP = "6h";
20990
- DAY = 864e5;
20991
- autoSchedule = (reads, fallback) => reads ? { every: AUTO_GAP, reads } : { every: fallback };
20992
- failures = (attempts) => {
20993
- let n = 0;
20994
- for (const a of [...attempts].sort((x, y) => y.startedAt - x.startedAt)) {
20995
- if (a.status === "running") continue;
20996
- if (a.status === "done") break;
20997
- n++;
20998
- }
20999
- return n;
21000
- };
21001
- stampMs = (stamp4) => parseStamp(stamp4)?.getTime() ?? 0;
21002
- }
21003
- });
21004
-
21005
20778
  // ../cli/src/lib/agent/scheduled.ts
21006
20779
  function scheduledBusy(pass, store = readStore()) {
21007
20780
  const args = { agent: pass.agent, workflow: pass.workflow };
@@ -21013,25 +20786,6 @@ function scheduledBusy(pass, store = readStore()) {
21013
20786
  }
21014
20787
  return null;
21015
20788
  }
21016
- function scheduledWait(pass, one, store = readStore(), now = /* @__PURE__ */ new Date()) {
21017
- const reads = specAgentCatalog().agents.find((a) => a.name === one.agent)?.reads;
21018
- const lastRun = stampMs(one.lastRun);
21019
- return scheduleDue(
21020
- {
21021
- cadence: one.cadence,
21022
- fallback: SCHEDULED_CADENCE,
21023
- reads,
21024
- from: stampMs(scheduledClock(one)),
21025
- attempts: store.runs.filter((r) => isPass(r, pass)),
21026
- newWork: () => !reads || readsNew(reads, lastRun),
21027
- building: () => building(store)
21028
- },
21029
- now.getTime()
21030
- );
21031
- }
21032
- function scheduledNext(workflow, one) {
21033
- return one.off || !scheduledClock(one) ? null : scheduledWait({ workflow, agent: one.agent }, one);
21034
- }
21035
20789
  function insideScheduledPass() {
21036
20790
  const sessionId = insideRun();
21037
20791
  const run2 = sessionId ? readStore().runs.find((r) => r.sessionId === sessionId) : void 0;
@@ -21054,13 +20808,10 @@ var scheduledRequest, isPass, openPasses, stalePasses;
21054
20808
  var init_scheduled = __esm({
21055
20809
  "../cli/src/lib/agent/scheduled.ts"() {
21056
20810
  "use strict";
21057
- init_catalog2();
21058
- init_due();
21059
20811
  init_env();
21060
20812
  init_stages();
21061
20813
  init_store();
21062
20814
  init_types3();
21063
- init_workflows();
21064
20815
  scheduledRequest = (pass) => ({
21065
20816
  action: "scheduled",
21066
20817
  workflow: pass.workflow,
@@ -21075,15 +20826,15 @@ var init_scheduled = __esm({
21075
20826
  });
21076
20827
 
21077
20828
  // ../cli/src/lib/card-age.ts
21078
- import fs59 from "node:fs";
20829
+ import fs57 from "node:fs";
21079
20830
  import path60 from "node:path";
21080
20831
  function staleAfterIn(config) {
21081
20832
  const days = Number(LINE.exec(config)?.[1]);
21082
20833
  return Number.isInteger(days) && days > 0 ? days : STALE_AFTER_DEFAULT;
21083
20834
  }
21084
20835
  function staleAfter() {
21085
- if (!fs59.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
21086
- return staleAfterIn(fs59.readFileSync(CONFIG, "utf8"));
20836
+ if (!fs57.existsSync(CONFIG)) return STALE_AFTER_DEFAULT;
20837
+ return staleAfterIn(fs57.readFileSync(CONFIG, "utf8"));
21087
20838
  }
21088
20839
  function todoPathspec(top) {
21089
20840
  const from = path60.relative(real(top), real(TODO));
@@ -21125,11 +20876,11 @@ function cardAges(now = Date.now()) {
21125
20876
  if (!file || ages.has(file) || dirty.has(file)) continue;
21126
20877
  const at = Date.parse(when);
21127
20878
  if (Number.isNaN(at)) continue;
21128
- ages.set(file, { lastTouched: when.slice(0, 10), days: Math.max(0, Math.floor((now - at) / DAY2)) });
20879
+ ages.set(file, { lastTouched: when.slice(0, 10), days: Math.max(0, Math.floor((now - at) / DAY)) });
21129
20880
  }
21130
20881
  return ages;
21131
20882
  }
21132
- var STALE_AFTER_DEFAULT, LINE, gitTop, real, unquotePath, DAY2;
20883
+ var STALE_AFTER_DEFAULT, LINE, gitTop, real, unquotePath, DAY;
21133
20884
  var init_card_age = __esm({
21134
20885
  "../cli/src/lib/card-age.ts"() {
21135
20886
  "use strict";
@@ -21140,7 +20891,7 @@ var init_card_age = __esm({
21140
20891
  gitTop = () => git(["rev-parse", "--show-toplevel"])?.trim() || null;
21141
20892
  real = (p) => {
21142
20893
  try {
21143
- return fs59.realpathSync(p);
20894
+ return fs57.realpathSync(p);
21144
20895
  } catch {
21145
20896
  return path60.resolve(p);
21146
20897
  }
@@ -21153,7 +20904,7 @@ var init_card_age = __esm({
21153
20904
  return line.slice(1, -1);
21154
20905
  }
21155
20906
  };
21156
- DAY2 = 24 * 60 * 60 * 1e3;
20907
+ DAY = 24 * 60 * 60 * 1e3;
21157
20908
  }
21158
20909
  });
21159
20910
 
@@ -21177,7 +20928,7 @@ var init_card_holds = __esm({
21177
20928
  });
21178
20929
 
21179
20930
  // ../cli/src/commands/list.ts
21180
- import fs60 from "node:fs";
20931
+ import fs58 from "node:fs";
21181
20932
  import path61 from "node:path";
21182
20933
  function summaryLine(body) {
21183
20934
  const lines = body.split("\n");
@@ -21200,7 +20951,7 @@ function openRows() {
21200
20951
  const id = isRoot ? idPrefix(path61.basename(path61.dirname(file))) : idPrefix(base);
21201
20952
  if (id == null) continue;
21202
20953
  if (isLegacyRecurring(path61.relative(TODO, file))) continue;
21203
- const { meta, body } = parseFrontmatter(fs60.readFileSync(file, "utf8"));
20954
+ const { meta, body } = parseFrontmatter(fs58.readFileSync(file, "utf8"));
21204
20955
  rows.push({
21205
20956
  id,
21206
20957
  file,
@@ -21242,7 +20993,7 @@ function cmdStale(rows, all2, scope, mod) {
21242
20993
  }
21243
20994
  function archivedFiles() {
21244
20995
  const files2 = /* @__PURE__ */ new Map();
21245
- if (!fs60.existsSync(ARCHIVE)) return files2;
20996
+ if (!fs58.existsSync(ARCHIVE)) return files2;
21246
20997
  for (const file of walkMd(ARCHIVE)) {
21247
20998
  const id = idPrefix(path61.basename(file));
21248
20999
  if (id !== null) files2.set(id, file);
@@ -21250,7 +21001,7 @@ function archivedFiles() {
21250
21001
  for (const dir of walkDirs(ARCHIVE)) {
21251
21002
  const id = idPrefix(path61.basename(dir));
21252
21003
  const root = path61.join(dir, "root.md");
21253
- if (id !== null && fs60.existsSync(root)) files2.set(id, root);
21004
+ if (id !== null && fs58.existsSync(root)) files2.set(id, root);
21254
21005
  }
21255
21006
  return files2;
21256
21007
  }
@@ -21361,7 +21112,7 @@ var init_list = __esm({
21361
21112
  });
21362
21113
 
21363
21114
  // ../cli/src/commands/misc.ts
21364
- import fs61 from "node:fs";
21115
+ import fs59 from "node:fs";
21365
21116
  import path62 from "node:path";
21366
21117
  function extractOldMeta(text5, file) {
21367
21118
  const grab = (re) => {
@@ -21399,7 +21150,7 @@ function cmdMigrate(opts) {
21399
21150
  let changed = 0;
21400
21151
  let skipped = 0;
21401
21152
  for (const file of files2) {
21402
- const text5 = fs61.readFileSync(file, "utf8");
21153
+ const text5 = fs59.readFileSync(file, "utf8");
21403
21154
  if (text5.trimStart().startsWith("---")) {
21404
21155
  skipped++;
21405
21156
  continue;
@@ -21408,7 +21159,7 @@ function cmdMigrate(opts) {
21408
21159
  const out = serializeFrontmatter(meta) + "\n\n" + stripOldHeader(text5).replace(/^\n+/, "");
21409
21160
  if (dry) say(`would migrate ${rel(file)} (priority=${meta.priority})`);
21410
21161
  else {
21411
- fs61.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
21162
+ fs59.writeFileSync(file, out.endsWith("\n") ? out : out + "\n");
21412
21163
  say(`migrated ${rel(file)}`);
21413
21164
  }
21414
21165
  changed++;
@@ -21429,10 +21180,10 @@ var init_misc = __esm({
21429
21180
  });
21430
21181
 
21431
21182
  // ../cli/src/lib/group-close.ts
21432
- import fs62 from "node:fs";
21183
+ import fs60 from "node:fs";
21433
21184
  function groupCloses(rootFile) {
21434
21185
  try {
21435
- return groupCloseOf(fs62.readFileSync(rootFile, "utf8"));
21186
+ return groupCloseOf(fs60.readFileSync(rootFile, "utf8"));
21436
21187
  } catch {
21437
21188
  return false;
21438
21189
  }
@@ -21445,7 +21196,7 @@ var init_group_close = __esm({
21445
21196
  });
21446
21197
 
21447
21198
  // ../cli/src/commands/remove.ts
21448
- import fs63 from "node:fs";
21199
+ import fs61 from "node:fs";
21449
21200
  import path63 from "node:path";
21450
21201
  function leavingIds(id, found2) {
21451
21202
  const ids = /* @__PURE__ */ new Set([id]);
@@ -21462,10 +21213,10 @@ function findMentions(ids) {
21462
21213
  const hits = [];
21463
21214
  const re = new RegExp(ids.map((id) => `#${id}(?!\\d)`).join("|"));
21464
21215
  for (const dir of [TODO, MEMORY]) {
21465
- if (!fs63.existsSync(dir)) continue;
21216
+ if (!fs61.existsSync(dir)) continue;
21466
21217
  for (const file of walkMd(dir)) {
21467
21218
  if (path63.basename(file) === "README.md") continue;
21468
- const lines = fs63.readFileSync(file, "utf8").split("\n");
21219
+ const lines = fs61.readFileSync(file, "utf8").split("\n");
21469
21220
  const fmEnd = frontmatterEnd(lines);
21470
21221
  lines.forEach((line, i) => {
21471
21222
  if (!re.test(line)) return;
@@ -21495,10 +21246,10 @@ function stampLeaving(cards, rejection) {
21495
21246
  const day = formatDay();
21496
21247
  const stamp4 = rejection && !rejection.discard ? { reason: rejection.reason, at: (/* @__PURE__ */ new Date()).toISOString(), by: insideRun() ? "agent" : "user" } : rejection && { reason: rejection.reason };
21497
21248
  for (const card of cards) {
21498
- if (!fs63.existsSync(card.file)) continue;
21499
- const text5 = fs63.readFileSync(card.file, "utf8");
21249
+ if (!fs61.existsSync(card.file)) continue;
21250
+ const text5 = fs61.readFileSync(card.file, "utf8");
21500
21251
  const stamped = stampLeavingIn(text5, day, stamp4);
21501
- if (stamped !== text5) fs63.writeFileSync(card.file, stamped);
21252
+ if (stamped !== text5) fs61.writeFileSync(card.file, stamped);
21502
21253
  }
21503
21254
  }
21504
21255
  function unfinishedDelivery(id) {
@@ -21514,7 +21265,7 @@ function unfinishedDelivery(id) {
21514
21265
  }[gap];
21515
21266
  return `#${id} is not finished: ${what}.`;
21516
21267
  }
21517
- const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs63.existsSync(path63.join(ASSETS, src.slice(".assets/".length))));
21268
+ const missing = planDeliverables(card.body).filter((src) => src.startsWith(".assets/")).find((src) => !fs61.existsSync(path63.join(ASSETS, src.slice(".assets/".length))));
21518
21269
  return missing ? `#${id} is not finished: ${missing} is not on this machine.` : null;
21519
21270
  }
21520
21271
  function agentDecisions(text5) {
@@ -21542,7 +21293,7 @@ function removeCard2(id, metric, options) {
21542
21293
  if (unfinished) die(unfinished, { kind: "plan-delivery-unfinished" });
21543
21294
  const dest = options.cleanupDiscarded && locateArchived(id) ? null : archiveDest(found2);
21544
21295
  const cardFile3 = found2.kind === "group" ? path63.join(found2.target, "root.md") : found2.target;
21545
- const cardText = fs63.existsSync(cardFile3) ? fs63.readFileSync(cardFile3, "utf8") : "";
21296
+ const cardText = fs61.existsSync(cardFile3) ? fs61.readFileSync(cardFile3, "utf8") : "";
21546
21297
  const { meta: cardMeta3 } = parseFrontmatter(cardText);
21547
21298
  const alsoRemoved = found2.kind === "group" ? walkMd(found2.target).filter((f) => f !== cardFile3).map((f) => rel(f)).sort() : [];
21548
21299
  const leftIds = leavingIds(id, found2);
@@ -21573,10 +21324,10 @@ function removeCard2(id, metric, options) {
21573
21324
  const groupRoot = found2.kind === "file" && !options.closing ? enclosingGroupRoot(found2.target) : null;
21574
21325
  if (dest) {
21575
21326
  stampLeaving(leaving, metric === "rejected" ? { reason: options.reason?.trim() ?? "", discard: options.discard === true } : null);
21576
- fs63.mkdirSync(ARCHIVE, { recursive: true });
21577
- fs63.renameSync(found2.target, dest);
21327
+ fs61.mkdirSync(ARCHIVE, { recursive: true });
21328
+ fs61.renameSync(found2.target, dest);
21578
21329
  } else {
21579
- fs63.rmSync(found2.target, { recursive: true, force: true });
21330
+ fs61.rmSync(found2.target, { recursive: true, force: true });
21580
21331
  }
21581
21332
  const removedRefs = stripReadmeRefs(found2);
21582
21333
  let marked2 = null;
@@ -21627,11 +21378,12 @@ function removeCard2(id, metric, options) {
21627
21378
  function closeGroup(rootFile) {
21628
21379
  const rootId = idPrefix(path63.basename(path63.dirname(rootFile)));
21629
21380
  if (rootId === null) return null;
21381
+ if (runnerProject(KANBAN)) return null;
21630
21382
  if (!groupCloses(rootFile)) return null;
21631
21383
  say(`
21632
21384
  every subtask line on #${rootId} is resolved \u2014 closing the group:`);
21633
21385
  try {
21634
- const { meta, body } = parseFrontmatter(fs63.readFileSync(rootFile, "utf8"));
21386
+ const { meta, body } = parseFrontmatter(fs61.readFileSync(rootFile, "utf8"));
21635
21387
  const res = cmdRemove(rootId, "completed", { closing: true });
21636
21388
  return {
21637
21389
  id: rootId,
@@ -21698,6 +21450,7 @@ var init_remove = __esm({
21698
21450
  "use strict";
21699
21451
  init_deliveries();
21700
21452
  init_store();
21453
+ init_tasks();
21701
21454
  init_env();
21702
21455
  init_creation_lock();
21703
21456
  init_lock();
@@ -21773,7 +21526,7 @@ var init_memory_file = __esm({
21773
21526
  });
21774
21527
 
21775
21528
  // ../cli/src/commands/rule.ts
21776
- import fs64 from "node:fs";
21529
+ import fs62 from "node:fs";
21777
21530
  import path64 from "node:path";
21778
21531
  function cmdRule(askedName, flags) {
21779
21532
  const agent = askedName.trim();
@@ -21791,7 +21544,7 @@ function readRuleText(file, text5) {
21791
21544
  if (file !== void 0 && text5 !== void 0) die("pass --file or --text, not both");
21792
21545
  if (file !== void 0) {
21793
21546
  try {
21794
- return fs64.readFileSync(file, "utf8").trim();
21547
+ return fs62.readFileSync(file, "utf8").trim();
21795
21548
  } catch {
21796
21549
  die(`can't read ${file} \u2014 write the rule to a file, then pass its path`);
21797
21550
  }
@@ -21918,15 +21671,15 @@ var init_storyboard = __esm({
21918
21671
  });
21919
21672
 
21920
21673
  // ../cli/src/lib/spec-contract.ts
21921
- import fs65 from "node:fs";
21674
+ import fs63 from "node:fs";
21922
21675
  import path65 from "node:path";
21923
21676
  function snapshotSpecs() {
21924
21677
  const out = /* @__PURE__ */ new Map();
21925
- if (!fs65.existsSync(TODO)) return out;
21678
+ if (!fs63.existsSync(TODO)) return out;
21926
21679
  for (const file of walkMd(TODO)) {
21927
21680
  if (isLegacyRecurring(path65.relative(TODO, file))) continue;
21928
21681
  const id = idPrefix(path65.basename(file) === "root.md" ? path65.basename(path65.dirname(file)) : path65.basename(file));
21929
- if (id !== null) out.set(file, { id, text: fs65.readFileSync(file, "utf8") });
21682
+ if (id !== null) out.set(file, { id, text: fs63.readFileSync(file, "utf8") });
21930
21683
  }
21931
21684
  return out;
21932
21685
  }
@@ -22102,9 +21855,9 @@ function validateSpec(file, text5, id) {
22102
21855
  }
22103
21856
  function inside(dir, name) {
22104
21857
  try {
22105
- const root = fs65.realpathSync(dir);
22106
- const real2 = fs65.realpathSync(path65.join(dir, name));
22107
- return real2.startsWith(root + path65.sep) && fs65.statSync(real2).isFile() ? real2 : null;
21858
+ const root = fs63.realpathSync(dir);
21859
+ const real2 = fs63.realpathSync(path65.join(dir, name));
21860
+ return real2.startsWith(root + path65.sep) && fs63.statSync(real2).isFile() ? real2 : null;
22108
21861
  } catch {
22109
21862
  return null;
22110
21863
  }
@@ -22152,7 +21905,7 @@ function compareProblem(attrs, id) {
22152
21905
  const file = inside(dir, named2.name);
22153
21906
  if (!file) return { message: `Compare shots ${at} names no file in this card's asset folder. Write the JSON there or fix the path.` };
22154
21907
  try {
22155
- return { data: JSON.parse(fs65.readFileSync(file, "utf8")) };
21908
+ return { data: JSON.parse(fs63.readFileSync(file, "utf8")) };
22156
21909
  } catch {
22157
21910
  return { message: `${at} is not valid JSON.` };
22158
21911
  }
@@ -22321,7 +22074,7 @@ var init_mockups = __esm({
22321
22074
  });
22322
22075
 
22323
22076
  // ../cli/src/commands/spec-write.ts
22324
- import fs66 from "node:fs";
22077
+ import fs64 from "node:fs";
22325
22078
  import path66 from "node:path";
22326
22079
  function cmdSpecWrite(id, askedName, flags) {
22327
22080
  const agent = findSpecAgent(askedName);
@@ -22331,10 +22084,10 @@ function cmdSpecWrite(id, askedName, flags) {
22331
22084
  const found2 = locate(id);
22332
22085
  if (!found2) die(`no task with id ${id} under ${rel(TODO)}`, { kind: "card-not-found", id });
22333
22086
  const file = found2.kind === "group" ? path66.join(found2.target, "root.md") : found2.target;
22334
- const { meta, body } = parseFrontmatter(fs66.readFileSync(file, "utf8"));
22087
+ const { meta, body } = parseFrontmatter(fs64.readFileSync(file, "utf8"));
22335
22088
  if (!meta) die(`${rel(file)} has no frontmatter \u2014 run \`migrate\` first`);
22336
22089
  const { body: next, replaced } = splice(body, name, section);
22337
- fs66.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
22090
+ fs64.writeFileSync(file, serializeFrontmatter(meta) + "\n" + next);
22338
22091
  say(`${replaced ? "rewrote" : "wrote"} the \`${name}\` section on #${id} (${rel(file)})`);
22339
22092
  return { id, specAgent: name, replaced, file: rel(file) };
22340
22093
  }
@@ -22343,7 +22096,7 @@ function readSection(file, text5) {
22343
22096
  let raw;
22344
22097
  if (file !== void 0) {
22345
22098
  try {
22346
- raw = fs66.readFileSync(file, "utf8");
22099
+ raw = fs64.readFileSync(file, "utf8");
22347
22100
  } catch {
22348
22101
  die(`can't read ${file} \u2014 write your section to a file, then pass its path`);
22349
22102
  }
@@ -22809,11 +22562,11 @@ var init_shared = __esm({
22809
22562
  });
22810
22563
 
22811
22564
  // ../cli/src/commands/idea.ts
22812
- import fs67 from "node:fs";
22565
+ import fs65 from "node:fs";
22813
22566
  function load() {
22814
22567
  const file = ideasFile();
22815
22568
  if (!file) die("Ideas exist only inside a WildCrane idea conversation.");
22816
- const held4 = JSON.parse(fs67.readFileSync(file, "utf8"));
22569
+ const held4 = JSON.parse(fs65.readFileSync(file, "utf8"));
22817
22570
  return { file, held: { workflows: held4.workflows ?? [], ideas: held4.ideas ?? [] } };
22818
22571
  }
22819
22572
  function workflowOf(held4, asked) {
@@ -22835,7 +22588,7 @@ function splitBody2(text5) {
22835
22588
  }
22836
22589
  function readBody(bodyFile) {
22837
22590
  if (!bodyFile) die("--body-file is required; write the idea in a writable temporary file first.");
22838
- const text5 = fs67.readFileSync(bodyFile, "utf8");
22591
+ const text5 = fs65.readFileSync(bodyFile, "utf8");
22839
22592
  if (!text5.trim()) die("The idea must not be empty.");
22840
22593
  return splitBody2(text5);
22841
22594
  }
@@ -22847,7 +22600,7 @@ function named(held4, id) {
22847
22600
  function cmdIdea(args, opts) {
22848
22601
  const sub = args[0] ?? "";
22849
22602
  const { file, held: held4 } = load();
22850
- const write2 = () => fs67.writeFileSync(file, `${JSON.stringify(held4, null, 2)}
22603
+ const write2 = () => fs65.writeFileSync(file, `${JSON.stringify(held4, null, 2)}
22851
22604
  `);
22852
22605
  if (sub === "new") {
22853
22606
  const title = opts.title?.trim();
@@ -22916,7 +22669,7 @@ var board_exports = {};
22916
22669
  __export(board_exports, {
22917
22670
  buildBoardProgram: () => buildBoardProgram
22918
22671
  });
22919
- import fs68 from "node:fs";
22672
+ import fs66 from "node:fs";
22920
22673
  import path67 from "node:path";
22921
22674
  async function dispatch(move2, cmd, args, opts, cli) {
22922
22675
  const ctx = ctxOf(cmd, cli.program);
@@ -22943,7 +22696,7 @@ async function cloudCard(task, opts) {
22943
22696
  let request;
22944
22697
  if (typeof opts.bodyFile === "string") {
22945
22698
  try {
22946
- request = fs68.readFileSync(opts.bodyFile, "utf8");
22699
+ request = fs66.readFileSync(opts.bodyFile, "utf8");
22947
22700
  } catch {
22948
22701
  }
22949
22702
  }
@@ -23097,7 +22850,7 @@ var init_board2 = __esm({
23097
22850
  });
23098
22851
 
23099
22852
  // ../cli/src/lib/board-cli.ts
23100
- import fs69 from "node:fs";
22853
+ import fs67 from "node:fs";
23101
22854
  import path68 from "node:path";
23102
22855
  function useBoard(found2, dirNamed) {
23103
22856
  if (!found2.named && found2.board === path68.join(found2.root, "docs", "kanban")) setBoardRoot(found2.root, dirNamed);
@@ -23106,7 +22859,7 @@ function useBoard(found2, dirNamed) {
23106
22859
  }
23107
22860
  function whole(found2, move2) {
23108
22861
  if (move2 === MAKES_A_BOARD) return found2;
23109
- if (!fs69.existsSync(path68.join(found2.board, "todo"))) {
22862
+ if (!fs67.existsSync(path68.join(found2.board, "todo"))) {
23110
22863
  die(`the board in ${found2.board} has no todo/ \u2014 run \`init\` to add what is missing.`, {
23111
22864
  kind: "board-incomplete",
23112
22865
  dir: found2.root
@@ -23130,10 +22883,10 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
23130
22883
  const named2 = board2 ?? boardFromEnv();
23131
22884
  if (named2) {
23132
22885
  const folder = path68.resolve(cwd, named2);
23133
- if (!fs69.existsSync(folder) && move2 !== MAKES_A_BOARD) {
22886
+ if (!fs67.existsSync(folder) && move2 !== MAKES_A_BOARD) {
23134
22887
  die(`no such folder: ${folder}`, { kind: "no-such-folder", dir: folder });
23135
22888
  }
23136
- if (fs69.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
22889
+ if (fs67.existsSync(folder) && !isBoardDir(folder) && move2 !== MAKES_A_BOARD) {
23137
22890
  die(`${folder} is not a board \u2014 it has no todo/ and config.md. Run ${installHint} --board ${named2} to make one.`, {
23138
22891
  kind: "no-board",
23139
22892
  dir: folder
@@ -23143,7 +22896,7 @@ function resolveBoard(move2, { board: board2, dir, cwd, installHint }) {
23143
22896
  }
23144
22897
  if (dir !== null) {
23145
22898
  const root = path68.resolve(dir);
23146
- if (!fs69.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
22899
+ if (!fs67.existsSync(root)) die(`no such folder: ${root}`, { kind: "no-such-folder", dir: root });
23147
22900
  if (!hasBoard(root) && move2 !== MAKES_A_BOARD) {
23148
22901
  die(`no board in ${root} \u2014 it has no docs/kanban/. Run ${installHint} there to make one.`, {
23149
22902
  kind: "no-board",
@@ -23240,8 +22993,8 @@ var init_board_cli = __esm({
23240
22993
  init_pointer();
23241
22994
  init_tasks();
23242
22995
  MAKES_A_BOARD = "init";
23243
- hasBoard = (dir) => fs69.existsSync(path68.join(dir, "docs", "kanban"));
23244
- isBoardDir = (dir) => fs69.existsSync(path68.join(dir, "todo")) && fs69.existsSync(path68.join(dir, "config.md"));
22996
+ hasBoard = (dir) => fs67.existsSync(path68.join(dir, "docs", "kanban"));
22997
+ isBoardDir = (dir) => fs67.existsSync(path68.join(dir, "todo")) && fs67.existsSync(path68.join(dir, "config.md"));
23245
22998
  boardFromEnv = () => {
23246
22999
  const named2 = process.env.AI4KANBAN_BOARD?.trim();
23247
23000
  return named2 ? named2 : null;
@@ -23269,7 +23022,7 @@ var init_archive2 = __esm({
23269
23022
  var board_default;
23270
23023
  var init_board3 = __esm({
23271
23024
  "../cli/src/guide/board.md"() {
23272
- board_default = "# How this board works\n\nThese rules apply to every card workflow. Read them once; each workflow's guide covers\nthe remaining details.\n\n## Layout\n\n```\ndocs/kanban/\n\u251C\u2500\u2500 todo/ open tasks\n\u2502 \u251C\u2500\u2500 README.md the index \u2014 read it first\n\u2502 \u2514\u2500\u2500 <id>-<slug>.md\n\u2502 one card per file; a group keeps its cards in one folder\n\u251C\u2500\u2500 memory/ all memory \u2014 see \"Who owns a memory file\"\n\u2502 \u251C\u2500\u2500 readme.md what shipped \u2014 the board's own record\n\u2502 \u251C\u2500\u2500 project.md what the project is today \u2014 written only by `akb describe-project`\n\u2502 \u251C\u2500\u2500 <name>/ a global memory: `MEMORY.md` (its rules, unless built in), `README.md`\n\u2502 \u2502 (its index) and its entries\n\u2502 \u2514\u2500\u2500 agents/ one folder per agent that keeps memory, named after it \u2014\n\u2502 `planner/` holds `decisions.md`, `rejected.md`, `redesign.md`\n\u251C\u2500\u2500 rules/ `<agent>.md` \u2014 the user's rule for one agent, appended to every run it\n\u2502 does; written from the board UI or `akb raw rule`\n\u251C\u2500\u2500 triage/ items waiting to be sorted, one file each \u2014 not cards. Only\n\u2502 \u2502 Cloud's sort or Refine idea on the web's Triage page turns one\n\u2502 \u2502 into a card; `akb triage check <source-id>` finds an existing one\n\u2502 \u251C\u2500\u2500 archived/ items a card was made of\n\u2502 \u251C\u2500\u2500 dismissed/ items ignored, with the reason \u2014 `akb triage restore` puts one back\n\u2502 \u2514\u2500\u2500 files/ dropped-in files\n\u2514\u2500\u2500 modules.md one line per module \u2014 `akb guide module-map` writes it\n```\n\n## Task ID\n\nEvery task's id is the number at the front of its filename (`04-plan-cap-enforcement.md` \u2192\nid 4): its Cloud task's number (`WC-4`). Only Cloud assigns one; `akb raw create` takes it.\n\n## Never hand-write a card's frontmatter\n\n`akb raw create`, `update`, and `update-questions` manage all of a card's frontmatter,\nincluding its `previews`. Edit only the card's **body** by hand. `akb raw help`\nlists all operations; `akb raw help <move>` explains one operation.\n\n## Inside a board run\n\nA board run carries `KANBAN_RUN` in its environment. Never unset or bypass it (for example\n`env -u KANBAN_RUN`): when a command only prints a flow, follow the printed flow.\n\n## Group task\n\nA group lives in `todo/<id>-<slug>/`: `root.md` tracks the whole job and each subtask is its\nown card beside it; `akb guide add-task` says how to build one. The root's `## Todo` lists the\nsubtasks, one line each ending in `#<subid>`: archiving a subtask ticks its line, rejecting\none strikes it through, and resolving the last line archives the root in the same run, with\nany open question or unticked todo of its own. A root listing no subtasks is only closed by\nhand.\n\n## Who owns a memory file\n\n**A memory file belongs to whoever reads and writes it.**\n\n- **`memory/readme.md`**: the board's record of shipped user-facing work.\n- **`memory/project.md`**: what the project is today, from its users' side, and the\n direction every judgement serves. Only `akb describe-project` writes it.\n- **`memory/agents/planner/`**: `decisions.md` holds user-facing answers that guide future\n planning and `redesign.md` design mistakes to avoid, both written by the memory review (`akb\n guide review-memory`); `rejected.md` holds what the user turned down and why, written only\n by the rejection and dismissal review (`akb guide review-rejections`). Other flows only read\n them, `akb prune-memory` aside.\n- **`memory/agents/proposer/missed.md`**: kinds of follow-up the proposer failed to propose \u2014\n written only by the memory review (`akb guide review-memory`), read by every reflection.\n- **`memory/<name>/`**: a global memory, kept by every agent listing it under `akb.memory`\n by the rules each run is given. Only the user changes its `MEMORY.md`, from the board UI.\n- **`memory/agents/<agent>/`**: an agent keeps only the files and split paths its own AGENT.md\n declares. An agent whose instructions name none keeps none.\n\nFile a planning note about one module in that module's folder,\n`memory/agents/planner/<module>/`, under the same file name, creating it if missing. A note\nspanning modules, or from a card with none, stays in `memory/agents/planner/`.\n\n### What earns a note\n\nOnly what changes a future planning choice. Writing nothing is a complete outcome.\n\n- **Honor an opt-out**: told not to record, write no memory and finish the requested action.\n- **A conversation writes none**: a card chat, a discussion, a feedback conversation, and\n any flow started in it write no memory and say nothing about it.\n- **Require lasting value**: a durable preference, constraint, decision, or lesson.\n- **Skip housekeeping**: duplicates, routine status changes, and facts recorded elsewhere.\n A rejected duplicate is not a rejected feature.\n- **Merge, don't repeat**: rewrite an equivalent entry in place.\n\n`readme.md` is not planning memory.\n";
23025
+ board_default = "# How this board works\n\nThese rules apply to every card workflow. Read them once; each workflow's guide covers\nthe remaining details.\n\n## Layout\n\n```\ndocs/kanban/\n\u251C\u2500\u2500 todo/ open tasks\n\u2502 \u251C\u2500\u2500 README.md the index \u2014 read it first\n\u2502 \u2514\u2500\u2500 <id>-<slug>.md\n\u2502 one card per file; a group keeps its cards in one folder\n\u251C\u2500\u2500 memory/ all memory \u2014 see \"Who owns a memory file\"\n\u2502 \u251C\u2500\u2500 readme.md what shipped \u2014 the board's own record\n\u2502 \u251C\u2500\u2500 project.md what the project is today \u2014 written only by `akb describe-project`\n\u2502 \u251C\u2500\u2500 <name>/ a global memory: `MEMORY.md` (its rules, unless built in), `README.md`\n\u2502 \u2502 (its index) and its entries\n\u2502 \u2514\u2500\u2500 agents/ one folder per agent that keeps memory, named after it \u2014\n\u2502 `planner/` holds `decisions.md`, `rejected.md`, `redesign.md`\n\u251C\u2500\u2500 rules/ `<agent>.md` \u2014 the user's rule for one agent, appended to every run it\n\u2502 does; written from the board UI or `akb raw rule`\n\u251C\u2500\u2500 triage/ items waiting to be sorted, one file each \u2014 not cards. Only\n\u2502 \u2502 Cloud's sort or Refine idea on the web's Triage page turns one\n\u2502 \u2502 into a card; `akb triage check <source-id>` finds an existing one\n\u2502 \u251C\u2500\u2500 archived/ items a card was made of\n\u2502 \u251C\u2500\u2500 dismissed/ items ignored, with the reason \u2014 `akb triage restore` puts one back\n\u2502 \u2514\u2500\u2500 files/ dropped-in files\n\u2514\u2500\u2500 modules.md one line per module \u2014 `akb guide module-map` writes it\n```\n\n## Task ID\n\nEvery task's id is the number at the front of its filename (`04-plan-cap-enforcement.md` \u2192\nid 4): its Cloud task's number (`WC-4`). Only Cloud assigns one; `akb raw create` takes it.\n\n## Never hand-write a card's frontmatter\n\n`akb raw create`, `update`, and `update-questions` manage all of a card's frontmatter,\nincluding its `previews`. Edit only the card's **body** by hand. `akb raw help`\nlists all operations; `akb raw help <move>` explains one operation.\n\n## Inside a board run\n\nA board run carries `KANBAN_RUN` in its environment. Never unset or bypass it (for example\n`env -u KANBAN_RUN`): when a command only prints a flow, follow the printed flow.\n\n## Group task\n\nA group lives in `todo/<id>-<slug>/`: `root.md` tracks the whole job and each subtask is its\nown card beside it; `akb guide add-task` says how to build one. The root's `## Todo` lists the\nsubtasks, one line each ending in `#<subid>`: archiving a subtask ticks its line, rejecting\none strikes it through, and resolving the last line archives the root in the same run, with\nany open question or unticked todo of its own. A board a Runner keeps leaves that close to\nCloud, which waits for the root to be queued with no step on it. A root listing no subtasks is\nonly closed by hand.\n\n## Who owns a memory file\n\n**A memory file belongs to whoever reads and writes it.**\n\n- **`memory/readme.md`**: the board's record of shipped user-facing work.\n- **`memory/project.md`**: what the project is today, from its users' side, and the\n direction every judgement serves. Only `akb describe-project` writes it.\n- **`memory/agents/planner/`**: `decisions.md` holds user-facing answers that guide future\n planning and `redesign.md` design mistakes to avoid, both written by the memory review (`akb\n guide review-memory`); `rejected.md` holds what the user turned down and why, written only\n by the rejection and dismissal review (`akb guide review-rejections`). Other flows only read\n them, `akb prune-memory` aside.\n- **`memory/<name>/`**: a global memory, kept by every agent listing it under `akb.memory`\n by the rules each run is given. Only the user changes its `MEMORY.md`, from the board UI.\n- **`memory/agents/<agent>/`**: an agent keeps only the files and split paths its own AGENT.md\n declares, plus the `redesign.md` the memory review writes there. An agent whose instructions\n name none keeps none.\n\nFile a planning note about one module in that module's folder,\n`memory/agents/planner/<module>/`, under the same file name, creating it if missing. A note\nspanning modules, or from a card with none, stays in `memory/agents/planner/`.\n\n### What earns a note\n\nOnly what changes a future planning choice. Writing nothing is a complete outcome.\n\n- **Honor an opt-out**: told not to record, write no memory and finish the requested action.\n- **A conversation writes none**: a card chat, a discussion, a feedback conversation, and\n any flow started in it write no memory and say nothing about it.\n- **Require lasting value**: a durable preference, constraint, decision, or lesson.\n- **Skip housekeeping**: duplicates, routine status changes, and facts recorded elsewhere.\n A rejected duplicate is not a rejected feature.\n- **Merge, don't repeat**: rewrite an equivalent entry in place.\n\n`readme.md` is not planning memory.\n";
23273
23026
  }
23274
23027
  });
23275
23028
 
@@ -23277,7 +23030,7 @@ var init_board3 = __esm({
23277
23030
  var card_chat_default;
23278
23031
  var init_card_chat = __esm({
23279
23032
  "../cli/src/guide/card-chat.md"() {
23280
- card_chat_default = '# Answer a card\'s conversation\n\nThis is the conversation on one card. Answer what the user asked, and act on what they decided\n\u2014 on the turn they decide it, not when the conversation ends.\n\n- **Answer first**: this is a conversation. Say what you understand and answer them, in their\n own language, before you change anything.\n- **Read every turn for three things**: a question, a change to the card, and an answer to one\n of the card\'s open questions. A turn can carry several, or none. Judge it from what the\n conversation is about, never from whether they used a particular word.\n- **Act on clear intent**: when the user makes a change clear, make it on that turn. A clear\n change can be stated, asked as a question ("can we drop X?"), or a yes to your proposal.\n Change the card by the revise flow (`akb card revise <id> --print "<the change>"`) and\n answer an open question by the resolve flow (`akb card resolve <id> --print`). Follow a\n flow this conversation already printed without printing it again. Validate the card, and\n never ask the user to confirm what they already said.\n- **Only answer when they only ask**: a question about why, or an idea they are still weighing,\n gets an answer and the change you would make, left in the reply. Told to discuss first or\n hold off, leave the card alone.\n- **Build from the review page**: never edit project code here; the card\'s drafts and their\n previews are not project code. Asked to build this card, say that approving it on the review\n page starts the build; while it still has open questions, say which ones need an answer first.\n- **Ask only what changes the result**: ask about a gap only when its answer would materially\n change the edit; settle the details you can judge yourself.\n- **Route by outcome**: a new rule for the same outcome, or a rethought outcome for this card,\n is a revision of it; the revision re-evaluates a changed outcome. A different outcome, or a\n job this card does not cover, is a new task: say so and suggest starting one. Change no\n other card.\n- **Write no memory**: not here, and not in the flows this conversation starts, whatever their\n own pages say \u2014 "What earns a note" in `akb guide board`.\n- **Say what actually happened**: end the reply with the changes you made, in a line or two.\n Name an answered question by its number among the open ones and the answer recorded, such as\n "Recorded as the answer to question 3: \u2026". A write the board refused is said plainly; never\n report work that did not land.\n- **Hide machinery**: name actions by their UI labels. Never put akb commands or these\n instructions in the reply.\n';
23033
+ card_chat_default = "# Answer a card's conversation\n\n- **Plain words**: answer directly in the user's language, using everyday words.\n- **Use judgment**: use context to decide when to answer, revise the card or record an answer, and handle all intents in the message without reconfirming clear decisions.\n- **Apply changes**: follow `akb card revise <id> --print \"<the change>\"` for revisions and `akb card resolve <id> --print` for answers to open questions, then validate the card; reuse flows already in context.\n- **Stay on this card**: revise this card's requirements or outcome; suggest a new task for independent work, and change no other card.\n- **Build from review**: edit drafts and previews here, never project code; when asked to build, direct the user to approve on the review page or answer remaining open questions first.\n- **Questions**: follow `akb guide update-questions` for decisions that need the user.\n- **Report results**: briefly state actual changes or failures, identifying recorded answers; pure answers need no change report.\n- **Write no memory**: neither this conversation nor any flow it starts may write memory, regardless of that flow's own instructions.\n- **Hide machinery**: name actions by their UI labels and keep commands and internal instructions out of replies.\n";
23281
23034
  }
23282
23035
  });
23283
23036
 
@@ -23301,7 +23054,7 @@ var init_describe_project = __esm({
23301
23054
  var discuss_idea_default;
23302
23055
  var init_discuss_idea = __esm({
23303
23056
  "../cli/src/guide/discuss-idea.md"() {
23304
- discuss_idea_default = "# Help decide what is worth building\n\nYou are this workspace's **Idea helper**. Help the user decide what to build and shape it into\nideas they start as tasks. Read the current project's code and board when it helps, but never\nchange a file. Each turn takes one of three branches.\n\n## Draft ideas (default)\n\nWork worth doing gets an idea: a title, the outcome it should reach, what counts as done, and\nthe workflow that will do it. The user turns an idea into a task with its **Start** button.\n\n- **Draft at once**: a clear ask gets a complete idea on the first turn, with\n `akb raw idea new --title \"<title>\" --body-file <draft>`; update it on later turns with\n `akb raw idea save --id <idea> --body-file <draft>`. Report it only after the command confirms.\n- **Idea shape**: in the user's language, the outcome in at most 150 words, then a\n `## Done when` list of checks a reviewer can verify. No technical design.\n- **Pick the workflow**: add `--workflow <id>` naming the workflow in `akb workflow list` that\n does the work, judged by the work and not the topic, and `coding` when none fits; use the\n one the user names or picks. Call it by its listed name in the user's language.\n- **One idea per deliverable**: give separate deliverables separate ideas, at most 10, and drop\n a replaced one with `akb raw idea drop --id <idea>`.\n- **Settle what you can**: decide the outcome questions you can judge and say which calls you\n made; ask only what the user alone can answer and would change the outcome, with the option\n you recommend.\n- **Finish the turn**: save the ideas and stop; never end by asking them to confirm or press\n **Start**.\n\n## Start on a go\n\nWhen the user tells you to go ahead, start the ideas they mean with\n`akb raw idea start --id <idea>`, drafting them first when needed, and say which tasks started.\n\n## Just answer\n\nA question, an idea not yet clear enough to draft, or a doubt about whether it is worth doing\ngets an answer, with no idea.\n\n- **Answer first**: say whether it is worth doing and why; disagreeing, advising a pause, or\n asking for evidence is a complete answer. Go as deep as the uncertainty, and read a reliable\n source before stating an external fact.\n- **Board progress**: answer from the project's board as it stands now.\n\n## Always\n\n- **Hide machinery**: the user sees only ideas and tasks; never mention plans, files, paths,\n akb, commands, or the tools and connectors you run with, and refer to actions by their\n button labels.\n";
23057
+ discuss_idea_default = "# Help decide what is worth building\n\nYou are this workspace's **Idea helper**. Help the user decide what to build and shape it into\nideas they start as tasks. Read the current project's code and board when it helps, but never\nchange a file. Each turn takes one of three branches.\n\n## Draft ideas (default)\n\nWork worth doing gets an idea.\n\n- **Draft at once**: a clear ask gets a complete idea on the first turn, with\n `akb raw idea new --title \"<title>\" --body-file <draft>`; update it on later turns with\n `akb raw idea save --id <idea> --body-file <draft>`. Report it only after the command confirms.\n- **Idea shape**: in the user's language, the outcome in at most 150 words, then a\n `## Done when` list of checks a reviewer can verify. No technical design.\n- **Pick the workflow**: add `--workflow <id>` naming the workflow in `akb workflow list` that\n does the work, judged by the work and not the topic, and `coding` when none fits; use the\n one the user names or picks. Call it by its listed name in the user's language.\n- **One idea per deliverable**: give separate deliverables separate ideas, and drop a replaced\n one with `akb raw idea drop --id <idea>`.\n- **Settle what you can**: decide the outcome questions you can judge and say which calls you\n made; ask only what the user alone can answer and would change the outcome, with the option\n you recommend.\n- **Finish the turn**: save the ideas and stop; never end by asking them to confirm or press\n **Start**.\n\n## Start on a go\n\nWhen the user tells you to go ahead, start the ideas they mean with\n`akb raw idea start --id <idea>`, drafting them first when needed, and say which tasks started.\n\n## Just answer\n\nA question, an idea not yet clear enough to draft, or a doubt about whether it is worth doing\ngets an answer, with no idea.\n\n- **Answer first**: say whether it is worth doing and why; disagreeing, advising a pause, or\n asking for evidence is a complete answer. Read a reliable source before stating an external\n fact.\n\n## Always\n\n- **Plain words**: answer directly in the user's language, using everyday words.\n\n- **Hide machinery**: never mention plans, files, paths, commands, or the tools and connectors\n you run with, and refer to actions by their button labels.\n";
23305
23058
  }
23306
23059
  });
23307
23060
 
@@ -23317,7 +23070,7 @@ var init_evaluate_task = __esm({
23317
23070
  var follow_up_default;
23318
23071
  var init_follow_up = __esm({
23319
23072
  "../cli/src/guide/follow-up.md"() {
23320
- follow_up_default = '# Handle follow-up work\n\nUse when planning or implementation discovers additional work.\n\n- **Place it**: fix what the current card requires here; never defer a required fix to make\n the card pass. Queue independent follow-ups in triage without asking permission or blocking\n the original, and create no card for them.\n- **Another card\'s change is not a follow-up**: when it only changes an open card, run\n `akb card revise <id> "<the change and why>"` instead.\n- **Queue it**: `akb triage add --title "<one line>" --slug <short-english-slug> --source "#<id>" --workflow <workflow> --text "<what and why, readable without this session>"`,\n then resume the original workflow. Skip one the command reports as already there.\n- **Workflow**: pass the `workflow:` of the card you are working on; when it has none, pass\n the board\'s default workflow shown by `akb workflow`.\n';
23073
+ follow_up_default = '# Handle follow-up work\n\nUse when planning or implementation discovers additional work.\n\n- **Current task**: finish required work on this card; queue independent follow-ups in triage, then resume the original task.\n- **Drop what was turned down**: queue nothing that\n `docs/kanban/memory/agents/planner/rejected.md` or\n `docs/kanban/memory/agents/planner/*/rejected.md` turns down.\n- **Existing card**: when only another open card needs changing, run `akb card revise <id> "<the change and why>"`.\n- **Queue it**: use the command below; skip an item the command reports as already there.\n- **Workflow**: use the source card\'s `workflow:`, or the board default shown by `akb workflow` when it has none.\n\n```text\nakb triage add --title "<one line>" --slug <short-english-slug> --source "#<id>" --workflow <workflow> --text "<body>"\n```\n\n## Write the item\n\n- **Reader**: someone who has read neither the original card nor the code can decide in ten seconds whether the work is worth doing.\n- **Order**: first explain what happened and what the user runs into, then say what the work would do.\n- **Plain words**: keep file, function and field names, card ids, agent names and evidence trails out of the title and body.\n';
23321
23074
  }
23322
23075
  });
23323
23076
 
@@ -23325,7 +23078,7 @@ var init_follow_up = __esm({
23325
23078
  var implement_default;
23326
23079
  var init_implement = __esm({
23327
23080
  "../cli/src/guide/implement.md"() {
23328
- implement_default = "# Implement a settled card\n\nBuild the approved card. Preserve settled decisions and unrelated questions.\n\n- **Unchecked card**: a card with `triage:` set and an empty `## Scope` skipped planning; do\n what \"Unchecked card\" in `akb guide refine` says before building.\n- **User-action blockers**: follow `akb guide update-questions` and append a\n question describing the obstacle and action needed. Stop only dependent work; resume it\n after the user resolves the question. Settle routine technical choices yourself.\n- Do not replace or contradict what the card says.\n- **Approved mockups**: build each screen a card's mockup shows to match it, then compare\n your result with the mockup at desktop and phone widths and fix any difference.\n- **Approved API previews**: build each surface a card's API preview shows to match it,\n and write its documentation from it.\n- **Approved schema preview**: write the migration so the resulting schema matches the\n card's approved schema preview.\n- **Building is not shipping**: unless the card's outcome is itself a deploy, release, or\n publish, finish once the change lands and the checks pass; do not push, deploy, release,\n or publish to complete it.\n- **Defer what can wait**: queue as a follow-up any verification or step that needs the change\n live, and any other work this card does not require; never hold the card with a question\n about it.\n- **Follow-ups**: follow `akb guide follow-up` for additional work.\n- **Decisions made while building**: record an answered material decision the user could\n reasonably reverse under `## Worth noting after implementation`.\n- A printed, interactive implementation may stay uncommitted in the target checkout only\n when the card already supplies a concrete outcome, boundaries, and build steps.\n Otherwise use the tracked implementation path. Background runs always keep their delivery\n path.\n- **Checks**: nothing reviews the build after you; run focused checks for the affected path\n plus every repository-required check.\n\n## A build that writes its own card\n\n**Start now** sends a requirement with no card behind it \u2014 a typed sentence, the plan a\ndiscussion settled, or a triage item. Write the card first, in the same run, then build it. The board points\nthe run and its delivery at the card as the create lands, so from there it is an ordinary\nbuild.\n\n- **Read the plan first**: given a plan's path or a triage item's file, that file is the\n requirement \u2014 read it, and everything below reads \"the plan\" for \"the sentence\".\n- **Create it first**: `akb raw create --title \"..\"`. The title is one short line read off\n the sentence, or the plan's own title, in its own language \u2014 add\n `--slug <short-english-slug>` when that is not English, because filenames are ASCII. No `--modules`, no `--question` \u2014 nothing has evaluated this idea.\n- **The requirement is the summary**: replace the scaffold's opening paragraph with the\n sentence, or the plan's whole text, verbatim, in a fenced code block. Open the fence with\n more backticks than the longest run inside it, or the card fails validation on an unclosed\n fence.\n- **From a plan, name it**: pass `--source <plan path>` to the create.\n- **From a triage item, file it**: pass `--triage <source-id>` to the create, and run\n `akb triage archive <source-id> --card <id>` straight after, before any build work.\n- **Leave the rest of the scaffold**: `## Worth noting`, `## Scope`, `## Todo` and\n `## Decided by the agent` stay exactly as `raw create` wrote them.\n- **Then build that card**: the code block is the whole requirement \u2014 build exactly it and\n widen it no further. `akb card implement <id> --print` is the flow from here.\n";
23081
+ implement_default = "# Implement a settled card\n\nBuild the approved card. Preserve settled decisions and unrelated questions.\n\n- **Unchecked card**: a card with `triage:` set and an empty `## Scope` skipped planning; do\n what \"Unchecked card\" in `akb guide refine` says before building.\n- **User-action blockers**: follow `akb guide update-questions` and append a\n question describing the obstacle and action needed. Stop only dependent work; resume it\n after the user resolves the question. Settle routine technical choices yourself.\n- Do not replace or contradict what the card says.\n- **Approved mockups**: build each screen a card's mockup shows to match it, then compare\n your result with the mockup at desktop and phone widths and fix any difference.\n- **Approved API previews**: build each surface a card's API preview shows to match it,\n and write its documentation from it.\n- **Approved schema preview**: write the migration so the resulting schema matches the\n card's approved schema preview.\n- **Building is not shipping**: unless the card's outcome is itself a deploy, release, or\n publish, finish once the change lands and the checks pass; do not push, deploy, release,\n or publish to complete it.\n- **What needs it live**: leave any verification or step that needs the change live to the\n user. Name it in your final message; queue no follow-up and ask no question about it.\n- **Defer what can wait**: queue as a follow-up any other work this card does not require;\n never hold the card with a question about it.\n- **Follow-ups**: follow `akb guide follow-up` for additional work.\n- **Decisions made while building**: record an answered material decision the user could\n reasonably reverse under `## Worth noting after implementation`.\n- A printed, interactive implementation may stay uncommitted in the target checkout only\n when the card already supplies a concrete outcome, boundaries, and build steps.\n Otherwise use the tracked implementation path. Background runs always keep their delivery\n path.\n- **Checks**: nothing reviews the build after you; run focused checks for the affected path\n plus every repository-required check.\n\n## A build that writes its own card\n\n**Start now** sends a requirement with no card behind it \u2014 a typed sentence, the plan a\ndiscussion settled, or a triage item. Write the card first, in the same run, then build it. The board points\nthe run and its delivery at the card as the create lands, so from there it is an ordinary\nbuild.\n\n- **Read the plan first**: given a plan's path or a triage item's file, that file is the\n requirement \u2014 read it, and everything below reads \"the plan\" for \"the sentence\".\n- **Create it first**: `akb raw create --title \"..\"`. The title is one short line read off\n the sentence, or the plan's own title, in its own language \u2014 add\n `--slug <short-english-slug>` when that is not English, because filenames are ASCII. No `--modules`, no `--question` \u2014 nothing has evaluated this idea.\n- **The requirement is the summary**: replace the scaffold's opening paragraph with the\n sentence, or the plan's whole text, verbatim, in a fenced code block. Open the fence with\n more backticks than the longest run inside it, or the card fails validation on an unclosed\n fence.\n- **From a plan, name it**: pass `--source <plan path>` to the create.\n- **From a triage item, file it**: pass `--triage <source-id>` to the create, and run\n `akb triage archive <source-id> --card <id>` straight after, before any build work.\n- **Leave the rest of the scaffold**: `## Worth noting`, `## Scope`, `## Todo` and\n `## Decided by the agent` stay exactly as `raw create` wrote them.\n- **Then build that card**: the code block is the whole requirement \u2014 build exactly it and\n widen it no further. `akb card implement <id> --print` is the flow from here.\n";
23329
23082
  }
23330
23083
  });
23331
23084
 
@@ -23341,7 +23094,7 @@ var init_module_map = __esm({
23341
23094
  var prune_memory_default;
23342
23095
  var init_prune_memory = __esm({
23343
23096
  "../cli/src/guide/prune-memory.md"() {
23344
- prune_memory_default = "# Prune the memory\n\nPrune the board's own record in `docs/kanban/memory/` and every agent's memory beside it in\n`docs/kanban/memory/agents/<agent>/` \u2014 the planner's files and module folders among them (see\n\"Who owns a memory file\" in `akb guide board`). `project.md` is rewritten on its own schedule:\nleave it alone.\n\nOne principle for all files: they exist to stop us re-proposing work, re-making a\ndesign mistake, or re-asking a settled question. Rewrite each as **topics** (h2 title) with\nplain-language takeaways under each section. Keep only what helps\nfuture planning; drop code detail, dates, task ids, step-by-step stories.\n\nDrop every entry that fails \"What earns a note\" in `akb guide board` \u2014 the bar that governs a\nwrite governs what stays. Merge lines that say the same thing. Rewrite, don't just cut.\n\nOn top of that, per file:\n\n- `agents/planner/rejected.md` \u2014 one line per thing the user does not want, and why. Keep\n every source id in parentheses: they are how a restored item withdraws its line. \"Already\n done\" is a shipped fact, not a rejection \u2014 it belongs in the published doc (with a\n `readme.md` line pointing at it), not here; drop it from `rejected.md`.\n- `agents/planner/redesign.md` \u2014 one line per entry: the mistake, then the design to use.\n Drop an entry once that design is the obvious default.\n- `readme.md` \u2014 the board's own record, one line per shipped user-facing behavior. Where a\n published doc covers it, the line is just a link to that doc's path. **Replace a prose\n entry with a link only after you have confirmed the doc covers that behavior** \u2014 search the\n docs first; no doc yet means the prose line stays: `readme.md` is the only record until we\n document it. Anything else \u2014 watermarks, a last focus, open gaps, internal detail \u2014\n doesn't belong here; drop it.\n- `agents/planner/decisions.md` \u2014 one line per live decision, in plain user-facing words.\n Drop anything that fails its bar in `akb guide board` (\"Who owns a memory file\"): a call\n about code detail, or one the published docs now cover. Drop a decision once the question\n no longer arises or `redesign.md` states it as a rule.\n- `agents/<agent>/` \u2014 the files that agent's own AGENT.md defines, each pruned to what it says\n the file holds. Drop an entry once that agent's instructions or the design docs say it, and\n drop a file its instructions no longer name.\n\nPrune a preview agent's files together, and drop the whole folder when its agent is gone from `akb\nspec`. Drop anything a file says about how the product looks \u2014 colours, dimensions,\ncomponent detail belong in the app's `design.md`, not here \u2014 and fold a product fact worth\nkeeping into the lesson or the decision it supports.\n";
23097
+ prune_memory_default = "# Prune the memory\n\nPrune the board's own record in `docs/kanban/memory/` and every agent's memory beside it in\n`docs/kanban/memory/agents/<agent>/` \u2014 the planner's files and module folders among them (see\n\"Who owns a memory file\" in `akb guide board`). `project.md` is rewritten on its own schedule:\nleave it alone.\n\nOne principle for all files: they exist to stop us re-proposing work, re-making a\ndesign mistake, or re-asking a settled question. Rewrite each as **topics** (h2 title) with\nplain-language takeaways under each section. Keep only what helps\nfuture planning; drop code detail, dates, task ids, step-by-step stories.\n\nDrop every entry that fails \"What earns a note\" in `akb guide board` \u2014 the bar that governs a\nwrite governs what stays. Merge lines that say the same thing. Rewrite, don't just cut.\n\nOn top of that, per file:\n\n- `agents/planner/rejected.md` \u2014 one line per thing the user does not want, and why. Keep\n every source id in parentheses: they are how a restored item withdraws its line. \"Already\n done\" is a shipped fact, not a rejection \u2014 it belongs in the published doc (with a\n `readme.md` line pointing at it), not here; drop it from `rejected.md`.\n- `agents/planner/redesign.md` \u2014 one line per entry: the mistake, then the design to use.\n Drop an entry once that design is the obvious default.\n- `readme.md` \u2014 the board's own record, one line per shipped user-facing behavior. Where a\n published doc covers it, the line is just a link to that doc's path. **Replace a prose\n entry with a link only after you have confirmed the doc covers that behavior** \u2014 search the\n docs first; no doc yet means the prose line stays: `readme.md` is the only record until we\n document it. Anything else \u2014 watermarks, a last focus, open gaps, internal detail \u2014\n doesn't belong here; drop it.\n- `agents/planner/decisions.md` \u2014 one line per live decision, in plain user-facing words.\n Drop anything that fails its bar in `akb guide board` (\"Who owns a memory file\"): a call\n about code detail, or one the published docs now cover. Drop a decision once the question\n no longer arises or `redesign.md` states it as a rule.\n- `agents/<agent>/` \u2014 the files that agent's own AGENT.md defines, each pruned to what it says\n the file holds, and its `redesign.md`, pruned as the planner's is. Drop an entry once that\n agent's instructions or the design docs say it, and drop any other file its instructions no\n longer name.\n\nPrune a preview agent's files together, and drop the whole folder when its agent is gone from `akb\nspec`. Drop anything a file says about how the product looks \u2014 colours, dimensions,\ncomponent detail belong in the app's `design.md`, not here \u2014 and fold a product fact worth\nkeeping into the lesson or the decision it supports.\n";
23345
23098
  }
23346
23099
  });
23347
23100
 
@@ -23349,7 +23102,7 @@ var init_prune_memory = __esm({
23349
23102
  var review_memory_default;
23350
23103
  var init_review_memory = __esm({
23351
23104
  "../cli/src/guide/review-memory.md"() {
23352
- review_memory_default = "# Review what the conversations settled\n\nRead each conversation given to you right through, then write what it settled into memory.\n\nChats and planning flows write no memory themselves. One turn cannot see where an exchange\nis going, so a \"what if\" thrown out and taken back three turns later used to land as a\ndecision. You read the whole exchange, so you can tell a decision from a passing thought \u2014\nand that judgement is the whole job.\n\n- **Read what you are given**: each card comes in its own `<conversation>` block: its summary,\n its `Worth noting` and `Overruled by the user` lines, then its chat and the comments the user\n left reviewing it, when it had any, the user's words as written and the agent's replies,\n oldest first. The card is archived, so it is complete and reviewed this once; open no card or\n transcript file.\n- **Keep them apart**: judge each block by itself; what one conversation settled never\n becomes a note for another's card or agents.\n- **Hold to the same bar**: \"What earns a note\" in `akb guide board` governs what you write,\n exactly as it governs a flow that writes one. A conversation that settled nothing earns no\n note, and most do not \u2014 writing nothing is a complete result.\n- **Only what the user settled**: a durable preference, constraint, decision or correction\n they stated. Your own proposal is not their decision, and neither is a question, a \"what\n if\", or a direction the conversation then moved off.\n- **Take the last word**: where they said one thing and then another, the later one stands.\n Where a note already in the file says what they have since overturned, rewrite that note in\n place \u2014 never leave both and never add a second.\n- **Honour \"don't record this\"**: told in the conversation not to record something, write no\n note for it. If one is already there from an earlier review, delete it now.\n- **Where it goes**: a durable choice goes in the planner's `decisions.md` as one\n `**<key>**: <call>` line under the topic that fits, without its rationale; a design lesson\n goes in its `redesign.md` under the relevant topic as\n `- \u274C **<mistake>** \u2192 \u2705 <what the design should be instead, one line>.`, written as guidance\n for future tasks, not a history of the fix. Both go in the folder of the module each\n conversation is listed with (`akb guide board`). Each conversation also lists the agents\n whose sections its card holds and the memory files each keeps: where what was settled lands\n on one agent's own work, it goes in that agent's memory instead. Read that agent's AGENT.md\n (`akb raw agent-file <agent> AGENT.md`) only then, and keep its memory the way it says.\n Distill settled feedback into reusable preferences (dos) and corrections (don'ts), retaining\n their scope; never copy raw reviews. An agent whose instructions name no memory gets no new file.\n- **What the card settled**: the summary says what the card was about and earns no note. A\n `Worth noting` line is a decision the user approved, by answering it or letting it stand, so\n it can be a choice but never a correction, and it earns a planner note only where the user's\n own words in the conversation back it; a line under\n `Overruled by the user` is a call the user reversed \u2014 a correction for the agent whose\n section it came from, or for the planner when none did.\n- **A follow-up the proposer missed**: when the user points out work a finished card left\n behind that no proposal named, add the kind of miss, not the case, to\n `docs/kanban/memory/agents/proposer/missed.md` as `- **<kind>**: <what to check> (#<card>)`,\n creating the file if missing.\n- **Change nothing else**: no card, no open question, no code. The memory\n files are the whole of what this run writes.\n- **Say what you wrote**: finish by reporting each conversation you read, and for each the\n notes you added, rewrote, deleted, or that you wrote none.\n";
23105
+ review_memory_default = "# Review what the conversations settled\n\nRead each conversation given to you right through, then write what it settled into memory.\n\nChats and planning flows write no memory themselves. One turn cannot see where an exchange\nis going, so a \"what if\" thrown out and taken back three turns later used to land as a\ndecision. You read the whole exchange, so you can tell a decision from a passing thought \u2014\nand that judgement is the whole job.\n\n- **Read what you are given**: each card comes in its own `<conversation>` block: its summary,\n its `Worth noting` and `Overruled by the user` lines, then its chat and the comments the user\n left reviewing it, when it had any, the user's words as written and the agent's replies,\n oldest first. The card is archived, so it is complete and reviewed this once; open no card or\n transcript file.\n- **Keep them apart**: judge each block by itself; what one conversation settled never\n becomes a note for another's card or agents.\n- **Hold to the same bar**: \"What earns a note\" in `akb guide board` governs what you write,\n exactly as it governs a flow that writes one. A conversation that settled nothing earns no\n note, and most do not \u2014 writing nothing is a complete result.\n- **Only what the user settled**: a durable preference, constraint, decision or correction\n they stated. Your own proposal is not their decision, and neither is a question, a \"what\n if\", or a direction the conversation then moved off.\n- **Take the last word**: where they said one thing and then another, the later one stands.\n Where a note already in the file says what they have since overturned, rewrite that note in\n place \u2014 never leave both and never add a second.\n- **Honour \"don't record this\"**: told in the conversation not to record something, write no\n note for it. If one is already there from an earlier review, delete it now.\n- **Where it goes**: a durable choice goes in the planner's `decisions.md` as one\n `**<key>**: <call>` line under the topic that fits, without its rationale; a design lesson\n goes in its `redesign.md` under the relevant topic as\n `- \u274C **<mistake>** \u2192 \u2705 <what the design should be instead, one line>.`, written as guidance\n for future tasks, not a history of the fix. Both go in the folder of the module each\n conversation is listed with (`akb guide board`). Each conversation also lists the agents\n whose sections its card holds and the memory files each keeps: where what was settled lands\n on one agent's own work, it goes in that agent's memory instead. Read that agent's AGENT.md\n (`akb raw agent-file <agent> AGENT.md`) only then, and keep its memory the way it says.\n Distill settled feedback into reusable preferences (dos) and corrections (don'ts), retaining\n their scope; never copy raw reviews. An agent whose instructions name no memory gets no new file.\n- **What the card settled**: the summary says what the card was about and earns no note. A\n `Worth noting` line is a decision the user approved, by answering it or letting it stand, so\n it can be a choice but never a correction, and it earns a planner note only where the user's\n own words in the conversation back it; a line under\n `Overruled by the user` is a call the user reversed, so it goes in `redesign.md`: that of\n the agent whose section it came from, created there even if its instructions name other\n files, or the planner's when none did or its instructions name no memory.\n- **Change nothing else**: no card, no open question, no code. The memory\n files are the whole of what this run writes.\n- **Say what you wrote**: finish by reporting each conversation you read, and for each the\n notes you added, rewrote, deleted, or that you wrote none.\n";
23353
23106
  }
23354
23107
  });
23355
23108
 
@@ -23373,7 +23126,7 @@ var init_refine = __esm({
23373
23126
  var reflect_default;
23374
23127
  var init_reflect = __esm({
23375
23128
  "../cli/src/guide/reflect.md"() {
23376
- reflect_default = "# Reflect on completed cards\n\nSome cards have just been finished. Propose the work that should follow them, one inbox item\neach. Proposing nothing is a normal result. Do not read archived cards you were not given,\nand do not edit or archive any card yourself; create one only when your settings say so.\n\n## Inputs\n\nEach card comes in its own `<card>` block; read each in full and take nothing from one block\nfor another:\n\n- **The card**: what was planned.\n\nWhat helps check it \u2014 read only as far as it sharpens a candidate:\n\n- **What shipped**: what each block lists under `shipped`.\n- **Past misses**: `docs/kanban/memory/agents/proposer/missed.md`, when it exists, lists kinds\n of follow-up missed before; check every card for each one.\n\n## Candidates\n\nEach is work on the project itself that traces to a line in the card or in what shipped:\nwhat it left out, revealed, relied on without anyone providing it, or made worth doing now.\nVerify a gap in the project; never take the card's word.\n\nJudge for yourself what is worth the user's time to read, exploratory ideas included; say so\nin its text when one is a small chore.\n\nWhen what shipped makes an open card's plan untrue, propose no item for it; run\n`akb card revise <id> \"<the change and why>\"` instead.\n\n## Filter\n\nDrop a candidate that is already on the board (`akb raw list`) or in the inbox\n(`akb triage check <source-id>`), is work an agent in the `<scheduled-agents>` block does on its\nown runs, is turned down by `docs/kanban/memory/agents/planner/rejected.md` or\n`docs/kanban/memory/agents/planner/*/rejected.md`, or is a guess nothing in the card or the\nproject backs. When several cards lead to the same work, propose it once, from the card it\ntraces to most directly.\n\n## Write\n\nWrite each item's body to a temporary Markdown file: a short `####` heading per part \u2014 what,\nto do, why, and any caveat \u2014 then the archived card's path `docs/kanban/.archive/<file>`.\n\n```text\nakb triage add --title \"<one line>\" --slug <short-english-slug> --source \"#<id>\" --workflow <workflow> --file <body.md>\n```\n\n`--source` names the card the item traces to, and `--workflow` is that card's `workflow:`, or\nthe board's default workflow shown by `akb workflow` when it has none. Report what you\nproposed and what you skipped, with a reason for each skip.\n";
23129
+ reflect_default = '# Reflect on completed cards\n\n## Read\n\nRead each given `<card>` in full and check its own `shipped` content; keep cards separate.\nFind needs the work left behind or revealed, and verify them in the project. Proposing nothing is fine.\n\n## Filter\n\n- **Duplicates**: check `akb raw list` and `akb triage check <source-id>`; propose shared work only once.\n- **Exclude**: skip unsupported guesses, work covered by `<scheduled-agents>`, work rejected in `docs/kanban/memory/agents/planner/rejected.md` or its module folders, and calls the user reversed, in any `redesign.md` under `docs/kanban/memory/agents/` or under a card\'s `Overruled by the user`.\n- **Existing cards**: when shipped work invalidates an open card\'s plan, run `akb card revise <id> "<the change and why>"` instead of proposing an item.\n\n## Write\n\n- **Reader**: someone who has read neither the original card nor the code can decide in ten seconds whether the work is worth doing.\n- **Order**: first explain what happened and what the user runs into, then say what the work would do.\n- **Plain words**: keep file, function and field names, card ids, agent names and evidence trails out of the title and body.\n\nWrite the body to a temporary Markdown file and end with the source card\'s archive path.\nWrite one triage item per proposal. Exploratory ideas are welcome; identify small chores as such.\nUse the source card\'s `workflow:`, or the board default shown by `akb workflow` when it has none.\n\n```text\nakb triage add --title "<one line>" --slug <short-english-slug> --source "#<id>" --workflow <workflow> --file <body.md>\n```\n\nBriefly report what you proposed and what you skipped, with reasons.\n';
23377
23130
  }
23378
23131
  });
23379
23132
 
@@ -23401,14 +23154,6 @@ var init_preview_agent = __esm({
23401
23154
  }
23402
23155
  });
23403
23156
 
23404
- // ../cli/src/guide/update.md
23405
- var update_default;
23406
- var init_update = __esm({
23407
- "../cli/src/guide/update.md"() {
23408
- update_default = "# Updating the board\n\nUpdating is two things, and nothing else: **a newer `akb`, then a repaired board.** There\nis no third step where the skill is re-installed to pick up a fixed flow \u2014 the flows ship\ninside the command, so a newer command is a newer set of flows everywhere at once.\n\n## 1. A newer command\n\n```\nnpm install -g wildcrane@latest\n```\n\n`akb update` can't do this one to itself \u2014 replacing the file that is running is how you\nget half a command \u2014 so it checks npm and names this line when it is behind.\n\nStill on the old `ai4kanban` package? Run `npm uninstall -g ai4kanban` first; both install `akb`.\n\n## 2. A repaired board\n\n```\nakb update\n```\n\nRun it from the project root. It does the whole mechanical part:\n\n- rewrites the installed `SKILL.md` (the note) in whichever of `.claude/skills/kanban/`\n (Claude Code) and `.agents/skills/kanban/` (Codex) is already there, wholesale, which also\n clears out what older versions left beside it: the `references/` folder of copied flows,\n a skill-folder `config.md`, and the command's own `kanban.mjs`. The note is the whole\n skill now \u2014 the command comes from npm, so the update is two deletions to commit. It never\n adds a folder that isn't there: the skill is an extra a project opts into, so a project\n without one is not broken, and `akb skill install` is how it is added,\n- adds what an older version never wrote: `config.md`, `modules.md`, the planner's memory folder,\n an empty `docs/kanban/memory/project.md`,\n- deletes `releases.md` and every card's `release` field, keeping\n `.release-summaries/` as a record,\n- renames `docs/kanban/memory/product.md` to `project.md`,\n- moves a goal the user wrote in `docs/kanban/memory/goal.md` to the top of the planner's\n `decisions.md` and deletes the file,\n- moves a memory set still sitting at the board root into `docs/kanban/memory/`,\n- moves the memory onto whoever writes it: `decisions.md`, `rejected.md` and `redesign.md`\n merge into `docs/kanban/memory/agents/planner/<module>/` (the board's own copy into\n `planner/`), each module's `readme.md` merges into `docs/kanban/memory/readme.md`, and a\n `## <module>` topic in a planner file moves into that module's folder \u2014 merged, never\n overwritten, and only once,\n- rescues a filled-in `config.md` left inside an old skill folder to\n `docs/kanban/config.md`,\n- prints which version you moved from and to, with a link to everything that changed\n between those two releases.\n\n**The one rule: `docs/kanban/` is yours; the two installed files are upstream's and get\noverwritten wholesale. Never merge the two.** It is safe to run twice, and it never edits\nyour cards, your config, or your memory.\n\n## Then read what it printed\n\nAnything the command can't decide comes back under **Needs your attention**. Each line is\nyours to finish:\n\n- **A new config setting.** Add that one line to `docs/kanban/config.md` and ask the user\n only for the new value. Leave the rest of the config alone.\n- **A blank `docs/kanban/modules.md`.** Write it per `akb guide module-map`, then run\n `akb update` again so every module gets a memory path.\n- **A per-module `goal.md`.** Fold what it says into that module's `decisions.md` in\n `docs/kanban/memory/agents/planner/<module>/`, then delete it.\n- **A `docs/kanban/config.from-skill.md`.** An old skill folder held a config that differs\n from the board's. Fold anything worth keeping into `docs/kanban/config.md`, then delete it.\n- **A missing agent folder.** Only one of the two is here, or neither. `akb skill install`\n writes them if you want that agent to see the board too \u2014 it is never added behind the\n user's back.\n\nLeave the project-wide notes and the cards alone \u2014 don't move notes into the module paths,\nand don't hand-add `modules:` lines to cards.\n\n## Plugin install (the note lives in the plugin cache)\n\nThe plugin cache is read-only, so the note comes from the marketplace instead:\n\n```\n/plugin marketplace update kanban\n/plugin install kanban@kanban\n```\n\nThen run `akb update` anyway \u2014 it repairs the board, which the plugin never touches.\n\n## Verify\n\n```\nakb raw version\n```\n\nTell the user to review `git diff` before committing. The board app updates separately \u2014\nit says when a newer one is out and installs it from that notice.\n";
23409
- }
23410
- });
23411
-
23412
23157
  // ../cli/src/guide/update-questions.md
23413
23158
  var update_questions_default;
23414
23159
  var init_update_questions = __esm({
@@ -23429,7 +23174,7 @@ var init_write_agent = __esm({
23429
23174
  var writing_default;
23430
23175
  var init_writing = __esm({
23431
23176
  "../cli/src/guide/writing.md"() {
23432
- writing_default = '# Write a card\n\nA card has a **human half** for review and a folded **agent half** for implementation.\nEvery flow writes a card in this order:\n\n```text\n<one short paragraph: the observable result and the current behavior or constraint it changes.>\n\n## Worth noting <- what whoever builds it must not miss; keep empty when none\n- **<question the decision settles>**: <answer>\n\n## Worth noting after implementation\n- **<question the decision settles>**: <answer>\n <- answered material decisions building turned up; written\n by the build, omit when empty. Never approved delivery scope\n\n## By `<name>` agent <- every preview agent\'s section\n\n<!-- agent --> <- boundary\n\n## Today\n## Scope <- requirements, not rationale\n## Todo <- one build step per checkbox\n## Decided by the agent\n- **<question the decision settles>**: <answer>\n### Overruled by the user <- always last\n```\n\n## `Worth noting`\n\n- **What goes here**: what whoever builds the card must not miss \u2014 the user\'s answers first,\n then a decision that passes the material-decision test in `akb guide update-questions`; a lasting\n answer that fails it goes below the human half.\n- **Keep it glanceable**: one entry is one reviewer decision, written as one sentence that\n states the chosen behavior and its main cost. Merge entries governed by one policy. One\n to three independent entries is normal.\n- **Cut the story**: omit chronology, evidence trails, exhaustive consequences, and the\n rejected option unless it is necessary to understand the main cost.\n\n## `Worth noting after implementation`\n\n- **What goes here**: the same, for what the build surfaced and settled.\n- **Keep it non-blocking**: this section never changes or blocks a delivery already in\n flight.\n- **Use the same compact format**: one reviewer decision and its main cost in one sentence.\n- **Exclude work logs**: never put diagnostics, run history, check results, suggestions,\n or unresolved questions here.\n- **Never approve a deviation here**: work that contradicts the approved requirements must\n be fixed or sent to the user by the build.\n\n## Agent sections\n\n- **Owner\'s rules first**: before rewriting, removing, or overruling content a preview agent\n owns, read that agent\'s rules with `akb raw agent-file <agent> AGENT.md`, and the\n references they point to for the affected part with the same command and their path. When\n they cannot be read, surface the gap before changing that content.\n\n## `Today`\n\n- **Omit by default**: write it, heading included, only when the build depends on findings\n the code does not make obvious; a small change to a few simple files needs none.\n\n## `Todo`\n\n- **Build steps only**: finish planning on the card; never defer it to a todo.\n- **Preserve completed work**: never edit, delete, or untick a checked todo; append a todo to\n reverse it.\n\n## `Decided by the agent`\n\n- **Only what changes the build**: include answered decisions the builder would otherwise\n get wrong and neither `Worth noting` section holds; omit a finding that needs no action.\n- **Preserve superseded calls**: move agent or review decisions the user reverses to\n `Overruled by the user`; delete one the card leaves the builder no room to repeat.\n\n## General writing requirements\n\n- **Keep section titles fixed**: write every `##` and `###` section title in English, never\n rename or translate an existing one, and leave empty scaffold sections in place.\n- **Edit only the body**: use board commands for every frontmatter field (`akb guide board`).\n- **File name**: when the card\'s file name does not say in English what the card is, rename\n it with `akb raw update <id> --slug <short-english-slug>`; leave a group\'s root as it is.\n- **Name the source in frontmatter**: pass each plan path, card `#id`, or URL the card came\n from as `--source` to `akb raw create`, or `--add-source` to `akb raw update`; never write\n a `## Source` section.\n- **Write for a fresh reader**: use professional, comprehensible language with no assumed\n conversation context.\n- **Keep human half self-contained**: nothing above `<!-- agent -->` may rely on the folded\n agent half.\n- **Keep bullets atomic**: use `- **<short title>**: <one clear sentence>` for one rule per\n bullet.\n- **Specify behavior**: omit planning notes and unnecessary coding details.\n- **Supported embeds**: only valid `<Asset>`, `<Compare>` and `<Storyboard>` markers alone in a\n paragraph embed content; other HTML or JSX is not allowed. Inline or fenced examples remain text.\n- **Compare**: to show two images or two videos against each other, such as a source and its\n clone, write `<Compare a="<src>" b="<src>" label="..." a-label="..." b-label="..." />`;\n never render a side-by-side file for it. For videos, add `shots="<src>"`: a JSON list of\n `{ "id": "S1", "start": 0, "end": 2.25, "name": "..." }` covering video A in order, or a\n storyboard of A. B may be a storyboard instead of a video; it then shows its shot with the\n id A is on, beside that shot of a storyboard `shots`.\n- **Asset label and alt**: `label` is a tag of a few words; an image\'s caption goes in its\n optional `alt="..."`, its shipped alt text, which cannot contain `<`, `>` or its own quote mark.\n- **Asset variants**: versions of the same content, such as languages or devices, are one tag\n with `variants=\'{"<label>": "<src>", ...}\'`, `src` being the one shown first; alternatives the\n user picks between stay separate tags.\n- **Say each fact once**: a section holds only what the card does not already say; leave it\n empty rather than restate the opening paragraph or another section. Cut only the repeated\n part of a line: a name, value or case stated nowhere else stays.\n\n- **Validate after edits**: run `akb raw validate <id>` for every card you wrote. Fix each\n reported file and line before finishing; validation does not rewrite the card for you.\n';
23177
+ writing_default = '# Write a card\n\nA card has a **human half** for review and a folded **agent half** for implementation.\nEvery flow writes a card in this order:\n\n```text\n<\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>\n\n## Worth noting <- what whoever builds it must not miss; keep empty when none\n- **<question the decision settles>**: <answer>\n\n## Worth noting after implementation\n- **<question the decision settles>**: <answer>\n <- answered material decisions building turned up; written\n by the build, omit when empty. Never approved delivery scope\n\n## By `<name>` agent <- every preview agent\'s section\n\n<!-- agent --> <- boundary\n\n## Today\n## Scope <- requirements, not rationale\n## Todo <- one build step per checkbox\n## Decided by the agent\n- **<question the decision settles>**: <answer>\n### Overruled by the user <- always last\n```\n\n## `Worth noting`\n\n- **What goes here**: what whoever builds the card must not miss \u2014 the user\'s answers first,\n then a decision that passes the material-decision test in `akb guide update-questions`; a lasting\n answer that fails it goes below the human half.\n- **Keep it glanceable**: one entry is one reviewer decision, written as one sentence that\n states the chosen behavior and its main cost. Merge entries governed by one policy. One\n to three independent entries is normal.\n- **Cut the story**: omit chronology, evidence trails, exhaustive consequences, and the\n rejected option unless it is necessary to understand the main cost.\n\n## `Worth noting after implementation`\n\n- **What goes here**: the same, for what the build surfaced and settled.\n- **Keep it non-blocking**: this section never changes or blocks a delivery already in\n flight.\n- **Use the same compact format**: one reviewer decision and its main cost in one sentence.\n- **Exclude work logs**: never put diagnostics, run history, check results, suggestions,\n or unresolved questions here.\n- **Never approve a deviation here**: work that contradicts the approved requirements must\n be fixed or sent to the user by the build.\n\n## Agent sections\n\n- **Owner\'s rules first**: before rewriting, removing, or overruling content a preview agent\n owns, read that agent\'s rules with `akb raw agent-file <agent> AGENT.md`, and the\n references they point to for the affected part with the same command and their path. When\n they cannot be read, surface the gap before changing that content.\n\n## `Today`\n\n- **Omit by default**: write it, heading included, only when the build depends on findings\n the code does not make obvious; a small change to a few simple files needs none.\n\n## `Todo`\n\n- **Build steps only**: finish planning on the card; never defer it to a todo.\n- **Preserve completed work**: never edit, delete, or untick a checked todo; append a todo to\n reverse it.\n\n## `Decided by the agent`\n\n- **Only what changes the build**: include answered decisions the builder would otherwise\n get wrong and neither `Worth noting` section holds; omit a finding that needs no action.\n- **Preserve superseded calls**: move agent or review decisions the user reverses to\n `Overruled by the user`; delete one the card leaves the builder no room to repeat.\n\n## General writing requirements\n\n- **Keep section titles fixed**: write every `##` and `###` section title in English, never\n rename or translate an existing one, and leave empty scaffold sections in place.\n- **Edit only the body**: use board commands for every frontmatter field (`akb guide board`).\n- **File name**: when the card\'s file name does not say in English what the card is, rename\n it with `akb raw update <id> --slug <short-english-slug>`; leave a group\'s root as it is.\n- **Name the source in frontmatter**: pass each plan path, card `#id`, or URL the card came\n from as `--source` to `akb raw create`, or `--add-source` to `akb raw update`; never write\n a `## Source` section.\n- **Write for a fresh reader**: use professional, comprehensible language with no assumed\n conversation context.\n- **Keep human half self-contained**: nothing above `<!-- agent -->` may rely on the folded\n agent half.\n- **Keep bullets atomic**: use `- **<short title>**: <one clear sentence>` for one rule per\n bullet.\n- **Specify behavior**: omit planning notes and unnecessary coding details.\n- **Supported embeds**: only valid `<Asset>`, `<Compare>` and `<Storyboard>` markers alone in a\n paragraph embed content; other HTML or JSX is not allowed. Inline or fenced examples remain text.\n- **Compare**: to show two images or two videos against each other, such as a source and its\n clone, write `<Compare a="<src>" b="<src>" label="..." a-label="..." b-label="..." />`;\n never render a side-by-side file for it. For videos, add `shots="<src>"`: a JSON list of\n `{ "id": "S1", "start": 0, "end": 2.25, "name": "..." }` covering video A in order, or a\n storyboard of A. B may be a storyboard instead of a video; it then shows its shot with the\n id A is on, beside that shot of a storyboard `shots`.\n- **Asset label and alt**: `label` is a tag of a few words; an image\'s caption goes in its\n optional `alt="..."`, its shipped alt text, which cannot contain `<`, `>` or its own quote mark.\n- **Asset variants**: versions of the same content, such as languages or devices, are one tag\n with `variants=\'{"<label>": "<src>", ...}\'`, `src` being the one shown first; alternatives the\n user picks between stay separate tags.\n- **Say each fact once**: a section holds only what the card does not already say; leave it\n empty rather than restate the opening paragraph or another section. Cut only the repeated\n part of a line: a name, value or case stated nowhere else stays.\n\n- **Validate after edits**: run `akb raw validate <id>` for every card you wrote. Fix each\n reported file and line before finishing; validation does not rewrite the card for you.\n';
23433
23178
  }
23434
23179
  });
23435
23180
 
@@ -23478,7 +23223,6 @@ var init_guide = __esm({
23478
23223
  init_resolve2();
23479
23224
  init_revise();
23480
23225
  init_preview_agent();
23481
- init_update();
23482
23226
  init_update_questions();
23483
23227
  init_write_agent();
23484
23228
  init_writing();
@@ -23505,8 +23249,7 @@ var init_guide = __esm({
23505
23249
  { name: "reflect", when: "propose the work just-completed cards leave behind", text: reflect_default },
23506
23250
  { name: "preview-agent", when: "be a preview agent: write your one section of a card", text: preview_agent_default },
23507
23251
  { name: "write-agent", when: "create or change an agent, including one built from a skill \u2014 its AGENT.md, files, memory, and how to check it", text: write_agent_default },
23508
- { name: "module-map", when: "write or rebuild docs/kanban/modules.md", text: module_map_default },
23509
- { name: "update", when: "pull a newer version into a project that already has one", text: update_default }
23252
+ { name: "module-map", when: "write or rebuild docs/kanban/modules.md", text: module_map_default }
23510
23253
  ];
23511
23254
  guideNames = () => GUIDES.map((g) => g.name);
23512
23255
  RENAMED = {
@@ -23517,7 +23260,7 @@ var init_guide = __esm({
23517
23260
  });
23518
23261
 
23519
23262
  // ../cli/src/lib/agent/approve.ts
23520
- import fs75 from "node:fs";
23263
+ import fs73 from "node:fs";
23521
23264
  import path74 from "node:path";
23522
23265
  function approvalRequest(req) {
23523
23266
  const id = req.id;
@@ -23537,11 +23280,11 @@ function advancePhase(id) {
23537
23280
  const found2 = locate(id);
23538
23281
  if (!card || !found2 || !awaitingApproval(card)) return { error: `#${id} is no longer waiting on approval.` };
23539
23282
  const file = found2.kind === "group" ? path74.join(found2.target, "root.md") : found2.target;
23540
- const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
23283
+ const { meta, body } = parseFrontmatter(fs73.readFileSync(file, "utf8"));
23541
23284
  if (!meta) return { error: `#${id} has no frontmatter` };
23542
23285
  meta.phase = card.stage + 1;
23543
23286
  meta.status = "todo";
23544
- fs75.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
23287
+ fs73.writeFileSync(file, serializeFrontmatter(meta) + "\n" + body);
23545
23288
  return null;
23546
23289
  }
23547
23290
  var init_approve = __esm({
@@ -23661,7 +23404,7 @@ var init_start = __esm({
23661
23404
  });
23662
23405
 
23663
23406
  // ../cli/src/lib/agent/task-chats.ts
23664
- import fs76 from "node:fs";
23407
+ import fs74 from "node:fs";
23665
23408
  import path75 from "node:path";
23666
23409
  function taskChatFile(task, home3) {
23667
23410
  if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
@@ -23670,7 +23413,7 @@ function taskChatFile(task, home3) {
23670
23413
  function readTaskChat(task, home3) {
23671
23414
  let text5;
23672
23415
  try {
23673
- text5 = fs76.readFileSync(taskChatFile(task, home3), "utf8");
23416
+ text5 = fs74.readFileSync(taskChatFile(task, home3), "utf8");
23674
23417
  } catch {
23675
23418
  return [];
23676
23419
  }
@@ -23696,7 +23439,7 @@ var init_task_chats = __esm({
23696
23439
  });
23697
23440
 
23698
23441
  // ../cli/src/lib/agent/memory-review.ts
23699
- import fs77 from "node:fs";
23442
+ import fs75 from "node:fs";
23700
23443
  import path76 from "node:path";
23701
23444
  function taskChatsToReview(tasks) {
23702
23445
  const out = [];
@@ -23711,7 +23454,7 @@ function taskChatsToReview(tasks) {
23711
23454
  }
23712
23455
  const messages = lines.map((l) => ({ role: l.role === "user" ? "you" : "agent", text: l.text }));
23713
23456
  const review = handed(task, messages, card);
23714
- if (review.settled || review.transcript) out.push(review);
23457
+ if (review.settled) out.push(review);
23715
23458
  }
23716
23459
  return out;
23717
23460
  }
@@ -23720,7 +23463,7 @@ function readArchived2(id) {
23720
23463
  if (!found2) return null;
23721
23464
  try {
23722
23465
  const file = found2.kind === "group" ? path76.join(found2.target, "root.md") : found2.target;
23723
- const { meta, body } = parseFrontmatter(fs77.readFileSync(file, "utf8"));
23466
+ const { meta, body } = parseFrontmatter(fs75.readFileSync(file, "utf8"));
23724
23467
  if (!meta) return null;
23725
23468
  return { id, title: meta.title, modules: meta.modules ?? [], rejected: meta.rejected, body };
23726
23469
  } catch {
@@ -23788,9 +23531,74 @@ var init_memory_review = __esm({
23788
23531
  }
23789
23532
  });
23790
23533
 
23791
- // ../cli/src/lib/agent/flow.ts
23792
- import fs78 from "node:fs";
23534
+ // ../cli/src/lib/agent/dismissal-review.ts
23535
+ import fs76 from "node:fs";
23793
23536
  import path77 from "node:path";
23537
+ function rejectionsToReview() {
23538
+ const dir = path77.join(ARCHIVE, REJECTIONS_TO_REVIEW);
23539
+ if (!fs76.existsSync(dir)) return [];
23540
+ const found2 = [];
23541
+ for (const file of walkMd(dir)) {
23542
+ const id = archivedId(file);
23543
+ if (id === null) continue;
23544
+ try {
23545
+ const { meta } = parseFrontmatter(fs76.readFileSync(file, "utf8"));
23546
+ if (!meta?.rejected || meta.rejected_by !== "user" || !meta.rejected_reason.trim()) continue;
23547
+ found2.push({ id, title: meta.title, file: rel(file), reason: meta.rejected_reason.trim(), rejectedAt: meta.rejected_at });
23548
+ } catch {
23549
+ continue;
23550
+ }
23551
+ }
23552
+ return found2.sort((a, b) => a.rejectedAt.localeCompare(b.rejectedAt) || a.id - b.id);
23553
+ }
23554
+ function dismissalsToReview() {
23555
+ return readAllDismissed().filter((s) => s.dismissedBy === "user" && s.dismissedReason.trim()).sort((a, b) => a.dismissedAt.localeCompare(b.dismissedAt)).map((s) => ({
23556
+ sourceId: s.sourceId,
23557
+ title: s.title,
23558
+ file: s.relPath,
23559
+ reason: s.dismissedReason.trim(),
23560
+ dismissedAt: s.dismissedAt
23561
+ }));
23562
+ }
23563
+ function citedSources(text5) {
23564
+ const ids = /* @__PURE__ */ new Set();
23565
+ for (const line of text5.split("\n")) {
23566
+ if (!/^\s*[-*]\s/.test(line)) continue;
23567
+ const m = line.match(SOURCES);
23568
+ for (const id of m?.[1].split(",") ?? []) if (id.trim()) ids.add(id.trim());
23569
+ }
23570
+ return [...ids];
23571
+ }
23572
+ function withdrawnSources() {
23573
+ const cited = [...new Set(plannerCopies("rejected.md").flatMap((file) => {
23574
+ try {
23575
+ return citedSources(fs76.readFileSync(file, "utf8"));
23576
+ } catch {
23577
+ return [];
23578
+ }
23579
+ }))];
23580
+ if (cited.length === 0) return [];
23581
+ const dismissed = new Set(readAllDismissed().map((s) => s.sourceId));
23582
+ const restored = new Set([...readInbox(), ...readArchived()].map((s) => s.sourceId));
23583
+ return cited.filter((id) => restored.has(id) && !dismissed.has(id));
23584
+ }
23585
+ var SOURCES;
23586
+ var init_dismissal_review = __esm({
23587
+ "../cli/src/lib/agent/dismissal-review.ts"() {
23588
+ "use strict";
23589
+ init_cards();
23590
+ init_frontmatter();
23591
+ init_memory();
23592
+ init_paths();
23593
+ init_inbox();
23594
+ init_archive();
23595
+ SOURCES = /\(([^()]+)\)\s*$/;
23596
+ }
23597
+ });
23598
+
23599
+ // ../cli/src/lib/agent/flow.ts
23600
+ import fs77 from "node:fs";
23601
+ import path78 from "node:path";
23794
23602
  function readCard2(id, home3 = "board") {
23795
23603
  const found2 = home3 === "archive" ? locateArchived(id) : locate(id);
23796
23604
  if (!found2) {
@@ -23799,10 +23607,10 @@ function readCard2(id, home3 = "board") {
23799
23607
  { kind: "card-not-found", id }
23800
23608
  );
23801
23609
  }
23802
- const file = found2.kind === "group" ? path77.join(found2.target, "root.md") : found2.target;
23610
+ const file = found2.kind === "group" ? path78.join(found2.target, "root.md") : found2.target;
23803
23611
  let text5;
23804
23612
  try {
23805
- text5 = fs78.readFileSync(file, "utf8");
23613
+ text5 = fs77.readFileSync(file, "utf8");
23806
23614
  } catch {
23807
23615
  die(`#${id} is on the board but ${rel(file)} can't be read.`, { kind: "card-unreadable", id });
23808
23616
  }
@@ -23926,7 +23734,7 @@ function workspaceField(delivery) {
23926
23734
  if (!delivery?.worktree) return [];
23927
23735
  return field("workspace", [
23928
23736
  `write code in ${delivery.worktree} \u2014 this delivery's own worktree, on branch ${delivery.branch}.`,
23929
- path77.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.`,
23737
+ path78.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.`,
23930
23738
  `every board command names the project's own copy: \`${boardCommandFor()} <command>\`.`,
23931
23739
  `${rel(REPO_ROOT)} is the project \u2014 the card, the memory files and the docs are changed there, not here.`
23932
23740
  ]);
@@ -24107,12 +23915,12 @@ function buildFlow(req, program2) {
24107
23915
  }
24108
23916
  case "resolve": {
24109
23917
  facts.push(...questionsField(card.meta), ...stageField(card.meta));
24110
- const building2 = activeDelivery(req.id);
24111
- if (building2) {
24112
- facts.push(...answeringField(building2, self));
23918
+ const building = activeDelivery(req.id);
23919
+ if (building) {
23920
+ facts.push(...answeringField(building, self));
24113
23921
  facts.push(...notesField(card));
24114
23922
  }
24115
- if (building2) close.push(answeredClose(building2, self));
23923
+ if (building) close.push(answeredClose(building, self));
24116
23924
  close.push(...stageClose(card.meta));
24117
23925
  break;
24118
23926
  }
@@ -24189,8 +23997,7 @@ function buildFlow(req, program2) {
24189
23997
  facts.push(
24190
23998
  ...field("memory", [
24191
23999
  `${rel(agentMemoryDir(PLANNER))}/<module>/ \u2014 decisions.md, rejected.md, redesign.md: where a planning note goes, the planner's own files for one spanning modules`,
24192
- `${proposerMissedFile()} \u2014 a follow-up the user says the proposer missed`,
24193
- `${rel(AGENT_MEMORY)}/<agent>/ \u2014 an agent's own files, which its AGENT.md names`
24000
+ `${rel(AGENT_MEMORY)}/<agent>/ \u2014 an agent's own files, which its AGENT.md names, and its redesign.md`
24194
24001
  ])
24195
24002
  );
24196
24003
  for (const chat of chats) facts.push("", ...conversationBlock(chat));
@@ -24204,12 +24011,9 @@ function buildFlow(req, program2) {
24204
24011
  }
24205
24012
  // The rejection review (#929, #1497): the lists are the job, decided before any run starts.
24206
24013
  case "review-dismissals": {
24207
- const lastRun = req.allReasons ? void 0 : dismissalReview().lastRun;
24208
- const since = parseStamp(lastRun)?.getTime() ?? 0;
24209
- const rejections = rejectionsToReview(since);
24210
- const dismissals = dismissalsToReview(since);
24014
+ const rejections = rejectionsToReview();
24015
+ const dismissals = dismissalsToReview();
24211
24016
  const withdrawn = withdrawnSources();
24212
- facts.push(...field("window", lastRun ? `since the last review that passed, ${lastRun}` : req.allReasons ? "every reason on the board" : "every reason \u2014 none has been reviewed yet"));
24213
24017
  facts.push(
24214
24018
  ...field(
24215
24019
  "rejections",
@@ -24327,7 +24131,6 @@ var init_flow = __esm({
24327
24131
  init_frontmatter();
24328
24132
  init_io();
24329
24133
  init_guide();
24330
- init_cadence();
24331
24134
  init_memory();
24332
24135
  init_paths();
24333
24136
  init_start();
@@ -24338,7 +24141,6 @@ var init_flow = __esm({
24338
24141
  init_deliveries();
24339
24142
  init_facts();
24340
24143
  init_memory_review();
24341
- init_settings2();
24342
24144
  init_dismissal_review();
24343
24145
  init_language();
24344
24146
  init_prompts();
@@ -24676,7 +24478,7 @@ __export(watch_exports, {
24676
24478
  watchRun: () => watchRun
24677
24479
  });
24678
24480
  import { spawn as spawn4 } from "node:child_process";
24679
- import fs79 from "node:fs";
24481
+ import fs78 from "node:fs";
24680
24482
  async function watchRun(sessionId, resume = startResume) {
24681
24483
  const spec = readSpec(sessionId);
24682
24484
  const run2 = peekRun(sessionId);
@@ -24712,11 +24514,11 @@ async function watchRun(sessionId, resume = startResume) {
24712
24514
  const record = claimed ?? run2;
24713
24515
  const sub = record.action === "sub";
24714
24516
  if (claim) await setCardStatus(claim.cardId, claim.status);
24715
- fs79.mkdirSync(SESSIONS_DIR, { recursive: true });
24716
- const logFile = fs79.createWriteStream(record.logPath, { flags: "a" });
24517
+ fs78.mkdirSync(SESSIONS_DIR, { recursive: true });
24518
+ const logFile = fs78.createWriteStream(record.logPath, { flags: "a" });
24717
24519
  logFile.on("error", () => {
24718
24520
  });
24719
- const eventsFile = fs79.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
24521
+ const eventsFile = fs78.createWriteStream(eventsPathOf(sessionId), { flags: "a" });
24720
24522
  eventsFile.on("error", () => {
24721
24523
  });
24722
24524
  const event2 = (e) => eventsFile.write(`${JSON.stringify(e)}
@@ -24925,7 +24727,7 @@ async function watchRun(sessionId, resume = startResume) {
24925
24727
  }
24926
24728
  const retrying = !!again && !asked;
24927
24729
  const spent = spoken ? spoken.usage : renderer?.usage?.();
24928
- const lost = !!spec.again && !held4.deliveryId && !ours && !retrying && !spawnError && code !== 0 && lastUsed === 0 && !(spent && spent.input + spent.output + spent.cacheRead + spent.cacheCreation > 0);
24730
+ const lost = !!spec.again && (!held4.deliveryId || held4.action === "implement") && !ours && !retrying && !spawnError && code !== 0 && lastUsed === 0 && !(spent && spent.input + spent.output + spent.cacheRead + spent.cacheCreation > 0);
24929
24731
  let status = asked ? "stopped" : silent || unfinished ? "error" : code === 0 ? "done" : "error";
24930
24732
  const reported = spoken ? spoken.costUsd : renderer?.costUsd?.();
24931
24733
  const totals = reportsSessionTotal(record.harness);
@@ -25297,7 +25099,7 @@ var init_watch = __esm({
25297
25099
  });
25298
25100
 
25299
25101
  // ../cli/src/kanban.ts
25300
- import fs87 from "node:fs";
25102
+ import fs86 from "node:fs";
25301
25103
  import { fileURLToPath as fileURLToPath3 } from "node:url";
25302
25104
 
25303
25105
  // ../cli/src/lib/cli/agent.ts
@@ -25786,7 +25588,7 @@ async function testAgent(args) {
25786
25588
  }
25787
25589
 
25788
25590
  // ../cli/src/commands/cloud.ts
25789
- import fs71 from "node:fs";
25591
+ import fs69 from "node:fs";
25790
25592
  import path70 from "node:path";
25791
25593
 
25792
25594
  // ../cli/src/lib/cloud/avatar.ts
@@ -25835,13 +25637,13 @@ init_config();
25835
25637
  init_home();
25836
25638
  init_config();
25837
25639
  init_session();
25838
- import fs70 from "node:fs";
25640
+ import fs68 from "node:fs";
25839
25641
  import path69 from "node:path";
25840
25642
  var FRESH_MS = 10 * 6e4;
25841
25643
  var GRACE_MS = 7 * 24 * 60 * 6e4;
25842
25644
  var heldFile = () => path69.join(machineHome(), "pro.json");
25843
25645
  function forgetPro() {
25844
- fs70.rmSync(heldFile(), { force: true });
25646
+ fs68.rmSync(heldFile(), { force: true });
25845
25647
  }
25846
25648
 
25847
25649
  // ../cli/src/lib/cloud/account.ts
@@ -25989,12 +25791,12 @@ var SAID = {
25989
25791
  en: "Could not reach WildCrane Cloud. Check your connection and try again."
25990
25792
  },
25991
25793
  credits_used_up: {
25992
- zh: "\u672C\u6708\u79EF\u5206\u5DF2\u7528\u5B8C\uFF0C\u4E0B\u6708 1 \u65E5\uFF08UTC\uFF09\u91CD\u7F6E\uFF1B\u53EF\u5728\u684C\u9762\u5E94\u7528\u7684 Billing \u9875\u67E5\u770B\u4F59\u989D\u3002",
25993
- en: "This month\u2019s AI credits are used up. They reset on the 1st of next month (UTC); check your balance on the Billing page in the desktop app."
25794
+ zh: "\u672C\u6708\u79EF\u5206\u5DF2\u7528\u5B8C\uFF0C\u4E0B\u6708 1 \u65E5\uFF08UTC\uFF09\u91CD\u7F6E\uFF1B\u53EF\u5728 WildCrane \u7F51\u9875\u8BBE\u7F6E\u4E2D\u67E5\u770B\u4F59\u989D\u3002",
25795
+ en: "This month\u2019s AI credits are used up. They reset on the 1st of next month (UTC); check your balance in WildCrane web settings."
25994
25796
  },
25995
25797
  trial_credits_used_up: {
25996
- zh: "\u8BD5\u7528\u671F\u7684 AI \u79EF\u5206\u5DF2\u7528\u5B8C\u3002\u8BD5\u7528\u7ED3\u675F\u540E\u53EF\u5728\u684C\u9762\u5E94\u7528\u7684 Billing \u9875\u8D2D\u4E70 Pro\u3002",
25997
- en: "Your trial\u2019s AI credits are used up. Get Pro from the Billing page in the desktop app once the trial ends."
25798
+ zh: "\u8BD5\u7528\u671F\u7684 AI \u79EF\u5206\u5DF2\u7528\u5B8C\u3002\u8BD5\u7528\u7ED3\u675F\u540E\u53EF\u5728 WildCrane \u7F51\u9875\u8BBE\u7F6E\u4E2D\u8D2D\u4E70 Pro\u3002",
25799
+ en: "Your trial\u2019s AI credits are used up. Get Pro from WildCrane web settings once the trial ends."
25998
25800
  }
25999
25801
  };
26000
25802
  var REF_TYPES = {
@@ -26021,7 +25823,7 @@ async function cloudImage(opts, program2) {
26021
25823
  const type = REF_TYPES[path70.extname(file).toLowerCase()];
26022
25824
  if (!type) usage(`--ref ${file}: use a PNG, JPEG or WebP file.`, line);
26023
25825
  try {
26024
- return `data:${type};base64,${fs71.readFileSync(file).toString("base64")}`;
25826
+ return `data:${type};base64,${fs69.readFileSync(file).toString("base64")}`;
26025
25827
  } catch (e) {
26026
25828
  usage(`--ref ${file}: ${e instanceof Error ? e.message : String(e)}`, line);
26027
25829
  }
@@ -26127,31 +25929,31 @@ async function generate(g) {
26127
25929
  refuse(g.failed, g.fallback);
26128
25930
  }
26129
25931
  const out = g.outFor?.(res, g.out) ?? g.out;
26130
- fs71.mkdirSync(path70.dirname(path70.resolve(out)), { recursive: true });
26131
- fs71.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
25932
+ fs69.mkdirSync(path70.dirname(path70.resolve(out)), { recursive: true });
25933
+ fs69.writeFileSync(out, Buffer.from(await res.arrayBuffer()));
26132
25934
  say(`${out} (${g.label(res)})`);
26133
25935
  return { out };
26134
25936
  }
26135
25937
 
26136
25938
  // ../cli/src/commands/cloud-import.ts
26137
25939
  import crypto3 from "node:crypto";
26138
- import fs73 from "node:fs";
25940
+ import fs71 from "node:fs";
26139
25941
  import path72 from "node:path";
26140
25942
 
26141
25943
  // ../cloud/src/board-files.ts
26142
25944
  var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
26143
25945
  var SETTINGS = ["modules.md", "config.md"];
26144
- var isSettingsPath = (path85) => SETTINGS.includes(path85) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path85);
25946
+ var isSettingsPath = (path86) => SETTINGS.includes(path86) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path86);
26145
25947
  var BOARD_FILE_LIMIT = 256 * 1024;
26146
- function isBoardPath(path85) {
26147
- if (!path85 || path85.length > 500 || path85.startsWith("/") || path85.includes("\\") || path85.includes("\0")) return false;
26148
- if (path85.split("/").some((part) => !part || part === "." || part === "..")) return false;
26149
- if (path85 === "todo/README.md" || path85.split("/").includes(".sync")) return false;
26150
- if (path85.startsWith("workflows/")) return true;
26151
- if (SETTINGS.includes(path85)) return true;
26152
- if (path85.startsWith("rules/")) return isSettingsPath(path85);
26153
- if (path85.startsWith("assets/")) return path85.endsWith(".json");
26154
- return ROOTS.some((root) => path85.startsWith(root)) && path85.endsWith(".md");
25948
+ function isBoardPath(path86) {
25949
+ if (!path86 || path86.length > 500 || path86.startsWith("/") || path86.includes("\\") || path86.includes("\0")) return false;
25950
+ if (path86.split("/").some((part) => !part || part === "." || part === "..")) return false;
25951
+ if (path86 === "todo/README.md" || path86.split("/").includes(".sync")) return false;
25952
+ if (path86.startsWith("workflows/")) return true;
25953
+ if (SETTINGS.includes(path86)) return true;
25954
+ if (path86.startsWith("rules/")) return isSettingsPath(path86);
25955
+ if (path86.startsWith("assets/")) return path86.endsWith(".json");
25956
+ return ROOTS.some((root) => path86.startsWith(root)) && path86.endsWith(".md");
26155
25957
  }
26156
25958
  var workflowFolder = (id) => id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
26157
25959
 
@@ -26255,7 +26057,7 @@ init_bundled();
26255
26057
  // ../cli/src/lib/agents/shared-memory.ts
26256
26058
  init_bundled();
26257
26059
  init_parse();
26258
- import fs72 from "node:fs";
26060
+ import fs70 from "node:fs";
26259
26061
  import path71 from "node:path";
26260
26062
  function sharedMemory(board2) {
26261
26063
  const declared = /* @__PURE__ */ new Map();
@@ -26271,13 +26073,13 @@ function sharedMemory(board2) {
26271
26073
  const root = path71.join(board2, "agents");
26272
26074
  let folders2 = [];
26273
26075
  try {
26274
- folders2 = fs72.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
26076
+ folders2 = fs70.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
26275
26077
  } catch {
26276
26078
  }
26277
26079
  for (const folder of folders2.sort()) {
26278
26080
  let text5 = null;
26279
26081
  try {
26280
- text5 = fs72.readFileSync(path71.join(root, folder, "AGENT.md"), "utf8");
26082
+ text5 = fs70.readFileSync(path71.join(root, folder, "AGENT.md"), "utf8");
26281
26083
  } catch {
26282
26084
  }
26283
26085
  take(folder, text5, false);
@@ -26366,12 +26168,12 @@ async function findProject(asked) {
26366
26168
  }
26367
26169
  throw new Refused(`No project of yours has the id or prefix "${asked}".`, "not_found");
26368
26170
  }
26369
- var read = (file) => fs73.readFileSync(file, "utf8");
26171
+ var read = (file) => fs71.readFileSync(file, "utf8");
26370
26172
  var bytes2 = (text5) => Buffer.byteLength(text5, "utf8");
26371
26173
  function files(dir) {
26372
- if (!fs73.existsSync(dir)) return [];
26174
+ if (!fs71.existsSync(dir)) return [];
26373
26175
  const out = [];
26374
- for (const entry of fs73.readdirSync(dir, { withFileTypes: true })) {
26176
+ for (const entry of fs71.readdirSync(dir, { withFileTypes: true })) {
26375
26177
  if (entry.name.startsWith(".")) continue;
26376
26178
  const full = path72.join(dir, entry.name);
26377
26179
  if (entry.isDirectory()) out.push(...files(full));
@@ -26423,7 +26225,7 @@ function packWorkflow(flow) {
26423
26225
  const named2 = [...flow.previews, ...flow.land, ...flow.scheduled].map((h) => h.agent).filter((a) => !LAND_ROLES.includes(a));
26424
26226
  for (const agent of new Set(named2)) {
26425
26227
  const dir = path72.join(AGENTS, agent);
26426
- if (fs73.existsSync(path72.join(dir, "AGENT.md"))) {
26228
+ if (fs71.existsSync(path72.join(dir, "AGENT.md"))) {
26427
26229
  for (const file of files(dir)) out[`agents/${agent}/${posix2(path72.relative(dir, file))}`] = read(file);
26428
26230
  continue;
26429
26231
  }
@@ -26477,7 +26279,7 @@ function draftsOf(text5) {
26477
26279
  if (!at) continue;
26478
26280
  const base = at[1] === "assets" ? ASSETS : MOCKUPS;
26479
26281
  const file = path72.resolve(base, at[2]);
26480
- if (file.startsWith(base + path72.sep) && fs73.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
26282
+ if (file.startsWith(base + path72.sep) && fs71.statSync(file, { throwIfNoEntry: false })?.isFile()) out.set(name, file);
26481
26283
  }
26482
26284
  return [...out].map(([name, file]) => ({ name, file }));
26483
26285
  }
@@ -26513,7 +26315,7 @@ async function uploadDrafts(task, drafts) {
26513
26315
  if (!card.body.includes(name)) continue;
26514
26316
  const ext = path72.extname(file).toLowerCase();
26515
26317
  const type = TYPES[ext] ?? "application/octet-stream";
26516
- const data = fs73.readFileSync(file);
26318
+ const data = fs71.readFileSync(file);
26517
26319
  const artifact = await api("POST", `/v1/tasks/${task}/artifacts`, { type: kindOf(ext), name: name.slice(0, 200) });
26518
26320
  const upload = await api("POST", `/v1/artifacts/${artifact.id}/uploads`, { content_type: type, size: Math.max(data.length, 1) });
26519
26321
  await api("PUT", `/v1/uploads/${upload.id}`, void 0, { bytes: data, type });
@@ -26556,7 +26358,7 @@ usage: ${program2} cloud import --project <id or prefix> [--dry-run]`, { kind: "
26556
26358
  async function run(asked, dryRun) {
26557
26359
  const project = await findProject(asked);
26558
26360
  const cloudFlows = await all(`/v1/workflows?workspace=${encodeURIComponent(project.workspace)}`);
26559
- const board2 = crypto3.createHash("sha256").update(fs73.realpathSync(KANBAN)).digest("hex").slice(0, 16);
26361
+ const board2 = crypto3.createHash("sha256").update(fs71.realpathSync(KANBAN)).digest("hex").slice(0, 16);
26560
26362
  const cards = openCards();
26561
26363
  const problems = [];
26562
26364
  const where = (file) => posix2(path72.relative(KANBAN, file));
@@ -26586,7 +26388,7 @@ async function run(asked, dryRun) {
26586
26388
  const root = `projects/${project.id}/`;
26587
26389
  const boardFiles = [];
26588
26390
  for (const file of [...files(MEMORY), ...files(RULES), CONFIG, MODULES_MD]) {
26589
- if (!fs73.existsSync(file)) continue;
26391
+ if (!fs71.existsSync(file)) continue;
26590
26392
  const rel2 = where(file);
26591
26393
  if (!isBoardPath(rel2)) continue;
26592
26394
  const content = read(file);
@@ -26697,16 +26499,16 @@ async function run(asked, dryRun) {
26697
26499
  }
26698
26500
  function leftLocal(own) {
26699
26501
  const out = [];
26700
- const count4 = (dir) => fs73.existsSync(dir) ? walkMd(dir).length : 0;
26502
+ const count4 = (dir) => fs71.existsSync(dir) ? walkMd(dir).length : 0;
26701
26503
  const archived = count4(ARCHIVE);
26702
26504
  if (archived) out.push(`.archive/ (${archived})`);
26703
26505
  const triage = count4(TRIAGE);
26704
26506
  if (triage) out.push(`triage/ (${triage})`);
26705
- if (fs73.existsSync(DELIVERIES)) out.push("deliveries/");
26706
- if (fs73.existsSync(path72.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
26507
+ if (fs71.existsSync(DELIVERIES)) out.push("deliveries/");
26508
+ if (fs71.existsSync(path72.join(KANBAN, "metrics.csv"))) out.push("metrics.csv");
26707
26509
  const named2 = new Set(own.flatMap((w) => [...w.previews, ...w.land, ...w.scheduled].map((h) => h.agent)));
26708
- if (fs73.existsSync(AGENTS)) {
26709
- for (const e of fs73.readdirSync(AGENTS, { withFileTypes: true })) {
26510
+ if (fs71.existsSync(AGENTS)) {
26511
+ for (const e of fs71.readdirSync(AGENTS, { withFileTypes: true })) {
26710
26512
  if (e.isDirectory() && !e.name.startsWith(".") && !named2.has(e.name)) out.push(`agents/${e.name}/`);
26711
26513
  }
26712
26514
  }
@@ -26724,7 +26526,7 @@ init_session();
26724
26526
  init_io();
26725
26527
  init_paths();
26726
26528
  import { createHash as createHash4 } from "node:crypto";
26727
- import fs74 from "node:fs";
26529
+ import fs72 from "node:fs";
26728
26530
  import path73 from "node:path";
26729
26531
  var SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
26730
26532
  var CAPTURED = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
@@ -26826,8 +26628,8 @@ This template has no preview.`);
26826
26628
  const bytes3 = Buffer.from(await (await ask(`/${at}/previews/${route(p.path)}`, {}, WITHOUT)).arrayBuffer());
26827
26629
  const file = path73.basename(p.path);
26828
26630
  const out = path73.join(opts.to, file.startsWith(`${name}-`) ? file : `${name}-${file}`);
26829
- fs74.mkdirSync(opts.to, { recursive: true });
26830
- fs74.writeFileSync(out, bytes3);
26631
+ fs72.mkdirSync(opts.to, { recursive: true });
26632
+ fs72.writeFileSync(out, bytes3);
26831
26633
  written.push(out);
26832
26634
  say(out);
26833
26635
  }
@@ -26848,7 +26650,7 @@ async function templateGet(name, opts, program2) {
26848
26650
  }
26849
26651
  fetched.push({ out, bytes: bytes3 });
26850
26652
  }
26851
- const taken = fetched.filter((f) => fs74.existsSync(f.out)).map((f) => path73.relative(to, f.out));
26653
+ const taken = fetched.filter((f) => fs72.existsSync(f.out)).map((f) => path73.relative(to, f.out));
26852
26654
  if (taken.length > 0) {
26853
26655
  refuse2({
26854
26656
  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`,
@@ -26856,8 +26658,8 @@ async function templateGet(name, opts, program2) {
26856
26658
  });
26857
26659
  }
26858
26660
  for (const f of fetched) {
26859
- fs74.mkdirSync(path73.dirname(f.out), { recursive: true });
26860
- fs74.writeFileSync(f.out, f.bytes);
26661
+ fs72.mkdirSync(path73.dirname(f.out), { recursive: true });
26662
+ fs72.writeFileSync(f.out, f.bytes);
26861
26663
  }
26862
26664
  say(`\u5DF2\u53D6\u56DE ${name}\uFF1A${fetched.length} \u4E2A\u6587\u4EF6\uFF0C\u4F4D\u4E8E ${to}
26863
26665
  Fetched ${name}: ${fetched.length} files into ${to}`);
@@ -26876,8 +26678,8 @@ async function templateSource(name, opts) {
26876
26678
  continue;
26877
26679
  }
26878
26680
  const out = path73.join(opts.to, `${name}-${path73.basename(e.path)}`);
26879
- fs74.mkdirSync(opts.to, { recursive: true });
26880
- fs74.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
26681
+ fs72.mkdirSync(opts.to, { recursive: true });
26682
+ fs72.writeFileSync(out, Buffer.from(await (await ask(`/${at}/evidence/${route(e.path)}`)).arrayBuffer()));
26881
26683
  written.push(out);
26882
26684
  say(out);
26883
26685
  }
@@ -26886,13 +26688,13 @@ async function templateSource(name, opts) {
26886
26688
  async function templatePublish(dir, opts) {
26887
26689
  const root = path73.resolve(dir);
26888
26690
  const manifest = path73.join(root, "template.md");
26889
- 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.");
26890
- const meta = readTemplateMeta(fs74.readFileSync(manifest, "utf8"));
26691
+ if (!fs72.existsSync(manifest)) invalid("\u6587\u4EF6\u5939\u9876\u5C42\u7F3A\u5C11 template.md\u3002", "template.md is missing from the folder\u2019s top level.");
26692
+ const meta = readTemplateMeta(fs72.readFileSync(manifest, "utf8"));
26891
26693
  const files2 = walk2(root);
26892
- const total = files2.reduce((sum, f) => sum + fs74.statSync(path73.join(root, f)).size, 0);
26694
+ const total = files2.reduce((sum, f) => sum + fs72.statSync(path73.join(root, f)).size, 0);
26893
26695
  if (total > MAX_BYTES3) invalid("\u6A21\u677F\u6587\u4EF6\u5408\u8BA1\u8D85\u8FC7 50 MB\u3002", "the template\u2019s files exceed 50 MB.");
26894
26696
  for (const extra of [...opts.preview, ...opts.evidence]) {
26895
- if (!fs74.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
26697
+ if (!fs72.existsSync(extra)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${extra}\u3002`, `${extra} does not exist.`);
26896
26698
  if (path73.resolve(extra).startsWith(root + path73.sep)) {
26897
26699
  invalid(
26898
26700
  `${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`,
@@ -26905,7 +26707,7 @@ async function templatePublish(dir, opts) {
26905
26707
  }
26906
26708
  const form = new FormData();
26907
26709
  form.set("meta", JSON.stringify({ ...meta, replace: !!opts.replace }));
26908
- const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs74.readFileSync(file)]), path73.basename(name));
26710
+ const add = (kind, name, file) => form.append(`${kind}:${name}`, new Blob([fs72.readFileSync(file)]), path73.basename(name));
26909
26711
  for (const f of files2) add("files", f, path73.join(root, f));
26910
26712
  for (const f of opts.preview) add("previews", path73.basename(f), f);
26911
26713
  for (const f of opts.evidence) add("evidence", path73.basename(f), f);
@@ -26925,10 +26727,10 @@ Deleted ${name} from the catalog.`);
26925
26727
  }
26926
26728
  async function templateShowcase(name, dir) {
26927
26729
  const file = path73.join(dir, "showcase.json");
26928
- if (!fs74.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
26730
+ if (!fs72.existsSync(file)) invalid(`${dir} \u4E2D\u7F3A\u5C11 showcase.json\u3002`, `showcase.json is missing from ${dir}.`);
26929
26731
  let meta;
26930
26732
  try {
26931
- meta = JSON.parse(fs74.readFileSync(file, "utf8"));
26733
+ meta = JSON.parse(fs72.readFileSync(file, "utf8"));
26932
26734
  } catch {
26933
26735
  return invalid("showcase.json \u4E0D\u662F\u6709\u6548\u7684 JSON\u3002", "showcase.json is not valid JSON.");
26934
26736
  }
@@ -26941,8 +26743,8 @@ async function templateShowcase(name, dir) {
26941
26743
  form.set("meta", JSON.stringify(meta));
26942
26744
  for (const p of new Set(named2)) {
26943
26745
  const at = path73.join(dir, p);
26944
- if (!fs74.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
26945
- form.append(`media:${p}`, new Blob([fs74.readFileSync(at)]), p);
26746
+ if (!fs72.existsSync(at)) invalid(`\u627E\u4E0D\u5230\u6587\u4EF6 ${at}\u3002`, `${at} does not exist.`);
26747
+ form.append(`media:${p}`, new Blob([fs72.readFileSync(at)]), p);
26946
26748
  }
26947
26749
  const done2 = await asJson(await ask(`/${encodeURIComponent(name)}/showcase`, { method: "PUT", body: form }));
26948
26750
  say(`\u5DF2\u66F4\u65B0 ${name} \u7684\u5C55\u793A\u8D44\u6599\uFF1A${done2.files} \u4E2A\u6587\u4EF6\u3002
@@ -26951,7 +26753,7 @@ Updated ${name}'s showcase: ${done2.files} files.`);
26951
26753
  }
26952
26754
  function walk2(root, sub = "") {
26953
26755
  const out = [];
26954
- for (const entry of fs74.readdirSync(path73.join(root, sub), { withFileTypes: true })) {
26756
+ for (const entry of fs72.readdirSync(path73.join(root, sub), { withFileTypes: true })) {
26955
26757
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
26956
26758
  const rel2 = sub ? `${sub}/${entry.name}` : entry.name;
26957
26759
  if (entry.isDirectory()) out.push(...walk2(root, rel2));
@@ -27092,12 +26894,6 @@ init_io();
27092
26894
  init_paths();
27093
26895
  init_workflows();
27094
26896
  init_workflow_cards();
27095
- init_cadence();
27096
- init_scheduled();
27097
- init_start();
27098
- var cadenceLine = (one) => one.off ? "off" : isAuto(one.cadence) ? "auto" : `every ${one.cadence}`;
27099
- var WAITS = { tooSoon: "", retrying: "retrying after a failed run", nothingNew: "nothing new yet", building: "waits for the build to finish" };
27100
- var nextLine = (due) => !due ? "" : ` \xB7 next after ${formatStamp(due.next)}${due.wait && WAITS[due.wait] ? ` (${WAITS[due.wait]})` : ""}`;
27101
26897
  var found = (id) => {
27102
26898
  const flow = workflowById(id);
27103
26899
  if (!flow) {
@@ -27121,11 +26917,7 @@ function cmdWorkflowList() {
27121
26917
  if (description !== flow.name) say(` ${description}`);
27122
26918
  say(` previews ${previewNames(previewMembers(flow)) || "(none)"}`);
27123
26919
  say(` land ${names3(flow.land) || "(none) \u2014 the last approval archives the card"}`);
27124
- for (const one of scheduledMembers(flow)) {
27125
- const due = scheduledNext(flow.id, one);
27126
- const ran = one.lastRun ? `last run ${one.lastRun}` : "never run";
27127
- say(` scheduled ${one.agent} ${cadenceLine(one)} \xB7 ${ran}${nextLine(due)}`);
27128
- }
26920
+ for (const agent of listedScheduled(flow)) say(` scheduled ${agent}`);
27129
26921
  for (const problem of workflowProblems(flow.id)) say(` ! ${problem}`);
27130
26922
  }
27131
26923
  const fileProblems = workflowFileProblems();
@@ -27134,7 +26926,7 @@ function cmdWorkflowList() {
27134
26926
  workflows: rows.map((flow) => ({
27135
26927
  ...flow,
27136
26928
  previews: previewMembers(flow).map((h) => ({ ...h, after: agentAfter(h.agent) })),
27137
- scheduled: scheduledMembers(flow),
26929
+ scheduled: listedScheduled(flow),
27138
26930
  description: workflowPurpose(flow),
27139
26931
  problems: workflowProblems(flow.id)
27140
26932
  })),
@@ -27208,48 +27000,6 @@ function cmdWorkflowAgent(id, flags) {
27208
27000
  say(`${flow.name}: ${changes.join(", ")}`);
27209
27001
  return { id: flow.id, changes };
27210
27002
  }
27211
- async function cmdWorkflowSchedule(id, flags) {
27212
- const flow = found(id);
27213
- const changes = [];
27214
- if (flags.on !== void 0) {
27215
- done(switchWorkflowScheduled(flow.id, flags.on, true));
27216
- changes.push(`${flags.on.trim()} on`);
27217
- }
27218
- if (flags.off !== void 0) {
27219
- done(switchWorkflowScheduled(flow.id, flags.off, false));
27220
- changes.push(`${flags.off.trim()} off`);
27221
- }
27222
- const who = (flags.on ?? flags.off ?? flags.run ?? "").trim();
27223
- if (flags.cadence !== void 0) {
27224
- if (!who) die("--cadence says how often one agent runs, so name it: --on <agent> --cadence 1d");
27225
- done(setWorkflowScheduledCadence(flow.id, who, flags.cadence));
27226
- changes.push(`${who}: every ${flags.cadence.trim()}`);
27227
- }
27228
- if (flags.extra !== void 0) {
27229
- if (!who) die('--extra says what one agent is asked for here, so name it: --on <agent> --extra "\u2026"');
27230
- done(setWorkflowScheduledExtra(flow.id, who, flags.extra));
27231
- changes.push(`${who}: extra requirements`);
27232
- }
27233
- if (flags.run !== void 0) {
27234
- const started = await startRun(scheduledRequest({ workflow: flow.id, agent: flags.run.trim() }));
27235
- if ("error" in started) die(started.error);
27236
- say(`${flow.name}: started ${flags.run.trim()} \u2014 run ${started.run.sessionId.slice(0, 8)}`);
27237
- return { id: flow.id, changes, sessionId: started.run.sessionId };
27238
- }
27239
- if (!changes.length) {
27240
- const rows = scheduledMembers(workflowById(flow.id) ?? flow);
27241
- say(`${flow.name} \xB7 scheduled`);
27242
- if (!rows.length) say(" (none) \u2014 an agent no workflow lists can be switched on here");
27243
- for (const one of rows) {
27244
- const due = scheduledNext(flow.id, one);
27245
- const ran = one.lastRun ? `last run ${one.lastRun}` : "never run";
27246
- say(` ${one.agent} ${cadenceLine(one)} \xB7 ${ran}${nextLine(due)}`);
27247
- }
27248
- return { id: flow.id, scheduled: rows };
27249
- }
27250
- say(`${flow.name} \xB7 scheduled: ${changes.join(", ")}`);
27251
- return { id: flow.id, changes };
27252
- }
27253
27003
 
27254
27004
  // ../cli/src/commands/run.ts
27255
27005
  init_answers();
@@ -27288,7 +27038,7 @@ init_opencode();
27288
27038
  init_locate();
27289
27039
  init_store();
27290
27040
  import { spawnSync as spawnSync5 } from "node:child_process";
27291
- import path78 from "node:path";
27041
+ import path79 from "node:path";
27292
27042
  var AGENTS2 = {
27293
27043
  claude: "claude-code",
27294
27044
  codex: "codex",
@@ -27303,10 +27053,10 @@ var AGENTS2 = {
27303
27053
  var RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
27304
27054
  function agentOf2(args) {
27305
27055
  const words = args.trim().split(/\s+/);
27306
- const first = path78.basename(words[0] ?? "");
27056
+ const first = path79.basename(words[0] ?? "");
27307
27057
  const script = RUNTIMES.has(first) ? words[1] ?? "" : words[0] ?? "";
27308
27058
  if (script.includes("/@anthropic-ai/claude-code/")) return "claude-code";
27309
- return AGENTS2[path78.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
27059
+ return AGENTS2[path79.basename(script).replace(/\.(m?js|cjs|exe)$/, "")];
27310
27060
  }
27311
27061
  function processChain() {
27312
27062
  if (process.platform === "win32") return [];
@@ -27369,8 +27119,8 @@ init_tasks();
27369
27119
  init_read();
27370
27120
  init_rules();
27371
27121
  init_board();
27372
- import fs80 from "node:fs";
27373
- import path79 from "node:path";
27122
+ import fs79 from "node:fs";
27123
+ import path80 from "node:path";
27374
27124
  var FOLLOW_MS = 400;
27375
27125
  async function cmdStartRun(action, args, opts, program2 = "akb") {
27376
27126
  const { req, follow: follow2, print } = readRequest(action, args, opts);
@@ -27532,7 +27282,7 @@ function readRequest(action, args, opts) {
27532
27282
  if (!opts.task?.length) die("name the archived Cloud tasks to review: --task <task:card...>", { kind: "run-refused", action });
27533
27283
  return { req: { action, tasks: opts.task }, follow: follow2, print };
27534
27284
  }
27535
- if (action === "review-dismissals") return { req: { action, ...opts.all ? { allReasons: true } : {} }, follow: follow2, print };
27285
+ if (action === "review-dismissals") return { req: { action }, follow: follow2, print };
27536
27286
  if (action === "reflect") {
27537
27287
  const cards = (opts.cards ?? []).map(Number);
27538
27288
  if (!cards.length || !cards.every((n) => Number.isInteger(n) && n > 0)) {
@@ -27655,7 +27405,7 @@ async function cmdSubStart(words, opts) {
27655
27405
  let prompt = words.join(" ");
27656
27406
  if (opts.file) {
27657
27407
  try {
27658
- prompt = fs80.readFileSync(path79.resolve(opts.file), "utf8");
27408
+ prompt = fs79.readFileSync(path80.resolve(opts.file), "utf8");
27659
27409
  } catch {
27660
27410
  die(`--file: cannot read ${opts.file}`, { kind: "run-refused" });
27661
27411
  }
@@ -27937,7 +27687,7 @@ async function cmdWatch(sessionId) {
27937
27687
  // ../cli/src/commands/triage.ts
27938
27688
  init_scheduled();
27939
27689
  init_workflows();
27940
- import fs82 from "node:fs";
27690
+ import fs81 from "node:fs";
27941
27691
 
27942
27692
  // ../cli/src/lib/signals/index.ts
27943
27693
  init_cards();
@@ -27947,16 +27697,16 @@ init_yaml();
27947
27697
  // ../cli/src/lib/signals/config.ts
27948
27698
  init_settings2();
27949
27699
  init_paths();
27950
- import fs81 from "node:fs";
27951
- import path80 from "node:path";
27700
+ import fs80 from "node:fs";
27701
+ import path81 from "node:path";
27952
27702
  var TOKEN_KEY = "TRIAGE_ENDPOINT_TOKEN";
27953
27703
  var ENDPOINT_SETTING = "Triage endpoint";
27954
27704
  var LINE2 = /^- \*\*Triage endpoint\*\*\s*[—-]\s*(.+)$/m;
27955
- var boardRel3 = (file) => rel(file).split(path80.sep).join("/");
27705
+ var boardRel3 = (file) => rel(file).split(path81.sep).join("/");
27956
27706
  function signalEndpoint() {
27957
27707
  let text5;
27958
27708
  try {
27959
- text5 = fs81.readFileSync(CONFIG, "utf8");
27709
+ text5 = fs80.readFileSync(CONFIG, "utf8");
27960
27710
  } catch {
27961
27711
  return "";
27962
27712
  }
@@ -28132,7 +27882,7 @@ async function cmdTriageAdd(opts) {
28132
27882
  let body = opts.text;
28133
27883
  if (opts.file !== void 0) {
28134
27884
  try {
28135
- body = fs82.readFileSync(opts.file, "utf8");
27885
+ body = fs81.readFileSync(opts.file, "utf8");
28136
27886
  } catch {
28137
27887
  die(`can't read ${opts.file} \u2014 write the item to a file, then pass its path`, { kind: "needs-input" });
28138
27888
  }
@@ -28485,7 +28235,8 @@ function declareRuns(program2, cli) {
28485
28235
  }
28486
28236
  const workflow = withShared(program2.command("workflow")).summary("the workflows this board runs, and the agents each one lists").description(
28487
28237
  "Every card runs through one workflow: previews approved by you, each waiting on the approvals its `after` names, then the agents that land the work, after which the card is archived; with none, the last approval archives it. A card chooses its previews when first planned. The built-ins can have their agents switched, and can be copied but not renamed or deleted. A card names its workflow in its own frontmatter (`akb raw create --workflow`), and a delivery freezes the one it started with."
28488
- ).action(async function() {
28238
+ ).allowExcessArguments().allowUnknownOption().action(async function() {
28239
+ if (this.args.length) this.error(`unknown command '${this.args[0]}'`);
28489
28240
  await workflowList(this, cli);
28490
28241
  });
28491
28242
  const flowWord = (name) => withShared(workflow.command(name));
@@ -28517,12 +28268,6 @@ function declareRuns(program2, cli) {
28517
28268
  const flags = this.opts();
28518
28269
  await onBoard2(this, cli, () => cmdWorkflowAgent(id, flags));
28519
28270
  });
28520
- flowWord("schedule").argument("<id>", "the workflow, from `workflow list`").option("--on <agent>", "switch one of this workflow's scheduled agents on").option("--off <agent>", "switch one off; it stays in the workflow").option("--cadence <cadence>", "how often the --on agent runs, e.g. 6h, 1d, 1d at 09:30, or auto (the default)").option("--extra <text>", "what the --on or --off agent is asked for here, on top of its own instructions").option("--run <agent>", "start one run of it now, due or not").summary("switch a workflow's scheduled agents, set how often each runs, or run one now").description(
28521
- "With no change asked for, it lists them with their cadence and last run. A scheduled agent is listed under its workflow's `scheduled` and runs by itself on no card \u2014 in `auto`, when what its `akb.reads` names has something new; what it changes is committed and landed like a build."
28522
- ).action(async function(id) {
28523
- const flags = this.opts();
28524
- await onBoard2(this, cli, () => cmdWorkflowSchedule(id, flags));
28525
- });
28526
28271
  withShared(program2.command("guide")).argument("[topic]", "one flow in full; left off, every flow is listed one line each").summary("the flows the board works by, shipped with this command").description(
28527
28272
  "A printed flow already carries the flows its action is done by, so this is for the rest: how the board works at all, the module map, updating, the local UI. `guide board` is the card format, the layout and the memory set."
28528
28273
  ).action(async function(...vals) {
@@ -28701,8 +28446,8 @@ function declareAgent(program2, cli) {
28701
28446
  }
28702
28447
 
28703
28448
  // ../cli/src/lib/cli/setup.ts
28704
- import fs86 from "node:fs";
28705
- import path84 from "node:path";
28449
+ import fs85 from "node:fs";
28450
+ import path85 from "node:path";
28706
28451
 
28707
28452
  // ../cli/src/commands/install.ts
28708
28453
  init_local();
@@ -28714,14 +28459,14 @@ init_io();
28714
28459
  init_paths();
28715
28460
  init_memory();
28716
28461
  init_yaml2();
28717
- import fs85 from "node:fs";
28718
- import path83 from "node:path";
28462
+ import fs84 from "node:fs";
28463
+ import path84 from "node:path";
28719
28464
  import { spawnSync as spawnSync8 } from "node:child_process";
28720
28465
 
28721
28466
  // ../cli/src/lib/skill/install.ts
28722
28467
  import { spawnSync as spawnSync7 } from "node:child_process";
28723
- import fs84 from "node:fs";
28724
- import path82 from "node:path";
28468
+ import fs83 from "node:fs";
28469
+ import path83 from "node:path";
28725
28470
  import { fileURLToPath as fileURLToPath2 } from "node:url";
28726
28471
 
28727
28472
  // ../skill/SKILL.md
@@ -28735,8 +28480,8 @@ init_version();
28735
28480
  init_version();
28736
28481
  init_project();
28737
28482
  import { spawnSync as spawnSync6 } from "node:child_process";
28738
- import fs83 from "node:fs";
28739
- import path81 from "node:path";
28483
+ import fs82 from "node:fs";
28484
+ import path82 from "node:path";
28740
28485
  var MARKER3 = "# ai4kanban commit guard";
28741
28486
  var MARKER_LINE = new RegExp(`^${MARKER3}\\b`, "m");
28742
28487
  var CHECK = [
@@ -28770,7 +28515,7 @@ function hookScript(sessions) {
28770
28515
  }
28771
28516
  var read3 = (file) => {
28772
28517
  try {
28773
- return fs83.readFileSync(file, "utf8");
28518
+ return fs82.readFileSync(file, "utf8");
28774
28519
  } catch {
28775
28520
  return null;
28776
28521
  }
@@ -28782,7 +28527,7 @@ function hooksDir(root) {
28782
28527
  windowsHide: true
28783
28528
  });
28784
28529
  if (out.error || out.status !== 0 || !out.stdout.trim()) return null;
28785
- return path81.resolve(root, out.stdout.trim());
28530
+ return path82.resolve(root, out.stdout.trim());
28786
28531
  }
28787
28532
  function hasOwnHooksPath(root) {
28788
28533
  const out = spawnSync6("git", ["config", "--get", "core.hooksPath"], {
@@ -28792,18 +28537,18 @@ function hasOwnHooksPath(root) {
28792
28537
  });
28793
28538
  return !out.error && out.status === 0 && !!out.stdout.trim();
28794
28539
  }
28795
- var sessionsPath = (root) => path81.join(projectStateDir(path81.join(root, "docs", "kanban"), root), SESSIONS_FILE);
28540
+ var sessionsPath = (root) => path82.join(projectStateDir(path82.join(root, "docs", "kanban"), root), SESSIONS_FILE);
28796
28541
  function readCommitHook(root) {
28797
28542
  const dir = hooksDir(root);
28798
28543
  if (!dir) return { state: "no-git" };
28799
- const where = shown(root, path81.join(dir, "pre-commit"));
28544
+ const where = shown(root, path82.join(dir, "pre-commit"));
28800
28545
  const line = guardLine(sessionsPath(root));
28801
28546
  if (hasOwnHooksPath(root)) return { state: "hooks-path", path: where, line };
28802
- const text5 = read3(path81.join(dir, "pre-commit"));
28547
+ const text5 = read3(path82.join(dir, "pre-commit"));
28803
28548
  if (text5 === null) return { state: "absent", path: where };
28804
28549
  return MARKER_LINE.test(text5) ? { state: "ours", path: where } : { state: "foreign", path: where, line };
28805
28550
  }
28806
- var shown = (root, p) => (path81.relative(root, p) || p).split(path81.sep).join("/");
28551
+ var shown = (root, p) => (path82.relative(root, p) || p).split(path82.sep).join("/");
28807
28552
  function installCommitHook(root) {
28808
28553
  const hook = readCommitHook(root);
28809
28554
  if (hook.state === "no-git") return {};
@@ -28814,11 +28559,11 @@ function installCommitHook(root) {
28814
28559
  ${hook.line}`
28815
28560
  };
28816
28561
  }
28817
- const file = path81.join(root, hook.path);
28562
+ const file = path82.join(root, hook.path);
28818
28563
  try {
28819
- fs83.mkdirSync(path81.dirname(file), { recursive: true });
28820
- fs83.writeFileSync(file, hookScript(sessionsPath(root)), { mode: 493 });
28821
- fs83.chmodSync(file, 493);
28564
+ fs82.mkdirSync(path82.dirname(file), { recursive: true });
28565
+ fs82.writeFileSync(file, hookScript(sessionsPath(root)), { mode: 493 });
28566
+ fs82.chmodSync(file, 493);
28822
28567
  } catch (e) {
28823
28568
  return { note: `${hook.path} could not be written \u2014 ${e instanceof Error ? e.message : String(e)}` };
28824
28569
  }
@@ -28834,9 +28579,9 @@ function sayCommitHook(state) {
28834
28579
 
28835
28580
  // ../cli/src/lib/skill/install.ts
28836
28581
  var TARGETS = [
28837
- { rel: path82.join(".claude", "skills", "kanban"), agent: "Claude Code" },
28582
+ { rel: path83.join(".claude", "skills", "kanban"), agent: "Claude Code" },
28838
28583
  {
28839
- rel: path82.join(".agents", "skills", "kanban"),
28584
+ rel: path83.join(".agents", "skills", "kanban"),
28840
28585
  agent: "Codex, Cursor, OpenCode, Kimi Code, DeepSeek Harness, ZCode, Grok Build"
28841
28586
  }
28842
28587
  ];
@@ -28850,23 +28595,23 @@ function rulesPath() {
28850
28595
  }
28851
28596
  function read4(file) {
28852
28597
  try {
28853
- return fs84.readFileSync(file, "utf8");
28598
+ return fs83.readFileSync(file, "utf8");
28854
28599
  } catch {
28855
28600
  return null;
28856
28601
  }
28857
28602
  }
28858
28603
  function statOf(p) {
28859
28604
  try {
28860
- return fs84.lstatSync(p);
28605
+ return fs83.lstatSync(p);
28861
28606
  } catch {
28862
28607
  return null;
28863
28608
  }
28864
28609
  }
28865
28610
  function versionIn2(dir) {
28866
- const note = read4(path82.join(dir, "SKILL.md"));
28611
+ const note = read4(path83.join(dir, "SKILL.md"));
28867
28612
  const stampedNote = note?.match(NOTE_STAMP);
28868
28613
  if (stampedNote) return stampedNote[1];
28869
- const src = read4(path82.join(dir, "kanban.mjs"));
28614
+ const src = read4(path83.join(dir, "kanban.mjs"));
28870
28615
  if (!src) return null;
28871
28616
  const stamped = src.slice(0, 400).match(BUILT_STAMP);
28872
28617
  if (stamped) return stamped[1];
@@ -28890,7 +28635,7 @@ function folderState(dir) {
28890
28635
  const st = statOf(dir);
28891
28636
  if (!st) return { state: "absent", version: null };
28892
28637
  if (st.isSymbolicLink()) return { state: "linked", version: versionIn2(dir) };
28893
- if (!fs84.existsSync(path82.join(dir, "SKILL.md"))) return { state: "absent", version: null };
28638
+ if (!fs83.existsSync(path83.join(dir, "SKILL.md"))) return { state: "absent", version: null };
28894
28639
  const version = versionIn2(dir);
28895
28640
  if (!version) return { state: "unknown", version: null };
28896
28641
  return { state: isOlder(version, SKILL_VERSION) ? "stale" : "current", version };
@@ -28898,9 +28643,9 @@ function folderState(dir) {
28898
28643
  function readSkillState(root) {
28899
28644
  const dir = rootOr(root);
28900
28645
  const folders2 = TARGETS.map((target) => ({
28901
- path: target.rel.split(path82.sep).join("/"),
28646
+ path: target.rel.split(path83.sep).join("/"),
28902
28647
  agent: target.agent,
28903
- ...folderState(path82.join(dir, target.rel))
28648
+ ...folderState(path83.join(dir, target.rel))
28904
28649
  }));
28905
28650
  return {
28906
28651
  version: SKILL_VERSION,
@@ -28910,9 +28655,9 @@ function readSkillState(root) {
28910
28655
  };
28911
28656
  }
28912
28657
  function writeFolder(dir) {
28913
- fs84.rmSync(dir, { recursive: true, force: true });
28914
- fs84.mkdirSync(dir, { recursive: true });
28915
- fs84.writeFileSync(path82.join(dir, "SKILL.md"), `${SKILL_default.trimEnd()}
28658
+ fs83.rmSync(dir, { recursive: true, force: true });
28659
+ fs83.mkdirSync(dir, { recursive: true });
28660
+ fs83.writeFileSync(path83.join(dir, "SKILL.md"), `${SKILL_default.trimEnd()}
28916
28661
 
28917
28662
  <!-- ai4kanban ${SKILL_VERSION} -->
28918
28663
  `);
@@ -28927,8 +28672,8 @@ function installSkill(root, only) {
28927
28672
  const wrote = [];
28928
28673
  const skipped = [];
28929
28674
  for (const target of TARGETS) {
28930
- const dest = path82.join(dir, target.rel);
28931
- const shown2 = target.rel.split(path82.sep).join("/");
28675
+ const dest = path83.join(dir, target.rel);
28676
+ const shown2 = target.rel.split(path83.sep).join("/");
28932
28677
  const { state: state2 } = folderState(dest);
28933
28678
  if (state2 === "linked") {
28934
28679
  skipped.push({ path: shown2, agent: target.agent, why: "a symlink into a source checkout \u2014 left untouched" });
@@ -28997,10 +28742,10 @@ var Report = class {
28997
28742
  for (const line of this.notes) say(` ! ${line}`);
28998
28743
  }
28999
28744
  };
29000
- var read5 = (file) => fs85.existsSync(file) ? fs85.readFileSync(file, "utf8") : null;
28745
+ var read5 = (file) => fs84.existsSync(file) ? fs84.readFileSync(file, "utf8") : null;
29001
28746
  function statOf2(p) {
29002
28747
  try {
29003
- return fs85.lstatSync(p);
28748
+ return fs84.lstatSync(p);
29004
28749
  } catch {
29005
28750
  return null;
29006
28751
  }
@@ -29011,10 +28756,10 @@ async function boardMove(root, args, board2) {
29011
28756
  if (code !== 0) throw new BoardError(`\`board ${args.join(" ")}\` failed \u2014 nothing else was changed`, { kind: "board-move-failed" });
29012
28757
  }
29013
28758
  function boardAbove(root) {
29014
- let at = path83.dirname(path83.resolve(root));
28759
+ let at = path84.dirname(path84.resolve(root));
29015
28760
  for (; ; ) {
29016
- if (fs85.existsSync(path83.join(at, "docs", "kanban"))) return at;
29017
- const up = path83.dirname(at);
28761
+ if (fs84.existsSync(path84.join(at, "docs", "kanban"))) return at;
28762
+ const up = path84.dirname(at);
29018
28763
  if (up === at) return null;
29019
28764
  at = up;
29020
28765
  }
@@ -29022,10 +28767,10 @@ function boardAbove(root) {
29022
28767
  async function cmdInstall(ctx) {
29023
28768
  const report3 = new Report();
29024
28769
  const board2 = ctx.board ?? null;
29025
- const where = board2 ? path83.relative(ctx.dir, board2) || board2 : "docs/kanban";
28770
+ const where = board2 ? path84.relative(ctx.dir, board2) || board2 : "docs/kanban";
29026
28771
  say(`wildcrane ${SKILL_VERSION} \u2014 installing into ${ctx.dir}`);
29027
28772
  say("");
29028
- const above = board2 || fs85.existsSync(path83.join(ctx.dir, "docs", "kanban")) ? null : boardAbove(ctx.dir);
28773
+ const above = board2 || fs84.existsSync(path84.join(ctx.dir, "docs", "kanban")) ? null : boardAbove(ctx.dir);
29029
28774
  if (above) {
29030
28775
  report3.notes.push(`there is already a board at ${above} \u2014 this makes a second one, and commands run here will find this one`);
29031
28776
  }
@@ -29047,7 +28792,7 @@ async function cmdInstall(ctx) {
29047
28792
  say("board UI (Configuration \u2192 Agent setup), or here:");
29048
28793
  say("");
29049
28794
  say(` ${ctx.program} skill`);
29050
- return { installed: ctx.dir, board: board2 ?? path83.join(ctx.dir, "docs", "kanban") };
28795
+ return { installed: ctx.dir, board: board2 ?? path84.join(ctx.dir, "docs", "kanban") };
29051
28796
  }
29052
28797
  async function cmdSkill(ctx, mode) {
29053
28798
  const report3 = new Report();
@@ -29088,7 +28833,7 @@ function placeSkill(ctx, mode, report3) {
29088
28833
  const before = readSkillState(ctx.dir).folders;
29089
28834
  for (const folder of before) {
29090
28835
  if (folder.state !== "absent" && folder.state !== "linked") {
29091
- rescueSkillConfig(ctx.dir, path83.join(ctx.dir, folder.path), report3);
28836
+ rescueSkillConfig(ctx.dir, path84.join(ctx.dir, folder.path), report3);
29092
28837
  }
29093
28838
  }
29094
28839
  const result = installSkill(ctx.dir, mode === "update" ? "present" : void 0);
@@ -29111,20 +28856,20 @@ function placeSkill(ctx, mode, report3) {
29111
28856
  return { result, from: before.map((f) => f.version).find(Boolean) || null };
29112
28857
  }
29113
28858
  function rescueSkillConfig(root, skillDir, report3) {
29114
- const old = read5(path83.join(skillDir, "config.md"));
28859
+ const old = read5(path84.join(skillDir, "config.md"));
29115
28860
  if (!old || old.includes("{{")) return;
29116
- const boardConfig = path83.join(root, "docs", "kanban", "config.md");
28861
+ const boardConfig = path84.join(root, "docs", "kanban", "config.md");
29117
28862
  const current = read5(boardConfig);
29118
28863
  if (current === null) {
29119
- fs85.mkdirSync(path83.dirname(boardConfig), { recursive: true });
29120
- fs85.writeFileSync(boardConfig, old);
28864
+ fs84.mkdirSync(path84.dirname(boardConfig), { recursive: true });
28865
+ fs84.writeFileSync(boardConfig, old);
29121
28866
  report3.did.push("moved your filled-in config.md out of the skill folder to docs/kanban/config.md");
29122
28867
  return;
29123
28868
  }
29124
28869
  if (current === old) return;
29125
- const aside = path83.join(root, "docs", "kanban", "config.from-skill.md");
29126
- if (fs85.existsSync(aside)) return;
29127
- fs85.writeFileSync(aside, old);
28870
+ const aside = path84.join(root, "docs", "kanban", "config.from-skill.md");
28871
+ if (fs84.existsSync(aside)) return;
28872
+ fs84.writeFileSync(aside, old);
29128
28873
  report3.notes.push(
29129
28874
  "the old skill folder held a different filled-in config.md \u2014 saved as docs/kanban/config.from-skill.md; fold anything worth keeping into docs/kanban/config.md and delete it"
29130
28875
  );
@@ -29177,8 +28922,8 @@ async function cmdUpdate2(ctx) {
29177
28922
  return { updated: ctx.dir, from, to: SKILL_VERSION };
29178
28923
  }
29179
28924
  async function repairBoard(root, report3) {
29180
- const board2 = path83.join(root, "docs", "kanban");
29181
- if (!fs85.existsSync(board2)) {
28925
+ const board2 = path84.join(root, "docs", "kanban");
28926
+ if (!fs84.existsSync(board2)) {
29182
28927
  report3.notes.push("no docs/kanban/ here \u2014 run `akb install` to scaffold the board");
29183
28928
  return;
29184
28929
  }
@@ -29208,20 +28953,20 @@ function moveRuntimes(root, board2, report3) {
29208
28953
  }
29209
28954
  }
29210
28955
  function moveLegacyMemory(board2, report3) {
29211
- const memory = path83.join(board2, "memory");
28956
+ const memory = path84.join(board2, "memory");
29212
28957
  const moved = [];
29213
28958
  for (const name of MEMORY_FILES) {
29214
- const from = path83.join(board2, name);
28959
+ const from = path84.join(board2, name);
29215
28960
  const st = statOf2(from);
29216
28961
  if (!st || !st.isFile()) continue;
29217
- fs85.mkdirSync(memory, { recursive: true });
29218
- const to = path83.join(memory, name);
28962
+ fs84.mkdirSync(memory, { recursive: true });
28963
+ const to = path84.join(memory, name);
29219
28964
  const there = read5(to);
29220
28965
  if (there === null) {
29221
- fs85.renameSync(from, to);
28966
+ fs84.renameSync(from, to);
29222
28967
  moved.push(name);
29223
28968
  } else if (there === read5(from)) {
29224
- fs85.rmSync(from);
28969
+ fs84.rmSync(from);
29225
28970
  moved.push(name);
29226
28971
  } else {
29227
28972
  report3.notes.push(`both docs/kanban/${name} and docs/kanban/memory/${name} exist and differ \u2014 merge them by hand`);
@@ -29230,23 +28975,23 @@ function moveLegacyMemory(board2, report3) {
29230
28975
  if (moved.length) report3.did.push(`moved the memory set into docs/kanban/memory/: ${moved.join(", ")}`);
29231
28976
  }
29232
28977
  function dropRecordFile(board2) {
29233
- const file = path83.join(board2, "record.csv");
29234
- if (!fs85.existsSync(file)) return null;
29235
- fs85.rmSync(file);
28978
+ const file = path84.join(board2, "record.csv");
28979
+ if (!fs84.existsSync(file)) return null;
28980
+ fs84.rmSync(file);
29236
28981
  return "removed docs/kanban/record.csv (the planning scores it fed are gone)";
29237
28982
  }
29238
28983
  function dropModuleGoals(board2, report3) {
29239
- const memory = path83.join(board2, "memory");
29240
- if (!fs85.existsSync(memory)) return;
29241
- const root = read5(path83.join(memory, "goal.md"));
28984
+ const memory = path84.join(board2, "memory");
28985
+ if (!fs84.existsSync(memory)) return;
28986
+ const root = read5(path84.join(memory, "goal.md"));
29242
28987
  let dropped = 0;
29243
- for (const entry of fs85.readdirSync(memory, { withFileTypes: true })) {
28988
+ for (const entry of fs84.readdirSync(memory, { withFileTypes: true })) {
29244
28989
  if (!entry.isDirectory()) continue;
29245
- const file = path83.join(memory, entry.name, "goal.md");
28990
+ const file = path84.join(memory, entry.name, "goal.md");
29246
28991
  const text5 = read5(file);
29247
28992
  if (text5 === null) continue;
29248
28993
  if (text5 === root || text5.includes(UNFILLED) || !goalWords(text5)) {
29249
- fs85.rmSync(file);
28994
+ fs84.rmSync(file);
29250
28995
  dropped++;
29251
28996
  } else {
29252
28997
  report3.notes.push(
@@ -29268,10 +29013,10 @@ var PLAN_LEADS = {
29268
29013
  var GONE_LEADS = ["software-planner", "planner"];
29269
29014
  function planLeads(board2) {
29270
29015
  const leads = { ...PLAN_LEADS };
29271
- const root = path83.join(board2, "workflows");
29272
- for (const entry of fs85.existsSync(root) ? fs85.readdirSync(root, { withFileTypes: true }) : []) {
29016
+ const root = path84.join(board2, "workflows");
29017
+ for (const entry of fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []) {
29273
29018
  if (!entry.isDirectory()) continue;
29274
- const meta = splitFrontmatter(read5(path83.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
29019
+ const meta = splitFrontmatter(read5(path84.join(root, entry.name, "WORKFLOW.md")) ?? "").meta;
29275
29020
  const stages = meta === null ? void 0 : parseYamlBlock(meta).stages;
29276
29021
  const plan = stages && typeof stages === "object" && !Array.isArray(stages) ? stages.plan : void 0;
29277
29022
  const lead = plan && typeof plan === "object" && !Array.isArray(plan) && typeof plan.lead === "string" ? plan.lead.trim() : "";
@@ -29280,30 +29025,30 @@ function planLeads(board2) {
29280
29025
  return leads;
29281
29026
  }
29282
29027
  function movePlanRules(board2, report3) {
29283
- const rules = path83.join(board2, "rules");
29028
+ const rules = path84.join(board2, "rules");
29284
29029
  const moved = [];
29285
29030
  for (const [id, leads] of Object.entries(planLeads(board2))) {
29286
29031
  for (const lead of leads) {
29287
- const from = path83.join(rules, `${lead}.md`);
29032
+ const from = path84.join(rules, `${lead}.md`);
29288
29033
  const rule = read5(from)?.trim();
29289
29034
  if (!rule) continue;
29290
- const to = path83.join(rules, "workflows", `${id}.md`);
29035
+ const to = path84.join(rules, "workflows", `${id}.md`);
29291
29036
  const there = read5(to)?.trim() ?? "";
29292
29037
  if (!there.includes(rule)) {
29293
- fs85.mkdirSync(path83.dirname(to), { recursive: true });
29294
- fs85.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
29038
+ fs84.mkdirSync(path84.dirname(to), { recursive: true });
29039
+ fs84.writeFileSync(to, `${[there, rule].filter(Boolean).join("\n\n")}
29295
29040
  `);
29296
29041
  moved.push(`${lead} \u2192 ${id}`);
29297
29042
  }
29298
29043
  }
29299
29044
  }
29300
- for (const lead of GONE_LEADS) fs85.rmSync(path83.join(rules, `${lead}.md`), { force: true });
29045
+ for (const lead of GONE_LEADS) fs84.rmSync(path84.join(rules, `${lead}.md`), { force: true });
29301
29046
  if (moved.length) report3.did.push(`made each plan agent's rule its workflow's card rule (docs/kanban/rules/workflows/): ${moved.join(", ")}`);
29302
29047
  }
29303
29048
  function noteOldWorkflows(board2, report3) {
29304
29049
  const old = (dir, file, keys) => {
29305
- const root = path83.join(board2, dir);
29306
- return (fs85.existsSync(root) ? fs85.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read5(path83.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
29050
+ const root = path84.join(board2, dir);
29051
+ return (fs84.existsSync(root) ? fs84.readdirSync(root, { withFileTypes: true }) : []).filter((e) => e.isDirectory() && keys.test(splitFrontmatter(read5(path84.join(root, e.name, file)) ?? "").meta ?? "")).map((e) => `docs/kanban/${dir}/${e.name}/${file}`);
29307
29052
  };
29308
29053
  const files2 = [...old("workflows", "WORKFLOW.md", /^(stages|delivers):/m), ...old("agents", "AGENT.md", /^\s+(hook|lead):/m)];
29309
29054
  if (files2.length) {
@@ -29313,7 +29058,7 @@ function noteOldWorkflows(board2, report3) {
29313
29058
  }
29314
29059
  }
29315
29060
  function checkConfig(board2, report3) {
29316
- const current = read5(path83.join(board2, "config.md"));
29061
+ const current = read5(path84.join(board2, "config.md"));
29317
29062
  if (!current) return;
29318
29063
  const added = missingConfigKeys(current);
29319
29064
  if (added.length) {
@@ -29323,7 +29068,7 @@ function checkConfig(board2, report3) {
29323
29068
  }
29324
29069
  }
29325
29070
  function checkModules(board2, report3) {
29326
- const map2 = read5(path83.join(board2, "modules.md"));
29071
+ const map2 = read5(path84.join(board2, "modules.md"));
29327
29072
  if (map2 && map2.includes(UNFILLED)) {
29328
29073
  report3.notes.push(
29329
29074
  "docs/kanban/modules.md is still blank \u2014 write it from the repo (`akb guide module-map`)"
@@ -29377,13 +29122,13 @@ function declareSetup(program2, cli) {
29377
29122
  program2.version(SKILL_VERSION, "-v, --version", "print this version").addHelpText("beforeAll", spelled(cli.program));
29378
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");
29379
29124
  const where = (cmd) => {
29380
- const dir = path84.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
29381
- if (!fs86.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
29125
+ const dir = path85.resolve(String(cmd.optsWithGlobals().dir ?? cli.cwd));
29126
+ if (!fs85.existsSync(dir)) throw new BoardError(`no such folder: ${dir}`, { kind: "no-such-folder", dir });
29382
29127
  return dir;
29383
29128
  };
29384
29129
  const boardOf = (cmd) => {
29385
29130
  const named2 = cmd.optsWithGlobals().board;
29386
- return named2 ? path84.resolve(cli.cwd, named2) : null;
29131
+ return named2 ? path85.resolve(cli.cwd, named2) : null;
29387
29132
  };
29388
29133
  dirOption(program2.command("install")).option("--board <dir>", "put the board here instead of docs/kanban/ (relative to this folder)").summary("create the board in this project, and nothing else").description(
29389
29134
  "Installing writes the board and nothing outside docs/kanban/. Driving that board from a coding agent is a later extra \u2014 `akb skill install`, or the button in the board UI under Configuration \u2192 Agent setup. Safe to run twice."
@@ -29456,7 +29201,7 @@ function invokedDirectly() {
29456
29201
  const entry = process.argv[1];
29457
29202
  if (!entry) return false;
29458
29203
  try {
29459
- return fs87.realpathSync(entry) === fs87.realpathSync(SELF3);
29204
+ return fs86.realpathSync(entry) === fs86.realpathSync(SELF3);
29460
29205
  } catch {
29461
29206
  return false;
29462
29207
  }