@ordewell/core 0.11.2 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -472,6 +472,7 @@ __export(index_exports, {
472
472
  repoBaseLine: () => repoBaseLine,
473
473
  repoResultLines: () => repoResultLines,
474
474
  reposWithWork: () => reposWithWork,
475
+ repositoryRunnerConfigPaths: () => repositoryRunnerConfigPaths,
475
476
  researchShellWarning: () => researchShellWarning,
476
477
  researchToolsPath: () => researchToolsPath,
477
478
  resetInstalledPlanner: () => resetInstalledPlanner,
@@ -662,18 +663,18 @@ function dependencyTargetErrors(tasks, taskId, dependencies) {
662
663
  }
663
664
  function findDependencyCycle(tasks) {
664
665
  const byId = new Map([...walkPlan(tasks)].map(({ task: task2 }) => [task2.id, task2]));
665
- const path48 = [];
666
+ const path50 = [];
666
667
  const done = /* @__PURE__ */ new Set();
667
668
  const visit = (id2) => {
668
669
  if (done.has(id2)) return null;
669
- const at = path48.indexOf(id2);
670
- if (at >= 0) return path48.slice(at);
671
- path48.push(id2);
670
+ const at = path50.indexOf(id2);
671
+ if (at >= 0) return path50.slice(at);
672
+ path50.push(id2);
672
673
  for (const dep of byId.get(id2)?.dependencies ?? []) {
673
674
  const hit = visit(dep);
674
675
  if (hit) return hit;
675
676
  }
676
- path48.pop();
677
+ path50.pop();
677
678
  done.add(id2);
678
679
  return null;
679
680
  };
@@ -1738,7 +1739,7 @@ function lastServedModel(events) {
1738
1739
  return void 0;
1739
1740
  }
1740
1741
  function taskLogForSurface(events) {
1741
- return events.map((event) => event.type === "task_skills" ? { type: "task_skills", skills: event.skills.map(({ name, source, path: path48 }) => ({ name, source, path: path48 })) } : event);
1742
+ return events.map((event) => event.type === "task_skills" ? { type: "task_skills", skills: event.skills.map(({ name, source, path: path50 }) => ({ name, source, path: path50 })) } : event);
1742
1743
  }
1743
1744
  function coalesceTaskLog(events) {
1744
1745
  const out = [];
@@ -6220,7 +6221,7 @@ function pathRefs(command, opts = {}) {
6220
6221
  return lexAll(command.trim(), dialect, opts.cdpathSet).segments.flatMap((seg) => {
6221
6222
  const patterns = patternArgs(seg);
6222
6223
  const cwd = seg.cwd ?? [];
6223
- return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path48) => ({ path: path48, cwd }));
6224
+ return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path50) => ({ path: path50, cwd }));
6224
6225
  });
6225
6226
  }
