@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.mjs CHANGED
@@ -4358,7 +4358,7 @@ function pathRefs(command, opts = {}) {
4358
4358
  return lexAll(command.trim(), dialect, opts.cdpathSet).segments.flatMap((seg) => {
4359
4359
  const patterns = patternArgs(seg);
4360
4360
  const cwd = seg.cwd ?? [];
4361
- return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path47) => ({ path: path47, cwd }));
4361
+ return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path49) => ({ path: path49, cwd }));
4362
4362
  });
4363
4363
  }
4364
4364
  function excludedBy(command, allow, opts = {}) {
@@ -6442,7 +6442,7 @@ ${outcome.output}${outcome.success ? "" : `
6442
6442
  }
6443
6443
  let shell;
6444
6444
  try {
6445
- const sealed = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path: path47, directory }) => ({ path: path47, directory })) } : site.sealed;
6445
+ const sealed = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path: path49, directory }) => ({ path: path49, directory })) } : site.sealed;
6446
6446
  shell = await site.sandbox.launchSealed(this.researchShell, sealed);
6447
6447
  } catch (err) {
6448
6448
  return { refused: refusal(`Command not run: its sealed sandbox could not be prepared (${err instanceof Error ? err.message : String(err)}).`) };
@@ -8225,9 +8225,9 @@ function claudeManagedSettingsDir(platform = process.platform) {
8225
8225
  }
8226
8226
  function readClaudeSettingsModels(files, platform = process.platform) {
8227
8227
  const managed = claudeManagedSettingsDir(platform);
8228
- const join43 = platform === "win32" ? path12.win32.join : path12.posix.join;
8229
- const dropIns = files.list(join43(managed, "managed-settings.d")).filter((name) => name.endsWith(".json") && !name.startsWith(".")).sort().map((name) => join43(managed, "managed-settings.d", name));
8230
- const ranked = [...dropIns.reverse(), join43(managed, "managed-settings.json"), join43(claudeConfigDir(files.env), "settings.json")];
8228
+ const join45 = platform === "win32" ? path12.win32.join : path12.posix.join;
8229
+ const dropIns = files.list(join45(managed, "managed-settings.d")).filter((name) => name.endsWith(".json") && !name.startsWith(".")).sort().map((name) => join45(managed, "managed-settings.d", name));
8230
+ const ranked = [...dropIns.reverse(), join45(managed, "managed-settings.json"), join45(claudeConfigDir(files.env), "settings.json")];
8231
8231
  for (const file of ranked) {
8232
8232
  const content = files.read(file);
8233
8233
  const lineup = content === null ? null : parseClaudeSettingsModels(content);
@@ -9088,12 +9088,12 @@ function checkpointReply(answer2) {
9088
9088
  return { text: `The checkpoint was withdrawn: ${answer2.why}`, isError: true };
9089
9089
  }
9090
9090
  }
9091
- function fieldPath(path47) {
9092
- return path47.reduce((text4, part) => typeof part === "number" ? `${text4}[${part}]` : text4 ? `${text4}.${String(part)}` : String(part), "");
9091
+ function fieldPath(path49) {
9092
+ return path49.reduce((text4, part) => typeof part === "number" ? `${text4}[${part}]` : text4 ? `${text4}.${String(part)}` : String(part), "");
9093
9093
  }
9094
- function valueAt(value, path47) {
9094
+ function valueAt(value, path49) {
9095
9095
  let current = value;
9096
- for (const part of path47) {
9096
+ for (const part of path49) {
9097
9097
  if (typeof part === "number") {
9098
9098
  if (!Array.isArray(current)) return void 0;
9099
9099
  current = current[part];
@@ -9115,7 +9115,7 @@ function boundedRefusal(issue, args) {
9115
9115
  var LEAKED_PARAMETER = /<\/parameter>|<parameter\s+name\s*=\s*"([^"]*)">/g;
9116
9116
  function leakedMarkupIn(value) {
9117
9117
  const found = [];
9118
- const walk = (node, path47) => {
9118
+ const walk = (node, path49) => {
9119
9119
  if (typeof node === "string") {
9120
9120
  const names = [];
9121
9121
  let closing = false;
@@ -9123,25 +9123,25 @@ function leakedMarkupIn(value) {
9123
9123
  if (match[1] !== void 0) names.push(match[1]);
9124
9124
  else closing = true;
9125
9125
  }
9126
- if (names.length > 0 || closing) found.push({ path: path47, names: [...new Set(names)], closing });
9126
+ if (names.length > 0 || closing) found.push({ path: path49, names: [...new Set(names)], closing });
9127
9127
  return;
9128
9128
  }
9129
9129
  if (Array.isArray(node)) {
9130
- node.forEach((item, i) => walk(item, `${path47}[${i}]`));
9130
+ node.forEach((item, i) => walk(item, `${path49}[${i}]`));
9131
9131
  return;
9132
9132
  }
9133
9133
  if (node !== null && typeof node === "object") {
9134
- for (const [key, item] of Object.entries(node)) walk(item, path47 ? `${path47}.${key}` : key);
9134
+ for (const [key, item] of Object.entries(node)) walk(item, path49 ? `${path49}.${key}` : key);
9135
9135
  }
9136
9136
  };
9137
9137
  walk(value, "");
9138
9138
  return found;
9139
9139
  }
9140
9140
  function markupRefusal(leaks) {
9141
- return leaks.map(({ path: path47, names, closing }) => {
9141
+ return leaks.map(({ path: path49, names, closing }) => {
9142
9142
  const tags = [...names.map((n) => `<parameter name="${n}">`), ...closing ? ["</parameter>"] : []];
9143
- const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${path47}.` : " Pass each argument separately, not inside one string.";
9144
- return `${path47} contains leaked tool-call markup: ${tags.join(", ")}.${guide}`;
9143
+ const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${path49}.` : " Pass each argument separately, not inside one string.";
9144
+ return `${path49} contains leaked tool-call markup: ${tags.join(", ")}.${guide}`;
9145
9145
  }).join("\n");
9146
9146
  }
9147
9147
  function invalidArguments(name, error, args) {
@@ -9606,9 +9606,9 @@ function mcpClientConfig(credential) {
9606
9606
  }
9607
9607
  function ownerOnlyConfigFile(fileName, contents) {
9608
9608
  const dir = mkdtempSync2(join9(tmpdir2(), "ordewell-mcp-"));
9609
- const path47 = join9(dir, fileName);
9610
- writeFileSync(path47, contents, { mode: 384 });
9611
- return { path: path47, remove: () => rmSync2(dir, { recursive: true, force: true }) };
9609
+ const path49 = join9(dir, fileName);
9610
+ writeFileSync(path49, contents, { mode: 384 });
9611
+ return { path: path49, remove: () => rmSync2(dir, { recursive: true, force: true }) };
9612
9612
  }
9613
9613
 
9614
9614
  // src/services/harness/repoConfig.ts
@@ -9737,33 +9737,33 @@ var TomlReader = class {
9737
9737
  if (this.i >= this.src.length) return root;
9738
9738
  if (this.src.startsWith("[[", this.i)) {
9739
9739
  this.i += 2;
9740
- const path47 = this.key();
9740
+ const path49 = this.key();
9741
9741
  this.expect("]]");
9742
- const parent = this.tableAt(root, path47.slice(0, -1));
9743
- const last = path47[path47.length - 1];
9742
+ const parent = this.tableAt(root, path49.slice(0, -1));
9743
+ const last = path49[path49.length - 1];
9744
9744
  const list = Object.hasOwn(parent, last) ? parent[last] : parent[last] = [];
9745
- if (!Array.isArray(list)) throw this.fail(`${path47.join(".")} is not an array of tables`);
9745
+ if (!Array.isArray(list)) throw this.fail(`${path49.join(".")} is not an array of tables`);
9746
9746
  table = newTable();
9747
9747
  list.push(table);
9748
9748
  } else if (this.src[this.i] === "[") {
9749
9749
  this.i += 1;
9750
- const path47 = this.key();
9750
+ const path49 = this.key();
9751
9751
  this.expect("]");
9752
- table = this.tableAt(root, path47);
9752
+ table = this.tableAt(root, path49);
9753
9753
  } else {
9754
- const path47 = this.key();
9754
+ const path49 = this.key();
9755
9755
  this.skipBlank(false);
9756
9756
  this.expect("=");
9757
9757
  this.skipBlank(false);
9758
- const owner = this.tableAt(table, path47.slice(0, -1));
9759
- owner[path47[path47.length - 1]] = this.value();
9758
+ const owner = this.tableAt(table, path49.slice(0, -1));
9759
+ owner[path49[path49.length - 1]] = this.value();
9760
9760
  }
9761
9761
  this.endOfLine();
9762
9762
  }
9763
9763
  }
9764
- tableAt(from, path47) {
9764
+ tableAt(from, path49) {
9765
9765
  let table = from;
9766
- for (const part of path47) {
9766
+ for (const part of path49) {
9767
9767
  const next = Object.hasOwn(table, part) ? table[part] : void 0;
9768
9768
  if (next === void 0) table = table[part] = newTable();
9769
9769
  else if (isRecord(next)) table = next;
@@ -9832,10 +9832,10 @@ var TomlReader = class {
9832
9832
  this.i++;
9833
9833
  return table;
9834
9834
  }
9835
- const path47 = this.key();
9835
+ const path49 = this.key();
9836
9836
  this.expect("=");
9837
9837
  this.skipBlank(false);
9838
- this.tableAt(table, path47.slice(0, -1))[path47[path47.length - 1]] = this.value();
9838
+ this.tableAt(table, path49.slice(0, -1))[path49[path49.length - 1]] = this.value();
9839
9839
  this.skipBlank(true);
9840
9840
  if (this.src[this.i] === ",") this.i++;
9841
9841
  else if (this.src[this.i] !== "}") throw this.fail("expected , or } in an inline table");
@@ -10210,7 +10210,7 @@ var LineBuffer = class {
10210
10210
  // src/services/harness/ClaudeCodeAdapter.ts
10211
10211
  import { randomUUID as randomUUID3 } from "crypto";
10212
10212
  import { realpathSync as realpathSync7 } from "fs";
10213
- import * as path21 from "path";
10213
+ import * as path22 from "path";
10214
10214
 
10215
10215
  // src/plugins/resolveArgs.ts
10216
10216
  function claudeThinkingArgs(effort) {
@@ -10293,28 +10293,28 @@ function posixBinDirs() {
10293
10293
  ];
10294
10294
  }
10295
10295
  function windowsBinDirs(env, home) {
10296
- const join43 = (...parts) => path14.win32.join(...parts);
10297
- const under = (base, ...parts) => base ? [join43(base, ...parts)] : [];
10296
+ const join45 = (...parts) => path14.win32.join(...parts);
10297
+ const under = (base, ...parts) => base ? [join45(base, ...parts)] : [];
10298
10298
  const appData = env.APPDATA;
10299
10299
  const localAppData = env.LOCALAPPDATA;
10300
10300
  const programData = env.ProgramData;
10301
10301
  return [
10302
10302
  // Native per-user installers, PowerShell-driven and otherwise.
10303
- join43(home, ".local", "bin"),
10304
- join43(home, ".opencode", "bin"),
10305
- join43(home, ".claude", "bin"),
10306
- join43(home, ".codex", "bin"),
10303
+ join45(home, ".local", "bin"),
10304
+ join45(home, ".opencode", "bin"),
10305
+ join45(home, ".claude", "bin"),
10306
+ join45(home, ".codex", "bin"),
10307
10307
  ...under(localAppData, "Programs", "claude"),
10308
10308
  // Node package managers. npm's global prefix is `%APPDATA%\npm`; the
10309
10309
  // literal spelling is kept as a fallback for a host that does not export
10310
10310
  // APPDATA (a service account, a stripped CI container).
10311
10311
  ...under(appData, "npm"),
10312
- join43(home, "AppData", "Roaming", "npm"),
10312
+ join45(home, "AppData", "Roaming", "npm"),
10313
10313
  ...under(localAppData, "pnpm"),
10314
10314
  ...under(localAppData, "Yarn", "bin"),
10315
- join43(home, ".bun", "bin"),
10315
+ join45(home, ".bun", "bin"),
10316
10316
  // Windows package managers.
10317
- join43(home, "scoop", "shims"),
10317
+ join45(home, "scoop", "shims"),
10318
10318
  ...under(env.SCOOP, "shims"),
10319
10319
  ...under(env.SCOOP_GLOBAL ?? programData, "scoop", "shims"),
10320
10320
  ...under(programData, "chocolatey", "bin"),
@@ -10324,7 +10324,7 @@ function windowsBinDirs(env, home) {
10324
10324
  ...under(localAppData, "Microsoft", "WindowsApps"),
10325
10325
  // Version managers. Volta's Windows home is not the POSIX `~/.volta`.
10326
10326
  ...under(localAppData, "Volta", "bin"),
10327
- join43(home, ".volta", "bin")
10327
+ join45(home, ".volta", "bin")
10328
10328
  ];
10329
10329
  }
10330
10330
  function wellKnownBinDirs(deps = {}) {
@@ -11572,7 +11572,7 @@ function readOnlySessionRules(site) {
11572
11572
  real = realpathSync4.native(root);
11573
11573
  } catch {
11574
11574
  }
11575
- for (const path47 of /* @__PURE__ */ new Set([root, real])) rules.push({ action: "external_directory", resource: `${path47.replace(/[\\/]$/, "")}/*`, effect: "allow" });
11575
+ for (const path49 of /* @__PURE__ */ new Set([root, real])) rules.push({ action: "external_directory", resource: `${path49.replace(/[\\/]$/, "")}/*`, effect: "allow" });
11576
11576
  }
11577
11577
  return rules;
11578
11578
  }