6226
6227
  function excludedBy(command, allow, opts = {}) {
@@ -6764,10 +6765,10 @@ function summarizeToolCall(tool2, argsJson, toolLabel) {
6764
6765
  const scope = args.include ? ` in ${args.include}` : "";
6765
6766
  return `grep ${ellipsize(String(args.pattern), 40)}${scope}${mode}`;
6766
6767
  }
6767
- const path48 = args.filePath || args.file_path || args.path;
6768
- if (path48) {
6769
- const basename6 = String(path48).split("/").pop() || String(path48);
6770
- return `${display} ${basename6}`;
6768
+ const path50 = args.filePath || args.file_path || args.path;
6769
+ if (path50) {
6770
+ const basename7 = String(path50).split("/").pop() || String(path50);
6771
+ return `${display} ${basename7}`;
6771
6772
  }
6772
6773
  if (args.pattern) {
6773
6774
  return `${display} ${args.pattern}`;
@@ -8382,7 +8383,7 @@ ${outcome.output}${outcome.success ? "" : `
8382
8383
  }
8383
8384
  let shell;
8384
8385
  try {
8385
- const sealed2 = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path: path48, directory }) => ({ path: path48, directory })) } : site.sealed;
8386
+ const sealed2 = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path: path50, directory }) => ({ path: path50, directory })) } : site.sealed;
8386
8387
  shell = await site.sandbox.launchSealed(this.researchShell, sealed2);
8387
8388
  } catch (err) {
8388
8389
  return { refused: refusal(`Command not run: its sealed sandbox could not be prepared (${err instanceof Error ? err.message : String(err)}).`) };
@@ -9931,8 +9932,8 @@ function firstLineOf(command) {
9931
9932
  function text(value) {
9932
9933
  return typeof value === "string" ? value : "";
9933
9934
  }
9934
- function searchArg(pattern, path48, include = "") {
9935
- return `${pattern}${path48 ? ` in ${path48}` : ""}${include ? ` (${include})` : ""}`;
9935
+ function searchArg(pattern, path50, include = "") {
9936
+ return `${pattern}${path50 ? ` in ${path50}` : ""}${include ? ` (${include})` : ""}`;
9936
9937
  }
9937
9938
  function hintArg(args) {
9938
9939
  for (const field of HINT_FIELDS) {
@@ -10037,8 +10038,8 @@ function userMessage(id2, text6, skills, { turnId, forced } = {}) {
10037
10038
  ...forced ? { forced: true } : {}
10038
10039
  };
10039
10040
  }
10040
- function skillLoadNotice({ invokedBy, name, source, path: path48, attaches }) {
10041
- return { invokedBy, name, source, path: path48, ...attaches ? { attaches } : {} };
10041
+ function skillLoadNotice({ invokedBy, name, source, path: path50, attaches }) {
10042
+ return { invokedBy, name, source, path: path50, ...attaches ? { attaches } : {} };
10042
10043
  }
10043
10044
  var LOAD_SKILL_TOOL = /(?:^|_)load_skill$/;
10044
10045
  function surfaceStep(step) {
@@ -10055,8 +10056,8 @@ function loadedSkillName(args) {
10055
10056
  return void 0;
10056
10057
  }
10057
10058
  }
10058
- function skillLoadBlock(id2, { invokedBy, name, source, path: path48, attaches }, turnId) {
10059
- return { type: "skill_load", id: id2, invokedBy, name, source, path: path48, ...attaches ? { attaches } : {}, ...turnId ? { turnId } : {} };
10059
+ function skillLoadBlock(id2, { invokedBy, name, source, path: path50, attaches }, turnId) {
10060
+ return { type: "skill_load", id: id2, invokedBy, name, source, path: path50, ...attaches ? { attaches } : {}, ...turnId ? { turnId } : {} };
10060
10061
  }
10061
10062
  function subagentBlock(id2, { toolCallId, model, usage, turnId, ...fields }) {
10062
10063
  return {
@@ -10934,9 +10935,9 @@ function claudeManagedSettingsDir(platform = process.platform) {
10934
10935
  }
10935
10936
  function readClaudeSettingsModels(files, platform = process.platform) {
10936
10937
  const managed = claudeManagedSettingsDir(platform);
10937
- const join44 = platform === "win32" ? path13.win32.join : path13.posix.join;
10938
- const dropIns = files.list(join44(managed, "managed-settings.d")).filter((name) => name.endsWith(".json") && !name.startsWith(".")).sort().map((name) => join44(managed, "managed-settings.d", name));
10939
- const ranked = [...dropIns.reverse(), join44(managed, "managed-settings.json"), join44(claudeConfigDir(files.env), "settings.json")];
10938
+ const join46 = platform === "win32" ? path13.win32.join : path13.posix.join;
10939
+ const dropIns = files.list(join46(managed, "managed-settings.d")).filter((name) => name.endsWith(".json") && !name.startsWith(".")).sort().map((name) => join46(managed, "managed-settings.d", name));
10940
+ const ranked = [...dropIns.reverse(), join46(managed, "managed-settings.json"), join46(claudeConfigDir(files.env), "settings.json")];
10940
10941
  for (const file of ranked) {
10941
10942
  const content = files.read(file);
10942
10943
  const lineup = content === null ? null : parseClaudeSettingsModels(content);
@@ -12048,12 +12049,12 @@ function checkpointReply(answer2) {
12048
12049
  return { text: `The checkpoint was withdrawn: ${answer2.why}`, isError: true };
12049
12050
  }
12050
12051
  }
12051
- function fieldPath(path48) {
12052
- return path48.reduce((text6, part) => typeof part === "number" ? `${text6}[${part}]` : text6 ? `${text6}.${String(part)}` : String(part), "");
12052
+ function fieldPath(path50) {
12053
+ return path50.reduce((text6, part) => typeof part === "number" ? `${text6}[${part}]` : text6 ? `${text6}.${String(part)}` : String(part), "");
12053
12054
  }
12054
- function valueAt(value, path48) {
12055
+ function valueAt(value, path50) {
12055
12056
  let current = value;
12056
- for (const part of path48) {
12057
+ for (const part of path50) {
12057
12058
  if (typeof part === "number") {
12058
12059
  if (!Array.isArray(current)) return void 0;
12059
12060
  current = current[part];
@@ -12075,7 +12076,7 @@ function boundedRefusal(issue, args) {
12075
12076
  var LEAKED_PARAMETER = /<\/parameter>|<parameter\s+name\s*=\s*"([^"]*)">/g;
12076
12077
  function leakedMarkupIn(value) {
12077
12078
  const found = [];
12078
- const walk = (node, path48) => {
12079
+ const walk = (node, path50) => {
12079
12080
  if (typeof node === "string") {
12080
12081
  const names = [];
12081
12082
  let closing = false;
@@ -12083,25 +12084,25 @@ function leakedMarkupIn(value) {
12083
12084
  if (match[1] !== void 0) names.push(match[1]);
12084
12085
  else closing = true;
12085
12086
  }
12086
- if (names.length > 0 || closing) found.push({ path: path48, names: [...new Set(names)], closing });
12087
+ if (names.length > 0 || closing) found.push({ path: path50, names: [...new Set(names)], closing });
12087
12088
  return;
12088
12089
  }
12089
12090
  if (Array.isArray(node)) {
12090
- node.forEach((item, i) => walk(item, `${path48}[${i}]`));
12091
+ node.forEach((item, i) => walk(item, `${path50}[${i}]`));
12091
12092
  return;
12092
12093
  }
12093
12094
  if (node !== null && typeof node === "object") {
12094
- for (const [key, item] of Object.entries(node)) walk(item, path48 ? `${path48}.${key}` : key);
12095
+ for (const [key, item] of Object.entries(node)) walk(item, path50 ? `${path50}.${key}` : key);
12095
12096
  }
12096
12097
  };
12097
12098
  walk(value, "");
12098
12099
  return found;
12099
12100
  }
12100
12101
  function markupRefusal(leaks) {
12101
- return leaks.map(({ path: path48, names, closing }) => {
12102
+ return leaks.map(({ path: path50, names, closing }) => {
12102
12103
  const tags = [...names.map((n) => `<parameter name="${n}">`), ...closing ? ["</parameter>"] : []];
12103
- const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${path48}.` : " Pass each argument separately, not inside one string.";
12104
- return `${path48} contains leaked tool-call markup: ${tags.join(", ")}.${guide}`;
12104
+ const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${path50}.` : " Pass each argument separately, not inside one string.";
12105
+ return `${path50} contains leaked tool-call markup: ${tags.join(", ")}.${guide}`;
12105
12106
  }).join("\n");
12106
12107
  }
12107
12108
  function invalidArguments(name, error, args) {
@@ -12566,9 +12567,9 @@ function mcpClientConfig(credential) {
12566
12567
  }
12567
12568
  function ownerOnlyConfigFile(fileName, contents) {
12568
12569
  const dir = (0, import_fs5.mkdtempSync)((0, import_path4.join)((0, import_os.tmpdir)(), "ordewell-mcp-"));
12569
- const path48 = (0, import_path4.join)(dir, fileName);
12570
- (0, import_fs5.writeFileSync)(path48, contents, { mode: 384 });
12571
- return { path: path48, remove: () => (0, import_fs5.rmSync)(dir, { recursive: true, force: true }) };
12570
+ const path50 = (0, import_path4.join)(dir, fileName);
12571
+ (0, import_fs5.writeFileSync)(path50, contents, { mode: 384 });
12572
+ return { path: path50, remove: () => (0, import_fs5.rmSync)(dir, { recursive: true, force: true }) };
12572
12573
  }
12573
12574
 
12574
12575
  // src/services/harness/repoConfig.ts
@@ -12697,33 +12698,33 @@ var TomlReader = class {
12697
12698
  if (this.i >= this.src.length) return root;
12698
12699
  if (this.src.startsWith("[[", this.i)) {
12699
12700
  this.i += 2;
12700
- const path48 = this.key();
12701
+ const path50 = this.key();
12701
12702
  this.expect("]]");
12702
- const parent = this.tableAt(root, path48.slice(0, -1));
12703
- const last = path48[path48.length - 1];
12703
+ const parent = this.tableAt(root, path50.slice(0, -1));
12704
+ const last = path50[path50.length - 1];
12704
12705
  const list = Object.hasOwn(parent, last) ? parent[last] : parent[last] = [];
12705
- if (!Array.isArray(list)) throw this.fail(`${path48.join(".")} is not an array of tables`);
12706
+ if (!Array.isArray(list)) throw this.fail(`${path50.join(".")} is not an array of tables`);
12706
12707
  table = newTable();
12707
12708
  list.push(table);
12708
12709
  } else if (this.src[this.i] === "[") {
12709
12710
  this.i += 1;
12710
- const path48 = this.key();
12711
+ const path50 = this.key();
12711
12712
  this.expect("]");
12712
- table = this.tableAt(root, path48);
12713
+ table = this.tableAt(root, path50);
12713
12714
  } else {
12714
- const path48 = this.key();
12715
+ const path50 = this.key();
12715
12716
  this.skipBlank(false);
12716
12717
  this.expect("=");
12717
12718
  this.skipBlank(false);
12718
- const owner = this.tableAt(table, path48.slice(0, -1));
12719
- owner[path48[path48.length - 1]] = this.value();
12719
+ const owner = this.tableAt(table, path50.slice(0, -1));
12720
+ owner[path50[path50.length - 1]] = this.value();
12720
12721
  }
12721
12722
  this.endOfLine();
12722
12723
  }
12723
12724
  }
12724
- tableAt(from, path48) {
12725
+ tableAt(from, path50) {
12725
12726
  let table = from;
12726
- for (const part of path48) {
12727
+ for (const part of path50) {
12727
12728
  const next = Object.hasOwn(table, part) ? table[part] : void 0;
12728
12729
  if (next === void 0) table = table[part] = newTable();
12729
12730
  else if (isRecord2(next)) table = next;
@@ -12792,10 +12793,10 @@ var TomlReader = class {
12792
12793
  this.i++;
12793
12794
  return table;
12794
12795
  }
12795
- const path48 = this.key();
12796
+ const path50 = this.key();
12796
12797
  this.expect("=");
12797
12798
  this.skipBlank(false);
12798
- this.tableAt(table, path48.slice(0, -1))[path48[path48.length - 1]] = this.value();
12799
+ this.tableAt(table, path50.slice(0, -1))[path50[path50.length - 1]] = this.value();
12799
12800
  this.skipBlank(true);
12800
12801
  if (this.src[this.i] === ",") this.i++;
12801
12802
  else if (this.src[this.i] !== "}") throw this.fail("expected , or } in an inline table");
@@ -13170,7 +13171,7 @@ var LineBuffer = class {
13170
13171
  // src/services/harness/ClaudeCodeAdapter.ts
13171
13172
  var import_crypto6 = require("crypto");
13172
13173
  var import_fs11 = require("fs");
13173
- var path22 = __toESM(require("path"));
13174
+ var path23 = __toESM(require("path"));
13174
13175
 
13175
13176
  // src/plugins/resolveArgs.ts
13176
13177
  function claudeThinkingArgs(effort) {
@@ -13253,28 +13254,28 @@ function posixBinDirs() {
13253
13254
  ];
13254
13255
  }
13255
13256
  function windowsBinDirs(env, home) {
13256
- const join44 = (...parts) => path15.win32.join(...parts);
13257
- const under = (base, ...parts) => base ? [join44(base, ...parts)] : [];
13257
+ const join46 = (...parts) => path15.win32.join(...parts);
13258
+ const under = (base, ...parts) => base ? [join46(base, ...parts)] : [];
13258
13259
  const appData = env.APPDATA;
13259
13260
  const localAppData = env.LOCALAPPDATA;
13260
13261
  const programData = env.ProgramData;
13261
13262
  return [
13262
13263
  // Native per-user installers, PowerShell-driven and otherwise.
13263
- join44(home, ".local", "bin"),
13264
- join44(home, ".opencode", "bin"),
13265
- join44(home, ".claude", "bin"),
13266
- join44(home, ".codex", "bin"),
13264
+ join46(home, ".local", "bin"),
13265
+ join46(home, ".opencode", "bin"),
13266
+ join46(home, ".claude", "bin"),
13267
+ join46(home, ".codex", "bin"),
13267
13268
  ...under(localAppData, "Programs", "claude"),
13268
13269
  // Node package managers. npm's global prefix is `%APPDATA%\npm`; the
13269
13270
  // literal spelling is kept as a fallback for a host that does not export
13270
13271
  // APPDATA (a service account, a stripped CI container).
13271
13272
  ...under(appData, "npm"),
13272
- join44(home, "AppData", "Roaming", "npm"),
13273
+ join46(home, "AppData", "Roaming", "npm"),
13273
13274
  ...under(localAppData, "pnpm"),
13274
13275
  ...under(localAppData, "Yarn", "bin"),
13275
- join44(home, ".bun", "bin"),
13276
+ join46(home, ".bun", "bin"),
13276
13277
  // Windows package managers.
13277
- join44(home, "scoop", "shims"),
13278
+ join46(home, "scoop", "shims"),
13278
13279
  ...under(env.SCOOP, "shims"),
13279
13280
  ...under(env.SCOOP_GLOBAL ?? programData, "scoop", "shims"),
13280
13281
  ...under(programData, "chocolatey", "bin"),
@@ -13284,7 +13285,7 @@ function windowsBinDirs(env, home) {
13284
13285
  ...under(localAppData, "Microsoft", "WindowsApps"),
13285
13286
  // Version managers. Volta's Windows home is not the POSIX `~/.volta`.
13286
13287
  ...under(localAppData, "Volta", "bin"),
13287
- join44(home, ".volta", "bin")
13288
+ join46(home, ".volta", "bin")
13288
13289
  ];
13289
13290
  }
13290
13291
  function wellKnownBinDirs(deps = {}) {
@@ -14532,7 +14533,7 @@ function readOnlySessionRules(site) {
14532
14533
  real = import_fs8.realpathSync.native(root);
14533
14534
  } catch {
14534
14535
  }
14535
- for (const path48 of /* @__PURE__ */ new Set([root, real])) rules.push({ action: "external_directory", resource: `${path48.replace(/[\\/]$/, "")}/*`, effect: "allow" });
14536
+ for (const path50 of /* @__PURE__ */ new Set([root, real])) rules.push({ action: "external_directory", resource: `${path50.replace(/[\\/]$/, "")}/*`, effect: "allow" });
14536
14537
  }
14537
14538
  return rules;
14538
14539
  }
@@ -15254,6 +15255,20 @@ ${lines2.map((line) => `${line}
15254
15255
  }
15255
15256
 
15256
15257
  // src/services/harness/claudeOrdewell.ts
15258
+ var path21 = __toESM(require("path"));
15259
+ function assertNoClaudeTaskCollision(cwd) {
15260
+ const text6 = readText(path21.join(cwd, ".mcp.json"));
15261
+ if (text6 === void 0) return;
15262
+ let parsed;
15263
+ try {
15264
+ parsed = JSON.parse(text6);
15265
+ } catch {
15266
+ return;
15267
+ }
15268
+ if (isRecord2(parsed) && isRecord2(parsed.mcpServers) && Object.hasOwn(parsed.mcpServers, ORDEWELL_MCP_SERVER_NAME)) {
15269
+ throw new Error(".mcp.json declares an MCP server named ordewell; rename or remove the ordewell server before starting this task.");
15270
+ }
15271
+ }
15257
15272
  var CLAUDE_ORDEWELL = {
15258
15273
  toolName: MCP_CLIENT_TOOL_NAMES.toolName,
15259
15274
  toolNames: MCP_CLIENT_TOOL_NAMES.toolNames,
@@ -15401,8 +15416,8 @@ var ReplyRuns = class {
15401
15416
  };
15402
15417
 
15403
15418
  // src/services/harness/claudeRepoConfig.ts
15404
- var path21 = __toESM(require("path"));
15405
- var MEMORY_FILES = ["CLAUDE.md", path21.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
15419
+ var path22 = __toESM(require("path"));
15420
+ var MEMORY_FILES = ["CLAUDE.md", path22.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
15406
15421
  var MAX_IMPORT_DEPTH = 5;
15407
15422
  function importsOf(text6) {
15408
15423
  const prose = text6.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
@@ -15413,11 +15428,11 @@ function withImports(workspace, file, depth, seen, into) {
15413
15428
  seen.add(file);
15414
15429
  const text6 = readText(file);
15415
15430
  if (text6 === void 0) return;
15416
- into.push({ path: path21.relative(workspace, file), text: text6 });
15431
+ into.push({ path: path22.relative(workspace, file), text: text6 });
15417
15432
  if (depth >= MAX_IMPORT_DEPTH) return;
15418
15433
  for (const target of importsOf(text6)) {
15419
15434
  if (target.startsWith("~")) continue;
15420
- const abs = path21.resolve(path21.dirname(file), target);
15435
+ const abs = path22.resolve(path22.dirname(file), target);
15421
15436
  if (insideOnDisk(workspace, abs)) withImports(workspace, abs, depth + 1, seen, into);
15422
15437
  }
15423
15438
  }
@@ -15438,12 +15453,12 @@ var CLAUDE_REPO_CONFIG = {
15438
15453
  instructions(workspace) {
15439
15454
  const files = [];
15440
15455
  const seen = /* @__PURE__ */ new Set();
15441
- for (const name of MEMORY_FILES) withImports(workspace, path21.join(workspace, name), 0, seen, files);
15456
+ for (const name of MEMORY_FILES) withImports(workspace, path22.join(workspace, name), 0, seen, files);
15442
15457
  return files;
15443
15458
  },
15444
15459
  projectServers(workspace) {
15445
15460
  const source = ".mcp.json";
15446
- const text6 = readText(path21.join(workspace, source));
15461
+ const text6 = readText(path22.join(workspace, source));
15447
15462
  if (text6 === void 0) return NO_SERVERS;
15448
15463
  let parsed;
15449
15464
  try {
@@ -15463,12 +15478,12 @@ var CLAUDE_REPO_CONFIG = {
15463
15478
  };
15464
15479
  function claudeLocalServers(env, workspace) {
15465
15480
  const configDir2 = env.CLAUDE_CONFIG_DIR?.trim();
15466
- const file = expandRunnerHome(configDir2 ? path21.join(configDir2, ".claude.json") : "~/.claude.json", env);
15481
+ const file = expandRunnerHome(configDir2 ? path22.join(configDir2, ".claude.json") : "~/.claude.json", env);
15467
15482
  const text6 = file === null ? void 0 : readText(file);
15468
15483
  if (text6 === void 0) return {};
15469
15484
  try {
15470
15485
  const parsed = JSON.parse(text6);
15471
- const project = isRecord2(parsed) && isRecord2(parsed.projects) ? parsed.projects[path21.resolve(workspace)] : void 0;
15486
+ const project = isRecord2(parsed) && isRecord2(parsed.projects) ? parsed.projects[path22.resolve(workspace)] : void 0;
15472
15487
  return isRecord2(project) && isRecord2(project.mcpServers) ? project.mcpServers : {};
15473
15488
  } catch {
15474
15489
  return {};
@@ -15723,13 +15738,13 @@ var NO_AUTO_MEMORY = { CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1" };
15723
15738
  function inSavedOutput(abs, configDir2, sessionId) {
15724
15739
  let projects;
15725
15740
  try {
15726
- projects = import_fs11.realpathSync.native(path22.join(configDir2, "projects"));
15741
+ projects = import_fs11.realpathSync.native(path23.join(configDir2, "projects"));
15727
15742
  } catch {
15728
15743
  return false;
15729
15744
  }
15730
- const rel = path22.relative(projects, abs);
15731
- if (rel.startsWith("..") || path22.isAbsolute(rel)) return false;
15732
- const [project, session, folder] = rel.split(path22.sep);
15745
+ const rel = path23.relative(projects, abs);
15746
+ if (rel.startsWith("..") || path23.isAbsolute(rel)) return false;
15747
+ const [project, session, folder] = rel.split(path23.sep);
15733
15748
  return Boolean(project) && session === sessionId && folder === "tool-results";
15734
15749
  }
15735
15750
  var PATH_TOOLS = {
@@ -16020,6 +16035,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
16020
16035
  */
16021
16036
  taskSpawnSpec(opts) {
16022
16037
  const readOnly = readOnlyTask(opts);
16038
+ if (!readOnly) assertNoClaudeTaskCollision(opts.cwd);
16023
16039
  this.readOnlySite = readOnly ? opts : null;
16024
16040
  this.requestedMode = readOnly ? PLANNER_PERMISSION_MODE : opts.flags.permissionMode;
16025
16041
  this.followedModel = opts.model;
@@ -16815,8 +16831,8 @@ function runProbe(deps, cwd, env, flags) {
16815
16831
  }
16816
16832
 
16817
16833
  // src/services/harness/codexRepoConfig.ts
16818
- var path23 = __toESM(require("path"));
16819
- var CONFIG_FILE = path23.join(".codex", "config.toml");
16834
+ var path24 = __toESM(require("path"));
16835
+ var CONFIG_FILE = path24.join(".codex", "config.toml");
16820
16836
  function stringsOf(value) {
16821
16837
  return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
16822
16838
  }
@@ -16842,9 +16858,9 @@ var CODEX_REPO_CONFIG = {
16842
16858
  const files = [];
16843
16859
  for (const dir of repoLevels(workspace)) {
16844
16860
  for (const name of ["AGENTS.override.md", "AGENTS.md"]) {
16845
- const text6 = readText(path23.join(dir, name));
16861
+ const text6 = readText(path24.join(dir, name));
16846
16862
  if (text6 === void 0) continue;
16847
- files.push({ path: path23.relative(workspace, path23.join(dir, name)) || name, text: text6 });
16863
+ files.push({ path: path24.relative(workspace, path24.join(dir, name)) || name, text: text6 });
16848
16864
  break;
16849
16865
  }
16850
16866
  }
@@ -16855,10 +16871,10 @@ var CODEX_REPO_CONFIG = {
16855
16871
  const byName = /* @__PURE__ */ new Map();
16856
16872
  const unreadable = [];
16857
16873
  for (const dir of repoLevels(workspace)) {
16858
- const file = path23.join(dir, CONFIG_FILE);
16874
+ const file = path24.join(dir, CONFIG_FILE);
16859
16875
  const text6 = readText(file);
16860
16876
  if (text6 === void 0) continue;
16861
- const source = path23.relative(workspace, file);
16877
+ const source = path24.relative(workspace, file);
16862
16878
  let parsed;
16863
16879
  try {
16864
16880
  parsed = parseToml(text6);
@@ -16878,9 +16894,9 @@ var CODEX_REPO_CONFIG = {
16878
16894
  };
16879
16895
  function untrustedProjects(workspace) {
16880
16896
  const projects = {};
16881
- for (let dir = path23.resolve(workspace); ; dir = path23.dirname(dir)) {
16897
+ for (let dir = path24.resolve(workspace); ; dir = path24.dirname(dir)) {
16882
16898
  projects[dir] = { trust_level: "untrusted" };
16883
- if (path23.dirname(dir) === dir) return projects;
16899
+ if (path24.dirname(dir) === dir) return projects;
16884
16900
  }
16885
16901
  }
16886
16902
 
@@ -18001,7 +18017,7 @@ ${this.exitMessage()}`);
18001
18017
  case "webSearch":
18002
18018
  return;
18003
18019
  case "fileChange": {
18004
- const paths = (item.changes ?? []).map((change) => change.path).filter((path48) => !!path48);
18020
+ const paths = (item.changes ?? []).map((change) => change.path).filter((path50) => !!path50);
18005
18021
  this.fileChangePaths.set(item.id, paths);
18006
18022
  if (this.startOpts?.kind === "task") {
18007
18023
  for (const change of item.changes ?? []) this.announceFileChange(item.id, change, emit, subagentId);
@@ -18104,20 +18120,20 @@ ${this.exitMessage()}`);
18104
18120
  announceFileChange(itemId, change, emit, subagentId) {
18105
18121
  const rows = this.fileChangeRows.get(itemId) ?? /* @__PURE__ */ new Map();
18106
18122
  this.fileChangeRows.set(itemId, rows);
18107
- const path48 = this.shownChangePath(change);
18108
- const known = rows.get(path48);
18123
+ const path50 = this.shownChangePath(change);
18124
+ const known = rows.get(path50);
18109
18125
  if (known) return known;
18110
- const row = rows.size === 0 ? itemId : `${itemId}:${path48}`;
18111
- rows.set(path48, row);
18112
- emit({ type: "tool_call", id: row, name: changeName(change), args: { path: path48 }, ...subagentId ? { subagentId } : {} });
18126
+ const row = rows.size === 0 ? itemId : `${itemId}:${path50}`;
18127
+ rows.set(path50, row);
18128
+ emit({ type: "tool_call", id: row, name: changeName(change), args: { path: path50 }, ...subagentId ? { subagentId } : {} });
18113
18129
  return row;
18114
18130
  }
18115
18131
  /** Relative to the task's worktree, where every path but a stray one lies; a move names both ends. */
18116
18132
  shownChangePath(change) {
18117
- const shown = (path48) => {
18133
+ const shown = (path50) => {
18118
18134
  const cwd = this.startOpts?.cwd;
18119
- const rel = cwd ? (0, import_path6.relative)(cwd, path48) : path48;
18120
- return rel && rel !== ".." && !rel.startsWith(`..${import_path6.sep}`) && !(0, import_path6.isAbsolute)(rel) ? rel : path48;
18135
+ const rel = cwd ? (0, import_path6.relative)(cwd, path50) : path50;
18136
+ return rel && rel !== ".." && !rel.startsWith(`..${import_path6.sep}`) && !(0, import_path6.isAbsolute)(rel) ? rel : path50;
18121
18137
  };
18122
18138
  const from = shown(change.path ?? "");
18123
18139
  return change.kind?.move_path ? `${from} \u2192 ${shown(change.kind.move_path)}` : from;
@@ -18168,7 +18184,7 @@ var import_fs12 = require("fs");
18168
18184
 
18169
18185
  // src/services/harness/projectServers.ts
18170
18186
  var import_crypto8 = require("crypto");
18171
- var path24 = __toESM(require("path"));
18187
+ var path25 = __toESM(require("path"));
18172
18188
  function canonical2(value) {
18173
18189
  if (Array.isArray(value)) return value.map(canonical2);
18174
18190
  if (!isRecord2(value)) return value;
@@ -18179,7 +18195,7 @@ function projectServerScope(server4) {
18179
18195
  return `mcp-server:${server4.name}@${hash}`;
18180
18196
  }
18181
18197
  var scopeName = (scope) => scope.slice(0, scope.lastIndexOf("@"));
18182
- function fileProjectServerApprovals(file = path24.join(globalDataDir(), "project-mcp-servers.json")) {
18198
+ function fileProjectServerApprovals(file = path25.join(globalDataDir(), "project-mcp-servers.json")) {
18183
18199
  const load = () => {
18184
18200
  const text6 = readText(file);
18185
18201
  if (text6 === void 0) return {};
@@ -19342,8 +19358,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
19342
19358
  this.v2 = new OpenCodeV2({
19343
19359
  version,
19344
19360
  nativeShell: this.nativeShell?.prefix ?? null,
19345
- request: (method, path48, body, signal, timeoutMs) => this.request(method, path48, body, signal, timeoutMs),
19346
- json: (method, path48, body, signal, timeoutMs) => this.json(method, path48, body, signal, timeoutMs),
19361
+ request: (method, path50, body, signal, timeoutMs) => this.request(method, path50, body, signal, timeoutMs),
19362
+ json: (method, path50, body, signal, timeoutMs) => this.json(method, path50, body, signal, timeoutMs),
19347
19363
  openEvents: (signal) => this.deps.fetch(`${this.baseUrl}/api/event`, { signal, headers: this.headers() }),
19348
19364
  processEnded: this.processEnded,
19349
19365
  isExited: () => this.exited,
@@ -19980,15 +19996,15 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
19980
19996
  onActivity
19981
19997
  );
19982
19998
  }
19983
- async json(method, path48, body, signal, timeoutMs) {
19984
- const response = await this.request(method, path48, body, signal, timeoutMs);
19985
- if (!response.ok) throw new OpenCodeHttpError(`${method} ${path48} failed: ${response.status} ${response.statusText}`);
19999
+ async json(method, path50, body, signal, timeoutMs) {
20000
+ const response = await this.request(method, path50, body, signal, timeoutMs);
20001
+ if (!response.ok) throw new OpenCodeHttpError(`${method} ${path50} failed: ${response.status} ${response.statusText}`);
19986
20002
  return await response.json().catch(() => null);
19987
20003
  }
19988
20004
  /** `timeoutMs` null is for a turn's own POST, which the server answers when the turn is done. */
19989
- request(method, path48, body, signal, timeoutMs = REQUEST_TIMEOUT_MS) {
20005
+ request(method, path50, body, signal, timeoutMs = REQUEST_TIMEOUT_MS) {
19990
20006
  const deadline = withDeadline(signal, timeoutMs);
19991
- return this.deps.fetch(`${this.baseUrl}${path48}`, {
20007
+ return this.deps.fetch(`${this.baseUrl}${path50}`, {
19992
20008
  method,
19993
20009
  headers: this.headers({ "content-type": "application/json" }),
19994
20010
  ...body === void 0 ? {} : { body: JSON.stringify(body) },
@@ -20049,8 +20065,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
20049
20065
  };
20050
20066
 
20051
20067
  // src/services/harness/openCodeRepoConfig.ts
20052
- var path25 = __toESM(require("path"));
20053
- var CONFIG_FILES = ["opencode.json", "opencode.jsonc", path25.join(".opencode", "opencode.json"), path25.join(".opencode", "opencode.jsonc")];
20068
+ var path26 = __toESM(require("path"));
20069
+ var CONFIG_FILES = ["opencode.json", "opencode.jsonc", path26.join(".opencode", "opencode.json"), path26.join(".opencode", "opencode.jsonc")];
20054
20070
  var INSTRUCTION_NAMES = ["AGENTS.md", "CLAUDE.md", "CONTEXT.md"];
20055
20071
  function server3(name, entry, source) {
20056
20072
  if (!isRecord2(entry)) return "not an object";
@@ -20069,8 +20085,8 @@ var OPENCODE_REPO_CONFIG = {
20069
20085
  const levels = repoLevels(workspace);
20070
20086
  for (const name of INSTRUCTION_NAMES) {
20071
20087
  const files = levels.flatMap((dir) => {
20072
- const text6 = readText(path25.join(dir, name));
20073
- return text6 === void 0 ? [] : [{ path: path25.relative(workspace, path25.join(dir, name)) || name, text: text6 }];
20088
+ const text6 = readText(path26.join(dir, name));
20089
+ return text6 === void 0 ? [] : [{ path: path26.relative(workspace, path26.join(dir, name)) || name, text: text6 }];
20074
20090
  });
20075
20091
  if (files.length > 0) return files;
20076
20092
  }
@@ -20082,10 +20098,10 @@ var OPENCODE_REPO_CONFIG = {
20082
20098
  const unreadable = [];
20083
20099
  for (const dir of repoLevels(workspace)) {
20084
20100
  for (const name of CONFIG_FILES) {
20085
- const file = path25.join(dir, name);
20101
+ const file = path26.join(dir, name);
20086
20102
  const text6 = readText(file);
20087
20103
  if (text6 === void 0) continue;
20088
- const source = path25.relative(workspace, file);
20104
+ const source = path26.relative(workspace, file);
20089
20105
  let parsed;
20090
20106
  try {
20091
20107
  parsed = parseJsonc(text6);
@@ -20274,7 +20290,7 @@ function knownModelId(value, optionIds, shortcuts) {
20274
20290
 
20275
20291
  // src/services/ModelCatalog.ts
20276
20292
  var fs6 = __toESM(require("fs"));
20277
- var path26 = __toESM(require("path"));
20293
+ var path27 = __toESM(require("path"));
20278
20294
  var CACHE_KEY = "model-catalog";
20279
20295
  var CACHE_TTL_MS = 60 * 60 * 1e3;
20280
20296
  function storageKey(baseUrl) {
@@ -20327,7 +20343,7 @@ var ModelCatalog = class {
20327
20343
  const url = (baseUrl || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
20328
20344
  const key = storageKey(url);
20329
20345
  const cacheDir = globalCacheDir();
20330
- const cacheFile = path26.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
20346
+ const cacheFile = path27.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
20331
20347
  if (fs6.existsSync(cacheFile)) fs6.rmSync(cacheFile);
20332
20348
  } catch {
20333
20349
  }
@@ -20335,7 +20351,7 @@ var ModelCatalog = class {
20335
20351
  static readCache(key) {
20336
20352
  try {
20337
20353
  const cacheDir = globalCacheDir();
20338
- const cacheFile = path26.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
20354
+ const cacheFile = path27.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
20339
20355
  if (!fs6.existsSync(cacheFile)) return null;
20340
20356
  const raw = fs6.readFileSync(cacheFile, "utf8");
20341
20357
  const data = JSON.parse(raw);
@@ -20349,7 +20365,7 @@ var ModelCatalog = class {
20349
20365
  try {
20350
20366
  const cacheDir = globalCacheDir();
20351
20367
  fs6.mkdirSync(cacheDir, { recursive: true });
20352
- const cacheFile = path26.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
20368
+ const cacheFile = path27.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
20353
20369
  fs6.writeFileSync(
20354
20370
  cacheFile,
20355
20371
  JSON.stringify({ models, cachedAt: Date.now() }, null, 2)
@@ -20360,7 +20376,7 @@ var ModelCatalog = class {
20360
20376
  };
20361
20377
 
20362
20378
  // src/services/ModelDiscovery.ts
20363
- var path27 = __toESM(require("path"));
20379
+ var path28 = __toESM(require("path"));
20364
20380
  var import_child_process6 = require("child_process");
20365
20381
  var import_util2 = require("util");
20366
20382
  var fsSync5 = __toESM(require("fs"));
@@ -20506,7 +20522,7 @@ function geminiDiscoveryCacheKey(apiKey) {
20506
20522
  return {
20507
20523
  key: `gemini-models:${hash}`,
20508
20524
  dir,
20509
- file: path27.join(dir, `gemini_models_${hash}.json`)
20525
+ file: path28.join(dir, `gemini_models_${hash}.json`)
20510
20526
  };
20511
20527
  }
20512
20528
  function readGeminiCache(apiKey) {
@@ -21014,7 +21030,7 @@ function parseMaxParallel(value) {
21014
21030
 
21015
21031
  // src/services/SettingsService.ts
21016
21032
  var fs12 = __toESM(require("fs"));
21017
- var path34 = __toESM(require("path"));
21033
+ var path35 = __toESM(require("path"));
21018
21034
 
21019
21035
  // src/utils/fileLock.ts
21020
21036
  var fs7 = __toESM(require("fs"));
@@ -23066,33 +23082,33 @@ ${skillBlock(entry.skill)}`, timestamp: entry.timestamp });
23066
23082
  }
23067
23083
 
23068
23084
  // src/services/taskSkills.ts
23069
- var path33 = __toESM(require("path"));
23085
+ var path34 = __toESM(require("path"));
23070
23086
 
23071
23087
  // src/services/SkillsService.ts
23072
23088
  var import_crypto12 = require("crypto");
23073
23089
  var fs11 = __toESM(require("fs"));
23074
- var path32 = __toESM(require("path"));
23090
+ var path33 = __toESM(require("path"));
23075
23091
 
23076
23092
  // src/services/builtinSkills.ts
23077
- var path28 = __toESM(require("path"));
23093
+ var path29 = __toESM(require("path"));
23078
23094
  var import_url = require("url");
23079
23095
  function builtinSkillsDir() {
23080
- const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path28.dirname((0, import_url.fileURLToPath)(
23096
+ const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path29.dirname((0, import_url.fileURLToPath)(
23081
23097
  // @ts-expect-error - import.meta is only valid under an ESM module setting
23082
23098
  __filename
23083
23099
  ));
23084
- return path28.join(dir, "..", "skills");
23100
+ return path29.join(dir, "..", "skills");
23085
23101
  }
23086
23102
 
23087
23103
  // src/services/repoGroup.ts
23088
23104
  var fs10 = __toESM(require("fs"));
23089
- var path31 = __toESM(require("path"));
23105
+ var path32 = __toESM(require("path"));
23090
23106
 
23091
23107
  // src/services/worktreeBootstrap.ts
23092
23108
  var import_child_process8 = require("child_process");
23093
23109
  var import_util4 = require("util");
23094
23110
  var fs9 = __toESM(require("fs"));
23095
- var path30 = __toESM(require("path"));
23111
+ var path31 = __toESM(require("path"));
23096
23112
 
23097
23113
  // src/services/gitExec.ts
23098
23114
  var import_child_process7 = require("child_process");
@@ -23174,7 +23190,7 @@ function subcommandOf2(args) {
23174
23190
 
23175
23191
  // src/services/worktreeLink.ts
23176
23192
  var fs8 = __toESM(require("fs"));
23177
- var path29 = __toESM(require("path"));
23193
+ var path30 = __toESM(require("path"));
23178
23194
  function linkPath(source, target, platform, hardLink = fs8.linkSync) {
23179
23195
  const isDir = fs8.statSync(source).isDirectory();
23180
23196
  if (platform !== "win32") {
@@ -23199,8 +23215,8 @@ function mirrorDir(source, target, opts) {
23199
23215
  const mirror = (from, to, prefix, top) => {
23200
23216
  fs8.mkdirSync(to, { recursive: true });
23201
23217
  for (const entry of fs8.readdirSync(from, { withFileTypes: true })) {
23202
- const name = path29.join(prefix, entry.name);
23203
- const at = { from: path29.join(from, entry.name), to: path29.join(to, entry.name), name };
23218
+ const name = path30.join(prefix, entry.name);
23219
+ const at = { from: path30.join(from, entry.name), to: path30.join(to, entry.name), name };
23204
23220
  if (entry.isSymbolicLink()) links.push(at);
23205
23221
  else if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin")) mirror(at.from, at.to, name, false);
23206
23222
  else if (linkPath(at.from, at.to, opts.platform, opts.hardLink) === "copy") copied.push(name);
@@ -23220,16 +23236,16 @@ function mirrorDir(source, target, opts) {
23220
23236
  }
23221
23237
  function relink(from, to, opts) {
23222
23238
  const text6 = fs8.readlinkSync(from);
23223
- const pointee = path29.isAbsolute(text6) ? rebased(text6, opts) : text6;
23239
+ const pointee = path30.isAbsolute(text6) ? rebased(text6, opts) : text6;
23224
23240
  if (opts.platform !== "win32") {
23225
- if (!fs8.existsSync(path29.resolve(path29.dirname(to), pointee)) && fs8.existsSync(path29.resolve(path29.dirname(from), pointee))) {
23226
- fs8.symlinkSync(rebased(path29.resolve(path29.dirname(from), pointee), opts), to);
23241
+ if (!fs8.existsSync(path30.resolve(path30.dirname(to), pointee)) && fs8.existsSync(path30.resolve(path30.dirname(from), pointee))) {
23242
+ fs8.symlinkSync(rebased(path30.resolve(path30.dirname(from), pointee), opts), to);
23227
23243
  return "symlink";
23228
23244
  }
23229
23245
  fs8.symlinkSync(pointee, to);
23230
23246
  return "symlink";
23231
23247
  }
23232
- const resolved = fs8.existsSync(path29.resolve(path29.dirname(to), pointee)) ? path29.resolve(path29.dirname(to), pointee) : rebased(path29.resolve(path29.dirname(from), pointee), opts);
23248
+ const resolved = fs8.existsSync(path30.resolve(path30.dirname(to), pointee)) ? path30.resolve(path30.dirname(to), pointee) : rebased(path30.resolve(path30.dirname(from), pointee), opts);
23233
23249
  try {
23234
23250
  fs8.statSync(resolved);
23235
23251
  } catch {
@@ -23238,9 +23254,9 @@ function relink(from, to, opts) {
23238
23254
  return linkPath(resolved, to, opts.platform, opts.hardLink);
23239
23255
  }
23240
23256
  function rebased(pointee, opts) {
23241
- const rel = path29.relative(opts.from, pointee);
23242
- const inside2 = rel !== "" && !rel.startsWith("..") && !path29.isAbsolute(rel);
23243
- return inside2 && !rel.split(/[\\/]/).includes("node_modules") ? path29.join(opts.to, rel) : pointee;
23257
+ const rel = path30.relative(opts.from, pointee);
23258
+ const inside2 = rel !== "" && !rel.startsWith("..") && !path30.isAbsolute(rel);
23259
+ return inside2 && !rel.split(/[\\/]/).includes("node_modules") ? path30.join(opts.to, rel) : pointee;
23244
23260
  }
23245
23261
 
23246
23262
  // src/services/worktreeBootstrap.ts
@@ -23277,18 +23293,18 @@ function matchLinks(root, patterns) {
23277
23293
  }
23278
23294
  function segmentMatches(root, base, segment) {
23279
23295
  const under = (name) => base ? `${base}/${name}` : name;
23280
- if (!/[*?]/.test(segment)) return lexists(path30.join(root, base, segment)) ? [under(segment)] : [];
23296
+ if (!/[*?]/.test(segment)) return lexists(path31.join(root, base, segment)) ? [under(segment)] : [];
23281
23297
  const pattern = new RegExp(`^${segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
23282
- return listDir2(path30.join(root, base)).filter((name) => !NEVER_SCANNED.has(name) && pattern.test(name)).map(under);
23298
+ return listDir2(path31.join(root, base)).filter((name) => !NEVER_SCANNED.has(name) && pattern.test(name)).map(under);
23283
23299
  }
23284
23300
  function installDirs(root) {
23285
23301
  const packages = matchLinks(root, workspaceGlobs(root)).map((pkg) => `${pkg}/node_modules`);
23286
- return ["node_modules", ...packages].filter((dir) => lexists(path30.join(root, dir)));
23302
+ return ["node_modules", ...packages].filter((dir) => lexists(path31.join(root, dir)));
23287
23303
  }
23288
23304
  function workspaceGlobs(root) {
23289
23305
  let manifest;
23290
23306
  try {
23291
- manifest = JSON.parse(fs9.readFileSync(path30.join(root, "package.json"), "utf8"));
23307
+ manifest = JSON.parse(fs9.readFileSync(path31.join(root, "package.json"), "utf8"));
23292
23308
  } catch {
23293
23309
  return [];
23294
23310
  }
@@ -23303,19 +23319,19 @@ async function bootstrap(repo, cwd, opts) {
23303
23319
  const linked = [];
23304
23320
  const copied = [];
23305
23321
  const link = (name) => {
23306
- const target = path30.join(cwd, name);
23322
+ const target = path31.join(cwd, name);
23307
23323
  if (lexists(target)) return;
23308
- fs9.mkdirSync(path30.dirname(target), { recursive: true });
23309
- if (linkPath(path30.join(repo.root, name), target, opts.platform) === "copy") copied.push(name);
23324
+ fs9.mkdirSync(path31.dirname(target), { recursive: true });
23325
+ if (linkPath(path31.join(repo.root, name), target, opts.platform) === "copy") copied.push(name);
23310
23326
  linked.push(name);
23311
23327
  };
23312
23328
  const mirror = (name) => {
23313
- const target = path30.join(cwd, name);
23314
- if (lexists(target) || !fs9.existsSync(path30.dirname(target))) return;
23315
- const source = path30.join(repo.root, name);
23329
+ const target = path31.join(cwd, name);
23330
+ if (lexists(target) || !fs9.existsSync(path31.dirname(target))) return;
23331
+ const source = path31.join(repo.root, name);
23316
23332
  if (!fs9.lstatSync(source).isDirectory()) return link(name);
23317
23333
  const mirrored = mirrorDir(source, target, { platform: opts.platform, from: repo.root, to: cwd });
23318
- copied.push(...mirrored.map((entry) => path30.join(name, entry)));
23334
+ copied.push(...mirrored.map((entry) => path31.join(name, entry)));
23319
23335
  linked.push(name);
23320
23336
  };
23321
23337
  if (!setup) {
@@ -23350,12 +23366,12 @@ async function runSetup(command, repo, cwd, resolvePath) {
23350
23366
 
23351
23367
  // src/services/repoGroup.ts
23352
23368
  function hasGitEntry(dir) {
23353
- return fs10.existsSync(path31.join(dir, ".git"));
23369
+ return fs10.existsSync(path32.join(dir, ".git"));
23354
23370
  }
23355
23371
  function groupPathOf(listed3) {
23356
23372
  const slashed = listed3.trim().replace(/\\/g, "/");
23357
- if (path31.posix.isAbsolute(slashed) || path31.win32.isAbsolute(slashed)) return null;
23358
- const rel = path31.posix.normalize(slashed).replace(/\/+$/, "");
23373
+ if (path32.posix.isAbsolute(slashed) || path32.win32.isAbsolute(slashed)) return null;
23374
+ const rel = path32.posix.normalize(slashed).replace(/\/+$/, "");
23359
23375
  return rel === "" || rel === "." || rel === ".." || rel.startsWith("../") ? null : rel;
23360
23376
  }
23361
23377
  function childDirs(dir) {
@@ -23366,14 +23382,14 @@ function childDirs(dir) {
23366
23382
  }
23367
23383
  }
23368
23384
  function groupPaths(workspaceRoot, listed3) {
23369
- if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(path31.join(workspaceRoot, name)));
23370
- const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(path31.join(workspaceRoot, rel)));
23385
+ if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(path32.join(workspaceRoot, name)));
23386
+ const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(path32.join(workspaceRoot, rel)));
23371
23387
  return [...new Set(paths)].sort();
23372
23388
  }
23373
23389
  function workspaceRepoGroup(workspaceRoot, listed3) {
23374
- for (let dir = path31.resolve(workspaceRoot); ; dir = path31.dirname(dir)) {
23390
+ for (let dir = path32.resolve(workspaceRoot); ; dir = path32.dirname(dir)) {
23375
23391
  if (hasGitEntry(dir)) return [SELF_REPO];
23376
- if (path31.dirname(dir) === dir) break;
23392
+ if (path32.dirname(dir) === dir) break;
23377
23393
  }
23378
23394
  return groupPaths(workspaceRoot, listed3);
23379
23395
  }
@@ -23454,8 +23470,8 @@ function copyDirSync(src, dest) {
23454
23470
  fs11.mkdirSync(dest, { recursive: true });
23455
23471
  const entries = fs11.readdirSync(src, { withFileTypes: true });
23456
23472
  for (const entry of entries) {
23457
- const srcPath = path32.join(src, entry.name);
23458
- const destPath = path32.join(dest, entry.name);
23473
+ const srcPath = path33.join(src, entry.name);
23474
+ const destPath = path33.join(dest, entry.name);
23459
23475
  if (entry.isDirectory()) copyDirSync(srcPath, destPath);
23460
23476
  else fs11.copyFileSync(srcPath, destPath);
23461
23477
  }
@@ -23479,7 +23495,7 @@ function readDir(dir, source) {
23479
23495
  if (!fs11.existsSync(dir)) return { skills, invalid };
23480
23496
  for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
23481
23497
  if (!entry.isDirectory()) continue;
23482
- const skillFile = path32.join(dir, entry.name, "SKILL.md");
23498
+ const skillFile = path33.join(dir, entry.name, "SKILL.md");
23483
23499
  if (!fs11.existsSync(skillFile)) continue;
23484
23500
  if (!isSkillName(entry.name)) {
23485
23501
  invalid.push({ name: entry.name, path: skillFile, source, reason: INVALID_SKILL_NAME_REASON });
@@ -23498,15 +23514,15 @@ function skillFileIn(dir, name) {
23498
23514
  return void 0;
23499
23515
  }
23500
23516
  if (!entries.some((entry) => entry.isDirectory() && entry.name === name)) return void 0;
23501
- const skillFile = path32.join(dir, name, "SKILL.md");
23517
+ const skillFile = path33.join(dir, name, "SKILL.md");
23502
23518
  return fs11.existsSync(skillFile) ? skillFile : void 0;
23503
23519
  }
23504
23520
  function skillsDirOf(root) {
23505
- return path32.join(root, STATE_DIR, "skills");
23521
+ return path33.join(root, STATE_DIR, "skills");
23506
23522
  }
23507
23523
  function workspaceSkillRoots(workspaceRoot, repos, checkout = workspaceRoot) {
23508
23524
  if (repos.length === 0 || repos.includes(SELF_REPO)) return [checkout];
23509
- return [workspaceRoot, ...repos.map((repo) => path32.join(checkout, repo))];
23525
+ return [workspaceRoot, ...repos.map((repo) => path33.join(checkout, repo))];
23510
23526
  }
23511
23527
  var SkillsService = class _SkillsService {
23512
23528
  workspaceRoots;
@@ -23529,11 +23545,11 @@ var SkillsService = class _SkillsService {
23529
23545
  return this.workspaceRoots.map(skillsDirOf);
23530
23546
  }
23531
23547
  globalDir() {
23532
- return path32.join(globalDataDir(), "skills");
23548
+ return path33.join(globalDataDir(), "skills");
23533
23549
  }
23534
23550
  readSeedManifest() {
23535
23551
  try {
23536
- const parsed = JSON.parse(fs11.readFileSync(path32.join(this.globalDir(), SEED_MANIFEST), "utf8"));
23552
+ const parsed = JSON.parse(fs11.readFileSync(path33.join(this.globalDir(), SEED_MANIFEST), "utf8"));
23537
23553
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
23538
23554
  return Object.fromEntries(
23539
23555
  Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
@@ -23549,7 +23565,7 @@ var SkillsService = class _SkillsService {
23549
23565
  manifest[name] = hash;
23550
23566
  try {
23551
23567
  fs11.mkdirSync(this.globalDir(), { recursive: true });
23552
- fs11.writeFileSync(path32.join(this.globalDir(), SEED_MANIFEST), JSON.stringify(manifest, null, 2));
23568
+ fs11.writeFileSync(path33.join(this.globalDir(), SEED_MANIFEST), JSON.stringify(manifest, null, 2));
23553
23569
  } catch (err) {
23554
23570
  console.warn(`Could not record seeded skill ${name}: ${String(err)}`);
23555
23571
  }
@@ -23560,11 +23576,11 @@ var SkillsService = class _SkillsService {
23560
23576
  return this.readSeedManifest()[name] === currentHash || (PRIOR_BUILTIN_HASHES[name] ?? []).includes(currentHash);
23561
23577
  }
23562
23578
  seed(name) {
23563
- const dest = path32.join(this.globalDir(), name);
23579
+ const dest = path33.join(this.globalDir(), name);
23564
23580
  const exists = fs11.existsSync(dest);
23565
23581
  let srcDir;
23566
23582
  try {
23567
- srcDir = path32.join(builtinSkillsDir(), name);
23583
+ srcDir = path33.join(builtinSkillsDir(), name);
23568
23584
  } catch (err) {
23569
23585
  if (!exists) console.warn(`Could not locate built-in skills dir: ${String(err)}`);
23570
23586
  return false;
@@ -23573,9 +23589,9 @@ var SkillsService = class _SkillsService {
23573
23589
  if (!exists) console.warn(`Built-in skill not found in package: ${name}`);
23574
23590
  return false;
23575
23591
  }
23576
- const srcHash = fileHash(path32.join(srcDir, "SKILL.md"));
23592
+ const srcHash = fileHash(path33.join(srcDir, "SKILL.md"));
23577
23593
  if (exists) {
23578
- const currentHash = fileHash(path32.join(dest, "SKILL.md"));
23594
+ const currentHash = fileHash(path33.join(dest, "SKILL.md"));
23579
23595
  if (currentHash !== void 0 && currentHash === srcHash) {
23580
23596
  this.recordSeed(name, currentHash);
23581
23597
  return false;
@@ -23619,7 +23635,7 @@ var SkillsService = class _SkillsService {
23619
23635
  if (entries.length !== 1 || entries[0].name !== "SKILL.md" || !entries[0].isFile()) return false;
23620
23636
  let raw;
23621
23637
  try {
23622
- raw = readSkillText(path32.join(dir, "SKILL.md"));
23638
+ raw = readSkillText(path33.join(dir, "SKILL.md"));
23623
23639
  } catch {
23624
23640
  return false;
23625
23641
  }
@@ -23627,7 +23643,7 @@ var SkillsService = class _SkillsService {
23627
23643
  }
23628
23644
  pruneRetired() {
23629
23645
  for (const name of RETIRED_BUILTIN_SKILL_NAMES) {
23630
- const dest = path32.join(this.globalDir(), name);
23646
+ const dest = path33.join(this.globalDir(), name);
23631
23647
  if (!fs11.existsSync(dest)) continue;
23632
23648
  if (!this.isUnmodifiedRetiredSeed(dest, name)) continue;
23633
23649
  try {
@@ -23765,12 +23781,12 @@ var runGit = execFileWithTimeout(1e4);
23765
23781
  async function committedInHead(files) {
23766
23782
  const byDir = /* @__PURE__ */ new Map();
23767
23783
  for (const file of new Set(files)) {
23768
- const dir = path33.dirname(path33.dirname(file));
23784
+ const dir = path34.dirname(path34.dirname(file));
23769
23785
  byDir.set(dir, [...byDir.get(dir) ?? [], file]);
23770
23786
  }
23771
23787
  const result = /* @__PURE__ */ new Map();
23772
23788
  await Promise.all([...byDir].map(async ([dir, inDir]) => {
23773
- const relative17 = (file) => path33.relative(dir, file).split(path33.sep).join("/");
23789
+ const relative17 = (file) => path34.relative(dir, file).split(path34.sep).join("/");
23774
23790
  try {
23775
23791
  const { stdout } = await runGit("git", ["ls-tree", "--name-only", "HEAD", "--", ...inDir.map(relative17)], { cwd: dir, env: cleanEnv() });
23776
23792
  const listed3 = new Set(stdout.split("\n").map((line) => line.trim()).filter(Boolean));
@@ -23790,9 +23806,9 @@ function plannedSkillLookup(skillsAt, workspaceRoot, layout) {
23790
23806
  searchedDirs: () => lookup2.searchedDirs(),
23791
23807
  listSkills: () => lookup2.listSkills(),
23792
23808
  uncommitted: async (skills) => {
23793
- const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace && path33.dirname(path33.dirname(skill.path)) === readInPlace));
23809
+ const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace && path34.dirname(path34.dirname(skill.path)) === readInPlace));
23794
23810
  const committed = await committedInHead(checked.map((skill) => skill.path));
23795
- return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path, path33.relative(workspaceRoot, path33.dirname(skill.path)).split(path33.sep).join("/")]));
23811
+ return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path, path34.relative(workspaceRoot, path34.dirname(skill.path)).split(path34.sep).join("/")]));
23796
23812
  }
23797
23813
  };
23798
23814
  }
@@ -23805,13 +23821,13 @@ function checkPlanSkills(tasks, lookup2) {
23805
23821
  function checkTaskSkillsEdit(task2, skills, lookup2) {
23806
23822
  return check([{ taskId: task2.id, owner: `Task "${task2.title}"`, ...attached(skills) }], lookup2);
23807
23823
  }
23808
- function subtaskEntries(op, spec, path48 = []) {
23824
+ function subtaskEntries(op, spec, path50 = []) {
23809
23825
  const subtasks = spec?.subtasks;
23810
23826
  if (!Array.isArray(subtasks)) return [];
23811
23827
  return subtasks.flatMap((sub, i) => {
23812
23828
  if (typeof sub !== "object" || sub === null) return [];
23813
23829
  const child = sub;
23814
- const at = [...path48, typeof child.title === "string" && child.title.trim() ? `"${child.title}"` : `#${i + 1}`];
23830
+ const at = [...path50, typeof child.title === "string" && child.title.trim() ? `"${child.title}"` : `#${i + 1}`];
23815
23831
  return [{ op, owner: `subtask ${at.join(" > ")}`, ...attached(child.skills) }, ...subtaskEntries(op, child, at)];
23816
23832
  });
23817
23833
  }
@@ -27681,7 +27697,7 @@ var RUN_SETTING_KEYS = Object.keys(RUN_SETTING_CHECKS);
27681
27697
  function getSettingsPath() {
27682
27698
  const override = process.env.ORDEWELL_SETTINGS_PATH;
27683
27699
  if (override && override.trim()) return override.trim();
27684
- return path34.join(globalDataDir(), "settings.json");
27700
+ return path35.join(globalDataDir(), "settings.json");
27685
27701
  }
27686
27702
  function readPlannerModels(raw) {
27687
27703
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
@@ -27761,8 +27777,8 @@ var SettingsService = class {
27761
27777
  * a watch on the old one pointing at nothing.
27762
27778
  */
27763
27779
  watch(listener) {
27764
- const dir = path34.dirname(this.filePath);
27765
- const name = path34.basename(this.filePath);
27780
+ const dir = path35.dirname(this.filePath);
27781
+ const name = path35.basename(this.filePath);
27766
27782
  let seen = this.fileStamp();
27767
27783
  let timer;
27768
27784
  let dead = false;
@@ -27771,7 +27787,7 @@ var SettingsService = class {
27771
27787
  fs12.mkdirSync(dir, { recursive: true });
27772
27788
  watcher = fs12.watch(dir, (_event, changed) => {
27773
27789
  if (dead) return;
27774
- if (changed && changed !== name && changed !== path34.basename(dir)) return;
27790
+ if (changed && changed !== name && changed !== path35.basename(dir)) return;
27775
27791
  clearTimeout(timer);
27776
27792
  timer = setTimeout(() => {
27777
27793
  const stamp2 = this.fileStamp();
@@ -28004,7 +28020,7 @@ var SettingsService = class {
28004
28020
  if (this.unreadable) {
28005
28021
  throw new Error(`Cannot save settings: ${this.filePath} could not be read, so it will not be overwritten. Fix or remove it and try again.`);
28006
28022
  }
28007
- const dir = path34.dirname(this.filePath);
28023
+ const dir = path35.dirname(this.filePath);
28008
28024
  if (!fs12.existsSync(dir)) {
28009
28025
  fs12.mkdirSync(dir, { recursive: true });
28010
28026
  }
@@ -28606,11 +28622,11 @@ function mergeOutcome(result, branch, group, repaired = []) {
28606
28622
  // src/services/taskDiffRead.ts
28607
28623
  var import_crypto13 = require("crypto");
28608
28624
  var fs13 = __toESM(require("fs"));
28609
- var path35 = __toESM(require("path"));
28625
+ var path36 = __toESM(require("path"));
28610
28626
  var import_os3 = require("os");
28611
28627
  var TASK_DIFF_UNAVAILABLE = "Task diff is not available for this run: no task-specific changes were recorded (shared runs cannot attribute workspace changes to one task).";
28612
28628
  function allowedPath(file) {
28613
- return file.length > 0 && !path35.isAbsolute(file) && !/^[A-Za-z]:/.test(file) && !file.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".ordewell");
28629
+ return file.length > 0 && !path36.isAbsolute(file) && !/^[A-Za-z]:/.test(file) && !file.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".ordewell");
28614
28630
  }
28615
28631
  function stats(raw) {
28616
28632
  return raw.split("\0").flatMap((row) => {
@@ -28619,8 +28635,8 @@ function stats(raw) {
28619
28635
  });
28620
28636
  }
28621
28637
  function inside(root, target) {
28622
- const rel = path35.relative(root, target);
28623
- return rel === "" || !rel.startsWith(`..${path35.sep}`) && rel !== ".." && !path35.isAbsolute(rel);
28638
+ const rel = path36.relative(root, target);
28639
+ return rel === "" || !rel.startsWith(`..${path36.sep}`) && rel !== ".." && !path36.isAbsolute(rel);
28624
28640
  }
28625
28641
  function unquoted(quoted4) {
28626
28642
  const bytes = [];
@@ -28668,10 +28684,10 @@ function patchFiles(patch) {
28668
28684
  const sections = patch.toString("utf8").split(/(?<![^\n])(?=diff --git )/).filter((section) => section.startsWith("diff --git "));
28669
28685
  const files = /* @__PURE__ */ new Map();
28670
28686
  for (const section of sections) {
28671
- const path48 = sectionPath(section);
28687
+ const path50 = sectionPath(section);
28672
28688
  const counts = sectionStats(section);
28673
- const earlier = files.get(path48);
28674
- files.set(path48, earlier ? { path: path48, section: earlier.section + section, added: summed(earlier.added, counts.added), removed: summed(earlier.removed, counts.removed) } : { path: path48, section, ...counts });
28689
+ const earlier = files.get(path50);
28690
+ files.set(path50, earlier ? { path: path50, section: earlier.section + section, added: summed(earlier.added, counts.added), removed: summed(earlier.removed, counts.removed) } : { path: path50, section, ...counts });
28675
28691
  }
28676
28692
  return [...files.values()];
28677
28693
  }
@@ -28741,11 +28757,11 @@ async function readTaskDiff(invoker, run, taskId, options) {
28741
28757
  files.push({ ...file, repo: repo.path, patch: () => git(reader, cwd, [...diffArgs, "--", `:(top,literal)${prefix}${file.path}`]) });
28742
28758
  }
28743
28759
  if (live3) {
28744
- const workspace = path35.join(entry.worktree, prefix);
28760
+ const workspace = path36.join(entry.worktree, prefix);
28745
28761
  const others = await git(reader, workspace, [...common, "ls-files", "--others", "--exclude-standard", "-z", "--", "."]);
28746
28762
  for (const file of others.split("\0").filter((f) => allowed(f))) {
28747
- const full = path35.join(workspace, file);
28748
- if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(path35.dirname(full)))) continue;
28763
+ const full = path36.join(workspace, file);
28764
+ if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(path36.dirname(full)))) continue;
28749
28765
  const untracked = async (numstat) => {
28750
28766
  const result = await tryGit(reader, workspace, [...common, "diff", "--no-index", "--no-renames", "--no-ext-diff", "--no-textconv", ...numstat ? ["--numstat", "-z"] : [], "--", import_os3.devNull, file]);
28751
28767
  if (!result.ok && result.code !== 1) throw new Error(result.stderr.trim() || "Could not read an untracked file diff.");
@@ -28775,11 +28791,11 @@ ${await file.patch()}`);
28775
28791
  var import_crypto14 = require("crypto");
28776
28792
  var import_child_process9 = require("child_process");
28777
28793
  var fs15 = __toESM(require("fs"));
28778
- var path37 = __toESM(require("path"));
28794
+ var path38 = __toESM(require("path"));
28779
28795
 
28780
28796
  // src/utils/prdStore.ts
28781
28797
  var fs14 = __toESM(require("fs"));
28782
- var path36 = __toESM(require("path"));
28798
+ var path37 = __toESM(require("path"));
28783
28799
  var PRD_BLOCK_RE = /<!--\s*ORDEWELL_PRD_START\s+slug="([^"]+)"\s*-->\s*([\s\S]*?)\s*<!--\s*ORDEWELL_PRD_END\s*-->/;
28784
28800
  function extractPrdBlock(text6) {
28785
28801
  const match = PRD_BLOCK_RE.exec(text6);
@@ -28793,9 +28809,9 @@ function sanitizeSlug(raw) {
28793
28809
  return raw.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
28794
28810
  }
28795
28811
  function savePrdMarkdown(workspace, slug, markdown) {
28796
- const dir = path36.join(workspace, ".scratch", sanitizeSlug(slug));
28812
+ const dir = path37.join(workspace, ".scratch", sanitizeSlug(slug));
28797
28813
  fs14.mkdirSync(dir, { recursive: true });
28798
- const file = path36.join(dir, "PRD.md");
28814
+ const file = path37.join(dir, "PRD.md");
28799
28815
  fs14.writeFileSync(file, markdown.endsWith("\n") ? markdown : markdown + "\n", "utf8");
28800
28816
  return file;
28801
28817
  }
@@ -28827,12 +28843,12 @@ var NESTED_REPO_DEPTH = 2;
28827
28843
  var GITLINK_MODE = "160000";
28828
28844
  var READ_ONLY = ["--no-optional-locks", "-c", "core.fsmonitor=false"];
28829
28845
  function isInside(parent, child) {
28830
- const rel = path37.relative(parent, child);
28831
- return rel !== "" && !rel.startsWith("..") && !path37.isAbsolute(rel);
28846
+ const rel = path38.relative(parent, child);
28847
+ return rel !== "" && !rel.startsWith("..") && !path38.isAbsolute(rel);
28832
28848
  }
28833
28849
  function resolvesAsNamed(base, p) {
28834
28850
  try {
28835
- return fs15.realpathSync(p) === path37.join(fs15.realpathSync(base), path37.relative(base, p));
28851
+ return fs15.realpathSync(p) === path38.join(fs15.realpathSync(base), path38.relative(base, p));
28836
28852
  } catch {
28837
28853
  return false;
28838
28854
  }
@@ -28887,7 +28903,7 @@ function sameDir(a, b) {
28887
28903
  try {
28888
28904
  return fs15.realpathSync.native(p);
28889
28905
  } catch {
28890
- return path37.resolve(p);
28906
+ return path38.resolve(p);
28891
28907
  }
28892
28908
  };
28893
28909
  return real(a) === real(b);
@@ -28909,7 +28925,7 @@ function clearImmutableFlags(platform, dir) {
28909
28925
  (0, import_child_process9.spawnSync)("/usr/bin/chflags", ["-R", "nouchg", dir], { stdio: "ignore" });
28910
28926
  }
28911
28927
  function leftoverLinks(dir) {
28912
- const named3 = listDir2(dir).filter((name) => LINKED_ARTIFACTS.has(name) || isEnvFile(name) || isLink(path37.join(dir, name)));
28928
+ const named3 = listDir2(dir).filter((name) => LINKED_ARTIFACTS.has(name) || isEnvFile(name) || isLink(path38.join(dir, name)));
28913
28929
  return [.../* @__PURE__ */ new Set([...named3, ...installDirs(dir)])];
28914
28930
  }
28915
28931
  function isLink(target) {
@@ -28982,7 +28998,7 @@ var GitWorktreeIsolation = class {
28982
28998
  const head = await this.headOf(root);
28983
28999
  const repo = { path: repoPath, head };
28984
29000
  if (scan.toplevel !== void 0) {
28985
- const top = path37.relative(workspaceRoot, scan.toplevel).split(path37.sep).join("/");
29001
+ const top = path38.relative(workspaceRoot, scan.toplevel).split(path38.sep).join("/");
28986
29002
  if (top !== "") repo.top = top;
28987
29003
  }
28988
29004
  if (enabled && (head.kind === "branch" || head.kind === "detached")) {
@@ -29039,9 +29055,9 @@ var GitWorktreeIsolation = class {
29039
29055
  async nestedRepos(workspaceRoot, toplevel) {
29040
29056
  const found = [];
29041
29057
  const walk = (rel, depth) => {
29042
- for (const name of childDirs(path37.join(workspaceRoot, rel))) {
29058
+ for (const name of childDirs(path38.join(workspaceRoot, rel))) {
29043
29059
  const child = rel ? `${rel}/${name}` : name;
29044
- if (hasGitEntry(path37.join(workspaceRoot, child))) found.push(child);
29060
+ if (hasGitEntry(path38.join(workspaceRoot, child))) found.push(child);
29045
29061
  else if (depth < NESTED_REPO_DEPTH) walk(child, depth + 1);
29046
29062
  }
29047
29063
  };
@@ -29059,7 +29075,7 @@ var GitWorktreeIsolation = class {
29059
29075
  const [meta, file] = entry.split(" ");
29060
29076
  if (file !== void 0 && meta.startsWith(`${GITLINK_MODE} `)) paths.add(file);
29061
29077
  }
29062
- const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f", path37.join(toplevel, ".gitmodules"), "--get-regexp", "^submodule\\..*\\.path$"]);
29078
+ const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f", path38.join(toplevel, ".gitmodules"), "--get-regexp", "^submodule\\..*\\.path$"]);
29063
29079
  for (const entry of declared.stdout.split("\0")) {
29064
29080
  const value = entry.split("\n")[1];
29065
29081
  if (value) paths.add(value.replace(/\/+$/, ""));
@@ -29117,7 +29133,7 @@ var GitWorktreeIsolation = class {
29117
29133
  async acceptsWorktree(run, repo) {
29118
29134
  const dir = this.integrationDir(run, repo);
29119
29135
  ensureStateDirIgnored(run.workspaceRoot);
29120
- fs15.mkdirSync(path37.dirname(dir), { recursive: true });
29136
+ fs15.mkdirSync(path38.dirname(dir), { recursive: true });
29121
29137
  const added = await this.tryGit(repo.root, ["worktree", "add", "-q", "--no-checkout", dir, repo.integrationBranch]);
29122
29138
  await this.removeWorktreeDir(run, repo, dir, []);
29123
29139
  return added.ok;
@@ -29132,12 +29148,12 @@ var GitWorktreeIsolation = class {
29132
29148
  if (isolated.includes(SELF_REPO)) return [];
29133
29149
  const shared2 = [];
29134
29150
  const walk = (rel) => {
29135
- for (const name of listDir2(path37.join(workspaceRoot, rel))) {
29151
+ for (const name of listDir2(path38.join(workspaceRoot, rel))) {
29136
29152
  if (!rel && (name === STATE_DIR || name === ".git")) continue;
29137
29153
  const child = rel ? `${rel}/${name}` : name;
29138
29154
  if (isolated.includes(child)) continue;
29139
29155
  if (isolated.some((p) => p.startsWith(`${child}/`))) walk(child);
29140
- else if (resolves(path37.join(workspaceRoot, child))) shared2.push(child);
29156
+ else if (resolves(path38.join(workspaceRoot, child))) shared2.push(child);
29141
29157
  }
29142
29158
  };
29143
29159
  walk("");
@@ -29177,9 +29193,9 @@ var GitWorktreeIsolation = class {
29177
29193
  try {
29178
29194
  ensureStateDirIgnored(run.workspaceRoot);
29179
29195
  for (const repo of run.repos) {
29180
- const worktree = path37.join(dir, repo.path);
29196
+ const worktree = path38.join(dir, repo.path);
29181
29197
  const tip = (await this.git(repo.root, ["rev-parse", repo.integrationBranch])).trim();
29182
- fs15.mkdirSync(path37.dirname(worktree), { recursive: true });
29198
+ fs15.mkdirSync(path38.dirname(worktree), { recursive: true });
29183
29199
  await this.git(repo.root, ["worktree", "add", "-q", "-B", branch, worktree, tip]);
29184
29200
  record.repos[repo.path] = { worktree, linked: [], baseRef: tip };
29185
29201
  }
@@ -29204,13 +29220,13 @@ var GitWorktreeIsolation = class {
29204
29220
  setupFailed.push({ repo: repo.path, detail: err.detail });
29205
29221
  }
29206
29222
  entry.linked = boot.linked;
29207
- copied.push(...boot.copied.map((name) => path37.posix.join(repo.path, name)));
29223
+ copied.push(...boot.copied.map((name) => path38.posix.join(repo.path, name)));
29208
29224
  }
29209
29225
  for (const rel of run.shared) {
29210
- const source = path37.join(run.workspaceRoot, rel);
29211
- const target = path37.join(cwd, rel);
29226
+ const source = path38.join(run.workspaceRoot, rel);
29227
+ const target = path38.join(cwd, rel);
29212
29228
  if (!resolves(source) || lexists(target)) continue;
29213
- fs15.mkdirSync(path37.dirname(target), { recursive: true });
29229
+ fs15.mkdirSync(path38.dirname(target), { recursive: true });
29214
29230
  if (linkPath(source, target, this.platform) === "copy") copied.push(rel);
29215
29231
  const entry = self ? record.repos[SELF_REPO] : void 0;
29216
29232
  if (entry) entry.linked.push(rel);
@@ -29242,7 +29258,7 @@ var GitWorktreeIsolation = class {
29242
29258
  */
29243
29259
  async clearLeftovers(run, task2, names) {
29244
29260
  const removal = noRemoval();
29245
- const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch || path37.resolve(r.workspace) === path37.resolve(names.dir) || isInside(r.workspace, names.dir)));
29261
+ const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch || path38.resolve(r.workspace) === path38.resolve(names.dir) || isInside(r.workspace, names.dir)));
29246
29262
  if (holder) throw new Error(`Its worktree path or branch is held by the record of task ${holder.order} (${holder.title}); Ordewell will not remove it`);
29247
29263
  const found = [];
29248
29264
  for (const repo of run.repos) {
@@ -29252,14 +29268,14 @@ var GitWorktreeIsolation = class {
29252
29268
  worktree: /^worktree (.+)$/m.exec(block)?.[1],
29253
29269
  branch: /^branch refs\/heads\/(.+)$/m.exec(block)?.[1]
29254
29270
  }));
29255
- const at = /* @__PURE__ */ new Set([path37.join(names.dir, repo.path)]);
29271
+ const at = /* @__PURE__ */ new Set([path38.join(names.dir, repo.path)]);
29256
29272
  for (const entry of registered) {
29257
- if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run), path37.resolve(entry.worktree))) at.add(entry.worktree);
29273
+ if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run), path38.resolve(entry.worktree))) at.add(entry.worktree);
29258
29274
  }
29259
- const base = { branch: names.branch, name: path37.basename(names.dir), title: task2.title };
29275
+ const base = { branch: names.branch, name: path38.basename(names.dir), title: task2.title };
29260
29276
  for (const worktree of at) {
29261
29277
  if (!fs15.existsSync(worktree)) continue;
29262
- const linked = leftoverLinks(path37.join(worktree, await this.prefixOf(repo)));
29278
+ const linked = leftoverLinks(path38.join(worktree, await this.prefixOf(repo)));
29263
29279
  found.push({ repo, worktree, target: { ...base, worktree, linked } });
29264
29280
  }
29265
29281
  found.push({ repo, worktree: names.dir, target: { ...base, linked: [] } });
@@ -29352,7 +29368,7 @@ var GitWorktreeIsolation = class {
29352
29368
  */
29353
29369
  async inWorkspacePlace(repo, worktree) {
29354
29370
  const prefix = await this.prefixOf(repo);
29355
- return prefix ? path37.join(worktree, prefix) : worktree;
29371
+ return prefix ? path38.join(worktree, prefix) : worktree;
29356
29372
  }
29357
29373
  async prefixOf(repo) {
29358
29374
  return (await this.git(repo.root, ["rev-parse", "--show-prefix"])).trim();
@@ -29882,15 +29898,15 @@ var GitWorktreeIsolation = class {
29882
29898
  return landingMessage(kind, from, this.deps.plannedWith?.() ?? false);
29883
29899
  }
29884
29900
  integrationDir(run, repo) {
29885
- return path37.join(this.runRoot(run), INTEGRATION_DIR, repo.path);
29901
+ return path38.join(this.runRoot(run), INTEGRATION_DIR, repo.path);
29886
29902
  }
29887
29903
  async ensureIntegrationWorktree(run, repo) {
29888
29904
  return this.admin(run.workspaceRoot, async () => {
29889
29905
  const dir = this.integrationDir(run, repo);
29890
- if (fs15.existsSync(path37.join(dir, ".git"))) return dir;
29906
+ if (fs15.existsSync(path38.join(dir, ".git"))) return dir;
29891
29907
  await this.tryGit(repo.root, ["worktree", "prune"]);
29892
29908
  ensureStateDirIgnored(run.workspaceRoot);
29893
- fs15.mkdirSync(path37.dirname(dir), { recursive: true });
29909
+ fs15.mkdirSync(path38.dirname(dir), { recursive: true });
29894
29910
  await this.git(repo.root, ["worktree", "add", "-q", dir, repo.integrationBranch]);
29895
29911
  return dir;
29896
29912
  });
@@ -29913,7 +29929,7 @@ var GitWorktreeIsolation = class {
29913
29929
  const removal = noRemoval();
29914
29930
  for (const repo of opts.preserve === false || record.readOnly ? [] : run.repos) {
29915
29931
  const entry = record.repos[repo.path];
29916
- const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name: path37.basename(record.workspace), title: record.title, commit: record.commit };
29932
+ const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name: path38.basename(record.workspace), title: record.title, commit: record.commit };
29917
29933
  try {
29918
29934
  const kept = await this.preserve(run, repo, target, opts.intent);
29919
29935
  if (kept) removal.preserved.push({ task: task2, ...kept });
@@ -29994,7 +30010,7 @@ var GitWorktreeIsolation = class {
29994
30010
  return;
29995
30011
  }
29996
30012
  for (const entry of entries) {
29997
- const target = path37.join(dir, entry.name);
30013
+ const target = path38.join(dir, entry.name);
29998
30014
  if (entry.isSymbolicLink()) this.unlinkIfLink(target);
29999
30015
  else if (entry.isDirectory()) this.unlinkLinksUnder(target);
30000
30016
  }
@@ -30016,20 +30032,20 @@ var GitWorktreeIsolation = class {
30016
30032
  return false;
30017
30033
  }
30018
30034
  };
30019
- const owned = new Set(Object.values(run.tasks).map((r) => path37.resolve(r.workspace)));
30035
+ const owned = new Set(Object.values(run.tasks).map((r) => path38.resolve(r.workspace)));
30020
30036
  const root = this.runRoot(run);
30021
30037
  if (fs15.existsSync(root)) {
30022
30038
  for (const entry of fs15.readdirSync(root)) {
30023
- const dir = path37.join(root, entry);
30024
- if (entry === INTEGRATION_DIR || owned.has(path37.resolve(dir))) continue;
30039
+ const dir = path38.join(root, entry);
30040
+ if (entry === INTEGRATION_DIR || owned.has(path38.resolve(dir))) continue;
30025
30041
  let kept = true;
30026
30042
  for (const repo of run.repos) {
30027
- const worktree = path37.join(dir, repo.path);
30028
- const linked = leftoverLinks(path37.join(worktree, await this.prefixOf(repo)));
30043
+ const worktree = path38.join(dir, repo.path);
30044
+ const linked = leftoverLinks(path38.join(worktree, await this.prefixOf(repo)));
30029
30045
  if (!await keep2(repo, { worktree, linked, branch: null, name: entry, title: entry }, worktree)) kept = false;
30030
30046
  }
30031
30047
  if (!kept) continue;
30032
- for (const repo of run.repos) await this.removeWorktreeDir(run, repo, path37.join(dir, repo.path), []);
30048
+ for (const repo of run.repos) await this.removeWorktreeDir(run, repo, path38.join(dir, repo.path), []);
30033
30049
  this.removeTaskWorkspace(run, dir);
30034
30050
  }
30035
30051
  }
@@ -30047,13 +30063,13 @@ var GitWorktreeIsolation = class {
30047
30063
  }
30048
30064
  async removeWorktreeDir(run, repo, dir, linked) {
30049
30065
  for (const name of /* @__PURE__ */ new Set([...linked, ...installDirs(dir)])) {
30050
- const target = path37.join(dir, name);
30051
- if (!resolvesAsNamed(this.runRoot(run), path37.dirname(target))) continue;
30066
+ const target = path38.join(dir, name);
30067
+ if (!resolvesAsNamed(this.runRoot(run), path38.dirname(target))) continue;
30052
30068
  this.unlinkIfLink(target);
30053
30069
  this.unlinkLinksUnder(target);
30054
30070
  }
30055
30071
  for (const name of resolvesAsNamed(this.runRoot(run), dir) ? fs15.readdirSync(dir) : []) {
30056
- if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(path37.join(dir, name));
30072
+ if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(path38.join(dir, name));
30057
30073
  }
30058
30074
  await this.tryGit(repo.root, ["worktree", "remove", "--force", dir]);
30059
30075
  if (fs15.existsSync(dir) && isInside(this.runRoot(run), dir)) {
@@ -30061,7 +30077,7 @@ var GitWorktreeIsolation = class {
30061
30077
  fs15.rmSync(dir, { recursive: true, force: true });
30062
30078
  }
30063
30079
  await this.tryGit(repo.root, ["worktree", "prune"]);
30064
- for (let parent = path37.dirname(dir); isInside(this.runRoot(run), parent); parent = path37.dirname(parent)) this.removeIfEmpty(parent);
30080
+ for (let parent = path38.dirname(dir); isInside(this.runRoot(run), parent); parent = path38.dirname(parent)) this.removeIfEmpty(parent);
30065
30081
  }
30066
30082
  unlinkIfLink(target) {
30067
30083
  try {
@@ -30076,14 +30092,14 @@ var GitWorktreeIsolation = class {
30076
30092
  }
30077
30093
  }
30078
30094
  runRoot(run) {
30079
- return path37.join(run.workspaceRoot, STATE_DIR, "worktrees", run.id);
30095
+ return path38.join(run.workspaceRoot, STATE_DIR, "worktrees", run.id);
30080
30096
  }
30081
30097
  namesFor(run, task2) {
30082
30098
  const slug = sanitizeSlug(task2.title).slice(0, 40).replace(/-+$/, "") || "task";
30083
30099
  let name = `${task2.order}-${slug}`;
30084
30100
  const clash = Object.values(run.tasks).some((r) => r.taskId !== task2.id && r.branch === `ordewell/${run.id}/${name}`);
30085
30101
  if (clash) name = `${name}-${sanitizeSlug(task2.id) || "x"}`;
30086
- return { branch: `ordewell/${run.id}/${name}`, dir: path37.join(this.runRoot(run), name) };
30102
+ return { branch: `ordewell/${run.id}/${name}`, dir: path38.join(this.runRoot(run), name) };
30087
30103
  }
30088
30104
  async unmergedPaths(cwd) {
30089
30105
  const listed3 = await this.tryGit(cwd, ["diff", "--name-only", "-z", "--diff-filter=U"]);
@@ -30097,7 +30113,7 @@ var GitWorktreeIsolation = class {
30097
30113
  async rebaseInProgress(cwd) {
30098
30114
  for (const dir of ["rebase-merge", "rebase-apply"]) {
30099
30115
  const found = await this.tryGit(cwd, ["rev-parse", "--git-path", dir]);
30100
- if (found.ok && fs15.existsSync(path37.resolve(cwd, found.stdout.trim()))) return true;
30116
+ if (found.ok && fs15.existsSync(path38.resolve(cwd, found.stdout.trim()))) return true;
30101
30117
  }
30102
30118
  return false;
30103
30119
  }
@@ -30171,6 +30187,29 @@ function describeIsolationPreview(preview) {
30171
30187
  }
30172
30188
  }
30173
30189
 
30190
+ // src/services/repositoryRunnerConfig.ts
30191
+ var RUNNER_CONFIG_ROOTS = [".mcp.json", ".claude", ".claude-plugin", "opencode.json", "opencode.jsonc", ".opencode", ".codex"];
30192
+ function repositoryRunnerConfigPaths(paths) {
30193
+ return [...new Set(paths.filter(
30194
+ (path50) => (
30195
+ // Project MCP servers start commands or send data to a repository-chosen URL.
30196
+ path50 === ".mcp.json" || // Settings can run hooks and helpers, load plugins, or redirect traffic.
30197
+ path50 === ".claude/settings.json" || // Local settings have the same execution capabilities as project settings.
30198
+ path50 === ".claude/settings.local.json" || // Hook files provide commands invoked by the runner.
30199
+ path50.startsWith(".claude/hooks/") || // Plugin files can provide code and hooks loaded by the runner.
30200
+ path50.startsWith(".claude/plugins/") || // Plugin metadata can load plugins and their executable components.
30201
+ path50.startsWith(".claude-plugin/") || // Plugin and marketplace manifests can select code for the runner to load.
30202
+ /^\.claude\/(?:[^/]+\/)*(?:plugin|marketplace)\.json$/.test(path50) || // OpenCode config declares MCP servers and plugins; its plugin and tool
30203
+ // directories are code it loads, and an agent definition sets the prompt,
30204
+ // model and permissions of the built-in agent its id names, which a task
30205
+ // launches with. The rest of `.opencode/` is dependencies OpenCode
30206
+ // installs itself, which a later launch must not be held for.
30207
+ /^(?:\.opencode\/)?opencode\.jsonc?$/.test(path50) || /^\.opencode\/(?:plugins?|tools?|agents?)\//.test(path50) || // Codex project config carries MCP servers, hooks and `notify` commands.
30208
+ path50.startsWith(".codex/")
30209
+ )
30210
+ ))].sort();
30211
+ }
30212
+
30174
30213
  // src/services/mergeWording.ts
30175
30214
  function isRepoGroup(handoff) {
30176
30215
  return handoff.repos.some((r) => r.path !== ".");
@@ -30205,7 +30244,7 @@ function handoffBaseLine(handoff, length) {
30205
30244
  return handoff.repos.map((repo) => `${group ? `${repo.path}: ` : ""}${repoBaseLine(repo, length)}`).join(", ");
30206
30245
  }
30207
30246
  function repoResultLines(handoff) {
30208
- return handoff.repos.map(({ path: path48, landed: landed2 }) => `${path48}: ${landed2.length === 0 ? "nothing to merge" : `${landed2.length} task${landed2.length === 1 ? "" : "s"} landed`}`);
30247
+ return handoff.repos.map(({ path: path50, landed: landed2 }) => `${path50}: ${landed2.length === 0 ? "nothing to merge" : `${landed2.length} task${landed2.length === 1 ? "" : "s"} landed`}`);
30209
30248
  }
30210
30249
 
30211
30250
  // src/interfaces/ILogger.ts
@@ -33264,6 +33303,7 @@ var IsolationRunController = class {
33264
33303
  /** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
33265
33304
  resolvers = {};
33266
33305
  opening = null;
33306
+ opened = 0;
33267
33307
  /** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
33268
33308
  activations = 0;
33269
33309
  /** The start a dirty tree turned away, handed back once the user chooses how to go on. */
@@ -33295,6 +33335,10 @@ var IsolationRunController = class {
33295
33335
  get isOpen() {
33296
33336
  return this.mode !== null;
33297
33337
  }
33338
+ /** How many runs have opened: what tells one run's launches from the next one's. */
33339
+ get epoch() {
33340
+ return this.opened;
33341
+ }
33298
33342
  /** The open run has decided whether it isolates. */
33299
33343
  get decided() {
33300
33344
  return this.mode === "isolated" || this.mode === "shared";
@@ -33392,6 +33436,7 @@ var IsolationRunController = class {
33392
33436
  open() {
33393
33437
  if (this.mode) return;
33394
33438
  this.mode = "undecided";
33439
+ this.opened += 1;
33395
33440
  }
33396
33441
  /**
33397
33442
  * Decide once per run, as its first change task starts, whether it executes
@@ -34391,7 +34436,7 @@ function heldFrom(state) {
34391
34436
  }
34392
34437
 
34393
34438
  // src/services/Attempts.ts
34394
- var path40 = __toESM(require("path"));
34439
+ var path42 = __toESM(require("path"));
34395
34440
 
34396
34441
  // src/services/VerdictEngine.ts
34397
34442
  var IDLE_TIMEOUT_MS = 10 * 6e4;
@@ -34779,10 +34824,132 @@ function reportedVerdict(report) {
34779
34824
  return { outcome: "fail", reason: `The runner reported the task ${report.status}: ${why}`, checks, decidedAt, reported };
34780
34825
  }
34781
34826
 
34827
+ // src/services/taskStartConfig.ts
34828
+ var path39 = __toESM(require("path"));
34829
+ var import_crypto15 = require("crypto");
34830
+ var import_fs13 = require("fs");
34831
+ var MAX_FILES = 2e3;
34832
+ var MAX_DEPTH = 8;
34833
+ function liveConfig(dir) {
34834
+ const found = /* @__PURE__ */ new Map();
34835
+ const visit = (rel, depth) => {
34836
+ if (found.size >= MAX_FILES || depth > MAX_DEPTH) return;
34837
+ const full = path39.join(dir, rel);
34838
+ try {
34839
+ const info = (0, import_fs13.statSync)(full);
34840
+ if (info.isFile()) {
34841
+ found.set(rel, (0, import_crypto15.createHash)("sha1").update((0, import_fs13.readFileSync)(full)).digest("hex"));
34842
+ } else if (info.isDirectory() && path39.basename(rel) !== "node_modules") {
34843
+ for (const name of (0, import_fs13.readdirSync)(full)) visit(path39.posix.join(rel, name), depth + 1);
34844
+ }
34845
+ } catch {
34846
+ }
34847
+ };
34848
+ for (const root of RUNNER_CONFIG_ROOTS) visit(root, 0);
34849
+ const selected = new Set(repositoryRunnerConfigPaths([...found.keys()]));
34850
+ return new Map([...found].filter(([name]) => selected.has(name)));
34851
+ }
34852
+ function linkedRoots(dir) {
34853
+ const linked = /* @__PURE__ */ new Set();
34854
+ for (const root of RUNNER_CONFIG_ROOTS) {
34855
+ try {
34856
+ if ((0, import_fs13.lstatSync)(path39.join(dir, root)).isSymbolicLink()) linked.add(root);
34857
+ } catch {
34858
+ }
34859
+ }
34860
+ return linked;
34861
+ }
34862
+ function readDirs(root) {
34863
+ const dirs = [{ dir: root, prefix: "" }];
34864
+ for (let dir = root, prefix = ""; !(0, import_fs13.existsSync)(path39.join(dir, ".git")); ) {
34865
+ const parent = path39.dirname(dir);
34866
+ if (parent === dir) return [dirs[0]];
34867
+ dir = parent;
34868
+ prefix += "../";
34869
+ dirs.push({ dir, prefix });
34870
+ }
34871
+ return dirs;
34872
+ }
34873
+ var TaskStartConfig = class {
34874
+ live = /* @__PURE__ */ new Map();
34875
+ /**
34876
+ * `epoch` names the run the check belongs to; a new run takes a new
34877
+ * baseline. `root` is the workspace root, the checkout a worktree run lands
34878
+ * in; `cwd` is where the runner starts, and `worktree` whether that is a
34879
+ * worktree prepared for it.
34880
+ */
34881
+ async check(run, taskId, where) {
34882
+ const found = where.worktree ? await this.gitDifferences(run, taskId) : [];
34883
+ const id2 = String(where.epoch);
34884
+ const checkout = readDirs(where.root).flatMap(({ dir, prefix }) => this.liveDifferences(`${id2}:checkout:${dir}`, dir).map((name) => `${prefix}${name}`));
34885
+ if (!where.worktree) found.push(...checkout);
34886
+ else {
34887
+ const cwd = where.cwd ?? run?.tasks[taskId]?.workspace;
34888
+ if (cwd) found.push(...this.worktreeDifferences(id2, cwd, where.root));
34889
+ }
34890
+ return [...new Set(found)].sort();
34891
+ }
34892
+ /** The baselines go with the plan they belong to. */
34893
+ clear() {
34894
+ this.live.clear();
34895
+ }
34896
+ /**
34897
+ * What a worktree's runner reads that differs from where the run started.
34898
+ * The `.claude` and `.opencode` directories it links from the checkout are
34899
+ * the checkout's own, so they are compared with the checkout's first state,
34900
+ * which an earlier task's write there does not move. Anything else is the
34901
+ * worktree's, and is compared with its own first state: a setup command's
34902
+ * files are trusted, and a task's later write is not.
34903
+ */
34904
+ worktreeDifferences(id2, dir, root) {
34905
+ const now = liveConfig(dir);
34906
+ const linked = linkedRoots(dir);
34907
+ const fromCheckout = (name) => linked.has(name.split("/")[0]);
34908
+ const key = `${id2}:worktree:${dir}`;
34909
+ let local = this.live.get(key);
34910
+ if (!local) {
34911
+ local = new Map([...now].filter(([name]) => !fromCheckout(name)));
34912
+ this.live.set(key, local);
34913
+ }
34914
+ const checkout = this.live.get(`${id2}:checkout:${root}`);
34915
+ return [...now].filter(([name, hash]) => fromCheckout(name) ? checkout?.get(name) !== hash : local.get(name) !== hash).map(([name]) => name);
34916
+ }
34917
+ liveDifferences(key, dir) {
34918
+ const now = liveConfig(dir);
34919
+ const before = this.live.get(key);
34920
+ if (!before) {
34921
+ this.live.set(key, now);
34922
+ return [];
34923
+ }
34924
+ return [...now].filter(([name, hash]) => before.get(name) !== hash).map(([name]) => name);
34925
+ }
34926
+ async gitDifferences(run, taskId) {
34927
+ if (!run) return [];
34928
+ const invoker = { exec: defaultExecFile2, resolvePath: augmentedPath };
34929
+ const found = [];
34930
+ for (const repo of run.repos) {
34931
+ const cwd = run.tasks[taskId]?.repos[repo.path]?.worktree;
34932
+ if (!cwd || !repo.baseRef || !(0, import_fs13.existsSync)(cwd)) continue;
34933
+ const prefix = repo.path === "." ? (await tryGit(invoker, repo.root, ["rev-parse", "--show-prefix"])).stdout.trim() : "";
34934
+ const roots = [...RUNNER_CONFIG_ROOTS, ...prefix ? RUNNER_CONFIG_ROOTS.map((r) => `${prefix}${r}`) : []];
34935
+ const diff = await tryGit(invoker, cwd, ["diff", "--no-ext-diff", "--no-textconv", "--no-renames", "--name-only", "--diff-filter=ACMRTU", "-z", repo.baseRef, "--"]);
34936
+ const untracked = await tryGit(invoker, cwd, ["ls-files", "--others", "-z", "--", ...roots]);
34937
+ if (!diff.ok || !untracked.ok) continue;
34938
+ for (const name of [...diff.stdout.split("\0"), ...untracked.stdout.split("\0")]) {
34939
+ const here = prefix && name.startsWith(prefix) ? name.slice(prefix.length) : void 0;
34940
+ if (repositoryRunnerConfigPaths([name]).length || here !== void 0 && repositoryRunnerConfigPaths([here]).length) {
34941
+ found.push(repo.path === "." ? name : path39.posix.join(repo.path, name));
34942
+ }
34943
+ }
34944
+ }
34945
+ return found;
34946
+ }
34947
+ };
34948
+
34782
34949
  // src/services/ScriptRunner.ts
34783
34950
  var import_child_process11 = require("child_process");
34784
34951
  var fs16 = __toESM(require("fs"));
34785
- var path38 = __toESM(require("path"));
34952
+ var path40 = __toESM(require("path"));
34786
34953
  var import_events2 = require("events");
34787
34954
  var import_string_decoder2 = require("string_decoder");
34788
34955
 
@@ -34923,7 +35090,7 @@ var ScriptSession = class extends AbstractRunnerSession {
34923
35090
  async launchOf(step) {
34924
35091
  if ("argv" in step) {
34925
35092
  const [named3, ...args] = step.argv;
34926
- const program = this.setup.platform === "win32" && path38.win32.basename(named3) !== named3 && !path38.win32.isAbsolute(named3) ? path38.win32.resolve(this.setup.cwd, named3) : named3;
35093
+ const program = this.setup.platform === "win32" && path40.win32.basename(named3) !== named3 && !path40.win32.isAbsolute(named3) ? path40.win32.resolve(this.setup.cwd, named3) : named3;
34927
35094
  const plan = await planDirectLaunch(program, args, this.setup.launch);
34928
35095
  return { file: plan.file, args: plan.args, shell: false, ...plan.verbatim ? { verbatim: true } : {} };
34929
35096
  }
@@ -35156,10 +35323,10 @@ function passed(status) {
35156
35323
  }
35157
35324
 
35158
35325
  // src/services/PatchCapture.ts
35159
- var import_crypto15 = require("crypto");
35326
+ var import_crypto16 = require("crypto");
35160
35327
  var fs17 = __toESM(require("fs"));
35161
35328
  var os10 = __toESM(require("os"));
35162
- var path39 = __toESM(require("path"));
35329
+ var path41 = __toESM(require("path"));
35163
35330
 
35164
35331
  // src/services/readTask.ts
35165
35332
  function readTask2(args, readerId, plan, log) {
@@ -35426,7 +35593,7 @@ function linkInPatch(patch) {
35426
35593
  function readOwn(scratch, name, max) {
35427
35594
  let fd;
35428
35595
  try {
35429
- fd = fs17.openSync(path39.join(scratch, name), fs17.constants.O_RDONLY | (fs17.constants.O_NOFOLLOW ?? 0));
35596
+ fd = fs17.openSync(path41.join(scratch, name), fs17.constants.O_RDONLY | (fs17.constants.O_NOFOLLOW ?? 0));
35430
35597
  } catch (err) {
35431
35598
  throw new Refusal(`the capture left no ${name.split(".").pop()} Ordewell could read (${err.code ?? String(err)})`);
35432
35599
  }
@@ -35463,8 +35630,8 @@ async function captureHeld(runs, opts, signal) {
35463
35630
  if (!runs.touched) return { kind: "empty", reason: "no command of this task has run sealed, so its worktree holds only what Ordewell checked out.", flags: [] };
35464
35631
  const [worktree] = runs.worktrees;
35465
35632
  const scratch = runs.scratch;
35466
- const tag = `ordewell-patch-${(0, import_crypto15.randomBytes)(8).toString("hex")}`;
35467
- const stem = path39.join(scratch, tag);
35633
+ const tag = `ordewell-patch-${(0, import_crypto16.randomBytes)(8).toString("hex")}`;
35634
+ const stem = path41.join(scratch, tag);
35468
35635
  const own = (suffix, max = LISTING_BYTES) => readOwn(scratch, `${tag}.${suffix}`, max);
35469
35636
  try {
35470
35637
  const stage = await runs.run(stageScript(stem, opts.linked ?? []), worktree);
@@ -35502,7 +35669,7 @@ async function captureHeld(runs, opts, signal) {
35502
35669
  return {
35503
35670
  kind: "saved",
35504
35671
  patch,
35505
- stats: { base, ...countedIn(patch), bytes: patch.length, sha256: (0, import_crypto15.createHash)("sha256").update(patch).digest("hex"), flags: [...flags, ...flag("normalized", normalized2)] }
35672
+ stats: { base, ...countedIn(patch), bytes: patch.length, sha256: (0, import_crypto16.createHash)("sha256").update(patch).digest("hex"), flags: [...flags, ...flag("normalized", normalized2)] }
35506
35673
  };
35507
35674
  } catch (err) {
35508
35675
  if (err instanceof Refusal) return { kind: "refused", reason: `${err.message}.` };
@@ -35556,8 +35723,8 @@ async function indexEntries(invoker, worktree) {
35556
35723
  return entries;
35557
35724
  }
35558
35725
  async function trialApply(invoker, worktree, patchFile, scratch) {
35559
- const index = path39.resolve(worktree, (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--git-path", "index"])).trim());
35560
- const copy = path39.join(scratch, "index");
35726
+ const index = path41.resolve(worktree, (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--git-path", "index"])).trim());
35727
+ const copy = path41.join(scratch, "index");
35561
35728
  fs17.copyFileSync(index, copy);
35562
35729
  const trial = { ...invoker, exec: (file, args, opts) => invoker.exec(file, args, { ...opts, env: { ...opts.env, GIT_INDEX_FILE: copy } }) };
35563
35730
  const tried = await tryGit(trial, worktree, [...APPLY_GIT, "apply", "--cached", "--3way", "--whitespace=nowarn", patchFile]);
@@ -35577,7 +35744,7 @@ function gitRefused(lead, stderr) {
35577
35744
  function onDisk3(worktree, names) {
35578
35745
  return names.map((name) => {
35579
35746
  try {
35580
- const stat = fs17.lstatSync(path39.join(worktree, name), { bigint: true, throwIfNoEntry: false });
35747
+ const stat = fs17.lstatSync(path41.join(worktree, name), { bigint: true, throwIfNoEntry: false });
35581
35748
  return stat ? `${stat.mode} ${stat.ino} ${stat.size} ${stat.mtimeNs} ${stat.ctimeNs}` : "none";
35582
35749
  } catch (err) {
35583
35750
  return err.code ?? "unreadable";
@@ -35617,7 +35784,7 @@ function leavesWorktree(worktree, names, linked, held2, left) {
35617
35784
  if (i < parts.length && held2.get(at)?.some((entry) => entry.mode === SUBMODULE)) {
35618
35785
  return `the patch writes ${name}, under ${at}, a submodule, whose files are another repository's.`;
35619
35786
  }
35620
- const full = path39.join(worktree, ...parts.slice(0, i));
35787
+ const full = path41.join(worktree, ...parts.slice(0, i));
35621
35788
  const stat = fs17.lstatSync(full, { throwIfNoEntry: false });
35622
35789
  if (!stat) break;
35623
35790
  const last = i === parts.length;
@@ -35638,14 +35805,14 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
35638
35805
  if (patch.length !== saved.bytes) {
35639
35806
  return refused4(`the patch is ${byteSize(patch.length)} where its artifact says ${byteSize(saved.bytes)}, so it was cut short or changed after it was saved.`);
35640
35807
  }
35641
- if ((0, import_crypto15.createHash)("sha256").update(patch).digest("hex") !== saved.sha256) {
35808
+ if ((0, import_crypto16.createHash)("sha256").update(patch).digest("hex") !== saved.sha256) {
35642
35809
  return refused4("the patch is not the file that was saved: its hash is not the one its artifact records, so it was changed after it was saved.");
35643
35810
  }
35644
35811
  const link = linkInPatch(patch);
35645
35812
  if (link !== void 0) {
35646
35813
  return refused4(`the patch holds a link or a submodule (${JSON.stringify(link.slice(0, 80))}), which a saved patch never does, so it is not one that was saved.`);
35647
35814
  }
35648
- const dir = fs17.mkdtempSync(path39.join(os10.tmpdir(), "ordewell-apply-"));
35815
+ const dir = fs17.mkdtempSync(path41.join(os10.tmpdir(), "ordewell-apply-"));
35649
35816
  try {
35650
35817
  const toplevel = (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--show-toplevel"])).trim();
35651
35818
  if (fs17.realpathSync(toplevel) !== fs17.realpathSync(worktree)) return refused4(`${worktree} is not the root of a worktree of its own.`);
@@ -35655,7 +35822,7 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
35655
35822
  }
35656
35823
  const before = await unmergedPaths(invoker, worktree);
35657
35824
  if (before.length > 0) return refused4(`your worktree still has unresolved conflicts (${listed(before.slice(0, 10))}${before.length > 10 ? `, +${before.length - 10} more` : ""}). Resolve them and git add them, or undo them, then apply the patch.`);
35658
- const file = path39.join(dir, "saved.patch");
35825
+ const file = path41.join(dir, "saved.patch");
35659
35826
  fs17.writeFileSync(file, patch, { flag: "wx", mode: 384 });
35660
35827
  const held2 = await indexEntries(invoker, worktree);
35661
35828
  const named3 = await pathsNamed(invoker, worktree, file);
@@ -35805,6 +35972,11 @@ var TaskWebReader = class {
35805
35972
  };
35806
35973
 
35807
35974
  // src/services/Attempts.ts
35975
+ var TaskStartConfigError = class extends Error {
35976
+ constructor(paths) {
35977
+ super(`repository configuration ${paths.map((p) => JSON.stringify(p)).join(", ")} differs from where this run started; remove it to continue, or start a new run once your branch holds it`);
35978
+ }
35979
+ };
35808
35980
  var DEFAULT_GIT = { exec: defaultExecFile2, resolvePath: () => augmentedPath() };
35809
35981
  var Attempts = class {
35810
35982
  constructor(deps) {
@@ -35836,6 +36008,7 @@ var Attempts = class {
35836
36008
  live = /* @__PURE__ */ new Map();
35837
36009
  /** Bumped by every plan load: an ended attempt's wind-down belongs to the scope before it. */
35838
36010
  planScope = 0;
36011
+ startConfig = new TaskStartConfig();
35839
36012
  /** Each task's runners still winding down once stopped: a continue waits for them (ADR-0018, L1). */
35840
36013
  windDowns = /* @__PURE__ */ new Map();
35841
36014
  /**
@@ -36005,6 +36178,7 @@ var Attempts = class {
36005
36178
  const ended = this.endAll("load");
36006
36179
  this.planScope += 1;
36007
36180
  this.spawnCounts.clear();
36181
+ this.startConfig.clear();
36008
36182
  return ended;
36009
36183
  }
36010
36184
  /** A task leaving the plan: no later attempt of it is numbered from its runs. */
@@ -36137,9 +36311,17 @@ var Attempts = class {
36137
36311
  if (unavailable2) throw new Error(unavailable2);
36138
36312
  }
36139
36313
  if (kind.kind === "continuation") await this.windDowns.get(task2.id);
36314
+ if (!attempt.script && kind.kind === "repair") {
36315
+ const paths = await this.startConfig.check(runs.current, task2.id, { epoch: runs.epoch, root: this.deps.workspaceRoot(), cwd: null, worktree: true });
36316
+ if (paths.length) throw new TaskStartConfigError(paths);
36317
+ }
36140
36318
  const { cwd, worktree, previousWork, workspace } = await attemptCwd(kind, task2, runs);
36141
36319
  attempt.cwd = cwd;
36142
36320
  attempt.worktree = worktree;
36321
+ if (!attempt.script && kind.kind !== "repair" && !landsNothing(kind)) {
36322
+ const paths = await this.startConfig.check(runs.current, task2.id, { epoch: runs.epoch, root: this.deps.workspaceRoot(), cwd, worktree });
36323
+ if (paths.length) throw new TaskStartConfigError(paths);
36324
+ }
36143
36325
  if (checksTree(kind)) attempt.snapshot = await runs.snapshotWorkspace();
36144
36326
  if (this.live.get(task2.id) !== attempt) {
36145
36327
  if (attempt.worktree && !this.live.has(task2.id) && !this.host.completing(task2.id)) await runs.release(task2.id, { keep: kind.kind === "repair" });
@@ -36222,6 +36404,9 @@ var Attempts = class {
36222
36404
  return { kind: "usage-limited", taskId: task2.id, attempt, said: { message }, output: "" };
36223
36405
  }
36224
36406
  const failed = (failure2) => ({ kind: "spawn-failed", task: task2, attempt, failure: failure2 });
36407
+ if (err instanceof TaskStartConfigError) {
36408
+ return failed({ leaves: "held", hold: err.message, preserveWorktree: true, settles: false, notice: { level: "warn", text: `Task "${task2.title}" is on hold: ${err.message}` } });
36409
+ }
36225
36410
  if (attempt.kind.kind === "continuation" && !attachTimeout) {
36226
36411
  const refused4 = err instanceof RunnerStartupExit;
36227
36412
  const reason = unresumedMessage(task2, refused4 ? `${attempt.runner} could not find its saved session` : `could not start: ${message}`);
@@ -36358,9 +36543,9 @@ var Attempts = class {
36358
36543
  const repos = Object.values(this.deps.runs.openRecord(taskId)?.repos ?? {});
36359
36544
  if (repos.length !== 1 || cwd === null) return [];
36360
36545
  const [{ worktree, linked }] = repos;
36361
- const prefix = path40.relative(worktree, cwd).split(path40.sep).join("/");
36362
- if (prefix.startsWith("..") || path40.isAbsolute(prefix)) return [];
36363
- return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(path40.sep).join("/"));
36546
+ const prefix = path42.relative(worktree, cwd).split(path42.sep).join("/");
36547
+ if (prefix.startsWith("..") || path42.isAbsolute(prefix)) return [];
36548
+ return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(path42.sep).join("/"));
36364
36549
  }
36365
36550
  /**
36366
36551
  * A script task's start (ADR-0035): its body as the plan holds it now, run
@@ -36398,7 +36583,7 @@ var Attempts = class {
36398
36583
  this.deps.notifications.warn(message);
36399
36584
  };
36400
36585
  if (resolved.blockedEnvrc) {
36401
- warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path40.dirname(resolved.blockedEnvrc)} to use them.`);
36586
+ warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path42.dirname(resolved.blockedEnvrc)} to use them.`);
36402
36587
  }
36403
36588
  if (resolved.trackedEnvFile) {
36404
36589
  warn(`tracked:${resolved.trackedEnvFile}`, `Ignored ${resolved.trackedEnvFile}: git tracks it, and a committed file must not choose the environment agents run in. Untrack it to use it.`);
@@ -36424,7 +36609,7 @@ var Attempts = class {
36424
36609
  uncommittedSkillFolder(name) {
36425
36610
  const root = this.deps.workspaceRoot();
36426
36611
  const skill = this.deps.skillsAt(this.skillRoots(root)).findSkill(name);
36427
- return skill?.source === "workspace" ? path40.relative(root, path40.dirname(skill.path)).split(path40.sep).join("/") : void 0;
36612
+ return skill?.source === "workspace" ? path42.relative(root, path42.dirname(skill.path)).split(path42.sep).join("/") : void 0;
36428
36613
  }
36429
36614
  /**
36430
36615
  * The roots whose `.ordewell/skills/` an attempt at `cwd` reads: in a repo
@@ -36854,29 +37039,29 @@ function ancestors(task2, store) {
36854
37039
 
36855
37040
  // src/services/sessionLease.ts
36856
37041
  var fs19 = __toESM(require("fs"));
36857
- var path42 = __toESM(require("path"));
36858
- var import_crypto17 = require("crypto");
37042
+ var path44 = __toESM(require("path"));
37043
+ var import_crypto18 = require("crypto");
36859
37044
 
36860
37045
  // src/utils/sessionStore.ts
36861
37046
  var fs18 = __toESM(require("fs"));
36862
- var path41 = __toESM(require("path"));
37047
+ var path43 = __toESM(require("path"));
36863
37048
 
36864
37049
  // src/utils/sessionId.ts
36865
- var import_crypto16 = require("crypto");
37050
+ var import_crypto17 = require("crypto");
36866
37051
  function mintSessionId() {
36867
- return `session-${(0, import_crypto16.randomBytes)(16).toString("hex")}`;
37052
+ return `session-${(0, import_crypto17.randomBytes)(16).toString("hex")}`;
36868
37053
  }
36869
37054
 
36870
37055
  // src/utils/sessionStore.ts
36871
37056
  var SESSIONS_SUBDIR = "sessions";
36872
37057
  function getSessionsDir(baseDir) {
36873
- return path41.join(getStateDir(baseDir), SESSIONS_SUBDIR);
37058
+ return path43.join(getStateDir(baseDir), SESSIONS_SUBDIR);
36874
37059
  }
36875
37060
  function idSegment(id2) {
36876
37061
  return /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,127}$/.test(id2) ? id2 : `~${Buffer.from(id2, "utf-8").toString("hex")}`;
36877
37062
  }
36878
37063
  function sessionDataDir(sessionId, baseDir) {
36879
- return path41.join(getSessionsDir(baseDir), idSegment(sessionId));
37064
+ return path43.join(getSessionsDir(baseDir), idSegment(sessionId));
36880
37065
  }
36881
37066
  var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
36882
37067
  function isSessionMeta(value) {
@@ -36903,8 +37088,8 @@ function legacySessionFilename(session) {
36903
37088
  return `${filenameStem(session)}_${session.id.slice(0, 8)}.json`;
36904
37089
  }
36905
37090
  function dropLegacyFile(sessionsDir, meta) {
36906
- const legacy = path41.join(sessionsDir, legacySessionFilename(meta));
36907
- if (legacy === path41.join(sessionsDir, sessionFilename(meta)) || !fs18.existsSync(legacy)) return;
37091
+ const legacy = path43.join(sessionsDir, legacySessionFilename(meta));
37092
+ if (legacy === path43.join(sessionsDir, sessionFilename(meta)) || !fs18.existsSync(legacy)) return;
36908
37093
  try {
36909
37094
  const held2 = JSON.parse(fs18.readFileSync(legacy, "utf-8")).meta?.id;
36910
37095
  if (held2 === meta.id) fs18.unlinkSync(legacy);
@@ -36931,7 +37116,7 @@ function saveSession(plan, goal, baseDir, id2) {
36931
37116
  };
36932
37117
  dropRetiredPlanFields(session.plan);
36933
37118
  const filename = sessionFilename(meta);
36934
- const filePath = path41.join(sessionsDir, filename);
37119
+ const filePath = path43.join(sessionsDir, filename);
36935
37120
  writeFileAtomic(filePath, JSON.stringify(session, null, 2));
36936
37121
  dropLegacyFile(sessionsDir, meta);
36937
37122
  return meta;
@@ -36943,7 +37128,7 @@ function listSessions(baseDir, logger = defaultLogger) {
36943
37128
  return files.map((f) => {
36944
37129
  let parsed;
36945
37130
  try {
36946
- parsed = JSON.parse(fs18.readFileSync(path41.join(sessionsDir, f), "utf-8"));
37131
+ parsed = JSON.parse(fs18.readFileSync(path43.join(sessionsDir, f), "utf-8"));
36947
37132
  } catch (err) {
36948
37133
  if (err instanceof SyntaxError) return moveAside(sessionsDir, f, "is not valid JSON", logger);
36949
37134
  logger.warn("sessionStore", `failed to read session file ${f}; skipping`, err);
@@ -36964,7 +37149,7 @@ function listSessions(baseDir, logger = defaultLogger) {
36964
37149
  function moveAside(sessionsDir, file, reason, logger) {
36965
37150
  const aside = `${file}.${Date.now()}.corrupt`;
36966
37151
  try {
36967
- fs18.renameSync(path41.join(sessionsDir, file), path41.join(sessionsDir, aside));
37152
+ fs18.renameSync(path43.join(sessionsDir, file), path43.join(sessionsDir, aside));
36968
37153
  logger.warn("sessionStore", `session file ${file} ${reason}; moved aside to ${aside} rather than overwritten`);
36969
37154
  } catch {
36970
37155
  logger.warn("sessionStore", `failed to read session file ${file}; skipping`);
@@ -36975,7 +37160,7 @@ function findSessionFile(sessionsDir, sessionId, logger) {
36975
37160
  const files = fs18.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
36976
37161
  for (const f of files) {
36977
37162
  try {
36978
- const raw = fs18.readFileSync(path41.join(sessionsDir, f), "utf-8");
37163
+ const raw = fs18.readFileSync(path43.join(sessionsDir, f), "utf-8");
36979
37164
  const session = JSON.parse(raw);
36980
37165
  if (session.meta.id === sessionId) return f;
36981
37166
  } catch (err) {
@@ -36990,7 +37175,7 @@ function loadSession(sessionId, baseDir, logger = defaultLogger) {
36990
37175
  try {
36991
37176
  const file = findSessionFile(sessionsDir, sessionId, logger);
36992
37177
  if (file) {
36993
- const raw = fs18.readFileSync(path41.join(sessionsDir, file), "utf-8");
37178
+ const raw = fs18.readFileSync(path43.join(sessionsDir, file), "utf-8");
36994
37179
  const parsed = JSON.parse(raw);
36995
37180
  if (hasScalarRunner(parsed)) {
36996
37181
  logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
@@ -37032,7 +37217,7 @@ function deleteSession(sessionId, baseDir, logger = defaultLogger) {
37032
37217
  try {
37033
37218
  const file = findSessionFile(sessionsDir, sessionId, logger);
37034
37219
  if (file) {
37035
- fs18.unlinkSync(path41.join(sessionsDir, file));
37220
+ fs18.unlinkSync(path43.join(sessionsDir, file));
37036
37221
  return true;
37037
37222
  }
37038
37223
  } catch (err) {
@@ -37085,7 +37270,7 @@ var UNLEASED = {
37085
37270
  };
37086
37271
  var hostsHere = /* @__PURE__ */ new Set();
37087
37272
  function leaseFile(sessionId, workspace) {
37088
- return path42.join(sessionDataDir(sessionId, workspace), LEASE_FILE);
37273
+ return path44.join(sessionDataDir(sessionId, workspace), LEASE_FILE);
37089
37274
  }
37090
37275
  function isRecord9(value) {
37091
37276
  if (typeof value !== "object" || value === null) return false;
@@ -37121,7 +37306,7 @@ var LeaseHost = class {
37121
37306
  held = /* @__PURE__ */ new Map();
37122
37307
  heartbeat = null;
37123
37308
  constructor(options = {}) {
37124
- this.id = options.id ?? (0, import_crypto17.randomUUID)();
37309
+ this.id = options.id ?? (0, import_crypto18.randomUUID)();
37125
37310
  this.pid = options.pid ?? process.pid;
37126
37311
  this.kindOf = options.kind ?? (() => void 0);
37127
37312
  this.alive = options.alive ?? processAlive;
@@ -37174,7 +37359,7 @@ var LeaseHost = class {
37174
37359
  */
37175
37360
  acquire(file, workspace, holder) {
37176
37361
  ensureStateDirIgnored(workspace);
37177
- ensurePrivateDir(path42.dirname(file));
37362
+ ensurePrivateDir(path44.dirname(file));
37178
37363
  const outcome = withFileLock(file, () => {
37179
37364
  const current = readRecord(file);
37180
37365
  const taken = current !== null && current.hostId !== this.id ? current : null;
@@ -37229,7 +37414,7 @@ var LeaseHost = class {
37229
37414
  withFileLock(file, () => {
37230
37415
  if (readRecord(file)?.hostId === this.id) fs19.rmSync(file, { force: true });
37231
37416
  }, "release a session lease");
37232
- fs19.rmdirSync(path42.dirname(file));
37417
+ fs19.rmdirSync(path44.dirname(file));
37233
37418
  } catch {
37234
37419
  }
37235
37420
  }
@@ -38175,7 +38360,9 @@ var TaskOrchestrator = class _TaskOrchestrator {
38175
38360
  this.store.markPending(task2.id);
38176
38361
  this.hold(task2.id, failure2.hold, false);
38177
38362
  }
38178
- await this.runs.release(task2.id, { keep: failure2.leaves === "failed" && failure2.keepWorktree });
38363
+ if (failure2.leaves !== "held" || !failure2.preserveWorktree) {
38364
+ await this.runs.release(task2.id, { keep: failure2.leaves === "failed" && failure2.keepWorktree });
38365
+ }
38179
38366
  const { level, text: text6, toastOnly } = failure2.notice;
38180
38367
  if (toastOnly) this.notifications[level](text6);
38181
38368
  else this.tell(level, text6);
@@ -39988,8 +40175,8 @@ function codexWindow(slot, raw) {
39988
40175
 
39989
40176
  // src/services/harness/claudeAccountLimits.ts
39990
40177
  var import_child_process13 = require("child_process");
39991
- var import_crypto18 = require("crypto");
39992
- var import_fs13 = require("fs");
40178
+ var import_crypto19 = require("crypto");
40179
+ var import_fs14 = require("fs");
39993
40180
  var import_os5 = require("os");
39994
40181
  var import_path8 = require("path");
39995
40182
  var DEFAULT_TIMEOUT_MS5 = 2e4;
@@ -40023,10 +40210,10 @@ function claudeAccountLimitCollector(options) {
40023
40210
  };
40024
40211
  }
40025
40212
  async function readClaudeAccountLimits(options, signal) {
40026
- const cwd = (0, import_fs13.mkdtempSync)((0, import_path8.join)((0, import_os5.tmpdir)(), "ordewell-claude-limits-"));
40213
+ const cwd = (0, import_fs14.mkdtempSync)((0, import_path8.join)((0, import_os5.tmpdir)(), "ordewell-claude-limits-"));
40027
40214
  const removeCwd = () => {
40028
40215
  try {
40029
- (0, import_fs13.rmSync)(cwd, { recursive: true, force: true });
40216
+ (0, import_fs14.rmSync)(cwd, { recursive: true, force: true });
40030
40217
  } catch {
40031
40218
  }
40032
40219
  };
@@ -40068,7 +40255,7 @@ async function readClaudeAccountLimits(options, signal) {
40068
40255
  }
40069
40256
  function claudeAccountOwner(account) {
40070
40257
  const email = isRecord11(account) ? text5(account.email) : void 0;
40071
- if (!isRecord11(account) || !email) return `unnamed\0${(0, import_crypto18.randomUUID)()}`;
40258
+ if (!isRecord11(account) || !email) return `unnamed\0${(0, import_crypto19.randomUUID)()}`;
40072
40259
  return [text5(account.apiProvider) ?? "", email, text5(account.organization) ?? ""].join("\0");
40073
40260
  }
40074
40261
  function claudeLimitReading(usage, owner, accountPlan) {
@@ -40110,8 +40297,8 @@ function isoInstant(value) {
40110
40297
  var import_os7 = require("os");
40111
40298
 
40112
40299
  // src/services/harness/openCodeCredentials.ts
40113
- var import_crypto19 = require("crypto");
40114
- var import_fs14 = require("fs");
40300
+ var import_crypto20 = require("crypto");
40301
+ var import_fs15 = require("fs");
40115
40302
  var import_os6 = require("os");
40116
40303
  var import_path9 = require("path");
40117
40304
  var import_worker_threads = require("worker_threads");
@@ -40168,7 +40355,7 @@ function walStore(path) {
40168
40355
  parentPort.postMessage(select());
40169
40356
  `;
40170
40357
  var isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
40171
- var digest = (...parts) => (0, import_crypto19.createHash)("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
40358
+ var digest = (...parts) => (0, import_crypto20.createHash)("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
40172
40359
  function openCodeStorePaths(env, home) {
40173
40360
  const data = (0, import_path9.join)(env.XDG_DATA_HOME || (0, import_path9.join)(home, ".local", "share"), "opencode");
40174
40361
  const file = env.OPENCODE_DB || "opencode.db";
@@ -40234,8 +40421,8 @@ function v2Login(db, row) {
40234
40421
  login: { route: "console", owner: digest("console", db, row.id, account, orgID ?? ""), access: value.access, ...orgID ? { orgID } : {}, expires: value.expires }
40235
40422
  };
40236
40423
  }
40237
- async function legacyLogin(path48, env) {
40238
- const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(path48);
40424
+ async function legacyLogin(path50, env) {
40425
+ const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(path50);
40239
40426
  if (content === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
40240
40427
  if (content === null) return { kind: "unreadable" };
40241
40428
  const auth = parseJson(content);
@@ -40244,15 +40431,15 @@ async function legacyLogin(path48, env) {
40244
40431
  if (entry === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
40245
40432
  if (!isRecord12(entry) || entry.type !== "api") return { kind: "unsupported" };
40246
40433
  if (typeof entry.key !== "string" || entry.key.length === 0) return { kind: "unreadable" };
40247
- return { kind: "login", login: { route: "key", owner: digest("v1", path48, entry.key), key: entry.key } };
40434
+ return { kind: "login", login: { route: "key", owner: digest("v1", path50, entry.key), key: entry.key } };
40248
40435
  }
40249
- function queryStore(path48, signal, deps) {
40436
+ function queryStore(path50, signal, deps) {
40250
40437
  return new Promise((settle) => {
40251
40438
  let worker;
40252
40439
  try {
40253
40440
  worker = (deps.spawnWorker ?? ((source, options) => new import_worker_threads.Worker(source, options)))(WORKER_SOURCE, {
40254
40441
  eval: true,
40255
- workerData: { path: path48, integrations: GO_INTEGRATIONS, busyMs: BUSY_MS, maxValueBytes: MAX_VALUE_BYTES },
40442
+ workerData: { path: path50, integrations: GO_INTEGRATIONS, busyMs: BUSY_MS, maxValueBytes: MAX_VALUE_BYTES },
40256
40443
  execArgv: deps.workerExecArgv ?? [],
40257
40444
  resourceLimits: { maxOldGenerationSizeMb: 64 }
40258
40445
  });
@@ -40279,19 +40466,19 @@ function queryStore(path48, signal, deps) {
40279
40466
  worker.once("exit", aborted);
40280
40467
  });
40281
40468
  }
40282
- async function stamp(path48) {
40469
+ async function stamp(path50) {
40283
40470
  try {
40284
- const stat = await import_fs14.promises.stat(path48);
40471
+ const stat = await import_fs15.promises.stat(path50);
40285
40472
  return [stat.ino, stat.size, stat.mtimeMs];
40286
40473
  } catch {
40287
40474
  return null;
40288
40475
  }
40289
40476
  }
40290
- async function readSmall(path48) {
40477
+ async function readSmall(path50) {
40291
40478
  try {
40292
- const stat = await import_fs14.promises.stat(path48);
40479
+ const stat = await import_fs15.promises.stat(path50);
40293
40480
  if (!stat.isFile() || stat.size > MAX_AUTH_FILE_BYTES) return null;
40294
- return await import_fs14.promises.readFile(path48, "utf8");
40481
+ return await import_fs15.promises.readFile(path50, "utf8");
40295
40482
  } catch (err) {
40296
40483
  return err.code === "ENOENT" ? void 0 : null;
40297
40484
  }
@@ -41420,20 +41607,20 @@ function createAiService(config, deps) {
41420
41607
  }
41421
41608
 
41422
41609
  // src/services/createSession.ts
41423
- var path45 = __toESM(require("path"));
41610
+ var path47 = __toESM(require("path"));
41424
41611
 
41425
41612
  // src/services/handoffReferences.ts
41426
41613
  var fs21 = __toESM(require("fs"));
41427
- var path43 = __toESM(require("path"));
41614
+ var path45 = __toESM(require("path"));
41428
41615
  var import_child_process15 = require("child_process");
41429
41616
  function resolveHandoffReferences(handoff, workspace) {
41430
41617
  const checked = /* @__PURE__ */ new Map();
41431
41618
  const contained = (candidate) => {
41432
41619
  try {
41433
41620
  const root = fs21.realpathSync(workspace);
41434
- const resolved = fs21.realpathSync(path43.resolve(root, candidate));
41435
- const relative17 = path43.relative(root, resolved);
41436
- return relative17 !== ".." && !relative17.startsWith(`..${path43.sep}`) && !path43.isAbsolute(relative17) ? resolved : void 0;
41621
+ const resolved = fs21.realpathSync(path45.resolve(root, candidate));
41622
+ const relative17 = path45.relative(root, resolved);
41623
+ return relative17 !== ".." && !relative17.startsWith(`..${path45.sep}`) && !path45.isAbsolute(relative17) ? resolved : void 0;
41437
41624
  } catch {
41438
41625
  return void 0;
41439
41626
  }
@@ -41454,7 +41641,7 @@ function resolveHandoffReferences(handoff, workspace) {
41454
41641
  }
41455
41642
  } else if (/^[0-9a-fA-F]{4,64}$/.test(ref.sha)) {
41456
41643
  const repo = contained(ref.repo ?? ".");
41457
- if (repo && fs21.existsSync(path43.join(repo, ".git"))) {
41644
+ if (repo && fs21.existsSync(path45.join(repo, ".git"))) {
41458
41645
  const result2 = (0, import_child_process15.spawnSync)("git", ["--no-optional-locks", "--no-replace-objects", "cat-file", "-t", ref.sha], {
41459
41646
  cwd: repo,
41460
41647
  encoding: "utf8",
@@ -42450,7 +42637,7 @@ function surfacePendingTurns(pending) {
42450
42637
  function surfaceTask({ attemptSkills, subtasks, runnerSessionId: _runnerSessionId, ...task2 }) {
42451
42638
  return {
42452
42639
  ...task2,
42453
- ...attemptSkills ? { attemptSkills: attemptSkills.map(({ name, source, path: path48 }) => ({ name, source, path: path48 })) } : {},
42640
+ ...attemptSkills ? { attemptSkills: attemptSkills.map(({ name, source, path: path50 }) => ({ name, source, path: path50 })) } : {},
42454
42641
  subtasks: (subtasks ?? []).map(surfaceTask)
42455
42642
  };
42456
42643
  }
@@ -43230,10 +43417,10 @@ var SessionEventRelay = class {
43230
43417
 
43231
43418
  // src/utils/taskLogStore.ts
43232
43419
  var fs22 = __toESM(require("fs"));
43233
- var path44 = __toESM(require("path"));
43420
+ var path46 = __toESM(require("path"));
43234
43421
  var EXTENSION = ".jsonl";
43235
43422
  function taskDir({ baseDir, sessionId }, taskId) {
43236
- return path44.join(sessionDataDir(sessionId, baseDir), "tasks", idSegment(taskId));
43423
+ return path46.join(sessionDataDir(sessionId, baseDir), "tasks", idSegment(taskId));
43237
43424
  }
43238
43425
  function listTaskLogAttempts(location, taskId) {
43239
43426
  let names;
@@ -43251,7 +43438,7 @@ function openTaskLog(location, taskId, attempt) {
43251
43438
  const next = (listTaskLogAttempts(location, taskId).pop() ?? 0) + 1;
43252
43439
  let number = attempt !== void 0 && Number.isInteger(attempt) && attempt >= 1 ? attempt : next;
43253
43440
  for (; ; ) {
43254
- const file = path44.join(dir, `${number}${EXTENSION}`);
43441
+ const file = path46.join(dir, `${number}${EXTENSION}`);
43255
43442
  try {
43256
43443
  fs22.writeFileSync(file, "", { flag: "wx" });
43257
43444
  } catch (err) {
@@ -43275,15 +43462,15 @@ function prepareTaskOutputFile(location, taskId, attempt) {
43275
43462
  ensureStateDirIgnored(location.baseDir);
43276
43463
  const dir = taskDir(location, taskId);
43277
43464
  fs22.mkdirSync(dir, { recursive: true });
43278
- return path44.join(dir, `${attempt}.output.txt`);
43465
+ return path46.join(dir, `${attempt}.output.txt`);
43279
43466
  }
43280
43467
  function taskPatchFile(location, taskId, attempt) {
43281
- return path44.join(taskDir(location, taskId), `${attempt}.patch`);
43468
+ return path46.join(taskDir(location, taskId), `${attempt}.patch`);
43282
43469
  }
43283
43470
  function saveTaskPatch(location, taskId, attempt, patch) {
43284
43471
  ensureStateDirIgnored(location.baseDir);
43285
43472
  const file = taskPatchFile(location, taskId, attempt);
43286
- fs22.mkdirSync(path44.dirname(file), { recursive: true });
43473
+ fs22.mkdirSync(path46.dirname(file), { recursive: true });
43287
43474
  const partial = `${file}.${process.pid}-${Date.now()}.partial`;
43288
43475
  fs22.writeFileSync(partial, patch, { flag: "wx" });
43289
43476
  fs22.renameSync(partial, file);
@@ -43304,7 +43491,7 @@ function readTaskLog(location, taskId, attempt) {
43304
43491
  if (!Number.isInteger(attempt) || attempt < 1) return [];
43305
43492
  let raw;
43306
43493
  try {
43307
- raw = fs22.readFileSync(path44.join(taskDir(location, taskId), `${attempt}${EXTENSION}`), "utf-8");
43494
+ raw = fs22.readFileSync(path46.join(taskDir(location, taskId), `${attempt}${EXTENSION}`), "utf-8");
43308
43495
  } catch {
43309
43496
  return [];
43310
43497
  }
@@ -45366,7 +45553,7 @@ function createSession(deps) {
45366
45553
  ...deps.openTaskLog ? {} : {
45367
45554
  keepPatch: (taskId, attempt, patch) => {
45368
45555
  const location = session.taskLogLocation;
45369
- return path45.relative(location.baseDir, saveTaskPatch(location, taskId, attempt, patch)).split(path45.sep).join("/");
45556
+ return path47.relative(location.baseDir, saveTaskPatch(location, taskId, attempt, patch)).split(path47.sep).join("/");
45370
45557
  }
45371
45558
  },
45372
45559
  recordHandoff: (sessionId, taskId, handoff) => taskLogs.note(sessionId, taskId, { type: "handoff", handoff }),
@@ -49260,7 +49447,7 @@ var RunnerRegistry = class {
49260
49447
 
49261
49448
  // src/plugins/removedPluginNotice.ts
49262
49449
  var fs23 = __toESM(require("fs"));
49263
- var path46 = __toESM(require("path"));
49450
+ var path48 = __toESM(require("path"));
49264
49451
  function isManifestFile(file) {
49265
49452
  try {
49266
49453
  return fs23.statSync(file).isFile();
@@ -49269,10 +49456,10 @@ function isManifestFile(file) {
49269
49456
  }
49270
49457
  }
49271
49458
  function removedPluginNotice(dataDir = globalDataDir()) {
49272
- const directory = path46.join(dataDir, "plugins");
49459
+ const directory = path48.join(dataDir, "plugins");
49273
49460
  try {
49274
- const hasManifest = isManifestFile(path46.join(directory, "manifest.json")) || fs23.readdirSync(directory, { withFileTypes: true }).some(
49275
- (entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(path46.join(directory, entry.name, "manifest.json"))
49461
+ const hasManifest = isManifestFile(path48.join(directory, "manifest.json")) || fs23.readdirSync(directory, { withFileTypes: true }).some(
49462
+ (entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(path48.join(directory, entry.name, "manifest.json"))
49276
49463
  );
49277
49464
  if (hasManifest) return "Plugin runners were removed; manifests in ~/.ordewell/plugins/ are ignored. See the CHANGELOG.";
49278
49465
  } catch {
@@ -49283,15 +49470,15 @@ function removedPluginNotice(dataDir = globalDataDir()) {
49283
49470
 
49284
49471
  // src/utils/stateStore.ts
49285
49472
  var fs24 = __toESM(require("fs"));
49286
- var path47 = __toESM(require("path"));
49473
+ var path49 = __toESM(require("path"));
49287
49474
  var STATE_FILE2 = "state.json";
49288
49475
  function getStatePath(baseDir) {
49289
- return path47.join(getStateDir(baseDir), STATE_FILE2);
49476
+ return path49.join(getStateDir(baseDir), STATE_FILE2);
49290
49477
  }
49291
49478
  var SESSION_ID_KEY = "sessionId";
49292
49479
  function saveState(plan, baseDir, sessionId) {
49293
49480
  const statePath = getStatePath(baseDir ?? process.cwd());
49294
- ensureDir(path47.dirname(statePath));
49481
+ ensureDir(path49.dirname(statePath));
49295
49482
  plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
49296
49483
  const file = sessionId ? { ...plan, [SESSION_ID_KEY]: sessionId } : plan;
49297
49484
  dropRetiredPlanFields(file);
@@ -49840,6 +50027,7 @@ var PlannerModelMemory = class {
49840
50027
  repoBaseLine,
49841
50028
  repoResultLines,
49842
50029
  reposWithWork,
50030
+ repositoryRunnerConfigPaths,
49843
50031
  researchShellWarning,
49844
50032
  researchToolsPath,
49845
50033
  resetInstalledPlanner,