@@ -12294,6 +12294,20 @@ ${lines.map((line) => `${line}
12294
12294
  }
12295
12295
 
12296
12296
  // src/services/harness/claudeOrdewell.ts
12297
+ import * as path20 from "path";
12298
+ function assertNoClaudeTaskCollision(cwd) {
12299
+ const text4 = readText(path20.join(cwd, ".mcp.json"));
12300
+ if (text4 === void 0) return;
12301
+ let parsed;
12302
+ try {
12303
+ parsed = JSON.parse(text4);
12304
+ } catch {
12305
+ return;
12306
+ }
12307
+ if (isRecord(parsed) && isRecord(parsed.mcpServers) && Object.hasOwn(parsed.mcpServers, ORDEWELL_MCP_SERVER_NAME)) {
12308
+ throw new Error(".mcp.json declares an MCP server named ordewell; rename or remove the ordewell server before starting this task.");
12309
+ }
12310
+ }
12297
12311
  var CLAUDE_ORDEWELL = {
12298
12312
  toolName: MCP_CLIENT_TOOL_NAMES.toolName,
12299
12313
  toolNames: MCP_CLIENT_TOOL_NAMES.toolNames,
@@ -12441,8 +12455,8 @@ var ReplyRuns = class {
12441
12455
  };
12442
12456
 
12443
12457
  // src/services/harness/claudeRepoConfig.ts
12444
- import * as path20 from "path";
12445
- var MEMORY_FILES = ["CLAUDE.md", path20.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
12458
+ import * as path21 from "path";
12459
+ var MEMORY_FILES = ["CLAUDE.md", path21.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
12446
12460
  var MAX_IMPORT_DEPTH = 5;
12447
12461
  function importsOf(text4) {
12448
12462
  const prose = text4.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
@@ -12453,11 +12467,11 @@ function withImports(workspace, file, depth, seen, into) {
12453
12467
  seen.add(file);
12454
12468
  const text4 = readText(file);
12455
12469
  if (text4 === void 0) return;
12456
- into.push({ path: path20.relative(workspace, file), text: text4 });
12470
+ into.push({ path: path21.relative(workspace, file), text: text4 });
12457
12471
  if (depth >= MAX_IMPORT_DEPTH) return;
12458
12472
  for (const target of importsOf(text4)) {
12459
12473
  if (target.startsWith("~")) continue;
12460
- const abs = path20.resolve(path20.dirname(file), target);
12474
+ const abs = path21.resolve(path21.dirname(file), target);
12461
12475
  if (insideOnDisk(workspace, abs)) withImports(workspace, abs, depth + 1, seen, into);
12462
12476
  }
12463
12477
  }
@@ -12478,12 +12492,12 @@ var CLAUDE_REPO_CONFIG = {
12478
12492
  instructions(workspace) {
12479
12493
  const files = [];
12480
12494
  const seen = /* @__PURE__ */ new Set();
12481
- for (const name of MEMORY_FILES) withImports(workspace, path20.join(workspace, name), 0, seen, files);
12495
+ for (const name of MEMORY_FILES) withImports(workspace, path21.join(workspace, name), 0, seen, files);
12482
12496
  return files;
12483
12497
  },
12484
12498
  projectServers(workspace) {
12485
12499
  const source = ".mcp.json";
12486
- const text4 = readText(path20.join(workspace, source));
12500
+ const text4 = readText(path21.join(workspace, source));
12487
12501
  if (text4 === void 0) return NO_SERVERS;
12488
12502
  let parsed;
12489
12503
  try {
@@ -12503,12 +12517,12 @@ var CLAUDE_REPO_CONFIG = {
12503
12517
  };
12504
12518
  function claudeLocalServers(env, workspace) {
12505
12519
  const configDir2 = env.CLAUDE_CONFIG_DIR?.trim();
12506
- const file = expandRunnerHome(configDir2 ? path20.join(configDir2, ".claude.json") : "~/.claude.json", env);
12520
+ const file = expandRunnerHome(configDir2 ? path21.join(configDir2, ".claude.json") : "~/.claude.json", env);
12507
12521
  const text4 = file === null ? void 0 : readText(file);
12508
12522
  if (text4 === void 0) return {};
12509
12523
  try {
12510
12524
  const parsed = JSON.parse(text4);
12511
- const project = isRecord(parsed) && isRecord(parsed.projects) ? parsed.projects[path20.resolve(workspace)] : void 0;
12525
+ const project = isRecord(parsed) && isRecord(parsed.projects) ? parsed.projects[path21.resolve(workspace)] : void 0;
12512
12526
  return isRecord(project) && isRecord(project.mcpServers) ? project.mcpServers : {};
12513
12527
  } catch {
12514
12528
  return {};
@@ -12763,13 +12777,13 @@ var NO_AUTO_MEMORY = { CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1" };
12763
12777
  function inSavedOutput(abs, configDir2, sessionId) {
12764
12778
  let projects;
12765
12779
  try {
12766
- projects = realpathSync7.native(path21.join(configDir2, "projects"));
12780
+ projects = realpathSync7.native(path22.join(configDir2, "projects"));
12767
12781
  } catch {
12768
12782
  return false;
12769
12783
  }
12770
- const rel = path21.relative(projects, abs);
12771
- if (rel.startsWith("..") || path21.isAbsolute(rel)) return false;
12772
- const [project, session, folder] = rel.split(path21.sep);
12784
+ const rel = path22.relative(projects, abs);
12785
+ if (rel.startsWith("..") || path22.isAbsolute(rel)) return false;
12786
+ const [project, session, folder] = rel.split(path22.sep);
12773
12787
  return Boolean(project) && session === sessionId && folder === "tool-results";
12774
12788
  }
12775
12789
  var PATH_TOOLS = {
@@ -13060,6 +13074,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
13060
13074
  */
13061
13075
  taskSpawnSpec(opts) {
13062
13076
  const readOnly = readOnlyTask(opts);
13077
+ if (!readOnly) assertNoClaudeTaskCollision(opts.cwd);
13063
13078
  this.readOnlySite = readOnly ? opts : null;
13064
13079
  this.requestedMode = readOnly ? PLANNER_PERMISSION_MODE : opts.flags.permissionMode;
13065
13080
  this.followedModel = opts.model;
@@ -13855,8 +13870,8 @@ function runProbe(deps, cwd, env, flags) {
13855
13870
  }
13856
13871
 
13857
13872
  // src/services/harness/codexRepoConfig.ts
13858
- import * as path22 from "path";
13859
- var CONFIG_FILE = path22.join(".codex", "config.toml");
13873
+ import * as path23 from "path";
13874
+ var CONFIG_FILE = path23.join(".codex", "config.toml");
13860
13875
  function stringsOf(value) {
13861
13876
  return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
13862
13877
  }
@@ -13882,9 +13897,9 @@ var CODEX_REPO_CONFIG = {
13882
13897
  const files = [];
13883
13898
  for (const dir of repoLevels(workspace)) {
13884
13899
  for (const name of ["AGENTS.override.md", "AGENTS.md"]) {
13885
- const text4 = readText(path22.join(dir, name));
13900
+ const text4 = readText(path23.join(dir, name));
13886
13901
  if (text4 === void 0) continue;
13887
- files.push({ path: path22.relative(workspace, path22.join(dir, name)) || name, text: text4 });
13902
+ files.push({ path: path23.relative(workspace, path23.join(dir, name)) || name, text: text4 });
13888
13903
  break;
13889
13904
  }
13890
13905
  }
@@ -13895,10 +13910,10 @@ var CODEX_REPO_CONFIG = {
13895
13910
  const byName = /* @__PURE__ */ new Map();
13896
13911
  const unreadable = [];
13897
13912
  for (const dir of repoLevels(workspace)) {
13898
- const file = path22.join(dir, CONFIG_FILE);
13913
+ const file = path23.join(dir, CONFIG_FILE);
13899
13914
  const text4 = readText(file);
13900
13915
  if (text4 === void 0) continue;
13901
- const source = path22.relative(workspace, file);
13916
+ const source = path23.relative(workspace, file);
13902
13917
  let parsed;
13903
13918
  try {
13904
13919
  parsed = parseToml(text4);
@@ -13918,9 +13933,9 @@ var CODEX_REPO_CONFIG = {
13918
13933
  };
13919
13934
  function untrustedProjects(workspace) {
13920
13935
  const projects = {};
13921
- for (let dir = path22.resolve(workspace); ; dir = path22.dirname(dir)) {
13936
+ for (let dir = path23.resolve(workspace); ; dir = path23.dirname(dir)) {
13922
13937
  projects[dir] = { trust_level: "untrusted" };
13923
- if (path22.dirname(dir) === dir) return projects;
13938
+ if (path23.dirname(dir) === dir) return projects;
13924
13939
  }
13925
13940
  }
13926
13941
 
@@ -15041,7 +15056,7 @@ ${this.exitMessage()}`);
15041
15056
  case "webSearch":
15042
15057
  return;
15043
15058
  case "fileChange": {
15044
- const paths = (item.changes ?? []).map((change) => change.path).filter((path47) => !!path47);
15059
+ const paths = (item.changes ?? []).map((change) => change.path).filter((path49) => !!path49);
15045
15060
  this.fileChangePaths.set(item.id, paths);
15046
15061
  if (this.startOpts?.kind === "task") {
15047
15062
  for (const change of item.changes ?? []) this.announceFileChange(item.id, change, emit, subagentId);
@@ -15144,20 +15159,20 @@ ${this.exitMessage()}`);
15144
15159
  announceFileChange(itemId, change, emit, subagentId) {
15145
15160
  const rows = this.fileChangeRows.get(itemId) ?? /* @__PURE__ */ new Map();
15146
15161
  this.fileChangeRows.set(itemId, rows);
15147
- const path47 = this.shownChangePath(change);
15148
- const known = rows.get(path47);
15162
+ const path49 = this.shownChangePath(change);
15163
+ const known = rows.get(path49);
15149
15164
  if (known) return known;
15150
- const row = rows.size === 0 ? itemId : `${itemId}:${path47}`;
15151
- rows.set(path47, row);
15152
- emit({ type: "tool_call", id: row, name: changeName(change), args: { path: path47 }, ...subagentId ? { subagentId } : {} });
15165
+ const row = rows.size === 0 ? itemId : `${itemId}:${path49}`;
15166
+ rows.set(path49, row);
15167
+ emit({ type: "tool_call", id: row, name: changeName(change), args: { path: path49 }, ...subagentId ? { subagentId } : {} });
15153
15168
  return row;
15154
15169
  }
15155
15170
  /** Relative to the task's worktree, where every path but a stray one lies; a move names both ends. */
15156
15171
  shownChangePath(change) {
15157
- const shown = (path47) => {
15172
+ const shown = (path49) => {
15158
15173
  const cwd = this.startOpts?.cwd;
15159
- const rel = cwd ? relative8(cwd, path47) : path47;
15160
- return rel && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute6(rel) ? rel : path47;
15174
+ const rel = cwd ? relative8(cwd, path49) : path49;
15175
+ return rel && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute6(rel) ? rel : path49;
15161
15176
  };
15162
15177
  const from = shown(change.path ?? "");
15163
15178
  return change.kind?.move_path ? `${from} \u2192 ${shown(change.kind.move_path)}` : from;
@@ -15208,7 +15223,7 @@ import { realpathSync as realpathSync8 } from "fs";
15208
15223
 
15209
15224
  // src/services/harness/projectServers.ts
15210
15225
  import { createHash } from "crypto";
15211
- import * as path23 from "path";
15226
+ import * as path24 from "path";
15212
15227
  function canonical2(value) {
15213
15228
  if (Array.isArray(value)) return value.map(canonical2);
15214
15229
  if (!isRecord(value)) return value;
@@ -15219,7 +15234,7 @@ function projectServerScope(server4) {
15219
15234
  return `mcp-server:${server4.name}@${hash}`;
15220
15235
  }
15221
15236
  var scopeName = (scope) => scope.slice(0, scope.lastIndexOf("@"));
15222
- function fileProjectServerApprovals(file = path23.join(globalDataDir(), "project-mcp-servers.json")) {
15237
+ function fileProjectServerApprovals(file = path24.join(globalDataDir(), "project-mcp-servers.json")) {
15223
15238
  const load = () => {
15224
15239
  const text4 = readText(file);
15225
15240
  if (text4 === void 0) return {};
@@ -16382,8 +16397,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
16382
16397
  this.v2 = new OpenCodeV2({
16383
16398
  version,
16384
16399
  nativeShell: this.nativeShell?.prefix ?? null,
16385
- request: (method, path47, body, signal, timeoutMs) => this.request(method, path47, body, signal, timeoutMs),
16386
- json: (method, path47, body, signal, timeoutMs) => this.json(method, path47, body, signal, timeoutMs),
16400
+ request: (method, path49, body, signal, timeoutMs) => this.request(method, path49, body, signal, timeoutMs),
16401
+ json: (method, path49, body, signal, timeoutMs) => this.json(method, path49, body, signal, timeoutMs),
16387
16402
  openEvents: (signal) => this.deps.fetch(`${this.baseUrl}/api/event`, { signal, headers: this.headers() }),
16388
16403
  processEnded: this.processEnded,
16389
16404
  isExited: () => this.exited,
@@ -17020,15 +17035,15 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
17020
17035
  onActivity
17021
17036
  );
17022
17037
  }
17023
- async json(method, path47, body, signal, timeoutMs) {
17024
- const response = await this.request(method, path47, body, signal, timeoutMs);
17025
- if (!response.ok) throw new OpenCodeHttpError(`${method} ${path47} failed: ${response.status} ${response.statusText}`);
17038
+ async json(method, path49, body, signal, timeoutMs) {
17039
+ const response = await this.request(method, path49, body, signal, timeoutMs);
17040
+ if (!response.ok) throw new OpenCodeHttpError(`${method} ${path49} failed: ${response.status} ${response.statusText}`);
17026
17041
  return await response.json().catch(() => null);
17027
17042
  }
17028
17043
  /** `timeoutMs` null is for a turn's own POST, which the server answers when the turn is done. */
17029
- request(method, path47, body, signal, timeoutMs = REQUEST_TIMEOUT_MS) {
17044
+ request(method, path49, body, signal, timeoutMs = REQUEST_TIMEOUT_MS) {
17030
17045
  const deadline = withDeadline(signal, timeoutMs);
17031
- return this.deps.fetch(`${this.baseUrl}${path47}`, {
17046
+ return this.deps.fetch(`${this.baseUrl}${path49}`, {
17032
17047
  method,
17033
17048
  headers: this.headers({ "content-type": "application/json" }),
17034
17049
  ...body === void 0 ? {} : { body: JSON.stringify(body) },
@@ -17089,8 +17104,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
17089
17104
  };
17090
17105
 
17091
17106
  // src/services/harness/openCodeRepoConfig.ts
17092
- import * as path24 from "path";
17093
- var CONFIG_FILES = ["opencode.json", "opencode.jsonc", path24.join(".opencode", "opencode.json"), path24.join(".opencode", "opencode.jsonc")];
17107
+ import * as path25 from "path";
17108
+ var CONFIG_FILES = ["opencode.json", "opencode.jsonc", path25.join(".opencode", "opencode.json"), path25.join(".opencode", "opencode.jsonc")];
17094
17109
  var INSTRUCTION_NAMES = ["AGENTS.md", "CLAUDE.md", "CONTEXT.md"];
17095
17110
  function server3(name, entry, source) {
17096
17111
  if (!isRecord(entry)) return "not an object";
@@ -17109,8 +17124,8 @@ var OPENCODE_REPO_CONFIG = {
17109
17124
  const levels = repoLevels(workspace);
17110
17125
  for (const name of INSTRUCTION_NAMES) {
17111
17126
  const files = levels.flatMap((dir) => {
17112
- const text4 = readText(path24.join(dir, name));
17113
- return text4 === void 0 ? [] : [{ path: path24.relative(workspace, path24.join(dir, name)) || name, text: text4 }];
17127
+ const text4 = readText(path25.join(dir, name));
17128
+ return text4 === void 0 ? [] : [{ path: path25.relative(workspace, path25.join(dir, name)) || name, text: text4 }];
17114
17129
  });
17115
17130
  if (files.length > 0) return files;
17116
17131
  }
@@ -17122,10 +17137,10 @@ var OPENCODE_REPO_CONFIG = {
17122
17137
  const unreadable = [];
17123
17138
  for (const dir of repoLevels(workspace)) {
17124
17139
  for (const name of CONFIG_FILES) {
17125
- const file = path24.join(dir, name);
17140
+ const file = path25.join(dir, name);
17126
17141
  const text4 = readText(file);
17127
17142
  if (text4 === void 0) continue;
17128
- const source = path24.relative(workspace, file);
17143
+ const source = path25.relative(workspace, file);
17129
17144
  let parsed;
17130
17145
  try {
17131
17146
  parsed = parseJsonc(text4);
@@ -17314,7 +17329,7 @@ function knownModelId(value, optionIds, shortcuts) {
17314
17329
 
17315
17330
  // src/services/ModelCatalog.ts
17316
17331
  import * as fs6 from "fs";
17317
- import * as path25 from "path";
17332
+ import * as path26 from "path";
17318
17333
  var CACHE_KEY = "model-catalog";
17319
17334
  var CACHE_TTL_MS = 60 * 60 * 1e3;
17320
17335
  function storageKey(baseUrl) {
@@ -17367,7 +17382,7 @@ var ModelCatalog = class {
17367
17382
  const url = (baseUrl || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
17368
17383
  const key = storageKey(url);
17369
17384
  const cacheDir = globalCacheDir();
17370
- const cacheFile = path25.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
17385
+ const cacheFile = path26.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
17371
17386
  if (fs6.existsSync(cacheFile)) fs6.rmSync(cacheFile);
17372
17387
  } catch {
17373
17388
  }
@@ -17375,7 +17390,7 @@ var ModelCatalog = class {
17375
17390
  static readCache(key) {
17376
17391
  try {
17377
17392
  const cacheDir = globalCacheDir();
17378
- const cacheFile = path25.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
17393
+ const cacheFile = path26.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
17379
17394
  if (!fs6.existsSync(cacheFile)) return null;
17380
17395
  const raw = fs6.readFileSync(cacheFile, "utf8");
17381
17396
  const data = JSON.parse(raw);
@@ -17389,7 +17404,7 @@ var ModelCatalog = class {
17389
17404
  try {
17390
17405
  const cacheDir = globalCacheDir();
17391
17406
  fs6.mkdirSync(cacheDir, { recursive: true });
17392
- const cacheFile = path25.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
17407
+ const cacheFile = path26.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
17393
17408
  fs6.writeFileSync(
17394
17409
  cacheFile,
17395
17410
  JSON.stringify({ models, cachedAt: Date.now() }, null, 2)
@@ -17400,7 +17415,7 @@ var ModelCatalog = class {
17400
17415
  };
17401
17416
 
17402
17417
  // src/services/ModelDiscovery.ts
17403
- import * as path26 from "path";
17418
+ import * as path27 from "path";
17404
17419
  import { exec as exec2 } from "child_process";
17405
17420
  import { promisify as promisify2 } from "util";
17406
17421
  import * as fsSync5 from "fs";
@@ -17546,7 +17561,7 @@ function geminiDiscoveryCacheKey(apiKey) {
17546
17561
  return {
17547
17562
  key: `gemini-models:${hash}`,
17548
17563
  dir,
17549
- file: path26.join(dir, `gemini_models_${hash}.json`)
17564
+ file: path27.join(dir, `gemini_models_${hash}.json`)
17550
17565
  };
17551
17566
  }
17552
17567
  function readGeminiCache(apiKey) {
@@ -18054,7 +18069,7 @@ function parseMaxParallel(value) {
18054
18069
 
18055
18070
  // src/services/SettingsService.ts
18056
18071
  import * as fs12 from "fs";
18057
- import * as path33 from "path";
18072
+ import * as path34 from "path";
18058
18073
 
18059
18074
  // src/utils/fileLock.ts
18060
18075
  import * as fs7 from "fs";
@@ -18658,33 +18673,33 @@ ${skillBlock(entry.skill)}`, timestamp: entry.timestamp });
18658
18673
  }
18659
18674
 
18660
18675
  // src/services/taskSkills.ts
18661
- import * as path32 from "path";
18676
+ import * as path33 from "path";
18662
18677
 
18663
18678
  // src/services/SkillsService.ts
18664
18679
  import { createHash as createHash2 } from "crypto";
18665
18680
  import * as fs11 from "fs";
18666
- import * as path31 from "path";
18681
+ import * as path32 from "path";
18667
18682
 
18668
18683
  // src/services/builtinSkills.ts
18669
- import * as path27 from "path";
18684
+ import * as path28 from "path";
18670
18685
  import { fileURLToPath } from "url";
18671
18686
  function builtinSkillsDir() {
18672
- const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path27.dirname(fileURLToPath(
18687
+ const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path28.dirname(fileURLToPath(
18673
18688
  // @ts-expect-error - import.meta is only valid under an ESM module setting
18674
18689
  import.meta.url
18675
18690
  ));
18676
- return path27.join(dir, "..", "skills");
18691
+ return path28.join(dir, "..", "skills");
18677
18692
  }
18678
18693
 
18679
18694
  // src/services/repoGroup.ts
18680
18695
  import * as fs10 from "fs";
18681
- import * as path30 from "path";
18696
+ import * as path31 from "path";
18682
18697
 
18683
18698
  // src/services/worktreeBootstrap.ts
18684
18699
  import { exec as exec3 } from "child_process";
18685
18700
  import { promisify as promisify4 } from "util";
18686
18701
  import * as fs9 from "fs";
18687
- import * as path29 from "path";
18702
+ import * as path30 from "path";
18688
18703
 
18689
18704
  // src/services/gitExec.ts
18690
18705
  import { execFile as execFile4 } from "child_process";
@@ -18766,7 +18781,7 @@ function subcommandOf2(args) {
18766
18781
 
18767
18782
  // src/services/worktreeLink.ts
18768
18783
  import * as fs8 from "fs";
18769
- import * as path28 from "path";
18784
+ import * as path29 from "path";
18770
18785
  function linkPath(source, target, platform, hardLink = fs8.linkSync) {
18771
18786
  const isDir = fs8.statSync(source).isDirectory();
18772
18787
  if (platform !== "win32") {
@@ -18791,8 +18806,8 @@ function mirrorDir(source, target, opts) {
18791
18806
  const mirror = (from, to, prefix, top) => {
18792
18807
  fs8.mkdirSync(to, { recursive: true });
18793
18808
  for (const entry of fs8.readdirSync(from, { withFileTypes: true })) {
18794
- const name = path28.join(prefix, entry.name);
18795
- const at = { from: path28.join(from, entry.name), to: path28.join(to, entry.name), name };
18809
+ const name = path29.join(prefix, entry.name);
18810
+ const at = { from: path29.join(from, entry.name), to: path29.join(to, entry.name), name };
18796
18811
  if (entry.isSymbolicLink()) links.push(at);
18797
18812
  else if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin")) mirror(at.from, at.to, name, false);
18798
18813
  else if (linkPath(at.from, at.to, opts.platform, opts.hardLink) === "copy") copied.push(name);
@@ -18812,16 +18827,16 @@ function mirrorDir(source, target, opts) {
18812
18827
  }
18813
18828
  function relink(from, to, opts) {
18814
18829
  const text4 = fs8.readlinkSync(from);
18815
- const pointee = path28.isAbsolute(text4) ? rebased(text4, opts) : text4;
18830
+ const pointee = path29.isAbsolute(text4) ? rebased(text4, opts) : text4;
18816
18831
  if (opts.platform !== "win32") {
18817
- if (!fs8.existsSync(path28.resolve(path28.dirname(to), pointee)) && fs8.existsSync(path28.resolve(path28.dirname(from), pointee))) {
18818
- fs8.symlinkSync(rebased(path28.resolve(path28.dirname(from), pointee), opts), to);
18832
+ if (!fs8.existsSync(path29.resolve(path29.dirname(to), pointee)) && fs8.existsSync(path29.resolve(path29.dirname(from), pointee))) {
18833
+ fs8.symlinkSync(rebased(path29.resolve(path29.dirname(from), pointee), opts), to);
18819
18834
  return "symlink";
18820
18835
  }
18821
18836
  fs8.symlinkSync(pointee, to);
18822
18837
  return "symlink";
18823
18838
  }
18824
- const resolved = fs8.existsSync(path28.resolve(path28.dirname(to), pointee)) ? path28.resolve(path28.dirname(to), pointee) : rebased(path28.resolve(path28.dirname(from), pointee), opts);
18839
+ const resolved = fs8.existsSync(path29.resolve(path29.dirname(to), pointee)) ? path29.resolve(path29.dirname(to), pointee) : rebased(path29.resolve(path29.dirname(from), pointee), opts);
18825
18840
  try {
18826
18841
  fs8.statSync(resolved);
18827
18842
  } catch {
@@ -18830,9 +18845,9 @@ function relink(from, to, opts) {
18830
18845
  return linkPath(resolved, to, opts.platform, opts.hardLink);
18831
18846
  }
18832
18847
  function rebased(pointee, opts) {
18833
- const rel = path28.relative(opts.from, pointee);
18834
- const inside2 = rel !== "" && !rel.startsWith("..") && !path28.isAbsolute(rel);
18835
- return inside2 && !rel.split(/[\\/]/).includes("node_modules") ? path28.join(opts.to, rel) : pointee;
18848
+ const rel = path29.relative(opts.from, pointee);
18849
+ const inside2 = rel !== "" && !rel.startsWith("..") && !path29.isAbsolute(rel);
18850
+ return inside2 && !rel.split(/[\\/]/).includes("node_modules") ? path29.join(opts.to, rel) : pointee;
18836
18851
  }
18837
18852
 
18838
18853
  // src/services/worktreeBootstrap.ts
@@ -18869,18 +18884,18 @@ function matchLinks(root, patterns) {
18869
18884
  }
18870
18885
  function segmentMatches(root, base, segment) {
18871
18886
  const under = (name) => base ? `${base}/${name}` : name;
18872
- if (!/[*?]/.test(segment)) return lexists(path29.join(root, base, segment)) ? [under(segment)] : [];
18887
+ if (!/[*?]/.test(segment)) return lexists(path30.join(root, base, segment)) ? [under(segment)] : [];
18873
18888
  const pattern = new RegExp(`^${segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
18874
- return listDir2(path29.join(root, base)).filter((name) => !NEVER_SCANNED.has(name) && pattern.test(name)).map(under);
18889
+ return listDir2(path30.join(root, base)).filter((name) => !NEVER_SCANNED.has(name) && pattern.test(name)).map(under);
18875
18890
  }
18876
18891
  function installDirs(root) {
18877
18892
  const packages = matchLinks(root, workspaceGlobs(root)).map((pkg) => `${pkg}/node_modules`);
18878
- return ["node_modules", ...packages].filter((dir) => lexists(path29.join(root, dir)));
18893
+ return ["node_modules", ...packages].filter((dir) => lexists(path30.join(root, dir)));
18879
18894
  }
18880
18895
  function workspaceGlobs(root) {
18881
18896
  let manifest;
18882
18897
  try {
18883
- manifest = JSON.parse(fs9.readFileSync(path29.join(root, "package.json"), "utf8"));
18898
+ manifest = JSON.parse(fs9.readFileSync(path30.join(root, "package.json"), "utf8"));
18884
18899
  } catch {
18885
18900
  return [];
18886
18901
  }
@@ -18895,19 +18910,19 @@ async function bootstrap(repo, cwd, opts) {
18895
18910
  const linked = [];
18896
18911
  const copied = [];
18897
18912
  const link = (name) => {
18898
- const target = path29.join(cwd, name);
18913
+ const target = path30.join(cwd, name);
18899
18914
  if (lexists(target)) return;
18900
- fs9.mkdirSync(path29.dirname(target), { recursive: true });
18901
- if (linkPath(path29.join(repo.root, name), target, opts.platform) === "copy") copied.push(name);
18915
+ fs9.mkdirSync(path30.dirname(target), { recursive: true });
18916
+ if (linkPath(path30.join(repo.root, name), target, opts.platform) === "copy") copied.push(name);
18902
18917
  linked.push(name);
18903
18918
  };
18904
18919
  const mirror = (name) => {
18905
- const target = path29.join(cwd, name);
18906
- if (lexists(target) || !fs9.existsSync(path29.dirname(target))) return;
18907
- const source = path29.join(repo.root, name);
18920
+ const target = path30.join(cwd, name);
18921
+ if (lexists(target) || !fs9.existsSync(path30.dirname(target))) return;
18922
+ const source = path30.join(repo.root, name);
18908
18923
  if (!fs9.lstatSync(source).isDirectory()) return link(name);
18909
18924
  const mirrored = mirrorDir(source, target, { platform: opts.platform, from: repo.root, to: cwd });
18910
- copied.push(...mirrored.map((entry) => path29.join(name, entry)));
18925
+ copied.push(...mirrored.map((entry) => path30.join(name, entry)));
18911
18926
  linked.push(name);
18912
18927
  };
18913
18928
  if (!setup) {
@@ -18942,12 +18957,12 @@ async function runSetup(command, repo, cwd, resolvePath) {
18942
18957
 
18943
18958
  // src/services/repoGroup.ts
18944
18959
  function hasGitEntry(dir) {
18945
- return fs10.existsSync(path30.join(dir, ".git"));
18960
+ return fs10.existsSync(path31.join(dir, ".git"));
18946
18961
  }
18947
18962
  function groupPathOf(listed3) {
18948
18963
  const slashed = listed3.trim().replace(/\\/g, "/");
18949
- if (path30.posix.isAbsolute(slashed) || path30.win32.isAbsolute(slashed)) return null;
18950
- const rel = path30.posix.normalize(slashed).replace(/\/+$/, "");
18964
+ if (path31.posix.isAbsolute(slashed) || path31.win32.isAbsolute(slashed)) return null;
18965
+ const rel = path31.posix.normalize(slashed).replace(/\/+$/, "");
18951
18966
  return rel === "" || rel === "." || rel === ".." || rel.startsWith("../") ? null : rel;
18952
18967
  }
18953
18968
  function childDirs(dir) {
@@ -18958,14 +18973,14 @@ function childDirs(dir) {
18958
18973
  }
18959
18974
  }
18960
18975
  function groupPaths(workspaceRoot, listed3) {
18961
- if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(path30.join(workspaceRoot, name)));
18962
- const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(path30.join(workspaceRoot, rel)));
18976
+ if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(path31.join(workspaceRoot, name)));
18977
+ const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(path31.join(workspaceRoot, rel)));
18963
18978
  return [...new Set(paths)].sort();
18964
18979
  }
18965
18980
  function workspaceRepoGroup(workspaceRoot, listed3) {
18966
- for (let dir = path30.resolve(workspaceRoot); ; dir = path30.dirname(dir)) {
18981
+ for (let dir = path31.resolve(workspaceRoot); ; dir = path31.dirname(dir)) {
18967
18982
  if (hasGitEntry(dir)) return [SELF_REPO];
18968
- if (path30.dirname(dir) === dir) break;
18983
+ if (path31.dirname(dir) === dir) break;
18969
18984
  }
18970
18985
  return groupPaths(workspaceRoot, listed3);
18971
18986
  }
@@ -19046,8 +19061,8 @@ function copyDirSync(src, dest) {
19046
19061
  fs11.mkdirSync(dest, { recursive: true });
19047
19062
  const entries = fs11.readdirSync(src, { withFileTypes: true });
19048
19063
  for (const entry of entries) {
19049
- const srcPath = path31.join(src, entry.name);
19050
- const destPath = path31.join(dest, entry.name);
19064
+ const srcPath = path32.join(src, entry.name);
19065
+ const destPath = path32.join(dest, entry.name);
19051
19066
  if (entry.isDirectory()) copyDirSync(srcPath, destPath);
19052
19067
  else fs11.copyFileSync(srcPath, destPath);
19053
19068
  }
@@ -19071,7 +19086,7 @@ function readDir(dir, source) {
19071
19086
  if (!fs11.existsSync(dir)) return { skills, invalid };
19072
19087
  for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
19073
19088
  if (!entry.isDirectory()) continue;
19074
- const skillFile = path31.join(dir, entry.name, "SKILL.md");
19089
+ const skillFile = path32.join(dir, entry.name, "SKILL.md");
19075
19090
  if (!fs11.existsSync(skillFile)) continue;
19076
19091
  if (!isSkillName(entry.name)) {
19077
19092
  invalid.push({ name: entry.name, path: skillFile, source, reason: INVALID_SKILL_NAME_REASON });
@@ -19090,15 +19105,15 @@ function skillFileIn(dir, name) {
19090
19105
  return void 0;
19091
19106
  }
19092
19107
  if (!entries.some((entry) => entry.isDirectory() && entry.name === name)) return void 0;
19093
- const skillFile = path31.join(dir, name, "SKILL.md");
19108
+ const skillFile = path32.join(dir, name, "SKILL.md");
19094
19109
  return fs11.existsSync(skillFile) ? skillFile : void 0;
19095
19110
  }
19096
19111
  function skillsDirOf(root) {
19097
- return path31.join(root, STATE_DIR, "skills");
19112
+ return path32.join(root, STATE_DIR, "skills");
19098
19113
  }
19099
19114
  function workspaceSkillRoots(workspaceRoot, repos, checkout = workspaceRoot) {
19100
19115
  if (repos.length === 0 || repos.includes(SELF_REPO)) return [checkout];
19101
- return [workspaceRoot, ...repos.map((repo) => path31.join(checkout, repo))];
19116
+ return [workspaceRoot, ...repos.map((repo) => path32.join(checkout, repo))];
19102
19117
  }
19103
19118
  var SkillsService = class _SkillsService {
19104
19119
  workspaceRoots;
@@ -19121,11 +19136,11 @@ var SkillsService = class _SkillsService {
19121
19136
  return this.workspaceRoots.map(skillsDirOf);
19122
19137
  }
19123
19138
  globalDir() {
19124
- return path31.join(globalDataDir(), "skills");
19139
+ return path32.join(globalDataDir(), "skills");
19125
19140
  }
19126
19141
  readSeedManifest() {
19127
19142
  try {
19128
- const parsed = JSON.parse(fs11.readFileSync(path31.join(this.globalDir(), SEED_MANIFEST), "utf8"));
19143
+ const parsed = JSON.parse(fs11.readFileSync(path32.join(this.globalDir(), SEED_MANIFEST), "utf8"));
19129
19144
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
19130
19145
  return Object.fromEntries(
19131
19146
  Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
@@ -19141,7 +19156,7 @@ var SkillsService = class _SkillsService {
19141
19156
  manifest[name] = hash;
19142
19157
  try {
19143
19158
  fs11.mkdirSync(this.globalDir(), { recursive: true });
19144
- fs11.writeFileSync(path31.join(this.globalDir(), SEED_MANIFEST), JSON.stringify(manifest, null, 2));
19159
+ fs11.writeFileSync(path32.join(this.globalDir(), SEED_MANIFEST), JSON.stringify(manifest, null, 2));
19145
19160
  } catch (err) {
19146
19161
  console.warn(`Could not record seeded skill ${name}: ${String(err)}`);
19147
19162
  }
@@ -19152,11 +19167,11 @@ var SkillsService = class _SkillsService {
19152
19167
  return this.readSeedManifest()[name] === currentHash || (PRIOR_BUILTIN_HASHES[name] ?? []).includes(currentHash);
19153
19168
  }
19154
19169
  seed(name) {
19155
- const dest = path31.join(this.globalDir(), name);
19170
+ const dest = path32.join(this.globalDir(), name);
19156
19171
  const exists = fs11.existsSync(dest);
19157
19172
  let srcDir;
19158
19173
  try {
19159
- srcDir = path31.join(builtinSkillsDir(), name);
19174
+ srcDir = path32.join(builtinSkillsDir(), name);
19160
19175
  } catch (err) {
19161
19176
  if (!exists) console.warn(`Could not locate built-in skills dir: ${String(err)}`);
19162
19177
  return false;
@@ -19165,9 +19180,9 @@ var SkillsService = class _SkillsService {
19165
19180
  if (!exists) console.warn(`Built-in skill not found in package: ${name}`);
19166
19181
  return false;
19167
19182
  }
19168
- const srcHash = fileHash(path31.join(srcDir, "SKILL.md"));
19183
+ const srcHash = fileHash(path32.join(srcDir, "SKILL.md"));
19169
19184
  if (exists) {
19170
- const currentHash = fileHash(path31.join(dest, "SKILL.md"));
19185
+ const currentHash = fileHash(path32.join(dest, "SKILL.md"));
19171
19186
  if (currentHash !== void 0 && currentHash === srcHash) {
19172
19187
  this.recordSeed(name, currentHash);
19173
19188
  return false;
@@ -19211,7 +19226,7 @@ var SkillsService = class _SkillsService {
19211
19226
  if (entries.length !== 1 || entries[0].name !== "SKILL.md" || !entries[0].isFile()) return false;
19212
19227
  let raw;
19213
19228
  try {
19214
- raw = readSkillText(path31.join(dir, "SKILL.md"));
19229
+ raw = readSkillText(path32.join(dir, "SKILL.md"));
19215
19230
  } catch {
19216
19231
  return false;
19217
19232
  }
@@ -19219,7 +19234,7 @@ var SkillsService = class _SkillsService {
19219
19234
  }
19220
19235
  pruneRetired() {
19221
19236
  for (const name of RETIRED_BUILTIN_SKILL_NAMES) {
19222
- const dest = path31.join(this.globalDir(), name);
19237
+ const dest = path32.join(this.globalDir(), name);
19223
19238
  if (!fs11.existsSync(dest)) continue;
19224
19239
  if (!this.isUnmodifiedRetiredSeed(dest, name)) continue;
19225
19240
  try {
@@ -19357,12 +19372,12 @@ var runGit = execFileWithTimeout(1e4);
19357
19372
  async function committedInHead(files) {
19358
19373
  const byDir = /* @__PURE__ */ new Map();
19359
19374
  for (const file of new Set(files)) {
19360
- const dir = path32.dirname(path32.dirname(file));
19375
+ const dir = path33.dirname(path33.dirname(file));
19361
19376
  byDir.set(dir, [...byDir.get(dir) ?? [], file]);
19362
19377
  }
19363
19378
  const result = /* @__PURE__ */ new Map();
19364
19379
  await Promise.all([...byDir].map(async ([dir, inDir]) => {
19365
- const relative17 = (file) => path32.relative(dir, file).split(path32.sep).join("/");
19380
+ const relative17 = (file) => path33.relative(dir, file).split(path33.sep).join("/");
19366
19381
  try {
19367
19382
  const { stdout } = await runGit("git", ["ls-tree", "--name-only", "HEAD", "--", ...inDir.map(relative17)], { cwd: dir, env: cleanEnv() });
19368
19383
  const listed3 = new Set(stdout.split("\n").map((line) => line.trim()).filter(Boolean));
@@ -19382,9 +19397,9 @@ function plannedSkillLookup(skillsAt, workspaceRoot, layout) {
19382
19397
  searchedDirs: () => lookup2.searchedDirs(),
19383
19398
  listSkills: () => lookup2.listSkills(),
19384
19399
  uncommitted: async (skills) => {
19385
- const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace && path32.dirname(path32.dirname(skill.path)) === readInPlace));
19400
+ const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace && path33.dirname(path33.dirname(skill.path)) === readInPlace));
19386
19401
  const committed = await committedInHead(checked.map((skill) => skill.path));
19387
- return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path, path32.relative(workspaceRoot, path32.dirname(skill.path)).split(path32.sep).join("/")]));
19402
+ 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("/")]));
19388
19403
  }
19389
19404
  };
19390
19405
  }
@@ -19397,13 +19412,13 @@ function checkPlanSkills(tasks, lookup2) {
19397
19412
  function checkTaskSkillsEdit(task2, skills, lookup2) {
19398
19413
  return check([{ taskId: task2.id, owner: `Task "${task2.title}"`, ...attached(skills) }], lookup2);
19399
19414
  }
19400
- function subtaskEntries(op, spec, path47 = []) {
19415
+ function subtaskEntries(op, spec, path49 = []) {
19401
19416
  const subtasks = spec?.subtasks;
19402
19417
  if (!Array.isArray(subtasks)) return [];
19403
19418
  return subtasks.flatMap((sub, i) => {
19404
19419
  if (typeof sub !== "object" || sub === null) return [];
19405
19420
  const child = sub;
19406
- const at = [...path47, typeof child.title === "string" && child.title.trim() ? `"${child.title}"` : `#${i + 1}`];
19421
+ const at = [...path49, typeof child.title === "string" && child.title.trim() ? `"${child.title}"` : `#${i + 1}`];
19407
19422
  return [{ op, owner: `subtask ${at.join(" > ")}`, ...attached(child.skills) }, ...subtaskEntries(op, child, at)];
19408
19423
  });
19409
19424
  }
@@ -23245,7 +23260,7 @@ var RUN_SETTING_KEYS = Object.keys(RUN_SETTING_CHECKS);
23245
23260
  function getSettingsPath() {
23246
23261
  const override = process.env.ORDEWELL_SETTINGS_PATH;
23247
23262
  if (override && override.trim()) return override.trim();
23248
- return path33.join(globalDataDir(), "settings.json");
23263
+ return path34.join(globalDataDir(), "settings.json");
23249
23264
  }
23250
23265
  function readPlannerModels(raw) {
23251
23266
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
@@ -23325,8 +23340,8 @@ var SettingsService = class {
23325
23340
  * a watch on the old one pointing at nothing.
23326
23341
  */
23327
23342
  watch(listener) {
23328
- const dir = path33.dirname(this.filePath);
23329
- const name = path33.basename(this.filePath);
23343
+ const dir = path34.dirname(this.filePath);
23344
+ const name = path34.basename(this.filePath);
23330
23345
  let seen = this.fileStamp();
23331
23346
  let timer;
23332
23347
  let dead = false;
@@ -23335,7 +23350,7 @@ var SettingsService = class {
23335
23350
  fs12.mkdirSync(dir, { recursive: true });
23336
23351
  watcher = fs12.watch(dir, (_event, changed) => {
23337
23352
  if (dead) return;
23338
- if (changed && changed !== name && changed !== path33.basename(dir)) return;
23353
+ if (changed && changed !== name && changed !== path34.basename(dir)) return;
23339
23354
  clearTimeout(timer);
23340
23355
  timer = setTimeout(() => {
23341
23356
  const stamp2 = this.fileStamp();
@@ -23568,7 +23583,7 @@ var SettingsService = class {
23568
23583
  if (this.unreadable) {
23569
23584
  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.`);
23570
23585
  }
23571
- const dir = path33.dirname(this.filePath);
23586
+ const dir = path34.dirname(this.filePath);
23572
23587
  if (!fs12.existsSync(dir)) {
23573
23588
  fs12.mkdirSync(dir, { recursive: true });
23574
23589
  }
@@ -24085,11 +24100,11 @@ var EnvConfig = class extends BaseConfig {
24085
24100
  // src/services/taskDiffRead.ts
24086
24101
  import { createHash as createHash3 } from "crypto";
24087
24102
  import * as fs13 from "fs";
24088
- import * as path34 from "path";
24103
+ import * as path35 from "path";
24089
24104
  import { devNull } from "os";
24090
24105
  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).";
24091
24106
  function allowedPath(file) {
24092
- return file.length > 0 && !path34.isAbsolute(file) && !/^[A-Za-z]:/.test(file) && !file.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".ordewell");
24107
+ return file.length > 0 && !path35.isAbsolute(file) && !/^[A-Za-z]:/.test(file) && !file.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".ordewell");
24093
24108
  }
24094
24109
  function stats(raw) {
24095
24110
  return raw.split("\0").flatMap((row) => {
@@ -24098,8 +24113,8 @@ function stats(raw) {
24098
24113
  });
24099
24114
  }
24100
24115
  function inside(root, target) {
24101
- const rel = path34.relative(root, target);
24102
- return rel === "" || !rel.startsWith(`..${path34.sep}`) && rel !== ".." && !path34.isAbsolute(rel);
24116
+ const rel = path35.relative(root, target);
24117
+ return rel === "" || !rel.startsWith(`..${path35.sep}`) && rel !== ".." && !path35.isAbsolute(rel);
24103
24118
  }
24104
24119
  function unquoted(quoted4) {
24105
24120
  const bytes = [];
@@ -24147,10 +24162,10 @@ function patchFiles(patch) {
24147
24162
  const sections = patch.toString("utf8").split(/(?<![^\n])(?=diff --git )/).filter((section) => section.startsWith("diff --git "));
24148
24163
  const files = /* @__PURE__ */ new Map();
24149
24164
  for (const section of sections) {
24150
- const path47 = sectionPath(section);
24165
+ const path49 = sectionPath(section);
24151
24166
  const counts = sectionStats(section);
24152
- const earlier = files.get(path47);
24153
- files.set(path47, earlier ? { path: path47, section: earlier.section + section, added: summed(earlier.added, counts.added), removed: summed(earlier.removed, counts.removed) } : { path: path47, section, ...counts });
24167
+ const earlier = files.get(path49);
24168
+ files.set(path49, earlier ? { path: path49, section: earlier.section + section, added: summed(earlier.added, counts.added), removed: summed(earlier.removed, counts.removed) } : { path: path49, section, ...counts });
24154
24169
  }
24155
24170
  return [...files.values()];
24156
24171
  }
@@ -24220,11 +24235,11 @@ async function readTaskDiff(invoker, run, taskId, options) {
24220
24235
  files.push({ ...file, repo: repo.path, patch: () => git(reader, cwd, [...diffArgs, "--", `:(top,literal)${prefix}${file.path}`]) });
24221
24236
  }
24222
24237
  if (live2) {
24223
- const workspace = path34.join(entry.worktree, prefix);
24238
+ const workspace = path35.join(entry.worktree, prefix);
24224
24239
  const others = await git(reader, workspace, [...common, "ls-files", "--others", "--exclude-standard", "-z", "--", "."]);
24225
24240
  for (const file of others.split("\0").filter((f) => allowed(f))) {
24226
- const full = path34.join(workspace, file);
24227
- if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(path34.dirname(full)))) continue;
24241
+ const full = path35.join(workspace, file);
24242
+ if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(path35.dirname(full)))) continue;
24228
24243
  const untracked = async (numstat) => {
24229
24244
  const result = await tryGit(reader, workspace, [...common, "diff", "--no-index", "--no-renames", "--no-ext-diff", "--no-textconv", ...numstat ? ["--numstat", "-z"] : [], "--", devNull, file]);
24230
24245
  if (!result.ok && result.code !== 1) throw new Error(result.stderr.trim() || "Could not read an untracked file diff.");
@@ -24254,11 +24269,11 @@ ${await file.patch()}`);
24254
24269
  import { randomBytes as randomBytes6 } from "crypto";
24255
24270
  import { spawnSync } from "child_process";
24256
24271
  import * as fs15 from "fs";
24257
- import * as path36 from "path";
24272
+ import * as path37 from "path";
24258
24273
 
24259
24274
  // src/utils/prdStore.ts
24260
24275
  import * as fs14 from "fs";
24261
- import * as path35 from "path";
24276
+ import * as path36 from "path";
24262
24277
  var PRD_BLOCK_RE = /<!--\s*ORDEWELL_PRD_START\s+slug="([^"]+)"\s*-->\s*([\s\S]*?)\s*<!--\s*ORDEWELL_PRD_END\s*-->/;
24263
24278
  function extractPrdBlock(text4) {
24264
24279
  const match = PRD_BLOCK_RE.exec(text4);
@@ -24272,9 +24287,9 @@ function sanitizeSlug(raw) {
24272
24287
  return raw.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
24273
24288
  }
24274
24289
  function savePrdMarkdown(workspace, slug, markdown) {
24275
- const dir = path35.join(workspace, ".scratch", sanitizeSlug(slug));
24290
+ const dir = path36.join(workspace, ".scratch", sanitizeSlug(slug));
24276
24291
  fs14.mkdirSync(dir, { recursive: true });
24277
- const file = path35.join(dir, "PRD.md");
24292
+ const file = path36.join(dir, "PRD.md");
24278
24293
  fs14.writeFileSync(file, markdown.endsWith("\n") ? markdown : markdown + "\n", "utf8");
24279
24294
  return file;
24280
24295
  }
@@ -24306,12 +24321,12 @@ var NESTED_REPO_DEPTH = 2;
24306
24321
  var GITLINK_MODE = "160000";
24307
24322
  var READ_ONLY = ["--no-optional-locks", "-c", "core.fsmonitor=false"];
24308
24323
  function isInside(parent, child) {
24309
- const rel = path36.relative(parent, child);
24310
- return rel !== "" && !rel.startsWith("..") && !path36.isAbsolute(rel);
24324
+ const rel = path37.relative(parent, child);
24325
+ return rel !== "" && !rel.startsWith("..") && !path37.isAbsolute(rel);
24311
24326
  }
24312
24327
  function resolvesAsNamed(base, p) {
24313
24328
  try {
24314
- return fs15.realpathSync(p) === path36.join(fs15.realpathSync(base), path36.relative(base, p));
24329
+ return fs15.realpathSync(p) === path37.join(fs15.realpathSync(base), path37.relative(base, p));
24315
24330
  } catch {
24316
24331
  return false;
24317
24332
  }
@@ -24366,7 +24381,7 @@ function sameDir(a, b) {
24366
24381
  try {
24367
24382
  return fs15.realpathSync.native(p);
24368
24383
  } catch {
24369
- return path36.resolve(p);
24384
+ return path37.resolve(p);
24370
24385
  }
24371
24386
  };
24372
24387
  return real(a) === real(b);
@@ -24388,7 +24403,7 @@ function clearImmutableFlags(platform, dir) {
24388
24403
  spawnSync("/usr/bin/chflags", ["-R", "nouchg", dir], { stdio: "ignore" });
24389
24404
  }
24390
24405
  function leftoverLinks(dir) {
24391
- const named3 = listDir2(dir).filter((name) => LINKED_ARTIFACTS.has(name) || isEnvFile(name) || isLink(path36.join(dir, name)));
24406
+ const named3 = listDir2(dir).filter((name) => LINKED_ARTIFACTS.has(name) || isEnvFile(name) || isLink(path37.join(dir, name)));
24392
24407
  return [.../* @__PURE__ */ new Set([...named3, ...installDirs(dir)])];
24393
24408
  }
24394
24409
  function isLink(target) {
@@ -24461,7 +24476,7 @@ var GitWorktreeIsolation = class {
24461
24476
  const head = await this.headOf(root);
24462
24477
  const repo = { path: repoPath, head };
24463
24478
  if (scan.toplevel !== void 0) {
24464
- const top = path36.relative(workspaceRoot, scan.toplevel).split(path36.sep).join("/");
24479
+ const top = path37.relative(workspaceRoot, scan.toplevel).split(path37.sep).join("/");
24465
24480
  if (top !== "") repo.top = top;
24466
24481
  }
24467
24482
  if (enabled && (head.kind === "branch" || head.kind === "detached")) {
@@ -24518,9 +24533,9 @@ var GitWorktreeIsolation = class {
24518
24533
  async nestedRepos(workspaceRoot, toplevel) {
24519
24534
  const found = [];
24520
24535
  const walk = (rel, depth) => {
24521
- for (const name of childDirs(path36.join(workspaceRoot, rel))) {
24536
+ for (const name of childDirs(path37.join(workspaceRoot, rel))) {
24522
24537
  const child = rel ? `${rel}/${name}` : name;
24523
- if (hasGitEntry(path36.join(workspaceRoot, child))) found.push(child);
24538
+ if (hasGitEntry(path37.join(workspaceRoot, child))) found.push(child);
24524
24539
  else if (depth < NESTED_REPO_DEPTH) walk(child, depth + 1);
24525
24540
  }
24526
24541
  };
@@ -24538,7 +24553,7 @@ var GitWorktreeIsolation = class {
24538
24553
  const [meta, file] = entry.split(" ");
24539
24554
  if (file !== void 0 && meta.startsWith(`${GITLINK_MODE} `)) paths.add(file);
24540
24555
  }
24541
- const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f", path36.join(toplevel, ".gitmodules"), "--get-regexp", "^submodule\\..*\\.path$"]);
24556
+ const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f", path37.join(toplevel, ".gitmodules"), "--get-regexp", "^submodule\\..*\\.path$"]);
24542
24557
  for (const entry of declared.stdout.split("\0")) {
24543
24558
  const value = entry.split("\n")[1];
24544
24559
  if (value) paths.add(value.replace(/\/+$/, ""));
@@ -24596,7 +24611,7 @@ var GitWorktreeIsolation = class {
24596
24611
  async acceptsWorktree(run, repo) {
24597
24612
  const dir = this.integrationDir(run, repo);
24598
24613
  ensureStateDirIgnored(run.workspaceRoot);
24599
- fs15.mkdirSync(path36.dirname(dir), { recursive: true });
24614
+ fs15.mkdirSync(path37.dirname(dir), { recursive: true });
24600
24615
  const added = await this.tryGit(repo.root, ["worktree", "add", "-q", "--no-checkout", dir, repo.integrationBranch]);
24601
24616
  await this.removeWorktreeDir(run, repo, dir, []);
24602
24617
  return added.ok;
@@ -24611,12 +24626,12 @@ var GitWorktreeIsolation = class {
24611
24626
  if (isolated.includes(SELF_REPO)) return [];
24612
24627
  const shared2 = [];
24613
24628
  const walk = (rel) => {
24614
- for (const name of listDir2(path36.join(workspaceRoot, rel))) {
24629
+ for (const name of listDir2(path37.join(workspaceRoot, rel))) {
24615
24630
  if (!rel && (name === STATE_DIR || name === ".git")) continue;
24616
24631
  const child = rel ? `${rel}/${name}` : name;
24617
24632
  if (isolated.includes(child)) continue;
24618
24633
  if (isolated.some((p) => p.startsWith(`${child}/`))) walk(child);
24619
- else if (resolves(path36.join(workspaceRoot, child))) shared2.push(child);
24634
+ else if (resolves(path37.join(workspaceRoot, child))) shared2.push(child);
24620
24635
  }
24621
24636
  };
24622
24637
  walk("");
@@ -24656,9 +24671,9 @@ var GitWorktreeIsolation = class {
24656
24671
  try {
24657
24672
  ensureStateDirIgnored(run.workspaceRoot);
24658
24673
  for (const repo of run.repos) {
24659
- const worktree = path36.join(dir, repo.path);
24674
+ const worktree = path37.join(dir, repo.path);
24660
24675
  const tip = (await this.git(repo.root, ["rev-parse", repo.integrationBranch])).trim();
24661
- fs15.mkdirSync(path36.dirname(worktree), { recursive: true });
24676
+ fs15.mkdirSync(path37.dirname(worktree), { recursive: true });
24662
24677
  await this.git(repo.root, ["worktree", "add", "-q", "-B", branch, worktree, tip]);
24663
24678
  record.repos[repo.path] = { worktree, linked: [], baseRef: tip };
24664
24679
  }
@@ -24683,13 +24698,13 @@ var GitWorktreeIsolation = class {
24683
24698
  setupFailed.push({ repo: repo.path, detail: err.detail });
24684
24699
  }
24685
24700
  entry.linked = boot.linked;
24686
- copied.push(...boot.copied.map((name) => path36.posix.join(repo.path, name)));
24701
+ copied.push(...boot.copied.map((name) => path37.posix.join(repo.path, name)));
24687
24702
  }
24688
24703
  for (const rel of run.shared) {
24689
- const source = path36.join(run.workspaceRoot, rel);
24690
- const target = path36.join(cwd, rel);
24704
+ const source = path37.join(run.workspaceRoot, rel);
24705
+ const target = path37.join(cwd, rel);
24691
24706
  if (!resolves(source) || lexists(target)) continue;
24692
- fs15.mkdirSync(path36.dirname(target), { recursive: true });
24707
+ fs15.mkdirSync(path37.dirname(target), { recursive: true });
24693
24708
  if (linkPath(source, target, this.platform) === "copy") copied.push(rel);
24694
24709
  const entry = self ? record.repos[SELF_REPO] : void 0;
24695
24710
  if (entry) entry.linked.push(rel);
@@ -24721,7 +24736,7 @@ var GitWorktreeIsolation = class {
24721
24736
  */
24722
24737
  async clearLeftovers(run, task2, names) {
24723
24738
  const removal = noRemoval();
24724
- const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch || path36.resolve(r.workspace) === path36.resolve(names.dir) || isInside(r.workspace, names.dir)));
24739
+ 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)));
24725
24740
  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`);
24726
24741
  const found = [];
24727
24742
  for (const repo of run.repos) {
@@ -24731,14 +24746,14 @@ var GitWorktreeIsolation = class {
24731
24746
  worktree: /^worktree (.+)$/m.exec(block)?.[1],
24732
24747
  branch: /^branch refs\/heads\/(.+)$/m.exec(block)?.[1]
24733
24748
  }));
24734
- const at = /* @__PURE__ */ new Set([path36.join(names.dir, repo.path)]);
24749
+ const at = /* @__PURE__ */ new Set([path37.join(names.dir, repo.path)]);
24735
24750
  for (const entry of registered) {
24736
- if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run), path36.resolve(entry.worktree))) at.add(entry.worktree);
24751
+ if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run), path37.resolve(entry.worktree))) at.add(entry.worktree);
24737
24752
  }
24738
- const base = { branch: names.branch, name: path36.basename(names.dir), title: task2.title };
24753
+ const base = { branch: names.branch, name: path37.basename(names.dir), title: task2.title };
24739
24754
  for (const worktree of at) {
24740
24755
  if (!fs15.existsSync(worktree)) continue;
24741
- const linked = leftoverLinks(path36.join(worktree, await this.prefixOf(repo)));
24756
+ const linked = leftoverLinks(path37.join(worktree, await this.prefixOf(repo)));
24742
24757
  found.push({ repo, worktree, target: { ...base, worktree, linked } });
24743
24758
  }
24744
24759
  found.push({ repo, worktree: names.dir, target: { ...base, linked: [] } });
@@ -24831,7 +24846,7 @@ var GitWorktreeIsolation = class {
24831
24846
  */
24832
24847
  async inWorkspacePlace(repo, worktree) {
24833
24848
  const prefix = await this.prefixOf(repo);
24834
- return prefix ? path36.join(worktree, prefix) : worktree;
24849
+ return prefix ? path37.join(worktree, prefix) : worktree;
24835
24850
  }
24836
24851
  async prefixOf(repo) {
24837
24852
  return (await this.git(repo.root, ["rev-parse", "--show-prefix"])).trim();
@@ -25361,15 +25376,15 @@ var GitWorktreeIsolation = class {
25361
25376
  return landingMessage(kind, from, this.deps.plannedWith?.() ?? false);
25362
25377
  }
25363
25378
  integrationDir(run, repo) {
25364
- return path36.join(this.runRoot(run), INTEGRATION_DIR, repo.path);
25379
+ return path37.join(this.runRoot(run), INTEGRATION_DIR, repo.path);
25365
25380
  }
25366
25381
  async ensureIntegrationWorktree(run, repo) {
25367
25382
  return this.admin(run.workspaceRoot, async () => {
25368
25383
  const dir = this.integrationDir(run, repo);
25369
- if (fs15.existsSync(path36.join(dir, ".git"))) return dir;
25384
+ if (fs15.existsSync(path37.join(dir, ".git"))) return dir;
25370
25385
  await this.tryGit(repo.root, ["worktree", "prune"]);
25371
25386
  ensureStateDirIgnored(run.workspaceRoot);
25372
- fs15.mkdirSync(path36.dirname(dir), { recursive: true });
25387
+ fs15.mkdirSync(path37.dirname(dir), { recursive: true });
25373
25388
  await this.git(repo.root, ["worktree", "add", "-q", dir, repo.integrationBranch]);
25374
25389
  return dir;
25375
25390
  });
@@ -25392,7 +25407,7 @@ var GitWorktreeIsolation = class {
25392
25407
  const removal = noRemoval();
25393
25408
  for (const repo of opts.preserve === false || record.readOnly ? [] : run.repos) {
25394
25409
  const entry = record.repos[repo.path];
25395
- const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name: path36.basename(record.workspace), title: record.title, commit: record.commit };
25410
+ const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name: path37.basename(record.workspace), title: record.title, commit: record.commit };
25396
25411
  try {
25397
25412
  const kept = await this.preserve(run, repo, target, opts.intent);
25398
25413
  if (kept) removal.preserved.push({ task: task2, ...kept });
@@ -25473,7 +25488,7 @@ var GitWorktreeIsolation = class {
25473
25488
  return;
25474
25489
  }
25475
25490
  for (const entry of entries) {
25476
- const target = path36.join(dir, entry.name);
25491
+ const target = path37.join(dir, entry.name);
25477
25492
  if (entry.isSymbolicLink()) this.unlinkIfLink(target);
25478
25493
  else if (entry.isDirectory()) this.unlinkLinksUnder(target);
25479
25494
  }
@@ -25495,20 +25510,20 @@ var GitWorktreeIsolation = class {
25495
25510
  return false;
25496
25511
  }
25497
25512
  };
25498
- const owned = new Set(Object.values(run.tasks).map((r) => path36.resolve(r.workspace)));
25513
+ const owned = new Set(Object.values(run.tasks).map((r) => path37.resolve(r.workspace)));
25499
25514
  const root = this.runRoot(run);
25500
25515
  if (fs15.existsSync(root)) {
25501
25516
  for (const entry of fs15.readdirSync(root)) {
25502
- const dir = path36.join(root, entry);
25503
- if (entry === INTEGRATION_DIR || owned.has(path36.resolve(dir))) continue;
25517
+ const dir = path37.join(root, entry);
25518
+ if (entry === INTEGRATION_DIR || owned.has(path37.resolve(dir))) continue;
25504
25519
  let kept = true;
25505
25520
  for (const repo of run.repos) {
25506
- const worktree = path36.join(dir, repo.path);
25507
- const linked = leftoverLinks(path36.join(worktree, await this.prefixOf(repo)));
25521
+ const worktree = path37.join(dir, repo.path);
25522
+ const linked = leftoverLinks(path37.join(worktree, await this.prefixOf(repo)));
25508
25523
  if (!await keep2(repo, { worktree, linked, branch: null, name: entry, title: entry }, worktree)) kept = false;
25509
25524
  }
25510
25525
  if (!kept) continue;
25511
- for (const repo of run.repos) await this.removeWorktreeDir(run, repo, path36.join(dir, repo.path), []);
25526
+ for (const repo of run.repos) await this.removeWorktreeDir(run, repo, path37.join(dir, repo.path), []);
25512
25527
  this.removeTaskWorkspace(run, dir);
25513
25528
  }
25514
25529
  }
@@ -25526,13 +25541,13 @@ var GitWorktreeIsolation = class {
25526
25541
  }
25527
25542
  async removeWorktreeDir(run, repo, dir, linked) {
25528
25543
  for (const name of /* @__PURE__ */ new Set([...linked, ...installDirs(dir)])) {
25529
- const target = path36.join(dir, name);
25530
- if (!resolvesAsNamed(this.runRoot(run), path36.dirname(target))) continue;
25544
+ const target = path37.join(dir, name);
25545
+ if (!resolvesAsNamed(this.runRoot(run), path37.dirname(target))) continue;
25531
25546
  this.unlinkIfLink(target);
25532
25547
  this.unlinkLinksUnder(target);
25533
25548
  }
25534
25549
  for (const name of resolvesAsNamed(this.runRoot(run), dir) ? fs15.readdirSync(dir) : []) {
25535
- if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(path36.join(dir, name));
25550
+ if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(path37.join(dir, name));
25536
25551
  }
25537
25552
  await this.tryGit(repo.root, ["worktree", "remove", "--force", dir]);
25538
25553
  if (fs15.existsSync(dir) && isInside(this.runRoot(run), dir)) {
@@ -25540,7 +25555,7 @@ var GitWorktreeIsolation = class {
25540
25555
  fs15.rmSync(dir, { recursive: true, force: true });
25541
25556
  }
25542
25557
  await this.tryGit(repo.root, ["worktree", "prune"]);
25543
- for (let parent = path36.dirname(dir); isInside(this.runRoot(run), parent); parent = path36.dirname(parent)) this.removeIfEmpty(parent);
25558
+ for (let parent = path37.dirname(dir); isInside(this.runRoot(run), parent); parent = path37.dirname(parent)) this.removeIfEmpty(parent);
25544
25559
  }
25545
25560
  unlinkIfLink(target) {
25546
25561
  try {
@@ -25555,14 +25570,14 @@ var GitWorktreeIsolation = class {
25555
25570
  }
25556
25571
  }
25557
25572
  runRoot(run) {
25558
- return path36.join(run.workspaceRoot, STATE_DIR, "worktrees", run.id);
25573
+ return path37.join(run.workspaceRoot, STATE_DIR, "worktrees", run.id);
25559
25574
  }
25560
25575
  namesFor(run, task2) {
25561
25576
  const slug = sanitizeSlug(task2.title).slice(0, 40).replace(/-+$/, "") || "task";
25562
25577
  let name = `${task2.order}-${slug}`;
25563
25578
  const clash = Object.values(run.tasks).some((r) => r.taskId !== task2.id && r.branch === `ordewell/${run.id}/${name}`);
25564
25579
  if (clash) name = `${name}-${sanitizeSlug(task2.id) || "x"}`;
25565
- return { branch: `ordewell/${run.id}/${name}`, dir: path36.join(this.runRoot(run), name) };
25580
+ return { branch: `ordewell/${run.id}/${name}`, dir: path37.join(this.runRoot(run), name) };
25566
25581
  }
25567
25582
  async unmergedPaths(cwd) {
25568
25583
  const listed3 = await this.tryGit(cwd, ["diff", "--name-only", "-z", "--diff-filter=U"]);
@@ -25576,7 +25591,7 @@ var GitWorktreeIsolation = class {
25576
25591
  async rebaseInProgress(cwd) {
25577
25592
  for (const dir of ["rebase-merge", "rebase-apply"]) {
25578
25593
  const found = await this.tryGit(cwd, ["rev-parse", "--git-path", dir]);
25579
- if (found.ok && fs15.existsSync(path36.resolve(cwd, found.stdout.trim()))) return true;
25594
+ if (found.ok && fs15.existsSync(path37.resolve(cwd, found.stdout.trim()))) return true;
25580
25595
  }
25581
25596
  return false;
25582
25597
  }
@@ -25603,6 +25618,29 @@ function createWorktreeIsolation(deps) {
25603
25618
  return new GitWorktreeIsolation(deps);
25604
25619
  }
25605
25620
 
25621
+ // src/services/repositoryRunnerConfig.ts
25622
+ var RUNNER_CONFIG_ROOTS = [".mcp.json", ".claude", ".claude-plugin", "opencode.json", "opencode.jsonc", ".opencode", ".codex"];
25623
+ function repositoryRunnerConfigPaths(paths) {
25624
+ return [...new Set(paths.filter(
25625
+ (path49) => (
25626
+ // Project MCP servers start commands or send data to a repository-chosen URL.
25627
+ path49 === ".mcp.json" || // Settings can run hooks and helpers, load plugins, or redirect traffic.
25628
+ path49 === ".claude/settings.json" || // Local settings have the same execution capabilities as project settings.
25629
+ path49 === ".claude/settings.local.json" || // Hook files provide commands invoked by the runner.
25630
+ path49.startsWith(".claude/hooks/") || // Plugin files can provide code and hooks loaded by the runner.
25631
+ path49.startsWith(".claude/plugins/") || // Plugin metadata can load plugins and their executable components.
25632
+ path49.startsWith(".claude-plugin/") || // Plugin and marketplace manifests can select code for the runner to load.
25633
+ /^\.claude\/(?:[^/]+\/)*(?:plugin|marketplace)\.json$/.test(path49) || // OpenCode config declares MCP servers and plugins; its plugin and tool
25634
+ // directories are code it loads, and an agent definition sets the prompt,
25635
+ // model and permissions of the built-in agent its id names, which a task
25636
+ // launches with. The rest of `.opencode/` is dependencies OpenCode
25637
+ // installs itself, which a later launch must not be held for.
25638
+ /^(?:\.opencode\/)?opencode\.jsonc?$/.test(path49) || /^\.opencode\/(?:plugins?|tools?|agents?)\//.test(path49) || // Codex project config carries MCP servers, hooks and `notify` commands.
25639
+ path49.startsWith(".codex/")
25640
+ )
25641
+ ))].sort();
25642
+ }
25643
+
25606
25644
  // src/interfaces/ILogger.ts
25607
25645
  var ConsoleLogger = class {
25608
25646
  warn(scope, message, err) {
@@ -28638,6 +28676,7 @@ var IsolationRunController = class {
28638
28676
  /** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
28639
28677
  resolvers = {};
28640
28678
  opening = null;
28679
+ opened = 0;
28641
28680
  /** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
28642
28681
  activations = 0;
28643
28682
  /** The start a dirty tree turned away, handed back once the user chooses how to go on. */
@@ -28669,6 +28708,10 @@ var IsolationRunController = class {
28669
28708
  get isOpen() {
28670
28709
  return this.mode !== null;
28671
28710
  }
28711
+ /** How many runs have opened: what tells one run's launches from the next one's. */
28712
+ get epoch() {
28713
+ return this.opened;
28714
+ }
28672
28715
  /** The open run has decided whether it isolates. */
28673
28716
  get decided() {
28674
28717
  return this.mode === "isolated" || this.mode === "shared";
@@ -28766,6 +28809,7 @@ var IsolationRunController = class {
28766
28809
  open() {
28767
28810
  if (this.mode) return;
28768
28811
  this.mode = "undecided";
28812
+ this.opened += 1;
28769
28813
  }
28770
28814
  /**
28771
28815
  * Decide once per run, as its first change task starts, whether it executes
@@ -29765,7 +29809,7 @@ function heldFrom(state) {
29765
29809
  }
29766
29810
 
29767
29811
  // src/services/Attempts.ts
29768
- import * as path39 from "path";
29812
+ import * as path41 from "path";
29769
29813
 
29770
29814
  // src/services/VerdictEngine.ts
29771
29815
  var IDLE_TIMEOUT_MS = 10 * 6e4;
@@ -30153,10 +30197,132 @@ function reportedVerdict(report) {
30153
30197
  return { outcome: "fail", reason: `The runner reported the task ${report.status}: ${why}`, checks, decidedAt, reported };
30154
30198
  }
30155
30199
 
30200
+ // src/services/taskStartConfig.ts
30201
+ import * as path38 from "path";
30202
+ import { createHash as createHash4 } from "crypto";
30203
+ import { existsSync as existsSync15, lstatSync as lstatSync4, readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync12 } from "fs";
30204
+ var MAX_FILES = 2e3;
30205
+ var MAX_DEPTH = 8;
30206
+ function liveConfig(dir) {
30207
+ const found = /* @__PURE__ */ new Map();
30208
+ const visit = (rel, depth) => {
30209
+ if (found.size >= MAX_FILES || depth > MAX_DEPTH) return;
30210
+ const full = path38.join(dir, rel);
30211
+ try {
30212
+ const info = statSync12(full);
30213
+ if (info.isFile()) {
30214
+ found.set(rel, createHash4("sha1").update(readFileSync13(full)).digest("hex"));
30215
+ } else if (info.isDirectory() && path38.basename(rel) !== "node_modules") {
30216
+ for (const name of readdirSync8(full)) visit(path38.posix.join(rel, name), depth + 1);
30217
+ }
30218
+ } catch {
30219
+ }
30220
+ };
30221
+ for (const root of RUNNER_CONFIG_ROOTS) visit(root, 0);
30222
+ const selected = new Set(repositoryRunnerConfigPaths([...found.keys()]));
30223
+ return new Map([...found].filter(([name]) => selected.has(name)));
30224
+ }
30225
+ function linkedRoots(dir) {
30226
+ const linked = /* @__PURE__ */ new Set();
30227
+ for (const root of RUNNER_CONFIG_ROOTS) {
30228
+ try {
30229
+ if (lstatSync4(path38.join(dir, root)).isSymbolicLink()) linked.add(root);
30230
+ } catch {
30231
+ }
30232
+ }
30233
+ return linked;
30234
+ }
30235
+ function readDirs(root) {
30236
+ const dirs = [{ dir: root, prefix: "" }];
30237
+ for (let dir = root, prefix = ""; !existsSync15(path38.join(dir, ".git")); ) {
30238
+ const parent = path38.dirname(dir);
30239
+ if (parent === dir) return [dirs[0]];
30240
+ dir = parent;
30241
+ prefix += "../";
30242
+ dirs.push({ dir, prefix });
30243
+ }
30244
+ return dirs;
30245
+ }
30246
+ var TaskStartConfig = class {
30247
+ live = /* @__PURE__ */ new Map();
30248
+ /**
30249
+ * `epoch` names the run the check belongs to; a new run takes a new
30250
+ * baseline. `root` is the workspace root, the checkout a worktree run lands
30251
+ * in; `cwd` is where the runner starts, and `worktree` whether that is a
30252
+ * worktree prepared for it.
30253
+ */
30254
+ async check(run, taskId, where) {
30255
+ const found = where.worktree ? await this.gitDifferences(run, taskId) : [];
30256
+ const id2 = String(where.epoch);
30257
+ const checkout = readDirs(where.root).flatMap(({ dir, prefix }) => this.liveDifferences(`${id2}:checkout:${dir}`, dir).map((name) => `${prefix}${name}`));
30258
+ if (!where.worktree) found.push(...checkout);
30259
+ else {
30260
+ const cwd = where.cwd ?? run?.tasks[taskId]?.workspace;
30261
+ if (cwd) found.push(...this.worktreeDifferences(id2, cwd, where.root));
30262
+ }
30263
+ return [...new Set(found)].sort();
30264
+ }
30265
+ /** The baselines go with the plan they belong to. */
30266
+ clear() {
30267
+ this.live.clear();
30268
+ }
30269
+ /**
30270
+ * What a worktree's runner reads that differs from where the run started.
30271
+ * The `.claude` and `.opencode` directories it links from the checkout are
30272
+ * the checkout's own, so they are compared with the checkout's first state,
30273
+ * which an earlier task's write there does not move. Anything else is the
30274
+ * worktree's, and is compared with its own first state: a setup command's
30275
+ * files are trusted, and a task's later write is not.
30276
+ */
30277
+ worktreeDifferences(id2, dir, root) {
30278
+ const now = liveConfig(dir);
30279
+ const linked = linkedRoots(dir);
30280
+ const fromCheckout = (name) => linked.has(name.split("/")[0]);
30281
+ const key = `${id2}:worktree:${dir}`;
30282
+ let local = this.live.get(key);
30283
+ if (!local) {
30284
+ local = new Map([...now].filter(([name]) => !fromCheckout(name)));
30285
+ this.live.set(key, local);
30286
+ }
30287
+ const checkout = this.live.get(`${id2}:checkout:${root}`);
30288
+ return [...now].filter(([name, hash]) => fromCheckout(name) ? checkout?.get(name) !== hash : local.get(name) !== hash).map(([name]) => name);
30289
+ }
30290
+ liveDifferences(key, dir) {
30291
+ const now = liveConfig(dir);
30292
+ const before = this.live.get(key);
30293
+ if (!before) {
30294
+ this.live.set(key, now);
30295
+ return [];
30296
+ }
30297
+ return [...now].filter(([name, hash]) => before.get(name) !== hash).map(([name]) => name);
30298
+ }
30299
+ async gitDifferences(run, taskId) {
30300
+ if (!run) return [];
30301
+ const invoker = { exec: defaultExecFile2, resolvePath: augmentedPath };
30302
+ const found = [];
30303
+ for (const repo of run.repos) {
30304
+ const cwd = run.tasks[taskId]?.repos[repo.path]?.worktree;
30305
+ if (!cwd || !repo.baseRef || !existsSync15(cwd)) continue;
30306
+ const prefix = repo.path === "." ? (await tryGit(invoker, repo.root, ["rev-parse", "--show-prefix"])).stdout.trim() : "";
30307
+ const roots = [...RUNNER_CONFIG_ROOTS, ...prefix ? RUNNER_CONFIG_ROOTS.map((r) => `${prefix}${r}`) : []];
30308
+ const diff = await tryGit(invoker, cwd, ["diff", "--no-ext-diff", "--no-textconv", "--no-renames", "--name-only", "--diff-filter=ACMRTU", "-z", repo.baseRef, "--"]);
30309
+ const untracked = await tryGit(invoker, cwd, ["ls-files", "--others", "-z", "--", ...roots]);
30310
+ if (!diff.ok || !untracked.ok) continue;
30311
+ for (const name of [...diff.stdout.split("\0"), ...untracked.stdout.split("\0")]) {
30312
+ const here = prefix && name.startsWith(prefix) ? name.slice(prefix.length) : void 0;
30313
+ if (repositoryRunnerConfigPaths([name]).length || here !== void 0 && repositoryRunnerConfigPaths([here]).length) {
30314
+ found.push(repo.path === "." ? name : path38.posix.join(repo.path, name));
30315
+ }
30316
+ }
30317
+ }
30318
+ return found;
30319
+ }
30320
+ };
30321
+
30156
30322
  // src/services/ScriptRunner.ts
30157
30323
  import { spawn as nodeSpawn2 } from "child_process";
30158
30324
  import * as fs16 from "fs";
30159
- import * as path37 from "path";
30325
+ import * as path39 from "path";
30160
30326
  import { EventEmitter as EventEmitter2 } from "events";
30161
30327
  import { StringDecoder as StringDecoder2 } from "string_decoder";
30162
30328
 
@@ -30297,7 +30463,7 @@ var ScriptSession = class extends AbstractRunnerSession {
30297
30463
  async launchOf(step) {
30298
30464
  if ("argv" in step) {
30299
30465
  const [named3, ...args] = step.argv;
30300
- const program = this.setup.platform === "win32" && path37.win32.basename(named3) !== named3 && !path37.win32.isAbsolute(named3) ? path37.win32.resolve(this.setup.cwd, named3) : named3;
30466
+ const program = this.setup.platform === "win32" && path39.win32.basename(named3) !== named3 && !path39.win32.isAbsolute(named3) ? path39.win32.resolve(this.setup.cwd, named3) : named3;
30301
30467
  const plan = await planDirectLaunch(program, args, this.setup.launch);
30302
30468
  return { file: plan.file, args: plan.args, shell: false, ...plan.verbatim ? { verbatim: true } : {} };
30303
30469
  }
@@ -30530,10 +30696,10 @@ function passed(status) {
30530
30696
  }
30531
30697
 
30532
30698
  // src/services/PatchCapture.ts
30533
- import { createHash as createHash4, randomBytes as randomBytes7 } from "crypto";
30699
+ import { createHash as createHash5, randomBytes as randomBytes7 } from "crypto";
30534
30700
  import * as fs17 from "fs";
30535
30701
  import * as os10 from "os";
30536
- import * as path38 from "path";
30702
+ import * as path40 from "path";
30537
30703
 
30538
30704
  // src/services/readTask.ts
30539
30705
  function readTask2(args, readerId, plan, log) {
@@ -30800,7 +30966,7 @@ function linkInPatch(patch) {
30800
30966
  function readOwn(scratch, name, max) {
30801
30967
  let fd;
30802
30968
  try {
30803
- fd = fs17.openSync(path38.join(scratch, name), fs17.constants.O_RDONLY | (fs17.constants.O_NOFOLLOW ?? 0));
30969
+ fd = fs17.openSync(path40.join(scratch, name), fs17.constants.O_RDONLY | (fs17.constants.O_NOFOLLOW ?? 0));
30804
30970
  } catch (err) {
30805
30971
  throw new Refusal(`the capture left no ${name.split(".").pop()} Ordewell could read (${err.code ?? String(err)})`);
30806
30972
  }
@@ -30838,7 +31004,7 @@ async function captureHeld(runs, opts, signal) {
30838
31004
  const [worktree] = runs.worktrees;
30839
31005
  const scratch = runs.scratch;
30840
31006
  const tag = `ordewell-patch-${randomBytes7(8).toString("hex")}`;
30841
- const stem = path38.join(scratch, tag);
31007
+ const stem = path40.join(scratch, tag);
30842
31008
  const own = (suffix, max = LISTING_BYTES) => readOwn(scratch, `${tag}.${suffix}`, max);
30843
31009
  try {
30844
31010
  const stage = await runs.run(stageScript(stem, opts.linked ?? []), worktree);
@@ -30876,7 +31042,7 @@ async function captureHeld(runs, opts, signal) {
30876
31042
  return {
30877
31043
  kind: "saved",
30878
31044
  patch,
30879
- stats: { base, ...countedIn(patch), bytes: patch.length, sha256: createHash4("sha256").update(patch).digest("hex"), flags: [...flags, ...flag("normalized", normalized2)] }
31045
+ stats: { base, ...countedIn(patch), bytes: patch.length, sha256: createHash5("sha256").update(patch).digest("hex"), flags: [...flags, ...flag("normalized", normalized2)] }
30880
31046
  };
30881
31047
  } catch (err) {
30882
31048
  if (err instanceof Refusal) return { kind: "refused", reason: `${err.message}.` };
@@ -30930,8 +31096,8 @@ async function indexEntries(invoker, worktree) {
30930
31096
  return entries;
30931
31097
  }
30932
31098
  async function trialApply(invoker, worktree, patchFile, scratch) {
30933
- const index = path38.resolve(worktree, (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--git-path", "index"])).trim());
30934
- const copy = path38.join(scratch, "index");
31099
+ const index = path40.resolve(worktree, (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--git-path", "index"])).trim());
31100
+ const copy = path40.join(scratch, "index");
30935
31101
  fs17.copyFileSync(index, copy);
30936
31102
  const trial = { ...invoker, exec: (file, args, opts) => invoker.exec(file, args, { ...opts, env: { ...opts.env, GIT_INDEX_FILE: copy } }) };
30937
31103
  const tried = await tryGit(trial, worktree, [...APPLY_GIT, "apply", "--cached", "--3way", "--whitespace=nowarn", patchFile]);
@@ -30951,7 +31117,7 @@ function gitRefused(lead, stderr) {
30951
31117
  function onDisk3(worktree, names) {
30952
31118
  return names.map((name) => {
30953
31119
  try {
30954
- const stat = fs17.lstatSync(path38.join(worktree, name), { bigint: true, throwIfNoEntry: false });
31120
+ const stat = fs17.lstatSync(path40.join(worktree, name), { bigint: true, throwIfNoEntry: false });
30955
31121
  return stat ? `${stat.mode} ${stat.ino} ${stat.size} ${stat.mtimeNs} ${stat.ctimeNs}` : "none";
30956
31122
  } catch (err) {
30957
31123
  return err.code ?? "unreadable";
@@ -30991,7 +31157,7 @@ function leavesWorktree(worktree, names, linked, held2, left) {
30991
31157
  if (i < parts.length && held2.get(at)?.some((entry) => entry.mode === SUBMODULE)) {
30992
31158
  return `the patch writes ${name}, under ${at}, a submodule, whose files are another repository's.`;
30993
31159
  }
30994
- const full = path38.join(worktree, ...parts.slice(0, i));
31160
+ const full = path40.join(worktree, ...parts.slice(0, i));
30995
31161
  const stat = fs17.lstatSync(full, { throwIfNoEntry: false });
30996
31162
  if (!stat) break;
30997
31163
  const last = i === parts.length;
@@ -31012,14 +31178,14 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
31012
31178
  if (patch.length !== saved.bytes) {
31013
31179
  return refused3(`the patch is ${byteSize(patch.length)} where its artifact says ${byteSize(saved.bytes)}, so it was cut short or changed after it was saved.`);
31014
31180
  }
31015
- if (createHash4("sha256").update(patch).digest("hex") !== saved.sha256) {
31181
+ if (createHash5("sha256").update(patch).digest("hex") !== saved.sha256) {
31016
31182
  return refused3("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.");
31017
31183
  }
31018
31184
  const link = linkInPatch(patch);
31019
31185
  if (link !== void 0) {
31020
31186
  return refused3(`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.`);
31021
31187
  }
31022
- const dir = fs17.mkdtempSync(path38.join(os10.tmpdir(), "ordewell-apply-"));
31188
+ const dir = fs17.mkdtempSync(path40.join(os10.tmpdir(), "ordewell-apply-"));
31023
31189
  try {
31024
31190
  const toplevel = (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--show-toplevel"])).trim();
31025
31191
  if (fs17.realpathSync(toplevel) !== fs17.realpathSync(worktree)) return refused3(`${worktree} is not the root of a worktree of its own.`);
@@ -31029,7 +31195,7 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
31029
31195
  }
31030
31196
  const before = await unmergedPaths(invoker, worktree);
31031
31197
  if (before.length > 0) return refused3(`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.`);
31032
- const file = path38.join(dir, "saved.patch");
31198
+ const file = path40.join(dir, "saved.patch");
31033
31199
  fs17.writeFileSync(file, patch, { flag: "wx", mode: 384 });
31034
31200
  const held2 = await indexEntries(invoker, worktree);
31035
31201
  const named3 = await pathsNamed(invoker, worktree, file);
@@ -31179,6 +31345,11 @@ var TaskWebReader = class {
31179
31345
  };
31180
31346
 
31181
31347
  // src/services/Attempts.ts
31348
+ var TaskStartConfigError = class extends Error {
31349
+ constructor(paths) {
31350
+ 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`);
31351
+ }
31352
+ };
31182
31353
  var DEFAULT_GIT = { exec: defaultExecFile2, resolvePath: () => augmentedPath() };
31183
31354
  var Attempts = class {
31184
31355
  constructor(deps) {
@@ -31210,6 +31381,7 @@ var Attempts = class {
31210
31381
  live = /* @__PURE__ */ new Map();
31211
31382
  /** Bumped by every plan load: an ended attempt's wind-down belongs to the scope before it. */
31212
31383
  planScope = 0;
31384
+ startConfig = new TaskStartConfig();
31213
31385
  /** Each task's runners still winding down once stopped: a continue waits for them (ADR-0018, L1). */
31214
31386
  windDowns = /* @__PURE__ */ new Map();
31215
31387
  /**
@@ -31379,6 +31551,7 @@ var Attempts = class {
31379
31551
  const ended = this.endAll("load");
31380
31552
  this.planScope += 1;
31381
31553
  this.spawnCounts.clear();
31554
+ this.startConfig.clear();
31382
31555
  return ended;
31383
31556
  }
31384
31557
  /** A task leaving the plan: no later attempt of it is numbered from its runs. */
@@ -31511,9 +31684,17 @@ var Attempts = class {
31511
31684
  if (unavailable2) throw new Error(unavailable2);
31512
31685
  }
31513
31686
  if (kind.kind === "continuation") await this.windDowns.get(task2.id);
31687
+ if (!attempt.script && kind.kind === "repair") {
31688
+ const paths = await this.startConfig.check(runs.current, task2.id, { epoch: runs.epoch, root: this.deps.workspaceRoot(), cwd: null, worktree: true });
31689
+ if (paths.length) throw new TaskStartConfigError(paths);
31690
+ }
31514
31691
  const { cwd, worktree, previousWork, workspace } = await attemptCwd(kind, task2, runs);
31515
31692
  attempt.cwd = cwd;
31516
31693
  attempt.worktree = worktree;
31694
+ if (!attempt.script && kind.kind !== "repair" && !landsNothing(kind)) {
31695
+ const paths = await this.startConfig.check(runs.current, task2.id, { epoch: runs.epoch, root: this.deps.workspaceRoot(), cwd, worktree });
31696
+ if (paths.length) throw new TaskStartConfigError(paths);
31697
+ }
31517
31698
  if (checksTree(kind)) attempt.snapshot = await runs.snapshotWorkspace();
31518
31699
  if (this.live.get(task2.id) !== attempt) {
31519
31700
  if (attempt.worktree && !this.live.has(task2.id) && !this.host.completing(task2.id)) await runs.release(task2.id, { keep: kind.kind === "repair" });
@@ -31596,6 +31777,9 @@ var Attempts = class {
31596
31777
  return { kind: "usage-limited", taskId: task2.id, attempt, said: { message }, output: "" };
31597
31778
  }
31598
31779
  const failed = (failure2) => ({ kind: "spawn-failed", task: task2, attempt, failure: failure2 });
31780
+ if (err instanceof TaskStartConfigError) {
31781
+ return failed({ leaves: "held", hold: err.message, preserveWorktree: true, settles: false, notice: { level: "warn", text: `Task "${task2.title}" is on hold: ${err.message}` } });
31782
+ }
31599
31783
  if (attempt.kind.kind === "continuation" && !attachTimeout) {
31600
31784
  const refused3 = err instanceof RunnerStartupExit;
31601
31785
  const reason = unresumedMessage(task2, refused3 ? `${attempt.runner} could not find its saved session` : `could not start: ${message}`);
@@ -31732,9 +31916,9 @@ var Attempts = class {
31732
31916
  const repos = Object.values(this.deps.runs.openRecord(taskId)?.repos ?? {});
31733
31917
  if (repos.length !== 1 || cwd === null) return [];
31734
31918
  const [{ worktree, linked }] = repos;
31735
- const prefix = path39.relative(worktree, cwd).split(path39.sep).join("/");
31736
- if (prefix.startsWith("..") || path39.isAbsolute(prefix)) return [];
31737
- return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(path39.sep).join("/"));
31919
+ const prefix = path41.relative(worktree, cwd).split(path41.sep).join("/");
31920
+ if (prefix.startsWith("..") || path41.isAbsolute(prefix)) return [];
31921
+ return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(path41.sep).join("/"));
31738
31922
  }
31739
31923
  /**
31740
31924
  * A script task's start (ADR-0035): its body as the plan holds it now, run
@@ -31772,7 +31956,7 @@ var Attempts = class {
31772
31956
  this.deps.notifications.warn(message);
31773
31957
  };
31774
31958
  if (resolved.blockedEnvrc) {
31775
- warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path39.dirname(resolved.blockedEnvrc)} to use them.`);
31959
+ warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path41.dirname(resolved.blockedEnvrc)} to use them.`);
31776
31960
  }
31777
31961
  if (resolved.trackedEnvFile) {
31778
31962
  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.`);
@@ -31798,7 +31982,7 @@ var Attempts = class {
31798
31982
  uncommittedSkillFolder(name) {
31799
31983
  const root = this.deps.workspaceRoot();
31800
31984
  const skill = this.deps.skillsAt(this.skillRoots(root)).findSkill(name);
31801
- return skill?.source === "workspace" ? path39.relative(root, path39.dirname(skill.path)).split(path39.sep).join("/") : void 0;
31985
+ return skill?.source === "workspace" ? path41.relative(root, path41.dirname(skill.path)).split(path41.sep).join("/") : void 0;
31802
31986
  }
31803
31987
  /**
31804
31988
  * The roots whose `.ordewell/skills/` an attempt at `cwd` reads: in a repo
@@ -32167,12 +32351,12 @@ function ancestors(task2, store) {
32167
32351
 
32168
32352
  // src/services/sessionLease.ts
32169
32353
  import * as fs19 from "fs";
32170
- import * as path41 from "path";
32354
+ import * as path43 from "path";
32171
32355
  import { randomUUID as randomUUID6 } from "crypto";
32172
32356
 
32173
32357
  // src/utils/sessionStore.ts
32174
32358
  import * as fs18 from "fs";
32175
- import * as path40 from "path";
32359
+ import * as path42 from "path";
32176
32360
 
32177
32361
  // src/utils/sessionId.ts
32178
32362
  import { randomBytes as randomBytes8 } from "crypto";
@@ -32183,13 +32367,13 @@ function mintSessionId() {
32183
32367
  // src/utils/sessionStore.ts
32184
32368
  var SESSIONS_SUBDIR = "sessions";
32185
32369
  function getSessionsDir(baseDir) {
32186
- return path40.join(getStateDir(baseDir), SESSIONS_SUBDIR);
32370
+ return path42.join(getStateDir(baseDir), SESSIONS_SUBDIR);
32187
32371
  }
32188
32372
  function idSegment(id2) {
32189
32373
  return /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,127}$/.test(id2) ? id2 : `~${Buffer.from(id2, "utf-8").toString("hex")}`;
32190
32374
  }
32191
32375
  function sessionDataDir(sessionId, baseDir) {
32192
- return path40.join(getSessionsDir(baseDir), idSegment(sessionId));
32376
+ return path42.join(getSessionsDir(baseDir), idSegment(sessionId));
32193
32377
  }
32194
32378
  var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
32195
32379
  function isSessionMeta(value) {
@@ -32216,8 +32400,8 @@ function legacySessionFilename(session) {
32216
32400
  return `${filenameStem(session)}_${session.id.slice(0, 8)}.json`;
32217
32401
  }
32218
32402
  function dropLegacyFile(sessionsDir, meta) {
32219
- const legacy = path40.join(sessionsDir, legacySessionFilename(meta));
32220
- if (legacy === path40.join(sessionsDir, sessionFilename(meta)) || !fs18.existsSync(legacy)) return;
32403
+ const legacy = path42.join(sessionsDir, legacySessionFilename(meta));
32404
+ if (legacy === path42.join(sessionsDir, sessionFilename(meta)) || !fs18.existsSync(legacy)) return;
32221
32405
  try {
32222
32406
  const held2 = JSON.parse(fs18.readFileSync(legacy, "utf-8")).meta?.id;
32223
32407
  if (held2 === meta.id) fs18.unlinkSync(legacy);
@@ -32244,7 +32428,7 @@ function saveSession(plan, goal, baseDir, id2) {
32244
32428
  };
32245
32429
  dropRetiredPlanFields(session.plan);
32246
32430
  const filename = sessionFilename(meta);
32247
- const filePath = path40.join(sessionsDir, filename);
32431
+ const filePath = path42.join(sessionsDir, filename);
32248
32432
  writeFileAtomic(filePath, JSON.stringify(session, null, 2));
32249
32433
  dropLegacyFile(sessionsDir, meta);
32250
32434
  return meta;
@@ -32256,7 +32440,7 @@ function listSessions(baseDir, logger = defaultLogger) {
32256
32440
  return files.map((f) => {
32257
32441
  let parsed;
32258
32442
  try {
32259
- parsed = JSON.parse(fs18.readFileSync(path40.join(sessionsDir, f), "utf-8"));
32443
+ parsed = JSON.parse(fs18.readFileSync(path42.join(sessionsDir, f), "utf-8"));
32260
32444
  } catch (err) {
32261
32445
  if (err instanceof SyntaxError) return moveAside(sessionsDir, f, "is not valid JSON", logger);
32262
32446
  logger.warn("sessionStore", `failed to read session file ${f}; skipping`, err);
@@ -32277,7 +32461,7 @@ function listSessions(baseDir, logger = defaultLogger) {
32277
32461
  function moveAside(sessionsDir, file, reason, logger) {
32278
32462
  const aside = `${file}.${Date.now()}.corrupt`;
32279
32463
  try {
32280
- fs18.renameSync(path40.join(sessionsDir, file), path40.join(sessionsDir, aside));
32464
+ fs18.renameSync(path42.join(sessionsDir, file), path42.join(sessionsDir, aside));
32281
32465
  logger.warn("sessionStore", `session file ${file} ${reason}; moved aside to ${aside} rather than overwritten`);
32282
32466
  } catch {
32283
32467
  logger.warn("sessionStore", `failed to read session file ${file}; skipping`);
@@ -32288,7 +32472,7 @@ function findSessionFile(sessionsDir, sessionId, logger) {
32288
32472
  const files = fs18.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
32289
32473
  for (const f of files) {
32290
32474
  try {
32291
- const raw = fs18.readFileSync(path40.join(sessionsDir, f), "utf-8");
32475
+ const raw = fs18.readFileSync(path42.join(sessionsDir, f), "utf-8");
32292
32476
  const session = JSON.parse(raw);
32293
32477
  if (session.meta.id === sessionId) return f;
32294
32478
  } catch (err) {
@@ -32303,7 +32487,7 @@ function loadSession(sessionId, baseDir, logger = defaultLogger) {
32303
32487
  try {
32304
32488
  const file = findSessionFile(sessionsDir, sessionId, logger);
32305
32489
  if (file) {
32306
- const raw = fs18.readFileSync(path40.join(sessionsDir, file), "utf-8");
32490
+ const raw = fs18.readFileSync(path42.join(sessionsDir, file), "utf-8");
32307
32491
  const parsed = JSON.parse(raw);
32308
32492
  if (hasScalarRunner(parsed)) {
32309
32493
  logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
@@ -32345,7 +32529,7 @@ function deleteSession(sessionId, baseDir, logger = defaultLogger) {
32345
32529
  try {
32346
32530
  const file = findSessionFile(sessionsDir, sessionId, logger);
32347
32531
  if (file) {
32348
- fs18.unlinkSync(path40.join(sessionsDir, file));
32532
+ fs18.unlinkSync(path42.join(sessionsDir, file));
32349
32533
  return true;
32350
32534
  }
32351
32535
  } catch (err) {
@@ -32398,7 +32582,7 @@ var UNLEASED = {
32398
32582
  };
32399
32583
  var hostsHere = /* @__PURE__ */ new Set();
32400
32584
  function leaseFile(sessionId, workspace) {
32401
- return path41.join(sessionDataDir(sessionId, workspace), LEASE_FILE);
32585
+ return path43.join(sessionDataDir(sessionId, workspace), LEASE_FILE);
32402
32586
  }
32403
32587
  function isRecord6(value) {
32404
32588
  if (typeof value !== "object" || value === null) return false;
@@ -32487,7 +32671,7 @@ var LeaseHost = class {
32487
32671
  */
32488
32672
  acquire(file, workspace, holder) {
32489
32673
  ensureStateDirIgnored(workspace);
32490
- ensurePrivateDir(path41.dirname(file));
32674
+ ensurePrivateDir(path43.dirname(file));
32491
32675
  const outcome = withFileLock(file, () => {
32492
32676
  const current = readRecord(file);
32493
32677
  const taken = current !== null && current.hostId !== this.id ? current : null;
@@ -32542,7 +32726,7 @@ var LeaseHost = class {
32542
32726
  withFileLock(file, () => {
32543
32727
  if (readRecord(file)?.hostId === this.id) fs19.rmSync(file, { force: true });
32544
32728
  }, "release a session lease");
32545
- fs19.rmdirSync(path41.dirname(file));
32729
+ fs19.rmdirSync(path43.dirname(file));
32546
32730
  } catch {
32547
32731
  }
32548
32732
  }
@@ -33488,7 +33672,9 @@ var TaskOrchestrator = class _TaskOrchestrator {
33488
33672
  this.store.markPending(task2.id);
33489
33673
  this.hold(task2.id, failure2.hold, false);
33490
33674
  }
33491
- await this.runs.release(task2.id, { keep: failure2.leaves === "failed" && failure2.keepWorktree });
33675
+ if (failure2.leaves !== "held" || !failure2.preserveWorktree) {
33676
+ await this.runs.release(task2.id, { keep: failure2.leaves === "failed" && failure2.keepWorktree });
33677
+ }
33492
33678
  const { level, text: text4, toastOnly } = failure2.notice;
33493
33679
  if (toastOnly) this.notifications[level](text4);
33494
33680
  else this.tell(level, text4);
@@ -35304,7 +35490,7 @@ import { spawn as nodeSpawn3 } from "child_process";
35304
35490
  import { randomUUID as randomUUID7 } from "crypto";
35305
35491
  import { mkdtempSync as mkdtempSync5, rmSync as rmSync12 } from "fs";
35306
35492
  import { tmpdir as tmpdir6 } from "os";
35307
- import { join as join37 } from "path";
35493
+ import { join as join39 } from "path";
35308
35494
  var DEFAULT_TIMEOUT_MS5 = 2e4;
35309
35495
  var INITIALIZE_TIMEOUT_MS2 = 1e4;
35310
35496
  var USAGE_TIMEOUT_MS = 1e4;
@@ -35336,7 +35522,7 @@ function claudeAccountLimitCollector(options) {
35336
35522
  };
35337
35523
  }
35338
35524
  async function readClaudeAccountLimits(options, signal) {
35339
- const cwd = mkdtempSync5(join37(tmpdir6(), "ordewell-claude-limits-"));
35525
+ const cwd = mkdtempSync5(join39(tmpdir6(), "ordewell-claude-limits-"));
35340
35526
  const removeCwd = () => {
35341
35527
  try {
35342
35528
  rmSync12(cwd, { recursive: true, force: true });
@@ -35423,10 +35609,10 @@ function isoInstant(value) {
35423
35609
  import { homedir as homedir9 } from "os";
35424
35610
 
35425
35611
  // src/services/harness/openCodeCredentials.ts
35426
- import { createHash as createHash5 } from "crypto";
35612
+ import { createHash as createHash6 } from "crypto";
35427
35613
  import { promises as fs20 } from "fs";
35428
35614
  import { homedir as osHomedir } from "os";
35429
- import { join as join38, resolve as resolve13 } from "path";
35615
+ import { join as join40, resolve as resolve13 } from "path";
35430
35616
  import { Worker } from "worker_threads";
35431
35617
  var DEFAULT_TIMEOUT_MS6 = 5e3;
35432
35618
  var BUSY_MS = 250;
@@ -35481,11 +35667,11 @@ function walStore(path) {
35481
35667
  parentPort.postMessage(select());
35482
35668
  `;
35483
35669
  var isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35484
- var digest = (...parts) => createHash5("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
35670
+ var digest = (...parts) => createHash6("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
35485
35671
  function openCodeStorePaths(env, home) {
35486
- const data = join38(env.XDG_DATA_HOME || join38(home, ".local", "share"), "opencode");
35672
+ const data = join40(env.XDG_DATA_HOME || join40(home, ".local", "share"), "opencode");
35487
35673
  const file = env.OPENCODE_DB || "opencode.db";
35488
- return { db: file === ":memory:" ? null : resolve13(data, file), auth: join38(data, "auth.json") };
35674
+ return { db: file === ":memory:" ? null : resolve13(data, file), auth: join40(data, "auth.json") };
35489
35675
  }
35490
35676
  function openCodeGoLoginReader(deps = {}) {
35491
35677
  const homedir10 = deps.homedir ?? osHomedir;
@@ -35547,8 +35733,8 @@ function v2Login(db, row) {
35547
35733
  login: { route: "console", owner: digest("console", db, row.id, account, orgID ?? ""), access: value.access, ...orgID ? { orgID } : {}, expires: value.expires }
35548
35734
  };
35549
35735
  }
35550
- async function legacyLogin(path47, env) {
35551
- const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(path47);
35736
+ async function legacyLogin(path49, env) {
35737
+ const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(path49);
35552
35738
  if (content === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
35553
35739
  if (content === null) return { kind: "unreadable" };
35554
35740
  const auth = parseJson(content);
@@ -35557,15 +35743,15 @@ async function legacyLogin(path47, env) {
35557
35743
  if (entry === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
35558
35744
  if (!isRecord9(entry) || entry.type !== "api") return { kind: "unsupported" };
35559
35745
  if (typeof entry.key !== "string" || entry.key.length === 0) return { kind: "unreadable" };
35560
- return { kind: "login", login: { route: "key", owner: digest("v1", path47, entry.key), key: entry.key } };
35746
+ return { kind: "login", login: { route: "key", owner: digest("v1", path49, entry.key), key: entry.key } };
35561
35747
  }
35562
- function queryStore(path47, signal, deps) {
35748
+ function queryStore(path49, signal, deps) {
35563
35749
  return new Promise((settle) => {
35564
35750
  let worker;
35565
35751
  try {
35566
35752
  worker = (deps.spawnWorker ?? ((source, options) => new Worker(source, options)))(WORKER_SOURCE, {
35567
35753
  eval: true,
35568
- workerData: { path: path47, integrations: GO_INTEGRATIONS, busyMs: BUSY_MS, maxValueBytes: MAX_VALUE_BYTES },
35754
+ workerData: { path: path49, integrations: GO_INTEGRATIONS, busyMs: BUSY_MS, maxValueBytes: MAX_VALUE_BYTES },
35569
35755
  execArgv: deps.workerExecArgv ?? [],
35570
35756
  resourceLimits: { maxOldGenerationSizeMb: 64 }
35571
35757
  });
@@ -35592,19 +35778,19 @@ function queryStore(path47, signal, deps) {
35592
35778
  worker.once("exit", aborted);
35593
35779
  });
35594
35780
  }
35595
- async function stamp(path47) {
35781
+ async function stamp(path49) {
35596
35782
  try {
35597
- const stat = await fs20.stat(path47);
35783
+ const stat = await fs20.stat(path49);
35598
35784
  return [stat.ino, stat.size, stat.mtimeMs];
35599
35785
  } catch {
35600
35786
  return null;
35601
35787
  }
35602
35788
  }
35603
- async function readSmall(path47) {
35789
+ async function readSmall(path49) {
35604
35790
  try {
35605
- const stat = await fs20.stat(path47);
35791
+ const stat = await fs20.stat(path49);
35606
35792
  if (!stat.isFile() || stat.size > MAX_AUTH_FILE_BYTES) return null;
35607
- return await fs20.readFile(path47, "utf8");
35793
+ return await fs20.readFile(path49, "utf8");
35608
35794
  } catch (err) {
35609
35795
  return err.code === "ENOENT" ? void 0 : null;
35610
35796
  }
@@ -36733,20 +36919,20 @@ function createAiService(config, deps) {
36733
36919
  }
36734
36920
 
36735
36921
  // src/services/createSession.ts
36736
- import * as path44 from "path";
36922
+ import * as path46 from "path";
36737
36923
 
36738
36924
  // src/services/handoffReferences.ts
36739
36925
  import * as fs21 from "fs";
36740
- import * as path42 from "path";
36926
+ import * as path44 from "path";
36741
36927
  import { spawnSync as spawnSync2 } from "child_process";
36742
36928
  function resolveHandoffReferences(handoff, workspace) {
36743
36929
  const checked = /* @__PURE__ */ new Map();
36744
36930
  const contained = (candidate) => {
36745
36931
  try {
36746
36932
  const root = fs21.realpathSync(workspace);
36747
- const resolved = fs21.realpathSync(path42.resolve(root, candidate));
36748
- const relative17 = path42.relative(root, resolved);
36749
- return relative17 !== ".." && !relative17.startsWith(`..${path42.sep}`) && !path42.isAbsolute(relative17) ? resolved : void 0;
36933
+ const resolved = fs21.realpathSync(path44.resolve(root, candidate));
36934
+ const relative17 = path44.relative(root, resolved);
36935
+ return relative17 !== ".." && !relative17.startsWith(`..${path44.sep}`) && !path44.isAbsolute(relative17) ? resolved : void 0;
36750
36936
  } catch {
36751
36937
  return void 0;
36752
36938
  }
@@ -36767,7 +36953,7 @@ function resolveHandoffReferences(handoff, workspace) {
36767
36953
  }
36768
36954
  } else if (/^[0-9a-fA-F]{4,64}$/.test(ref.sha)) {
36769
36955
  const repo = contained(ref.repo ?? ".");
36770
- if (repo && fs21.existsSync(path42.join(repo, ".git"))) {
36956
+ if (repo && fs21.existsSync(path44.join(repo, ".git"))) {
36771
36957
  const result2 = spawnSync2("git", ["--no-optional-locks", "--no-replace-objects", "cat-file", "-t", ref.sha], {
36772
36958
  cwd: repo,
36773
36959
  encoding: "utf8",
@@ -38242,10 +38428,10 @@ var SessionEventRelay = class {
38242
38428
 
38243
38429
  // src/utils/taskLogStore.ts
38244
38430
  import * as fs22 from "fs";
38245
- import * as path43 from "path";
38431
+ import * as path45 from "path";
38246
38432
  var EXTENSION = ".jsonl";
38247
38433
  function taskDir({ baseDir, sessionId }, taskId) {
38248
- return path43.join(sessionDataDir(sessionId, baseDir), "tasks", idSegment(taskId));
38434
+ return path45.join(sessionDataDir(sessionId, baseDir), "tasks", idSegment(taskId));
38249
38435
  }
38250
38436
  function listTaskLogAttempts(location, taskId) {
38251
38437
  let names;
@@ -38263,7 +38449,7 @@ function openTaskLog(location, taskId, attempt) {
38263
38449
  const next = (listTaskLogAttempts(location, taskId).pop() ?? 0) + 1;
38264
38450
  let number = attempt !== void 0 && Number.isInteger(attempt) && attempt >= 1 ? attempt : next;
38265
38451
  for (; ; ) {
38266
- const file = path43.join(dir, `${number}${EXTENSION}`);
38452
+ const file = path45.join(dir, `${number}${EXTENSION}`);
38267
38453
  try {
38268
38454
  fs22.writeFileSync(file, "", { flag: "wx" });
38269
38455
  } catch (err) {
@@ -38287,15 +38473,15 @@ function prepareTaskOutputFile(location, taskId, attempt) {
38287
38473
  ensureStateDirIgnored(location.baseDir);
38288
38474
  const dir = taskDir(location, taskId);
38289
38475
  fs22.mkdirSync(dir, { recursive: true });
38290
- return path43.join(dir, `${attempt}.output.txt`);
38476
+ return path45.join(dir, `${attempt}.output.txt`);
38291
38477
  }
38292
38478
  function taskPatchFile(location, taskId, attempt) {
38293
- return path43.join(taskDir(location, taskId), `${attempt}.patch`);
38479
+ return path45.join(taskDir(location, taskId), `${attempt}.patch`);
38294
38480
  }
38295
38481
  function saveTaskPatch(location, taskId, attempt, patch) {
38296
38482
  ensureStateDirIgnored(location.baseDir);
38297
38483
  const file = taskPatchFile(location, taskId, attempt);
38298
- fs22.mkdirSync(path43.dirname(file), { recursive: true });
38484
+ fs22.mkdirSync(path45.dirname(file), { recursive: true });
38299
38485
  const partial = `${file}.${process.pid}-${Date.now()}.partial`;
38300
38486
  fs22.writeFileSync(partial, patch, { flag: "wx" });
38301
38487
  fs22.renameSync(partial, file);
@@ -38316,7 +38502,7 @@ function readTaskLog(location, taskId, attempt) {
38316
38502
  if (!Number.isInteger(attempt) || attempt < 1) return [];
38317
38503
  let raw;
38318
38504
  try {
38319
- raw = fs22.readFileSync(path43.join(taskDir(location, taskId), `${attempt}${EXTENSION}`), "utf-8");
38505
+ raw = fs22.readFileSync(path45.join(taskDir(location, taskId), `${attempt}${EXTENSION}`), "utf-8");
38320
38506
  } catch {
38321
38507
  return [];
38322
38508
  }
@@ -40345,7 +40531,7 @@ function createSession(deps) {
40345
40531
  ...deps.openTaskLog ? {} : {
40346
40532
  keepPatch: (taskId, attempt, patch) => {
40347
40533
  const location = session.taskLogLocation;
40348
- return path44.relative(location.baseDir, saveTaskPatch(location, taskId, attempt, patch)).split(path44.sep).join("/");
40534
+ return path46.relative(location.baseDir, saveTaskPatch(location, taskId, attempt, patch)).split(path46.sep).join("/");
40349
40535
  }
40350
40536
  },
40351
40537
  recordHandoff: (sessionId, taskId, handoff) => taskLogs.note(sessionId, taskId, { type: "handoff", handoff }),
@@ -43186,7 +43372,7 @@ var RunnerRegistry = class {
43186
43372
 
43187
43373
  // src/plugins/removedPluginNotice.ts
43188
43374
  import * as fs23 from "fs";
43189
- import * as path45 from "path";
43375
+ import * as path47 from "path";
43190
43376
  function isManifestFile(file) {
43191
43377
  try {
43192
43378
  return fs23.statSync(file).isFile();
@@ -43195,10 +43381,10 @@ function isManifestFile(file) {
43195
43381
  }
43196
43382
  }
43197
43383
  function removedPluginNotice(dataDir = globalDataDir()) {
43198
- const directory = path45.join(dataDir, "plugins");
43384
+ const directory = path47.join(dataDir, "plugins");
43199
43385
  try {
43200
- const hasManifest = isManifestFile(path45.join(directory, "manifest.json")) || fs23.readdirSync(directory, { withFileTypes: true }).some(
43201
- (entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(path45.join(directory, entry.name, "manifest.json"))
43386
+ const hasManifest = isManifestFile(path47.join(directory, "manifest.json")) || fs23.readdirSync(directory, { withFileTypes: true }).some(
43387
+ (entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(path47.join(directory, entry.name, "manifest.json"))
43202
43388
  );
43203
43389
  if (hasManifest) return "Plugin runners were removed; manifests in ~/.ordewell/plugins/ are ignored. See the CHANGELOG.";
43204
43390
  } catch {
@@ -43209,15 +43395,15 @@ function removedPluginNotice(dataDir = globalDataDir()) {
43209
43395
 
43210
43396
  // src/utils/stateStore.ts
43211
43397
  import * as fs24 from "fs";
43212
- import * as path46 from "path";
43398
+ import * as path48 from "path";
43213
43399
  var STATE_FILE2 = "state.json";
43214
43400
  function getStatePath(baseDir) {
43215
- return path46.join(getStateDir(baseDir), STATE_FILE2);
43401
+ return path48.join(getStateDir(baseDir), STATE_FILE2);
43216
43402
  }
43217
43403
  var SESSION_ID_KEY = "sessionId";
43218
43404
  function saveState(plan, baseDir, sessionId) {
43219
43405
  const statePath = getStatePath(baseDir ?? process.cwd());
43220
- ensureDir(path46.dirname(statePath));
43406
+ ensureDir(path48.dirname(statePath));
43221
43407
  plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
43222
43408
  const file = sessionId ? { ...plan, [SESSION_ID_KEY]: sessionId } : plan;
43223
43409
  dropRetiredPlanFields(file);
@@ -43765,6 +43951,7 @@ export {
43765
43951
  repoBaseLine,
43766
43952
  repoResultLines,
43767
43953
  reposWithWork,
43954
+ repositoryRunnerConfigPaths,
43768
43955
  researchShellWarning,
43769
43956
  researchToolsPath,
43770
43957
  resetInstalledPlanner,