@brainervirus/workit-claude-code 8.38.1 → 8.38.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/workit.js CHANGED
@@ -9317,7 +9317,7 @@ var package_default;
9317
9317
  var init_package = __esm(() => {
9318
9318
  package_default = {
9319
9319
  name: "@brainervirus/workit-cli",
9320
- version: "8.38.1",
9320
+ version: "8.38.3",
9321
9321
  private: false,
9322
9322
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
9323
9323
  keywords: [
@@ -9355,13 +9355,13 @@ var init_package = __esm(() => {
9355
9355
  },
9356
9356
  dependencies: {
9357
9357
  "@babel/parser": "7.29.9",
9358
- "@brainervirus/workit-cursor": "^8.38.1",
9358
+ "@brainervirus/workit-cursor": "^8.38.3",
9359
9359
  "@inkjs/ui": "2.0.0",
9360
9360
  ink: "7.1.1",
9361
9361
  react: "19.2.8"
9362
9362
  },
9363
9363
  devDependencies: {
9364
- "@brainervirus/workit-core": "^8.38.1"
9364
+ "@brainervirus/workit-core": "^8.38.3"
9365
9365
  },
9366
9366
  engines: {
9367
9367
  node: ">=24"
@@ -69625,8 +69625,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69625
69625
  return `${minutes} min ago`;
69626
69626
  const hours = Math.round(minutes / 60);
69627
69627
  return hours < 48 ? `${hours} h ago` : `${Math.round(hours / 24)} d ago`;
69628
- }, checkHookRuns = (res) => {
69629
- const runs = readHookRuns(res.hookStateDir).filter((run) => run.event === "PreToolUse" || run.event === "SessionStart");
69628
+ }, HOOK_RUN_BASELINE, checkHookRuns = (res) => {
69629
+ const runs = readHookRuns(res.hookStateDir);
69630
69630
  if (runs.length === 0)
69631
69631
  return {
69632
69632
  id: "hook_runs",
@@ -69636,7 +69636,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69636
69636
  const now = Date.now();
69637
69637
  const hosts = [...new Set(runs.map((run) => run.host))].toSorted();
69638
69638
  const parts = hosts.map((host) => {
69639
- const seen = ["SessionStart", "PreToolUse"].map((event) => {
69639
+ const others = runs.filter((run) => run.host === host && !HOOK_RUN_BASELINE.includes(run.event)).map((run) => run.event).toSorted();
69640
+ const seen = [...HOOK_RUN_BASELINE, ...new Set(others)].map((event) => {
69640
69641
  const run = runs.find((r) => r.host === host && r.event === event);
69641
69642
  return `${event} ${run ? `${ago(now - Date.parse(run.at))} (${run.count})` : "never"}`;
69642
69643
  });
@@ -69703,6 +69704,7 @@ var init_doctor = __esm(() => {
69703
69704
  DAY_MS = 24 * 60 * 60 * 1000;
69704
69705
  SEMVER = /\d+\.\d+\.\d+/;
69705
69706
  workitVersions = new Map;
69707
+ HOOK_RUN_BASELINE = ["SessionStart", "PreToolUse"];
69706
69708
  RUN_CHECKS = [
69707
69709
  checkRuntime,
69708
69710
  checkVersions,
@@ -74836,171 +74838,156 @@ var init_setup2 = __esm(() => {
74836
74838
  ALL_HOSTS = ["claude-code", "codex", "cursor", "opencode", "pi"];
74837
74839
  });
74838
74840
 
74839
- // packages/workit-cli/src/upgrade.ts
74840
- import { spawnSync as spawnSync17 } from "node:child_process";
74841
- import {
74842
- existsSync as existsSync20,
74843
- mkdtempSync as mkdtempSync3,
74844
- rmSync as rmSync8,
74845
- readFileSync as readFileSync21,
74846
- readdirSync as readdirSync12,
74847
- mkdirSync as mkdirSync11,
74848
- copyFileSync as copyFileSync6,
74849
- chmodSync as chmodSync2,
74850
- cpSync as cpSync5,
74851
- lstatSync as lstatSync7
74852
- } from "node:fs";
74841
+ // packages/workit-cli/src/upgrade/shared.ts
74842
+ import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
74853
74843
  import path44 from "node:path";
74854
74844
  import os16 from "node:os";
74855
74845
  import { createHash as createHash15 } from "node:crypto";
74856
- function previewUpgrade(hosts = HOSTS2, deps = {}) {
74857
- const paths = upgradePaths(deps);
74858
- const { home, configDir } = paths;
74859
- const run = runner(deps);
74860
- const state = readSetupState(configDir);
74861
- const plan = {
74862
- ok: true,
74863
- blocked: [],
74864
- failed: [],
74865
- skipped: [],
74866
- entries: [],
74867
- configDigests: {},
74868
- migrations: []
74846
+ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-opencode", OPENCODE_UPDATE, CODEX_MARKETPLACE = "workflow-toolkit", CLI_FIX = "npm install --global @brainervirus/workit-cli@latest", fixFor = (host, selector = "") => host === "pi" ? `pi update --extension ${selector || "npm:@brainervirus/workit-pi"}` : host === "codex" ? `codex plugin marketplace upgrade ${CODEX_MARKETPLACE} && codex plugin add workit@${CODEX_MARKETPLACE}` : host === "claude-code" ? "claude plugin marketplace update workit && claude plugin update workit@workit" : host === "opencode" ? OPENCODE_UPDATE : host === "cursor" ? "workit upgrade --hosts=cursor" : CLI_FIX, upgradePaths = (deps) => {
74847
+ const home = deps.home ?? deps.env?.HOME ?? os16.homedir();
74848
+ const env = {
74849
+ ...process.env,
74850
+ ...deps.home || deps.env?.HOME ? {
74851
+ HOME: home,
74852
+ CODEX_HOME: path44.join(home, ".codex"),
74853
+ CLAUDE_CONFIG_DIR: path44.join(home, ".claude"),
74854
+ PI_CODING_AGENT_DIR: path44.join(home, ".pi/agent"),
74855
+ XDG_CONFIG_HOME: path44.join(home, ".config"),
74856
+ WORKFLOW_TOOLKIT_CONFIG: undefined,
74857
+ WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
74858
+ } : {},
74859
+ ...deps.env,
74860
+ HOME: home
74869
74861
  };
74870
- for (const file of [state.config, state.workspaces, state.youtrack, state.vcs]) {
74871
- if (file.status === "malformed")
74872
- plan.blocked.push(file.error ?? file.file);
74873
- plan.configDigests[file.file] = digest2(file.file);
74862
+ const xdg = env.XDG_CONFIG_HOME || path44.join(home, ".config");
74863
+ const codexHome = env.CODEX_HOME ?? path44.join(home, ".codex");
74864
+ return {
74865
+ home,
74866
+ env,
74867
+ configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path44.join(xdg, "workit"),
74868
+ opencodeConfig: path44.join(xdg, "opencode/opencode.json"),
74869
+ piSettings: path44.join(env.PI_CODING_AGENT_DIR ?? path44.join(home, ".pi/agent"), "settings.json"),
74870
+ codexHome,
74871
+ codexConfig: path44.join(codexHome, "config.toml"),
74872
+ cursorRoot: path44.join(home, ".cursor/plugins/local/workit")
74873
+ };
74874
+ }, object2 = (file) => {
74875
+ if (!existsSync20(file))
74876
+ return null;
74877
+ let data;
74878
+ try {
74879
+ data = JSON.parse(readFileSync21(file, "utf8"));
74880
+ } catch {
74881
+ throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
74874
74882
  }
74875
- for (const file of [
74876
- paths.opencodeConfig,
74877
- paths.opencodeConfig + "c",
74878
- path44.join(home, ".cursor/settings.json"),
74879
- path44.join(home, ".cursor/mcp.json"),
74880
- path44.join(paths.cursorRoot, "package.json"),
74881
- paths.codexConfig,
74882
- paths.piSettings
74883
- ])
74884
- plan.configDigests[file] = digest2(file);
74883
+ if (!data || typeof data !== "object" || Array.isArray(data))
74884
+ throw new Error(`${file} is not a configuration object`);
74885
+ return data;
74886
+ }, objectOrNull = (file) => {
74885
74887
  try {
74886
- const config = object2(state.config.file);
74887
- if (config && Object.hasOwn(config, "trustedPaths"))
74888
- plan.migrations.push({
74889
- id: "2.0-remove-ignored-trusted-paths",
74890
- file: state.config.file,
74891
- description: "Remove ignored Workit trustedPaths; native host permissions remain unchanged"
74892
- });
74893
- } catch (error) {
74894
- plan.blocked.push(errorText(error));
74888
+ return object2(file);
74889
+ } catch {
74890
+ return null;
74895
74891
  }
74896
- const installedSources = sources(paths, run, hosts, plan.failed);
74897
- let opencodeHelp;
74898
- if (installedSources.some((source) => source.host === "opencode")) {
74899
- const probe = opencodeProbe(deps, ["--version"]);
74900
- const installed = probe.status === 0 ? probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] : undefined;
74901
- if (installed && !versionAtLeast(installed, SUPPORT_MATRIX.opencode.minimum))
74902
- (plan.warnings ??= []).push(`opencode ${installed}: ${OPENCODE_V1_FIX}`);
74892
+ }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync21(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
74893
+ const { home, env } = upgradePaths(deps);
74894
+ let step;
74895
+ try {
74896
+ step = hostCommand(command, args, { home, env });
74897
+ } catch {
74898
+ return { status: 1, stdout: "" };
74903
74899
  }
74904
- for (const host of ["opencode", "pi"]) {
74905
- if (new Set(installedSources.filter((source) => source.host === host).map((source) => source.selector)).size > 1)
74906
- plan.failed.push({
74907
- host,
74908
- status: "failed",
74909
- reason: "conflicting Workit registrations; reconcile them in the native host before upgrading"
74910
- });
74900
+ const result = runHostCommand(step, { home, env });
74901
+ return { status: result.exitCode, stdout: result.stdout };
74902
+ }), errorText = (error) => error instanceof Error ? error.message : "installed source could not be read", piInstalledVersion = (listing, selector) => {
74903
+ const lines = listing.split(/\r?\n/).map((line) => line.trim());
74904
+ const index = lines.indexOf(selector);
74905
+ if (index < 0 || !lines[index + 1] || !path44.isAbsolute(lines[index + 1]))
74906
+ return;
74907
+ const found = objectOrNull(path44.join(lines[index + 1], "package.json"));
74908
+ return found?.name === "@brainervirus/workit-pi" ? found.version : undefined;
74909
+ }, versionAtLeast = (installed, latest) => {
74910
+ if (!/^\d+\.\d+\.\d+$/.test(installed))
74911
+ return false;
74912
+ const current = installed.split(".").map(Number);
74913
+ const target = latest.split(".").map(Number);
74914
+ for (let index = 0;index < 3; index++) {
74915
+ if (current[index] !== target[index])
74916
+ return current[index] > target[index];
74911
74917
  }
74912
- const conflicted = new Set(plan.failed.map((entry) => entry.host));
74913
- for (const source of installedSources.filter((candidate, index, list) => !conflicted.has(candidate.host) && list.findIndex((entry) => entry.host === candidate.host && entry.selector === candidate.selector) === index)) {
74914
- if (source.kind === "pinned" || source.kind === "local") {
74915
- plan.skipped.push({
74916
- host: source.host,
74917
- reason: `${source.kind} source preserved; change its source explicitly to upgrade`
74918
- });
74919
- continue;
74920
- }
74921
- if (source.host === "claude-code") {
74922
- const [market] = source.selector.split("@").slice(1);
74923
- plan.entries.push({
74924
- host: "claude-code",
74925
- selector: source.selector,
74926
- latest: "",
74927
- installedVersion: source.installedVersion,
74928
- commands: [
74929
- {
74930
- command: "claude",
74931
- args: ["plugin", "marketplace", "update", market ?? "workit"]
74932
- },
74933
- { command: "claude", args: ["plugin", "update", source.selector] }
74934
- ],
74935
- fix: `claude plugin marketplace update ${market ?? "workit"} && claude plugin update ${source.selector}`
74936
- });
74937
- continue;
74938
- }
74939
- if (source.host === "opencode") {
74940
- opencodeHelp ??= opencodeProbe(deps, ["plugin", "--help"]).stdout;
74941
- if (!/^\s*(?:opencode\s+plugin\s+)?update\b/m.test(opencodeHelp)) {
74942
- plan.skipped.push({
74943
- host: "opencode",
74944
- reason: "this OpenCode has no `opencode plugin update`; upgrade OpenCode, then run the fix. Workit never updates unrelated plugins",
74945
- fix: OPENCODE_UPDATE
74946
- });
74947
- continue;
74948
- }
74949
- plan.entries.push({
74950
- host: "opencode",
74951
- selector: source.selector,
74952
- latest: "",
74953
- commands: [{ command: "opencode", args: ["plugin", "update", OPENCODE_PACKAGE] }],
74954
- fix: OPENCODE_UPDATE
74955
- });
74956
- continue;
74957
- }
74958
- const latest = registryVersion(run, `@brainervirus/workit-${source.host}`);
74959
- if (!latest) {
74960
- plan.failed.push({
74961
- host: source.host,
74962
- status: "failed",
74963
- reason: "registry version unavailable; existing installation preserved",
74964
- fix: fixFor(source.host, source.selector)
74965
- });
74966
- continue;
74967
- }
74968
- if (source.installedVersion && versionAtLeast(source.installedVersion, latest)) {
74969
- plan.skipped.push({
74970
- host: source.host,
74971
- reason: `already current (${source.installedVersion})`
74972
- });
74973
- continue;
74974
- }
74975
- const commands = source.host === "pi" ? [{ command: "pi", args: ["update", "--extension", source.selector] }] : source.host === "codex" ? [
74976
- {
74977
- command: "codex",
74978
- args: ["plugin", "marketplace", "upgrade", CODEX_MARKETPLACE]
74979
- },
74980
- {
74981
- command: "codex",
74982
- args: ["plugin", "add", `workit@${CODEX_MARKETPLACE}`, "--json"]
74983
- }
74984
- ] : planHostInstall("cursor", {
74985
- home,
74986
- env: deps.env,
74987
- cwd: home,
74988
- packageVersion: latest,
74989
- targetDir: paths.cursorRoot
74990
- });
74991
- plan.entries.push({
74992
- host: source.host,
74993
- selector: source.selector,
74994
- latest,
74995
- commands,
74996
- installedVersion: source.installedVersion,
74997
- ...source.kind === "repair" ? { repair: true } : {},
74998
- fix: fixFor(source.host, source.selector)
74918
+ return true;
74919
+ }, registryVersion = (run, name) => {
74920
+ const latest = run("npm", ["view", name, "version"]);
74921
+ const version = latest.stdout.trim();
74922
+ return latest.status === 0 && /^\d+\.\d+\.\d+$/.test(version) ? version : undefined;
74923
+ };
74924
+ var init_shared = __esm(() => {
74925
+ init_host_install();
74926
+ LAUNCH_HOSTS = ["opencode", "cursor", "codex", "pi"];
74927
+ HOSTS2 = [...LAUNCH_HOSTS, "claude-code"];
74928
+ LIVE_HOSTS = new Set(["opencode", "claude-code"]);
74929
+ OPENCODE_UPDATE = `opencode plugin update ${OPENCODE_PACKAGE}`;
74930
+ });
74931
+
74932
+ // packages/workit-cli/src/upgrade/processes.ts
74933
+ import path45 from "node:path";
74934
+ var PROCESS_HOSTS, SCRIPT_RUNTIMES, classifyProcess = (proc) => {
74935
+ if (proc.state.startsWith("Z") || proc.args.includes("<defunct>"))
74936
+ return null;
74937
+ const argv0 = proc.args.trim().split(/\s+/)[0] ?? "";
74938
+ const baseName = (value) => path45.basename(value.replaceAll("\\", "/")).replace(/^\[|\]$/g, "").replace(/\.exe$/i, "").toLowerCase();
74939
+ let host = PROCESS_HOSTS[baseName(argv0)];
74940
+ if (!host && SCRIPT_RUNTIMES.has(baseName(argv0))) {
74941
+ const script = proc.args.trim().split(/\s+/).slice(1).find((arg) => !arg.startsWith("-"));
74942
+ if (script)
74943
+ host = PROCESS_HOSTS[baseName(script).replace(/\.(?:[cm]?js|ts)$/, "")];
74944
+ }
74945
+ if (!host)
74946
+ return null;
74947
+ if (host === "codex" && /\bapp-server\b/.test(proc.args))
74948
+ return /\bdaemon\s+pid-update-loop\b/.test(proc.args) ? null : { ...proc, host, kind: "daemon" };
74949
+ if (host === "opencode" && /\bserve\b/.test(proc.args))
74950
+ return { ...proc, host, kind: "daemon" };
74951
+ return { ...proc, host, kind: "session" };
74952
+ }, listProcesses = (deps) => {
74953
+ if (deps.processes)
74954
+ return deps.processes();
74955
+ if (deps.activeHosts)
74956
+ return deps.activeHosts().map((host) => ({ pid: 0, state: "S", args: host }));
74957
+ const run = runner(deps);
74958
+ if (process.platform === "win32") {
74959
+ const list = run("tasklist", ["/fo", "csv", "/nh"]);
74960
+ if (list.status !== 0)
74961
+ return null;
74962
+ return list.stdout.split(/\r?\n/).flatMap((line) => {
74963
+ const [image, pid] = line.split('","').map((cell) => cell.replaceAll('"', ""));
74964
+ return image && /^\d+$/.test(pid ?? "") ? [{ pid: Number(pid), state: "R", args: image }] : [];
74999
74965
  });
75000
74966
  }
75001
- plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
75002
- return plan;
75003
- }
74967
+ const list = run("ps", ["-eo", "pid=,stat=,args="]);
74968
+ if (list.status !== 0)
74969
+ return null;
74970
+ return list.stdout.split(/\r?\n/).flatMap((line) => {
74971
+ const match = /^\s*(\d+)\s+(\S+)\s+(.+)$/.exec(line);
74972
+ return match ? [{ pid: Number(match[1]), state: match[2], args: match[3] }] : [];
74973
+ });
74974
+ }, describe2 = (proc) => `pid ${proc.pid}: ${proc.args.length > 120 ? `${proc.args.slice(0, 117)}...` : proc.args}`;
74975
+ var init_processes = __esm(() => {
74976
+ init_shared();
74977
+ PROCESS_HOSTS = {
74978
+ claude: "claude-code",
74979
+ codex: "codex",
74980
+ cursor: "cursor",
74981
+ opencode: "opencode",
74982
+ pi: "pi"
74983
+ };
74984
+ SCRIPT_RUNTIMES = new Set(["node", "nodejs", "bun"]);
74985
+ });
74986
+
74987
+ // packages/workit-cli/src/upgrade/apply.ts
74988
+ import { existsSync as existsSync21, readdirSync as readdirSync12, mkdirSync as mkdirSync11, copyFileSync as copyFileSync6, chmodSync as chmodSync2, cpSync as cpSync5 } from "node:fs";
74989
+ import path46 from "node:path";
74990
+ import { createHash as createHash16 } from "node:crypto";
75004
74991
  function applyUpgrade(plan, deps = {}) {
75005
74992
  const hosts = [...plan.failed];
75006
74993
  const done = (backup) => {
@@ -75034,11 +75021,11 @@ function applyUpgrade(plan, deps = {}) {
75034
75021
  };
75035
75022
  const paths = upgradePaths(deps);
75036
75023
  const { home, configDir } = paths;
75037
- const backup = path44.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
75024
+ const backup = path46.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
75038
75025
  mkdirSync11(backup, { recursive: true, mode: 448 });
75039
75026
  for (const file of Object.keys(plan.configDigests))
75040
- if (existsSync20(file)) {
75041
- const target = path44.join(backup, createHash15("sha256").update(file).digest("hex").slice(0, 12) + "-" + path44.basename(file));
75027
+ if (existsSync21(file)) {
75028
+ const target = path46.join(backup, createHash16("sha256").update(file).digest("hex").slice(0, 12) + "-" + path46.basename(file));
75042
75029
  copyFileSync6(file, target);
75043
75030
  chmodSync2(target, 384);
75044
75031
  }
@@ -75057,9 +75044,11 @@ function applyUpgrade(plan, deps = {}) {
75057
75044
  }
75058
75045
  const run = runner(deps);
75059
75046
  let cursorBackupError = null;
75060
- if (plan.entries.some((entry) => entry.host === "cursor") && existsSync20(paths.cursorRoot))
75047
+ if (plan.entries.some((entry) => entry.host === "cursor") && existsSync21(paths.cursorRoot))
75061
75048
  try {
75062
- cpSync5(paths.cursorRoot, path44.join(backup, "cursor-package"), { recursive: true });
75049
+ cpSync5(paths.cursorRoot, path46.join(backup, "cursor-package"), {
75050
+ recursive: true
75051
+ });
75063
75052
  } catch (error) {
75064
75053
  cursorBackupError = `could not back up the Cursor plugin: ${errorText(error)}`;
75065
75054
  }
@@ -75067,7 +75056,12 @@ function applyUpgrade(plan, deps = {}) {
75067
75056
  const running = (listed ?? []).flatMap((entry) => classifyProcess(entry) ?? []);
75068
75057
  for (const entry of plan.entries) {
75069
75058
  if (entry.host === "cursor" && cursorBackupError) {
75070
- hosts.push({ host: "cursor", status: "failed", reason: cursorBackupError, fix: entry.fix });
75059
+ hosts.push({
75060
+ host: "cursor",
75061
+ status: "failed",
75062
+ reason: cursorBackupError,
75063
+ fix: entry.fix
75064
+ });
75071
75065
  continue;
75072
75066
  }
75073
75067
  if (!LIVE_HOSTS.has(entry.host)) {
@@ -75176,238 +75170,356 @@ function applyUpgrade(plan, deps = {}) {
75176
75170
  try {
75177
75171
  hosts.push(applyCli(plan.cli, run, backup));
75178
75172
  } catch (error) {
75179
- hosts.push({ host: "cli", status: "failed", reason: errorText(error), fix: CLI_FIX });
75173
+ hosts.push({
75174
+ host: "cli",
75175
+ status: "failed",
75176
+ reason: errorText(error),
75177
+ fix: CLI_FIX
75178
+ });
75180
75179
  }
75181
75180
  return done(backup);
75182
75181
  }
75183
- async function runUpgradeCommand(argv, deps = {}) {
75184
- const out = deps.out ?? process.stdout;
75185
- const flags = new Set(argv);
75186
- const unknown = argv.filter((arg) => !["--apply", "--confirm", "--json", "--cli", "--preview", "--yes"].includes(arg) && !arg.startsWith("--hosts="));
75187
- const hostFlag = argv.find((arg) => arg.startsWith("--hosts="))?.slice(8);
75188
- const raw = hostFlag === "none" ? [] : hostFlag?.split(",") ?? HOSTS2;
75189
- if (unknown.length || raw.some((host) => !HOSTS2.includes(host)) || flags.has("--preview") && (flags.has("--apply") || flags.has("--yes"))) {
75190
- out.write(USAGE3);
75191
- return 2;
75182
+ var codexPluginRoot = (codexHome) => {
75183
+ const dir = path46.join(codexHome, "plugins", "cache", CODEX_MARKETPLACE, "workit");
75184
+ let versions = [];
75185
+ try {
75186
+ versions = readdirSync12(dir).filter((version) => /^\d+\.\d+\.\d+$/.test(version));
75187
+ } catch {
75188
+ return null;
75192
75189
  }
75193
- if (flags.has("--apply") && !flags.has("--confirm")) {
75194
- out.write(`Apply requires --confirm after reviewing the upgrade preview.
75195
- `);
75196
- return 2;
75190
+ const newest = versions.reduce((best, version) => best && versionAtLeast(best, version) ? best : version, null);
75191
+ return newest ? path46.join(dir, newest) : null;
75192
+ }, opencodeVersion = (run) => {
75193
+ const listing = run("opencode", ["plugin", "list"]);
75194
+ if (listing.status !== 0)
75195
+ return;
75196
+ for (const line of listing.stdout.split(/\r?\n/)) {
75197
+ const fields = line.trim().split(/\s+/);
75198
+ if (fields.length < 3)
75199
+ continue;
75200
+ const source = fields[fields.length - 1].replace(/^file:\/\//, "");
75201
+ const version = fields[fields.length - 2];
75202
+ if (/^(?:npm:)?@brainervirus\/workit-opencode(?:@[^/\s]+)?$/.test(source) && /^\d+\.\d+\.\d+$/.test(version))
75203
+ return version;
75197
75204
  }
75198
- const legacy = flags.has("--apply");
75199
- const json = flags.has("--json") || legacy;
75200
- const run = runner(deps);
75201
- const plan = previewUpgrade(raw, deps);
75202
- if (hostFlag === undefined || flags.has("--cli"))
75203
- planCli(plan, run);
75204
- plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
75205
- const { home, env } = upgradePaths(deps);
75206
- const permissions = planHostPermissions({
75207
- hosts: raw,
75208
- refreshOnly: true,
75209
- shell: false,
75210
- home,
75211
- env
75205
+ return;
75206
+ }, applyEntry = (entry, context) => {
75207
+ const { run, paths, configDir } = context;
75208
+ const { home, env } = paths;
75209
+ const fail = (reason) => ({
75210
+ host: entry.host,
75211
+ status: "failed",
75212
+ reason,
75213
+ fix: entry.fix
75212
75214
  });
75213
- const permissionPreview = {
75214
- changes: permissions.changes.map((c) => ({
75215
- host: c.host,
75216
- file: c.file,
75217
- diff: permissionDiff(c)
75218
- })),
75219
- ...permissions.refused ? { refused: permissions.refused } : {}
75220
- };
75221
- const withPermissions = (value) => permissions.changes.length ? { ...value, permissions: permissionPreview } : value;
75222
- const applyCommand = `workit upgrade ${[...hostFlag === undefined ? [] : [`--hosts=${hostFlag}`], ...flags.has("--cli") ? ["--cli"] : [], "--yes"].join(" ")}`;
75223
- const nothingChanged = `preview only; nothing changed. Apply with: ${applyCommand}
75224
- `;
75225
- if (json && !legacy && !flags.has("--yes")) {
75226
- out.write(JSON.stringify(withPermissions(plan), null, 2) + `
75227
- `);
75228
- return plan.blocked.length ? 1 : 0;
75215
+ if (entry.host === "cursor") {
75216
+ const preview = {
75217
+ ok: true,
75218
+ blocked: [],
75219
+ preserved: [],
75220
+ overrides: [],
75221
+ platforms: ["cursor"],
75222
+ state: readSetupState(configDir),
75223
+ mutations: [
75224
+ {
75225
+ type: "install-host",
75226
+ platform: "cursor",
75227
+ path: path46.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
75228
+ commands: entry.commands.map((command) => ({
75229
+ ...command,
75230
+ purpose: command.purpose ?? "Upgrade Cursor"
75231
+ }))
75232
+ },
75233
+ { type: "install-adapter", platform: "cursor", path: paths.cursorRoot }
75234
+ ]
75235
+ };
75236
+ const result = applySetupPreview(preview, {
75237
+ home,
75238
+ env,
75239
+ cwd: home,
75240
+ preferInstalledAdapter: true,
75241
+ runHostCommand: (command) => {
75242
+ const outcome = run(command.command, command.args);
75243
+ return {
75244
+ exitCode: outcome.status ?? 1,
75245
+ stdout: outcome.stdout,
75246
+ stderr: ""
75247
+ };
75248
+ }
75249
+ });
75250
+ const installed = objectOrNull(path46.join(paths.cursorRoot, "package.json"));
75251
+ if (!result.ok || installed?.version !== entry.latest)
75252
+ return fail("Cursor install did not verify the requested package version; inspect the installer before retrying");
75253
+ return {
75254
+ host: "cursor",
75255
+ status: "updated",
75256
+ reason: entry.repair ? `reinstalled missing plugin files (${entry.latest})` : `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75257
+ };
75229
75258
  }
75230
- if (!json)
75231
- out.write(summary(plan, permissions.changes));
75232
- if (flags.has("--preview")) {
75233
- out.write(nothingChanged);
75234
- return plan.blocked.length ? 1 : 0;
75259
+ const before = entry.host === "opencode" ? opencodeVersion(run) : undefined;
75260
+ for (const command of entry.commands)
75261
+ if (run(command.command, command.args).status !== 0)
75262
+ return fail(`\`${command.command} ${command.args.join(" ")}\` failed`);
75263
+ if (entry.host === "opencode") {
75264
+ const after = opencodeVersion(run);
75265
+ if (before && after)
75266
+ return after === before ? {
75267
+ host: "opencode",
75268
+ status: "current",
75269
+ reason: `already current (${after})`
75270
+ } : {
75271
+ host: "opencode",
75272
+ status: "updated",
75273
+ reason: `${before} -> ${after}; OpenCode V2 loads it live`
75274
+ };
75275
+ return {
75276
+ host: "opencode",
75277
+ status: "updated",
75278
+ reason: "scoped update ran; OpenCode V2 loads it live"
75279
+ };
75235
75280
  }
75236
- if (plan.blocked.length) {
75237
- out.write(`Upgrade blocked: ${plan.blocked.join("; ")}
75238
- Nothing changed.
75239
- `);
75240
- return 1;
75281
+ if (entry.host === "claude-code") {
75282
+ const after = claudeWorkitInstalls(home, env, null).find((install) => install.id === entry.selector)?.version;
75283
+ return after && after === entry.installedVersion ? {
75284
+ host: "claude-code",
75285
+ status: "current",
75286
+ reason: `already current (${after})`
75287
+ } : {
75288
+ host: "claude-code",
75289
+ status: "updated",
75290
+ reason: `${entry.installedVersion ?? "?"} -> ${after ?? "?"}; new sessions load it`
75291
+ };
75241
75292
  }
75242
- const work = plan.entries.length + plan.migrations.length + (plan.cli ? 1 : 0) + permissions.changes.length;
75243
- if (work > 0 && !flags.has("--yes") && !legacy) {
75244
- const confirmed = await (deps.confirm ?? ((q) => askConfirm(q, out)))("Apply this upgrade? [y/N] ");
75245
- if (!confirmed) {
75246
- out.write(nothingChanged);
75247
- return 0;
75248
- }
75293
+ const verify = entry.host === "codex" ? run("codex", ["plugin", "list", "--json"]) : run("pi", ["list"]);
75294
+ if (verify.status !== 0)
75295
+ return fail("update verification failed");
75296
+ if (entry.host === "codex") {
75297
+ let installed = [];
75298
+ try {
75299
+ installed = JSON.parse(verify.stdout).installed ?? [];
75300
+ } catch {}
75301
+ if (!installed.some((plugin) => plugin.name === "workit" && plugin.marketplaceName === CODEX_MARKETPLACE && plugin.version === entry.latest))
75302
+ return fail("Codex did not report the requested Workit version");
75249
75303
  }
75250
- if (json)
75251
- for (const warning of plan.warnings ?? [])
75252
- out.write(`warning: ${warning}
75253
- `);
75254
- const result = applyUpgrade(plan, deps);
75255
- let permissionsOk = true;
75256
- let written;
75257
- if (permissions.changes.length && !permissions.refused && !result.error?.startsWith("configuration changed")) {
75258
- written = applyHostPermissions(permissions, { home, env });
75259
- permissionsOk = written.every((entry) => entry.status === "written");
75260
- for (const entry of written)
75261
- result.hosts.push({
75262
- host: entry.host,
75263
- status: entry.status === "written" ? "updated" : "failed",
75264
- reason: `permission block ${entry.status === "written" ? "refreshed" : entry.status} in ${entry.file}`,
75265
- ...entry.status === "written" ? {} : { fix: `workit setup --hosts=${entry.host}` }
75304
+ if (entry.host === "pi" && piInstalledVersion(verify.stdout, entry.selector) !== entry.latest)
75305
+ return fail("Pi did not report the requested Workit package version");
75306
+ return {
75307
+ host: entry.host,
75308
+ status: "updated",
75309
+ reason: entry.repair ? `reinstalled missing plugin files (${entry.latest})` : `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75310
+ };
75311
+ }, refreshCodexAgents = (run, codexHome) => {
75312
+ const root = codexPluginRoot(codexHome);
75313
+ const launcher = root && path46.join(root, "dist", "launch-mcp.js");
75314
+ if (!launcher || !existsSync21(launcher))
75315
+ return { ok: true, note: "" };
75316
+ const fix = `node ${JSON.stringify(launcher)} --install-agents`;
75317
+ return run("node", [launcher, "--install-agents"]).status === 0 ? { ok: true, note: "agents refreshed" } : { ok: false, note: "agent refresh failed", fix };
75318
+ }, applyCli = (cli, run, backup) => {
75319
+ const fail = (reason) => ({
75320
+ host: "cli",
75321
+ status: "failed",
75322
+ reason,
75323
+ fix: CLI_FIX
75324
+ });
75325
+ const currentRoot = run("npm", ["root", "--global"]);
75326
+ if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== cli.root)
75327
+ return fail("Global npm location changed; preview again");
75328
+ const cliBackup = path46.join(backup, "cli-package");
75329
+ cpSync5(path46.join(cli.root, "@brainervirus/workit-cli"), cliBackup, {
75330
+ recursive: true
75331
+ });
75332
+ if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${cli.latest}`]).status !== 0)
75333
+ return fail(`CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path46.join(cliBackup, "dist/index.js")}`);
75334
+ const installed = objectOrNull(path46.join(cli.root, "@brainervirus/workit-cli/package.json"));
75335
+ if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== cli.latest)
75336
+ return fail(`CLI update did not verify the requested version; preserved package at ${cliBackup}`);
75337
+ return {
75338
+ host: "cli",
75339
+ status: "updated",
75340
+ reason: `${cli.installed} -> ${cli.latest}`
75341
+ };
75342
+ };
75343
+ var init_apply = __esm(() => {
75344
+ init_host_install();
75345
+ init_setup_state();
75346
+ init_setup();
75347
+ init_safe_write();
75348
+ init_processes();
75349
+ init_shared();
75350
+ });
75351
+
75352
+ // packages/workit-cli/src/upgrade/plan.ts
75353
+ import { existsSync as existsSync22, mkdtempSync as mkdtempSync3, rmSync as rmSync8, lstatSync as lstatSync7, readFileSync as readFileSync22 } from "node:fs";
75354
+ import path47 from "node:path";
75355
+ import os17 from "node:os";
75356
+ function previewUpgrade(hosts = HOSTS2, deps = {}) {
75357
+ const paths = upgradePaths(deps);
75358
+ const { home, configDir } = paths;
75359
+ const run = runner(deps);
75360
+ const state = readSetupState(configDir);
75361
+ const plan = {
75362
+ ok: true,
75363
+ blocked: [],
75364
+ failed: [],
75365
+ skipped: [],
75366
+ entries: [],
75367
+ configDigests: {},
75368
+ migrations: []
75369
+ };
75370
+ for (const file of [state.config, state.workspaces, state.youtrack, state.vcs]) {
75371
+ if (file.status === "malformed")
75372
+ plan.blocked.push(file.error ?? file.file);
75373
+ plan.configDigests[file.file] = digest2(file.file);
75374
+ }
75375
+ for (const file of [
75376
+ paths.opencodeConfig,
75377
+ paths.opencodeConfig + "c",
75378
+ path47.join(home, ".cursor/settings.json"),
75379
+ path47.join(home, ".cursor/mcp.json"),
75380
+ path47.join(paths.cursorRoot, "package.json"),
75381
+ paths.codexConfig,
75382
+ paths.piSettings
75383
+ ])
75384
+ plan.configDigests[file] = digest2(file);
75385
+ try {
75386
+ const config = object2(state.config.file);
75387
+ if (config && Object.hasOwn(config, "trustedPaths"))
75388
+ plan.migrations.push({
75389
+ id: "2.0-remove-ignored-trusted-paths",
75390
+ file: state.config.file,
75391
+ description: "Remove ignored Workit trustedPaths; native host permissions remain unchanged"
75266
75392
  });
75393
+ } catch (error) {
75394
+ plan.blocked.push(errorText(error));
75267
75395
  }
75268
- const failed = !result.ok || !permissionsOk;
75269
- const cliVersion = result.hosts.some((h) => h.host === "cli" && h.status === "updated") ? plan.cli.latest : package_default.version;
75270
- const report = (deps.doctor ?? ((version) => runDoctor({ home, env, cliVersion: version, only: "upgrade" })))(cliVersion);
75271
- const left = report.checks.filter((check) => check.status === "warn" || check.status === "fail");
75272
- if (json) {
75273
- out.write(JSON.stringify({
75274
- ...result,
75275
- ok: !failed,
75276
- ...written ? { permissions: { changes: permissionPreview.changes, written } } : permissions.refused ? { permissions: permissionPreview } : {},
75277
- doctor: { clean: left.length === 0, left }
75278
- }, null, 2) + `
75279
- `);
75280
- return failed ? 1 : 0;
75396
+ const installedSources = sources(paths, run, hosts, plan.failed);
75397
+ let opencodeHelp;
75398
+ if (installedSources.some((source) => source.host === "opencode")) {
75399
+ const probe = opencodeProbe(deps, ["--version"]);
75400
+ const installed = probe.status === 0 ? probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] : undefined;
75401
+ if (installed && !versionAtLeast(installed, SUPPORT_MATRIX.opencode.minimum))
75402
+ (plan.warnings ??= []).push(`opencode ${installed}: ${OPENCODE_V1_FIX}`);
75281
75403
  }
75282
- if (result.error && result.hosts.every((h) => h.status !== "failed"))
75283
- out.write(`Upgrade stopped: ${result.error}
75284
- `);
75285
- out.write(table(result.hosts));
75286
- if (permissions.refused)
75287
- out.write(`permissions: not refreshed: ${permissions.refused}
75288
- `);
75289
- if (result.backup)
75290
- out.write(`Backup: ${result.backup}
75291
- `);
75292
- out.write(left.length === 0 ? `Verified: workit doctor is clean for versions, assets, agents and permissions.
75293
- ` : `Left after upgrade (workit doctor):
75294
- ${left.map((check) => ` ${check.id}: ${check.detail}${check.fix ? `
75295
- fix: ${check.fix}` : ""}`).join(`
75296
- `)}
75297
- `);
75298
- return failed ? 1 : 0;
75299
- }
75300
- async function runLaunchCommand(argv, deps = {}) {
75301
- const [host, ...args] = argv;
75302
- if (!LAUNCH_HOSTS.includes(host)) {
75303
- (deps.out ?? process.stdout).write(`Usage: workit launch <opencode|cursor|codex|pi> [--auto-upgrade] [-- host arguments]
75304
- `);
75305
- return 2;
75404
+ for (const host of ["opencode", "pi"]) {
75405
+ if (new Set(installedSources.filter((source) => source.host === host).map((source) => source.selector)).size > 1)
75406
+ plan.failed.push({
75407
+ host,
75408
+ status: "failed",
75409
+ reason: "conflicting Workit registrations; reconcile them in the native host before upgrading"
75410
+ });
75306
75411
  }
75307
- const auto = args[0] === "--auto-upgrade";
75308
- const rest = auto ? args.slice(1) : args;
75309
- const hostArgs = rest[0] === "--" ? rest.slice(1) : rest;
75310
- const { home, env } = upgradePaths(deps);
75311
- let launch;
75312
- if (!deps.run) {
75313
- try {
75314
- launch = hostCommand(host, hostArgs, { home, env });
75315
- } catch (error) {
75316
- (deps.out ?? process.stderr).write(`${error instanceof Error ? error.message : "Host executable unavailable"}
75317
- `);
75318
- return 1;
75412
+ const conflicted = new Set(plan.failed.map((entry) => entry.host));
75413
+ for (const source of installedSources.filter((candidate, index, list) => !conflicted.has(candidate.host) && list.findIndex((entry) => entry.host === candidate.host && entry.selector === candidate.selector) === index)) {
75414
+ if (source.kind === "pinned" || source.kind === "local") {
75415
+ plan.skipped.push({
75416
+ host: source.host,
75417
+ reason: `${source.kind} source preserved; change its source explicitly to upgrade`
75418
+ });
75419
+ continue;
75319
75420
  }
75320
- }
75321
- if (auto) {
75322
- const plan = previewUpgrade([host], deps);
75323
- const reasons = [...plan.blocked, ...plan.failed.map((entry) => entry.reason)];
75324
- if (plan.ok) {
75325
- for (const skipped of plan.skipped)
75326
- (deps.out ?? process.stderr).write(`${skipped.host}: ${skipped.reason}${skipped.fix ? ` (run: ${skipped.fix})` : ""}
75327
- `);
75328
- const result = applyUpgrade(plan, deps);
75329
- if (!result.ok) {
75330
- (deps.out ?? process.stdout).write(`${result.error}
75331
- `);
75332
- return 1;
75421
+ if (source.host === "claude-code") {
75422
+ const [market] = source.selector.split("@").slice(1);
75423
+ plan.entries.push({
75424
+ host: "claude-code",
75425
+ selector: source.selector,
75426
+ latest: "",
75427
+ installedVersion: source.installedVersion,
75428
+ commands: [
75429
+ {
75430
+ command: "claude",
75431
+ args: ["plugin", "marketplace", "update", market ?? "workit"]
75432
+ },
75433
+ { command: "claude", args: ["plugin", "update", source.selector] }
75434
+ ],
75435
+ fix: `claude plugin marketplace update ${market ?? "workit"} && claude plugin update ${source.selector}`
75436
+ });
75437
+ continue;
75438
+ }
75439
+ if (source.host === "opencode") {
75440
+ opencodeHelp ??= opencodeProbe(deps, ["plugin", "--help"]).stdout;
75441
+ if (!/^\s*(?:opencode\s+plugin\s+)?update\b/m.test(opencodeHelp)) {
75442
+ plan.skipped.push({
75443
+ host: "opencode",
75444
+ reason: "this OpenCode has no `opencode plugin update`; upgrade OpenCode, then run the fix. Workit never updates unrelated plugins",
75445
+ fix: OPENCODE_UPDATE
75446
+ });
75447
+ continue;
75333
75448
  }
75334
- } else if (plan.blocked.length === 0 && reasons.every((reason) => reason.includes("registry version unavailable"))) {
75335
- (deps.out ?? process.stderr).write(`Upgrade unavailable: ${reasons.join("; ")}. Starting the unchanged host.
75336
- `);
75337
- } else {
75338
- (deps.out ?? process.stderr).write(`Upgrade blocked: ${reasons.join("; ")}
75339
- `);
75340
- return 1;
75449
+ plan.entries.push({
75450
+ host: "opencode",
75451
+ selector: source.selector,
75452
+ latest: "",
75453
+ commands: [{ command: "opencode", args: ["plugin", "update", OPENCODE_PACKAGE] }],
75454
+ fix: OPENCODE_UPDATE
75455
+ });
75456
+ continue;
75341
75457
  }
75458
+ const latest = registryVersion(run, `@brainervirus/workit-${source.host}`);
75459
+ if (!latest) {
75460
+ plan.failed.push({
75461
+ host: source.host,
75462
+ status: "failed",
75463
+ reason: "registry version unavailable; existing installation preserved",
75464
+ fix: fixFor(source.host, source.selector)
75465
+ });
75466
+ continue;
75467
+ }
75468
+ if (source.installedVersion && versionAtLeast(source.installedVersion, latest)) {
75469
+ plan.skipped.push({
75470
+ host: source.host,
75471
+ reason: `already current (${source.installedVersion})`
75472
+ });
75473
+ continue;
75474
+ }
75475
+ const commands = source.host === "pi" ? [{ command: "pi", args: ["update", "--extension", source.selector] }] : source.host === "codex" ? [
75476
+ {
75477
+ command: "codex",
75478
+ args: ["plugin", "marketplace", "upgrade", CODEX_MARKETPLACE]
75479
+ },
75480
+ {
75481
+ command: "codex",
75482
+ args: ["plugin", "add", `workit@${CODEX_MARKETPLACE}`, "--json"]
75483
+ }
75484
+ ] : planHostInstall("cursor", {
75485
+ home,
75486
+ env: deps.env,
75487
+ cwd: home,
75488
+ packageVersion: latest,
75489
+ targetDir: paths.cursorRoot
75490
+ });
75491
+ plan.entries.push({
75492
+ host: source.host,
75493
+ selector: source.selector,
75494
+ latest,
75495
+ commands,
75496
+ installedVersion: source.installedVersion,
75497
+ ...source.kind === "repair" ? { repair: true } : {},
75498
+ fix: fixFor(source.host, source.selector)
75499
+ });
75342
75500
  }
75343
- if (deps.run)
75344
- return deps.run(host, hostArgs).status ?? 1;
75345
- return spawnSync17(launch.command, launch.args, {
75346
- stdio: "inherit",
75347
- env
75348
- }).status ?? 1;
75501
+ plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
75502
+ return plan;
75349
75503
  }
75350
- var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-opencode", OPENCODE_UPDATE, CODEX_MARKETPLACE = "workflow-toolkit", CLI_FIX = "npm install --global @brainervirus/workit-cli@latest", fixFor = (host, selector = "") => host === "pi" ? `pi update --extension ${selector || "npm:@brainervirus/workit-pi"}` : host === "codex" ? `codex plugin marketplace upgrade ${CODEX_MARKETPLACE} && codex plugin add workit@${CODEX_MARKETPLACE}` : host === "claude-code" ? "claude plugin marketplace update workit && claude plugin update workit@workit" : host === "opencode" ? OPENCODE_UPDATE : host === "cursor" ? "workit upgrade --hosts=cursor" : CLI_FIX, upgradePaths = (deps) => {
75351
- const home = deps.home ?? deps.env?.HOME ?? os16.homedir();
75352
- const env = {
75353
- ...process.env,
75354
- ...deps.home || deps.env?.HOME ? {
75355
- HOME: home,
75356
- CODEX_HOME: path44.join(home, ".codex"),
75357
- CLAUDE_CONFIG_DIR: path44.join(home, ".claude"),
75358
- PI_CODING_AGENT_DIR: path44.join(home, ".pi/agent"),
75359
- XDG_CONFIG_HOME: path44.join(home, ".config"),
75360
- WORKFLOW_TOOLKIT_CONFIG: undefined,
75361
- WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
75362
- } : {},
75363
- ...deps.env,
75364
- HOME: home
75365
- };
75366
- const xdg = env.XDG_CONFIG_HOME || path44.join(home, ".config");
75367
- const codexHome = env.CODEX_HOME ?? path44.join(home, ".codex");
75368
- return {
75369
- home,
75370
- env,
75371
- configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path44.join(xdg, "workit"),
75372
- opencodeConfig: path44.join(xdg, "opencode/opencode.json"),
75373
- piSettings: path44.join(env.PI_CODING_AGENT_DIR ?? path44.join(home, ".pi/agent"), "settings.json"),
75374
- codexHome,
75375
- codexConfig: path44.join(codexHome, "config.toml"),
75376
- cursorRoot: path44.join(home, ".cursor/plugins/local/workit")
75377
- };
75378
- }, object2 = (file) => {
75379
- if (!existsSync20(file))
75380
- return null;
75381
- let data;
75382
- try {
75383
- data = JSON.parse(readFileSync21(file, "utf8"));
75384
- } catch {
75385
- throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
75386
- }
75387
- if (!data || typeof data !== "object" || Array.isArray(data))
75388
- throw new Error(`${file} is not a configuration object`);
75389
- return data;
75390
- }, objectOrNull = (file) => {
75391
- try {
75392
- return object2(file);
75393
- } catch {
75394
- return null;
75395
- }
75396
- }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync21(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
75397
- const { home, env } = upgradePaths(deps);
75398
- let step;
75504
+ var codexRegistered = (configFile) => {
75505
+ let text;
75399
75506
  try {
75400
- step = hostCommand(command, args, { home, env });
75507
+ text = readFileSync22(configFile, "utf8");
75401
75508
  } catch {
75402
- return { status: 1, stdout: "" };
75509
+ return false;
75403
75510
  }
75404
- const result = runHostCommand(step, { home, env });
75405
- return { status: result.exitCode, stdout: result.stdout };
75406
- }), classify = (host, selector) => ({
75511
+ const header = `[plugins."workit@${CODEX_MARKETPLACE}"]`;
75512
+ const start = text.split(/\r?\n/).findIndex((line) => line.trim() === header);
75513
+ if (start < 0)
75514
+ return false;
75515
+ const body = text.split(/\r?\n/).slice(start + 1).join(`
75516
+ `).split(/^\s*\[/m)[0];
75517
+ return !/^\s*enabled\s*=\s*false\b/m.test(body);
75518
+ }, classify = (host, selector) => ({
75407
75519
  host,
75408
75520
  selector,
75409
75521
  kind: selector === `@brainervirus/workit-${host}` || selector === `@brainervirus/workit-${host}@latest` || selector === `npm:@brainervirus/workit-${host}` || selector === `npm:@brainervirus/workit-${host}@latest` ? "floating" : new RegExp(`^(?:npm:)?@brainervirus/workit-${host}@[^/]+$`).test(selector) ? "pinned" : "local"
75410
- }), errorText = (error) => error instanceof Error ? error.message : "installed source could not be read", sources = (paths, run, hosts, failed) => {
75522
+ }), sources = (paths, run, hosts, failed) => {
75411
75523
  const { home } = paths;
75412
75524
  const result = [];
75413
75525
  const guard = (host, read) => {
@@ -75425,7 +75537,7 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75425
75537
  }
75426
75538
  };
75427
75539
  guard("opencode", () => {
75428
- if (existsSync20(paths.opencodeConfig + "c"))
75540
+ if (existsSync22(paths.opencodeConfig + "c"))
75429
75541
  throw new Error("OpenCode JSONC config is present; inspect effective native configuration before updating");
75430
75542
  const oc = object2(paths.opencodeConfig);
75431
75543
  if (oc && [oc.plugins, oc.plugin].some((value) => value !== undefined && !Array.isArray(value)))
@@ -75435,8 +75547,8 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75435
75547
  result.push(classify("opencode", pin));
75436
75548
  });
75437
75549
  guard("cursor", () => {
75438
- const cursor = objectOrNull(path44.join(paths.cursorRoot, "package.json"));
75439
- const mcp = objectOrNull(path44.join(home, ".cursor/mcp.json"));
75550
+ const cursor = objectOrNull(path47.join(paths.cursorRoot, "package.json"));
75551
+ const mcp = objectOrNull(path47.join(home, ".cursor/mcp.json"));
75440
75552
  const args = mcp?.mcpServers?.workit?.args ?? [];
75441
75553
  const arg = args.find((value) => typeof value === "string" && value.startsWith("--package="));
75442
75554
  const selector = typeof arg === "string" ? arg.slice(10) : "local";
@@ -75447,7 +75559,7 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75447
75559
  });
75448
75560
  return;
75449
75561
  }
75450
- const settings = objectOrNull(path44.join(home, ".cursor/settings.json"));
75562
+ const settings = objectOrNull(path47.join(home, ".cursor/settings.json"));
75451
75563
  const dirs = Array.isArray(settings?.plugin_dirs) ? settings.plugin_dirs : [];
75452
75564
  const registered = mcp?.mcpServers?.workit !== undefined || dirs.some((dir) => String(dir).replaceAll("\\", "/").replace(/\/+$/, "").endsWith("local/workit"));
75453
75565
  if (registered)
@@ -75462,7 +75574,7 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75462
75574
  if (typeof source !== "string")
75463
75575
  continue;
75464
75576
  const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
75465
- const local = path44.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path44.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
75577
+ const local = path47.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path47.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
75466
75578
  if (!npm && !local)
75467
75579
  continue;
75468
75580
  const found = classify("pi", source);
@@ -75482,7 +75594,7 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75482
75594
  hostCommand("codex", [], { home, env: paths.env });
75483
75595
  available = true;
75484
75596
  } catch {}
75485
- if (available || existsSync20(paths.codexConfig))
75597
+ if (available || existsSync22(paths.codexConfig))
75486
75598
  throw new Error("Codex plugin inventory failed; inspect the native host before upgrading");
75487
75599
  return;
75488
75600
  }
@@ -75504,6 +75616,12 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75504
75616
  installedVersion: plugin.version
75505
75617
  });
75506
75618
  }
75619
+ if (!result.some((found) => found.host === "codex") && codexRegistered(paths.codexConfig))
75620
+ result.push({
75621
+ host: "codex",
75622
+ selector: "@brainervirus/workit-codex",
75623
+ kind: "repair"
75624
+ });
75507
75625
  });
75508
75626
  guard("claude-code", () => {
75509
75627
  for (const install of claudeWorkitInstalls(home, paths.env, null))
@@ -75515,213 +75633,24 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75515
75633
  });
75516
75634
  });
75517
75635
  return result;
75518
- }, piInstalledVersion = (listing, selector) => {
75519
- const lines = listing.split(/\r?\n/).map((line) => line.trim());
75520
- const index = lines.indexOf(selector);
75521
- if (index < 0 || !lines[index + 1] || !path44.isAbsolute(lines[index + 1]))
75522
- return;
75523
- const found = objectOrNull(path44.join(lines[index + 1], "package.json"));
75524
- return found?.name === "@brainervirus/workit-pi" ? found.version : undefined;
75525
- }, versionAtLeast = (installed, latest) => {
75526
- if (!/^\d+\.\d+\.\d+$/.test(installed))
75527
- return false;
75528
- const current = installed.split(".").map(Number);
75529
- const target = latest.split(".").map(Number);
75530
- for (let index = 0;index < 3; index++) {
75531
- if (current[index] !== target[index])
75532
- return current[index] > target[index];
75533
- }
75534
- return true;
75535
75636
  }, opencodeProbe = (deps, args) => {
75536
- const scratch = mkdtempSync3(path44.join(os16.tmpdir(), "workit-upgrade-opencode-"));
75637
+ const scratch = mkdtempSync3(path47.join(os17.tmpdir(), "workit-upgrade-opencode-"));
75537
75638
  try {
75538
75639
  return runner({
75539
75640
  ...deps,
75540
75641
  env: {
75541
75642
  ...deps.env,
75542
- XDG_DATA_HOME: path44.join(scratch, "data"),
75543
- XDG_STATE_HOME: path44.join(scratch, "state"),
75544
- XDG_CACHE_HOME: path44.join(scratch, "cache")
75643
+ XDG_DATA_HOME: path47.join(scratch, "data"),
75644
+ XDG_STATE_HOME: path47.join(scratch, "state"),
75645
+ XDG_CACHE_HOME: path47.join(scratch, "cache")
75545
75646
  }
75546
75647
  })("opencode", args);
75547
75648
  } finally {
75548
75649
  rmSync8(scratch, { recursive: true, force: true });
75549
75650
  }
75550
- }, registryVersion = (run, name) => {
75551
- const latest = run("npm", ["view", name, "version"]);
75552
- const version = latest.stdout.trim();
75553
- return latest.status === 0 && /^\d+\.\d+\.\d+$/.test(version) ? version : undefined;
75554
- }, PROCESS_HOSTS, classifyProcess = (proc) => {
75555
- if (proc.state.startsWith("Z") || proc.args.includes("<defunct>"))
75556
- return null;
75557
- const argv0 = proc.args.trim().split(/\s+/)[0] ?? "";
75558
- const name = path44.basename(argv0.replaceAll("\\", "/")).replace(/^\[|\]$/g, "").replace(/\.exe$/i, "").toLowerCase();
75559
- const host = PROCESS_HOSTS[name];
75560
- if (!host)
75561
- return null;
75562
- if (host === "codex" && /\bapp-server\b/.test(proc.args))
75563
- return /\bdaemon\s+pid-update-loop\b/.test(proc.args) ? null : { ...proc, host, kind: "daemon" };
75564
- if (host === "opencode" && /\bserve\b/.test(proc.args))
75565
- return { ...proc, host, kind: "daemon" };
75566
- return { ...proc, host, kind: "session" };
75567
- }, listProcesses = (deps) => {
75568
- if (deps.processes)
75569
- return deps.processes();
75570
- if (deps.activeHosts)
75571
- return deps.activeHosts().map((host) => ({ pid: 0, state: "S", args: host }));
75572
- const run = runner(deps);
75573
- if (process.platform === "win32") {
75574
- const list = run("tasklist", ["/fo", "csv", "/nh"]);
75575
- if (list.status !== 0)
75576
- return null;
75577
- return list.stdout.split(/\r?\n/).flatMap((line) => {
75578
- const [image, pid] = line.split('","').map((cell) => cell.replaceAll('"', ""));
75579
- return image && /^\d+$/.test(pid ?? "") ? [{ pid: Number(pid), state: "R", args: image }] : [];
75580
- });
75581
- }
75582
- const list = run("ps", ["-eo", "pid=,stat=,args="]);
75583
- if (list.status !== 0)
75584
- return null;
75585
- return list.stdout.split(/\r?\n/).flatMap((line) => {
75586
- const match = /^\s*(\d+)\s+(\S+)\s+(.+)$/.exec(line);
75587
- return match ? [{ pid: Number(match[1]), state: match[2], args: match[3] }] : [];
75588
- });
75589
- }, describe2 = (proc) => `pid ${proc.pid}: ${proc.args.length > 120 ? `${proc.args.slice(0, 117)}...` : proc.args}`, codexPluginRoot = (codexHome) => {
75590
- const dir = path44.join(codexHome, "plugins", "cache", CODEX_MARKETPLACE, "workit");
75591
- let versions = [];
75592
- try {
75593
- versions = readdirSync12(dir).filter((version) => /^\d+\.\d+\.\d+$/.test(version));
75594
- } catch {
75595
- return null;
75596
- }
75597
- const newest = versions.reduce((best, version) => best && versionAtLeast(best, version) ? best : version, null);
75598
- return newest ? path44.join(dir, newest) : null;
75599
- }, applyEntry = (entry, context) => {
75600
- const { run, paths, configDir } = context;
75601
- const { home, env } = paths;
75602
- const fail = (reason) => ({
75603
- host: entry.host,
75604
- status: "failed",
75605
- reason,
75606
- fix: entry.fix
75607
- });
75608
- if (entry.host === "cursor") {
75609
- const preview = {
75610
- ok: true,
75611
- blocked: [],
75612
- preserved: [],
75613
- overrides: [],
75614
- platforms: ["cursor"],
75615
- state: readSetupState(configDir),
75616
- mutations: [
75617
- {
75618
- type: "install-host",
75619
- platform: "cursor",
75620
- path: path44.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
75621
- commands: entry.commands.map((command) => ({
75622
- ...command,
75623
- purpose: command.purpose ?? "Upgrade Cursor"
75624
- }))
75625
- },
75626
- { type: "install-adapter", platform: "cursor", path: paths.cursorRoot }
75627
- ]
75628
- };
75629
- const result = applySetupPreview(preview, {
75630
- home,
75631
- env,
75632
- cwd: home,
75633
- preferInstalledAdapter: true,
75634
- runHostCommand: (command) => {
75635
- const outcome = run(command.command, command.args);
75636
- return {
75637
- exitCode: outcome.status ?? 1,
75638
- stdout: outcome.stdout,
75639
- stderr: ""
75640
- };
75641
- }
75642
- });
75643
- const installed = objectOrNull(path44.join(paths.cursorRoot, "package.json"));
75644
- if (!result.ok || installed?.version !== entry.latest)
75645
- return fail("Cursor install did not verify the requested package version; inspect the installer before retrying");
75646
- return {
75647
- host: "cursor",
75648
- status: "updated",
75649
- reason: entry.repair ? `reinstalled missing plugin files (${entry.latest})` : `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75650
- };
75651
- }
75652
- for (const command of entry.commands)
75653
- if (run(command.command, command.args).status !== 0)
75654
- return fail(`\`${command.command} ${command.args.join(" ")}\` failed`);
75655
- if (entry.host === "opencode")
75656
- return {
75657
- host: "opencode",
75658
- status: "updated",
75659
- reason: "scoped update ran; OpenCode V2 loads it live"
75660
- };
75661
- if (entry.host === "claude-code") {
75662
- const after = claudeWorkitInstalls(home, env, null).find((install) => install.id === entry.selector)?.version;
75663
- return after && after === entry.installedVersion ? {
75664
- host: "claude-code",
75665
- status: "current",
75666
- reason: `already current (${after})`
75667
- } : {
75668
- host: "claude-code",
75669
- status: "updated",
75670
- reason: `${entry.installedVersion ?? "?"} -> ${after ?? "?"}; new sessions load it`
75671
- };
75672
- }
75673
- const verify = entry.host === "codex" ? run("codex", ["plugin", "list", "--json"]) : run("pi", ["list"]);
75674
- if (verify.status !== 0)
75675
- return fail("update verification failed");
75676
- if (entry.host === "codex") {
75677
- let installed = [];
75678
- try {
75679
- installed = JSON.parse(verify.stdout).installed ?? [];
75680
- } catch {}
75681
- if (!installed.some((plugin) => plugin.name === "workit" && plugin.marketplaceName === CODEX_MARKETPLACE && plugin.version === entry.latest))
75682
- return fail("Codex did not report the requested Workit version");
75683
- }
75684
- if (entry.host === "pi" && piInstalledVersion(verify.stdout, entry.selector) !== entry.latest)
75685
- return fail("Pi did not report the requested Workit package version");
75686
- return {
75687
- host: entry.host,
75688
- status: "updated",
75689
- reason: `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75690
- };
75691
- }, refreshCodexAgents = (run, codexHome) => {
75692
- const root = codexPluginRoot(codexHome);
75693
- const launcher = root && path44.join(root, "dist", "launch-mcp.js");
75694
- if (!launcher || !existsSync20(launcher))
75695
- return { ok: true, note: "" };
75696
- const fix = `node ${JSON.stringify(launcher)} --install-agents`;
75697
- return run("node", [launcher, "--install-agents"]).status === 0 ? { ok: true, note: "agents refreshed" } : { ok: false, note: "agent refresh failed", fix };
75698
- }, applyCli = (cli, run, backup) => {
75699
- const fail = (reason) => ({
75700
- host: "cli",
75701
- status: "failed",
75702
- reason,
75703
- fix: CLI_FIX
75704
- });
75705
- const currentRoot = run("npm", ["root", "--global"]);
75706
- if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== cli.root)
75707
- return fail("Global npm location changed; preview again");
75708
- const cliBackup = path44.join(backup, "cli-package");
75709
- cpSync5(path44.join(cli.root, "@brainervirus/workit-cli"), cliBackup, {
75710
- recursive: true
75711
- });
75712
- if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${cli.latest}`]).status !== 0)
75713
- return fail(`CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path44.join(cliBackup, "dist/index.js")}`);
75714
- const installed = objectOrNull(path44.join(cli.root, "@brainervirus/workit-cli/package.json"));
75715
- if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== cli.latest)
75716
- return fail(`CLI update did not verify the requested version; preserved package at ${cliBackup}`);
75717
- return {
75718
- host: "cli",
75719
- status: "updated",
75720
- reason: `${cli.installed} -> ${cli.latest}`
75721
- };
75722
75651
  }, planCli = (plan, run) => {
75723
75652
  const root = run("npm", ["root", "--global"]);
75724
- if (root.status !== 0 || !path44.isAbsolute(root.stdout.trim())) {
75653
+ if (root.status !== 0 || !path47.isAbsolute(root.stdout.trim())) {
75725
75654
  plan.skipped.push({
75726
75655
  host: "cli",
75727
75656
  reason: "global npm location could not be verified",
@@ -75729,9 +75658,9 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75729
75658
  });
75730
75659
  return;
75731
75660
  }
75732
- const packageFile = path44.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
75661
+ const packageFile = path47.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
75733
75662
  const installed = objectOrNull(packageFile);
75734
- if (installed?.name !== "@brainervirus/workit-cli" || lstatSync7(path44.dirname(packageFile)).isSymbolicLink()) {
75663
+ if (installed?.name !== "@brainervirus/workit-cli" || lstatSync7(path47.dirname(packageFile)).isSymbolicLink()) {
75735
75664
  plan.skipped.push({
75736
75665
  host: "cli",
75737
75666
  reason: "no managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
@@ -75758,7 +75687,18 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75758
75687
  host: "cli",
75759
75688
  reason: `already current (${installed.version})`
75760
75689
  });
75761
- }, pad2 = (text) => text.padEnd(12), summary = (plan, permissions) => {
75690
+ };
75691
+ var init_plan = __esm(() => {
75692
+ init_registration();
75693
+ init_setup_state();
75694
+ init_host_install();
75695
+ init_doctor();
75696
+ init_support_matrix();
75697
+ init_shared();
75698
+ });
75699
+
75700
+ // packages/workit-cli/src/upgrade/report.ts
75701
+ var pad2 = (text) => text.padEnd(12), summary = (plan, permissions) => {
75762
75702
  const lines = ["Workit upgrade plan:"];
75763
75703
  if (plan.cli)
75764
75704
  lines.push(` ${pad2("cli")}update ${plan.cli.installed} -> ${plan.cli.latest}`);
@@ -75795,29 +75735,8 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75795
75735
  }
75796
75736
  await Promise.resolve().then(() => init_prompt());
75797
75737
  return /^y(?:es)?$/i.test((await askLine(question)).trim());
75798
- }, USAGE3 = `Usage: workit upgrade [--hosts=opencode,cursor,codex,pi,claude-code|none] [--cli] [--preview | --yes | --apply --confirm] [--json]
75799
- `;
75800
- var init_upgrade = __esm(() => {
75801
- init_package();
75802
- init_registration();
75803
- init_setup_state();
75804
- init_host_install();
75805
- init_setup();
75806
- init_doctor();
75807
- init_support_matrix();
75808
- init_safe_write();
75809
- init_host_permissions();
75810
- LAUNCH_HOSTS = ["opencode", "cursor", "codex", "pi"];
75811
- HOSTS2 = [...LAUNCH_HOSTS, "claude-code"];
75812
- LIVE_HOSTS = new Set(["opencode", "claude-code"]);
75813
- OPENCODE_UPDATE = `opencode plugin update ${OPENCODE_PACKAGE}`;
75814
- PROCESS_HOSTS = {
75815
- claude: "claude-code",
75816
- codex: "codex",
75817
- cursor: "cursor",
75818
- opencode: "opencode",
75819
- pi: "pi"
75820
- };
75738
+ };
75739
+ var init_report = __esm(() => {
75821
75740
  LABEL = {
75822
75741
  updated: "updated",
75823
75742
  current: "already current",
@@ -75826,6 +75745,191 @@ var init_upgrade = __esm(() => {
75826
75745
  };
75827
75746
  });
75828
75747
 
75748
+ // packages/workit-cli/src/upgrade.ts
75749
+ import { spawnSync as spawnSync17 } from "node:child_process";
75750
+ async function runUpgradeCommand(argv, deps = {}) {
75751
+ const out = deps.out ?? process.stdout;
75752
+ const flags = new Set(argv);
75753
+ const unknown = argv.filter((arg) => !["--apply", "--confirm", "--json", "--cli", "--preview", "--yes"].includes(arg) && !arg.startsWith("--hosts="));
75754
+ const hostFlag = argv.find((arg) => arg.startsWith("--hosts="))?.slice(8);
75755
+ const raw = hostFlag === "none" ? [] : hostFlag?.split(",") ?? HOSTS2;
75756
+ if (unknown.length || raw.some((host) => !HOSTS2.includes(host)) || flags.has("--preview") && (flags.has("--apply") || flags.has("--yes"))) {
75757
+ out.write(USAGE3);
75758
+ return 2;
75759
+ }
75760
+ if (flags.has("--apply") && !flags.has("--confirm")) {
75761
+ out.write(`Apply requires --confirm after reviewing the upgrade preview.
75762
+ `);
75763
+ return 2;
75764
+ }
75765
+ const legacy = flags.has("--apply");
75766
+ const json = flags.has("--json") || legacy;
75767
+ const run = runner(deps);
75768
+ const plan = previewUpgrade(raw, deps);
75769
+ if (hostFlag === undefined || flags.has("--cli"))
75770
+ planCli(plan, run);
75771
+ plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
75772
+ const { home, env } = upgradePaths(deps);
75773
+ const permissions = planHostPermissions({
75774
+ hosts: raw,
75775
+ refreshOnly: true,
75776
+ shell: false,
75777
+ home,
75778
+ env
75779
+ });
75780
+ const permissionPreview = {
75781
+ changes: permissions.changes.map((c) => ({
75782
+ host: c.host,
75783
+ file: c.file,
75784
+ diff: permissionDiff(c)
75785
+ })),
75786
+ ...permissions.refused ? { refused: permissions.refused } : {}
75787
+ };
75788
+ const withPermissions = (value) => permissions.changes.length ? { ...value, permissions: permissionPreview } : value;
75789
+ const applyCommand = `workit upgrade ${[...hostFlag === undefined ? [] : [`--hosts=${hostFlag}`], ...flags.has("--cli") ? ["--cli"] : [], "--yes"].join(" ")}`;
75790
+ const nothingChanged = `preview only; nothing changed. Apply with: ${applyCommand}
75791
+ `;
75792
+ if (json && !legacy && !flags.has("--yes")) {
75793
+ out.write(JSON.stringify(withPermissions(plan), null, 2) + `
75794
+ `);
75795
+ return plan.blocked.length ? 1 : 0;
75796
+ }
75797
+ if (!json)
75798
+ out.write(summary(plan, permissions.changes));
75799
+ if (flags.has("--preview")) {
75800
+ out.write(nothingChanged);
75801
+ return plan.blocked.length ? 1 : 0;
75802
+ }
75803
+ if (plan.blocked.length) {
75804
+ out.write(`Upgrade blocked: ${plan.blocked.join("; ")}
75805
+ Nothing changed.
75806
+ `);
75807
+ return 1;
75808
+ }
75809
+ const work = plan.entries.length + plan.migrations.length + (plan.cli ? 1 : 0) + permissions.changes.length;
75810
+ if (work > 0 && !flags.has("--yes") && !legacy) {
75811
+ const confirmed = await (deps.confirm ?? ((q) => askConfirm(q, out)))("Apply this upgrade? [y/N] ");
75812
+ if (!confirmed) {
75813
+ out.write(nothingChanged);
75814
+ return 0;
75815
+ }
75816
+ }
75817
+ if (json)
75818
+ for (const warning of plan.warnings ?? [])
75819
+ out.write(`warning: ${warning}
75820
+ `);
75821
+ const result = applyUpgrade(plan, deps);
75822
+ let permissionsOk = true;
75823
+ let written;
75824
+ if (permissions.changes.length && !permissions.refused && !result.error?.startsWith("configuration changed")) {
75825
+ written = applyHostPermissions(permissions, { home, env });
75826
+ permissionsOk = written.every((entry) => entry.status === "written");
75827
+ for (const entry of written)
75828
+ result.hosts.push({
75829
+ host: entry.host,
75830
+ status: entry.status === "written" ? "updated" : "failed",
75831
+ reason: `permission block ${entry.status === "written" ? "refreshed" : entry.status} in ${entry.file}`,
75832
+ ...entry.status === "written" ? {} : { fix: `workit setup --hosts=${entry.host}` }
75833
+ });
75834
+ }
75835
+ const failed = !result.ok || !permissionsOk;
75836
+ const cliVersion = result.hosts.some((h) => h.host === "cli" && h.status === "updated") ? plan.cli.latest : package_default.version;
75837
+ const report = (deps.doctor ?? ((version) => runDoctor({ home, env, cliVersion: version, only: "upgrade" })))(cliVersion);
75838
+ const left = report.checks.filter((check) => check.status === "warn" || check.status === "fail");
75839
+ if (json) {
75840
+ out.write(JSON.stringify({
75841
+ ...result,
75842
+ ok: !failed,
75843
+ ...written ? { permissions: { changes: permissionPreview.changes, written } } : permissions.refused ? { permissions: permissionPreview } : {},
75844
+ doctor: { clean: left.length === 0, left }
75845
+ }, null, 2) + `
75846
+ `);
75847
+ return failed ? 1 : 0;
75848
+ }
75849
+ if (result.error && result.hosts.every((h) => h.status !== "failed"))
75850
+ out.write(`Upgrade stopped: ${result.error}
75851
+ `);
75852
+ out.write(table(result.hosts));
75853
+ if (permissions.refused)
75854
+ out.write(`permissions: not refreshed: ${permissions.refused}
75855
+ `);
75856
+ if (result.backup)
75857
+ out.write(`Backup: ${result.backup}
75858
+ `);
75859
+ out.write(left.length === 0 ? `Verified: workit doctor is clean for versions, assets, agents and permissions.
75860
+ ` : `Left after upgrade (workit doctor):
75861
+ ${left.map((check) => ` ${check.id}: ${check.detail}${check.fix ? `
75862
+ fix: ${check.fix}` : ""}`).join(`
75863
+ `)}
75864
+ `);
75865
+ return failed ? 1 : 0;
75866
+ }
75867
+ async function runLaunchCommand(argv, deps = {}) {
75868
+ const [host, ...args] = argv;
75869
+ if (!LAUNCH_HOSTS.includes(host)) {
75870
+ (deps.out ?? process.stdout).write(`Usage: workit launch <opencode|cursor|codex|pi> [--auto-upgrade] [-- host arguments]
75871
+ `);
75872
+ return 2;
75873
+ }
75874
+ const auto = args[0] === "--auto-upgrade";
75875
+ const rest = auto ? args.slice(1) : args;
75876
+ const hostArgs = rest[0] === "--" ? rest.slice(1) : rest;
75877
+ const { home, env } = upgradePaths(deps);
75878
+ let launch;
75879
+ if (!deps.run) {
75880
+ try {
75881
+ launch = hostCommand(host, hostArgs, { home, env });
75882
+ } catch (error) {
75883
+ (deps.out ?? process.stderr).write(`${error instanceof Error ? error.message : "Host executable unavailable"}
75884
+ `);
75885
+ return 1;
75886
+ }
75887
+ }
75888
+ if (auto) {
75889
+ const plan = previewUpgrade([host], deps);
75890
+ const reasons = [...plan.blocked, ...plan.failed.map((entry) => entry.reason)];
75891
+ if (plan.ok) {
75892
+ for (const skipped of plan.skipped)
75893
+ (deps.out ?? process.stderr).write(`${skipped.host}: ${skipped.reason}${skipped.fix ? ` (run: ${skipped.fix})` : ""}
75894
+ `);
75895
+ const result = applyUpgrade(plan, deps);
75896
+ if (!result.ok) {
75897
+ (deps.out ?? process.stdout).write(`${result.error}
75898
+ `);
75899
+ return 1;
75900
+ }
75901
+ } else if (plan.blocked.length === 0 && reasons.every((reason) => reason.includes("registry version unavailable"))) {
75902
+ (deps.out ?? process.stderr).write(`Upgrade unavailable: ${reasons.join("; ")}. Starting the unchanged host.
75903
+ `);
75904
+ } else {
75905
+ (deps.out ?? process.stderr).write(`Upgrade blocked: ${reasons.join("; ")}
75906
+ `);
75907
+ return 1;
75908
+ }
75909
+ }
75910
+ if (deps.run)
75911
+ return deps.run(host, hostArgs).status ?? 1;
75912
+ return spawnSync17(launch.command, launch.args, {
75913
+ stdio: "inherit",
75914
+ env
75915
+ }).status ?? 1;
75916
+ }
75917
+ var USAGE3 = `Usage: workit upgrade [--hosts=opencode,cursor,codex,pi,claude-code|none] [--cli] [--preview | --yes | --apply --confirm] [--json]
75918
+ `;
75919
+ var init_upgrade = __esm(() => {
75920
+ init_package();
75921
+ init_host_install();
75922
+ init_doctor();
75923
+ init_host_permissions();
75924
+ init_apply();
75925
+ init_plan();
75926
+ init_report();
75927
+ init_shared();
75928
+ init_apply();
75929
+ init_plan();
75930
+ init_processes();
75931
+ });
75932
+
75829
75933
  // packages/workit-cli/src/verbs/upgrade.ts
75830
75934
  var exports_upgrade = {};
75831
75935
  __export(exports_upgrade, {
@@ -75849,7 +75953,7 @@ var init_launch = __esm(() => {
75849
75953
  // packages/workit-core/src/knowledge.ts
75850
75954
  import { spawnSync as spawnSync18 } from "node:child_process";
75851
75955
  import fs20 from "node:fs";
75852
- import path45 from "node:path";
75956
+ import path48 from "node:path";
75853
75957
  function lintKnowledge(root) {
75854
75958
  const seen = new Set;
75855
75959
  const unreadable = [];
@@ -75953,7 +76057,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
75953
76057
  const end = lines.indexOf("---", 1);
75954
76058
  return end === -1 ? lines : [...lines.slice(0, end + 1).map(() => ""), ...lines.slice(end + 1)];
75955
76059
  }, read = (root, file) => {
75956
- const full = path45.join(root, file);
76060
+ const full = path48.join(root, file);
75957
76061
  try {
75958
76062
  fs20.lstatSync(full);
75959
76063
  } catch {
@@ -75994,7 +76098,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
75994
76098
  }
75995
76099
  }, brokenLinks = (root, doc, tracked) => {
75996
76100
  const findings = [];
75997
- const base = path45.dirname(path45.join(root, doc.file));
76101
+ const base = path48.dirname(path48.join(root, doc.file));
75998
76102
  const missing = (target, line, how) => findings.push({
75999
76103
  rule: "broken-link",
76000
76104
  file: doc.file,
@@ -76013,7 +76117,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
76013
76117
  const local = localTarget(target);
76014
76118
  if (!local)
76015
76119
  continue;
76016
- const resolved = local.startsWith("/") ? path45.join(root, local) : path45.resolve(base, local);
76120
+ const resolved = local.startsWith("/") ? path48.join(root, local) : path48.resolve(base, local);
76017
76121
  if (!exists2(resolved))
76018
76122
  missing(target, index + 1, "link");
76019
76123
  }
@@ -76023,9 +76127,9 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
76023
76127
  continue;
76024
76128
  const relative = /^\.\.?\//.test(candidate);
76025
76129
  const first = candidate.split("/")[0];
76026
- if (!relative && !(tracked ? tracked.has(first) : exists2(path45.join(root, first))))
76130
+ if (!relative && !(tracked ? tracked.has(first) : exists2(path48.join(root, first))))
76027
76131
  continue;
76028
- const resolved = relative ? path45.resolve(base, candidate) : path45.join(root, candidate);
76132
+ const resolved = relative ? path48.resolve(base, candidate) : path48.join(root, candidate);
76029
76133
  if (!exists2(resolved))
76030
76134
  missing(span, index + 1, "pointer");
76031
76135
  }
@@ -76348,10 +76452,10 @@ var init_redact = __esm(() => {
76348
76452
 
76349
76453
  // packages/workit-core/src/checks.ts
76350
76454
  import { spawn, spawnSync as spawnSync19 } from "node:child_process";
76351
- import { createHash as createHash16 } from "node:crypto";
76455
+ import { createHash as createHash17 } from "node:crypto";
76352
76456
  import fs21 from "node:fs";
76353
- import os17 from "node:os";
76354
- import path46 from "node:path";
76457
+ import os18 from "node:os";
76458
+ import path49 from "node:path";
76355
76459
  import { StringDecoder } from "node:string_decoder";
76356
76460
  function tailLines(text, lines = TAIL_LINES) {
76357
76461
  const all = text.replace(/\r\n?/gu, `
@@ -76471,7 +76575,7 @@ function planSpawn(argv, options) {
76471
76575
  return { file: argv[0], args: [], verbatim: false, shell: true };
76472
76576
  if (options.platform !== "win32")
76473
76577
  return { file: argv[0], args: argv.slice(1), verbatim: false, shell: false };
76474
- const win = path46.win32;
76578
+ const win = path49.win32;
76475
76579
  const isFile = options.isFile ?? ((file) => {
76476
76580
  try {
76477
76581
  return fs21.statSync(file).isFile();
@@ -76556,7 +76660,7 @@ function runCheckCommand(argv, options) {
76556
76660
  `);
76557
76661
  redactor.end();
76558
76662
  resolve({
76559
- exitCode: spawnError ? 127 : code !== null ? code : 128 + (signal ? os17.constants.signals[signal] ?? 1 : 1),
76663
+ exitCode: spawnError ? 127 : code !== null ? code : 128 + (signal ? os18.constants.signals[signal] ?? 1 : 1),
76560
76664
  signal: signal ?? null,
76561
76665
  timedOut,
76562
76666
  durationMs: Date.now() - started,
@@ -76633,10 +76737,10 @@ function runCheckCommand(argv, options) {
76633
76737
  function storeLog(storeRoot, log) {
76634
76738
  const digest = logDigest(log);
76635
76739
  const ref = `blobs/logs/${digest.slice("sha256:".length)}.log`;
76636
- const file = path46.join(storeRoot, ref);
76740
+ const file = path49.join(storeRoot, ref);
76637
76741
  try {
76638
76742
  if (!fs21.existsSync(file)) {
76639
- fs21.mkdirSync(path46.dirname(file), { recursive: true });
76743
+ fs21.mkdirSync(path49.dirname(file), { recursive: true });
76640
76744
  const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
76641
76745
  fs21.writeFileSync(temp, log, { mode: 384 });
76642
76746
  fs21.renameSync(temp, file);
@@ -76649,7 +76753,7 @@ function storeLog(storeRoot, log) {
76649
76753
  function pruneCheckLogs(storeRoot, options = {}) {
76650
76754
  const limits = { ...LOG_RETENTION, ...options.retention };
76651
76755
  const now = options.now ?? Date.now();
76652
- const dir = path46.join(storeRoot, "blobs", "logs");
76756
+ const dir = path49.join(storeRoot, "blobs", "logs");
76653
76757
  const report = { removed: 0, removedBytes: 0, kept: 0 };
76654
76758
  let names;
76655
76759
  try {
@@ -76659,7 +76763,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
76659
76763
  }
76660
76764
  const files = names.flatMap((name) => {
76661
76765
  try {
76662
- const stat = fs21.statSync(path46.join(dir, name));
76766
+ const stat = fs21.statSync(path49.join(dir, name));
76663
76767
  return stat.isFile() ? [{ name, size: stat.size, mtimeMs: stat.mtimeMs }] : [];
76664
76768
  } catch {
76665
76769
  return [];
@@ -76678,7 +76782,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
76678
76782
  continue;
76679
76783
  if (!options.dryRun)
76680
76784
  try {
76681
- fs21.rmSync(path46.join(dir, file.name), { force: true });
76785
+ fs21.rmSync(path49.join(dir, file.name), { force: true });
76682
76786
  } catch {
76683
76787
  continue;
76684
76788
  }
@@ -76687,7 +76791,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
76687
76791
  }
76688
76792
  return report;
76689
76793
  }
76690
- var MAX_LOG_BYTES, TAIL_LINES = 80, MAX_LINE_CHARS = 400, redactLog = (text) => redactText(text), PEM_BEGIN, PEM_END, BATCH_BYTES, MAX_PENDING, PEM_BODY, MAX_KEY_LINES = 4096, MAX_KEY_BYTES, CMD_META, DRAIN_MS = 250, FORWARDED_SIGNALS, logDigest = (log) => `sha256:${createHash16("sha256").update(log).digest("hex")}`, LOG_RETENTION;
76794
+ var MAX_LOG_BYTES, TAIL_LINES = 80, MAX_LINE_CHARS = 400, redactLog = (text) => redactText(text), PEM_BEGIN, PEM_END, BATCH_BYTES, MAX_PENDING, PEM_BODY, MAX_KEY_LINES = 4096, MAX_KEY_BYTES, CMD_META, DRAIN_MS = 250, FORWARDED_SIGNALS, logDigest = (log) => `sha256:${createHash17("sha256").update(log).digest("hex")}`, LOG_RETENTION;
76691
76795
  var init_checks3 = __esm(() => {
76692
76796
  init_redact();
76693
76797
  MAX_LOG_BYTES = 2 * 1024 * 1024;
@@ -76951,7 +77055,7 @@ __export(exports_workspace, {
76951
77055
  run: () => run11,
76952
77056
  runWorkspace: () => runWorkspace
76953
77057
  });
76954
- import { copyFileSync as copyFileSync7, existsSync as existsSync21 } from "node:fs";
77058
+ import { copyFileSync as copyFileSync7, existsSync as existsSync23 } from "node:fs";
76955
77059
  function list2(argv, io) {
76956
77060
  if (argv.length)
76957
77061
  return usage3(io, `unexpected argument ${argv[0]}`);
@@ -77161,7 +77265,7 @@ usage: ${USAGE5}`)), load = (io) => {
77161
77265
  return { ok: false, error: `${current.path} changed; retry` };
77162
77266
  let backup = null;
77163
77267
  try {
77164
- if (existsSync21(current.path)) {
77268
+ if (existsSync23(current.path)) {
77165
77269
  backup = `${current.path}.bak`;
77166
77270
  copyFileSync7(current.path, backup);
77167
77271
  }
@@ -77229,12 +77333,12 @@ var init_workspace = __esm(() => {
77229
77333
 
77230
77334
  // packages/workit-core/src/core/heavy-rules.ts
77231
77335
  import fs22 from "node:fs";
77232
- import path47 from "node:path";
77336
+ import path50 from "node:path";
77233
77337
  function gitTop(cwd) {
77234
- for (let dir = path47.resolve(cwd);; dir = path47.dirname(dir)) {
77235
- if (fs22.existsSync(path47.join(dir, ".git")))
77338
+ for (let dir = path50.resolve(cwd);; dir = path50.dirname(dir)) {
77339
+ if (fs22.existsSync(path50.join(dir, ".git")))
77236
77340
  return dir;
77237
- if (path47.dirname(dir) === dir)
77341
+ if (path50.dirname(dir) === dir)
77238
77342
  return null;
77239
77343
  }
77240
77344
  }
@@ -77275,19 +77379,19 @@ var init_heavy_rules = () => {};
77275
77379
 
77276
77380
  // packages/workit-core/src/core/heavy.ts
77277
77381
  import fs23 from "node:fs";
77278
- import os18 from "node:os";
77279
- import path48 from "node:path";
77382
+ import os19 from "node:os";
77383
+ import path51 from "node:path";
77280
77384
  function stateDir2(env, platform = process.platform) {
77281
77385
  if (env.WORKFLOW_TOOLKIT_STATE)
77282
77386
  return env.WORKFLOW_TOOLKIT_STATE;
77283
77387
  if (env.XDG_STATE_HOME)
77284
- return path48.join(env.XDG_STATE_HOME, "workit");
77285
- const home = env.HOME || os18.homedir();
77388
+ return path51.join(env.XDG_STATE_HOME, "workit");
77389
+ const home = env.HOME || os19.homedir();
77286
77390
  if (platform === "darwin")
77287
- return path48.join(home, "Library", "Application Support", "workit");
77391
+ return path51.join(home, "Library", "Application Support", "workit");
77288
77392
  if (platform === "win32")
77289
- return path48.join(env.LOCALAPPDATA ?? path48.join(home, "AppData", "Local"), "workit");
77290
- return path48.join(home, ".local", "state", "workit");
77393
+ return path51.join(env.LOCALAPPDATA ?? path51.join(home, "AppData", "Local"), "workit");
77394
+ return path51.join(home, ".local", "state", "workit");
77291
77395
  }
77292
77396
  function positional(words, from, values = PM_VALUES) {
77293
77397
  let index = from;
@@ -77427,7 +77531,7 @@ function machinePatterns(env) {
77427
77531
  return isRecord8(value) ? stringList2(value.patterns) : [];
77428
77532
  }
77429
77533
  function repoPatterns(cwd) {
77430
- return repoHeavyCommands(gitTop(cwd) ?? path48.resolve(cwd));
77534
+ return repoHeavyCommands(gitTop(cwd) ?? path51.resolve(cwd));
77431
77535
  }
77432
77536
  function heavyLines(lines, options) {
77433
77537
  const found = [];
@@ -77506,7 +77610,7 @@ function leaseRewrite(command) {
77506
77610
  return null;
77507
77611
  return `${prefix}workit check --heavy -- ${rest}`;
77508
77612
  }
77509
- var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path48.join(stateDir2(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), matches2 = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index])), readJson2 = (file) => {
77613
+ var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path51.join(stateDir2(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), matches2 = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index])), readJson2 = (file) => {
77510
77614
  try {
77511
77615
  return JSON.parse(fs23.readFileSync(file, "utf8"));
77512
77616
  } catch {
@@ -77652,8 +77756,8 @@ var init_heavy = __esm(() => {
77652
77756
  // packages/workit-core/src/core/resources.ts
77653
77757
  import { randomUUID as randomUUID5 } from "node:crypto";
77654
77758
  import fs24 from "node:fs";
77655
- import os19 from "node:os";
77656
- import path49 from "node:path";
77759
+ import os20 from "node:os";
77760
+ import path52 from "node:path";
77657
77761
  function memoryValue(value, host) {
77658
77762
  const text = typeof value === "number" ? String(value) : typeof value === "string" ? value : "";
77659
77763
  const percent = PERCENT.exec(text.trim());
@@ -77834,7 +77938,7 @@ function tryOnce(options, record, reclaimed) {
77834
77938
  const text = `${JSON.stringify(record)}
77835
77939
  `;
77836
77940
  for (let slot = 0;slot < options.slots; slot += 1) {
77837
- const file = path49.join(options.dir, `slot-${slot}.json`);
77941
+ const file = path52.join(options.dir, `slot-${slot}.json`);
77838
77942
  for (let attempt = 0;attempt < 2; attempt += 1) {
77839
77943
  try {
77840
77944
  const fd = fs24.openSync(file, "wx", 384);
@@ -77959,7 +78063,7 @@ function heldLease(dir, id, options = {}) {
77959
78063
  for (const name of names) {
77960
78064
  if (!/^slot-\d+\.json$/u.test(name))
77961
78065
  continue;
77962
- const lease = parseLease(readText5(path49.join(dir, name)));
78066
+ const lease = parseLease(readText5(path52.join(dir, name)));
77963
78067
  if (lease?.id === id && !staleReason(lease, options.probe) && (options.ancestor ?? isAncestor)(lease.pid))
77964
78068
  return lease;
77965
78069
  }
@@ -78014,10 +78118,10 @@ async function runScoped(run, wrapper, sinks) {
78014
78118
  }
78015
78119
  function firstNotice(env, session, key, now = Date.now()) {
78016
78120
  const id = (session ?? `ppid-${process.ppid}`).replace(/[^A-Za-z0-9_.-]/gu, "_").slice(0, 120);
78017
- const dir = path49.join(stateDir2(env), "notices");
78121
+ const dir = path52.join(stateDir2(env), "notices");
78018
78122
  try {
78019
78123
  fs24.mkdirSync(dir, { recursive: true, mode: 448 });
78020
- fs24.writeFileSync(path49.join(dir, `${key}-${id}`), `${new Date(now).toISOString()}
78124
+ fs24.writeFileSync(path52.join(dir, `${key}-${id}`), `${new Date(now).toISOString()}
78021
78125
  `, {
78022
78126
  flag: "wx",
78023
78127
  mode: 384
@@ -78027,7 +78131,7 @@ function firstNotice(env, session, key, now = Date.now()) {
78027
78131
  }
78028
78132
  try {
78029
78133
  for (const name of fs24.readdirSync(dir)) {
78030
- const file = path49.join(dir, name);
78134
+ const file = path52.join(dir, name);
78031
78135
  if (now - fs24.statSync(file).mtimeMs > NOTICE_TTL_MS)
78032
78136
  fs24.rmSync(file, { force: true });
78033
78137
  }
@@ -78037,7 +78141,7 @@ function firstNotice(env, session, key, now = Date.now()) {
78037
78141
  function leaseSlots(dir, slots, probe = {}) {
78038
78142
  const states = [];
78039
78143
  for (let slot = 0;slot < slots; slot += 1) {
78040
- const lease = parseLease(readText5(path49.join(dir, `slot-${slot}.json`)));
78144
+ const lease = parseLease(readText5(path52.join(dir, `slot-${slot}.json`)));
78041
78145
  states.push({
78042
78146
  slot,
78043
78147
  holder: lease ? holderOf(lease) : null,
@@ -78047,15 +78151,15 @@ function leaseSlots(dir, slots, probe = {}) {
78047
78151
  return states;
78048
78152
  }
78049
78153
  var DEFAULT_HEAVY_CHECKS, DEFAULT_SLOTS = 1, DEFAULT_WAIT_S = 600, DEFAULT_MEMORY_SHARE = 0.4, DEFAULT_CPU_SHARE = 0.7, MAX_SLOTS = 64, UNREADABLE_TTL_MS = 30000, GUARD_TTL_MS = 30000, hostShape = () => ({
78050
- totalMemBytes: os19.totalmem(),
78051
- cpus: Math.max(1, os19.availableParallelism?.() ?? os19.cpus().length)
78052
- }), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path49.join(stateDir2(env), "leases"), readText5 = (file) => {
78154
+ totalMemBytes: os20.totalmem(),
78155
+ cpus: Math.max(1, os20.availableParallelism?.() ?? os20.cpus().length)
78156
+ }), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path52.join(stateDir2(env), "leases"), readText5 = (file) => {
78053
78157
  try {
78054
78158
  return fs24.readFileSync(file, "utf8");
78055
78159
  } catch {
78056
78160
  return null;
78057
78161
  }
78058
- }, bootId = () => readText5("/proc/sys/kernel/random/boot_id")?.trim() || null, bootTime = () => Math.round((Date.now() - os19.uptime() * 1000) / 1000) * 1000, BOOT_SLACK_MS = 120000, statFields = (pid) => {
78162
+ }, bootId = () => readText5("/proc/sys/kernel/random/boot_id")?.trim() || null, bootTime = () => Math.round((Date.now() - os20.uptime() * 1000) / 1000) * 1000, BOOT_SLACK_MS = 120000, statFields = (pid) => {
78059
78163
  const stat = readText5(`/proc/${pid}/stat`);
78060
78164
  return stat ? stat.slice(stat.lastIndexOf(")") + 2).split(" ") : null;
78061
78165
  }, processStart = (pid) => statFields(pid)?.[19] ?? null, pidAlive2 = (pid) => {
@@ -78104,8 +78208,8 @@ __export(exports_check, {
78104
78208
  run: () => run12,
78105
78209
  workspaceResources: () => workspaceResources
78106
78210
  });
78107
- import os20 from "node:os";
78108
- import path50 from "node:path";
78211
+ import os21 from "node:os";
78212
+ import path53 from "node:path";
78109
78213
  function parse6(argv) {
78110
78214
  const split = argv.indexOf("--");
78111
78215
  const head = split >= 0 ? argv.slice(0, split) : [...argv];
@@ -78307,7 +78411,7 @@ function hostToolWaitCapMs(env) {
78307
78411
  }
78308
78412
  async function workspaceResources(io) {
78309
78413
  const env = io.env;
78310
- const dir = env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path50.join(env.XDG_CONFIG_HOME || path50.join(env.HOME || os20.homedir(), ".config"), "workit");
78414
+ const dir = env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path53.join(env.XDG_CONFIG_HOME || path53.join(env.HOME || os21.homedir(), ".config"), "workit");
78311
78415
  try {
78312
78416
  await Promise.resolve().then(() => init_workspaces());
78313
78417
  const workspace = resolveWorkspaceFrom(io.cwd, dir, env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
@@ -78426,7 +78530,7 @@ async function run12(argv, io) {
78426
78530
  if (options.shell && command.length !== 1)
78427
78531
  return emit(io, fail2("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
78428
78532
  }
78429
- const relCwd = posix(path50.relative(top, runCwd));
78533
+ const relCwd = posix(path53.relative(top, runCwd));
78430
78534
  const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
78431
78535
  const shadowed = !configured && name !== null ? config.checks.find((item) => item.name === name) : undefined;
78432
78536
  if (shadowed)
@@ -78488,7 +78592,7 @@ async function run12(argv, io) {
78488
78592
  const modifiedWorktree = key.tree !== null && treeAfter !== key.tree;
78489
78593
  const store = storeRoot(io.cwd);
78490
78594
  const blob = store.ok ? storeLog(store.value.root, result.log) : null;
78491
- const logPath = store.ok && blob ? path50.join(store.value.root, blob.ref) : null;
78595
+ const logPath = store.ok && blob ? path53.join(store.value.root, blob.ref) : null;
78492
78596
  const tail = tailLines(result.log, TAIL_LINES);
78493
78597
  const passed = result.exitCode === 0 && !result.timedOut;
78494
78598
  const bounded = boundedArgv(command);
@@ -78603,7 +78707,7 @@ async function run12(argv, io) {
78603
78707
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
78604
78708
  return 0;
78605
78709
  }
78606
- var USAGE6 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path50.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, ANSI_COLOR, WARNING_LINE, NO_WARNINGS, HOST_TOOL_WAIT_CAP_MS = 1e5;
78710
+ var USAGE6 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path53.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, ANSI_COLOR, WARNING_LINE, NO_WARNINGS, HOST_TOOL_WAIT_CAP_MS = 1e5;
78607
78711
  var init_check = __esm(() => {
78608
78712
  init_check_config();
78609
78713
  init_checks3();
@@ -78632,7 +78736,7 @@ __export(exports_lock, {
78632
78736
  run: () => run13
78633
78737
  });
78634
78738
  import fs25 from "node:fs";
78635
- import path51 from "node:path";
78739
+ import path54 from "node:path";
78636
78740
  async function settingsFor(io) {
78637
78741
  const workspace = await workspaceResources(io);
78638
78742
  const config = loadCheckConfig(io.cwd);
@@ -78714,7 +78818,7 @@ function readPatterns(file) {
78714
78818
  return { patterns: [] };
78715
78819
  }
78716
78820
  function writePatterns(file, value) {
78717
- fs25.mkdirSync(path51.dirname(file), { recursive: true, mode: 448 });
78821
+ fs25.mkdirSync(path54.dirname(file), { recursive: true, mode: 448 });
78718
78822
  const temp = `${file}.${process.pid}.tmp`;
78719
78823
  fs25.writeFileSync(temp, `${JSON.stringify(value, null, 2)}
78720
78824
  `, { mode: 384 });
@@ -78800,9 +78904,9 @@ var exports_models = {};
78800
78904
  __export(exports_models, {
78801
78905
  run: () => run14
78802
78906
  });
78803
- import { existsSync as existsSync22, readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync9 } from "node:fs";
78804
- import os21 from "node:os";
78805
- import path52 from "node:path";
78907
+ import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync23, statSync as statSync9 } from "node:fs";
78908
+ import os22 from "node:os";
78909
+ import path55 from "node:path";
78806
78910
  async function run14(argv, io) {
78807
78911
  const [sub, ...args] = argv.filter((arg) => arg !== "--json");
78808
78912
  if (sub !== "show")
@@ -78863,7 +78967,7 @@ var USAGE8 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78863
78967
  const found = new Set;
78864
78968
  let text = "";
78865
78969
  try {
78866
- text = readFileSync22(file, "utf8");
78970
+ text = readFileSync23(file, "utf8");
78867
78971
  } catch {
78868
78972
  return found;
78869
78973
  }
@@ -78877,26 +78981,26 @@ var USAGE8 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78877
78981
  } catch {}
78878
78982
  return found;
78879
78983
  }, matches3 = (ran, configured) => ran.toLowerCase().includes(configured.toLowerCase()), usageReport = (cwd, env, days) => {
78880
- const projects = path52.join(env.CLAUDE_CONFIG_DIR || path52.join(env.HOME || os21.homedir(), ".claude"), "projects");
78984
+ const projects = path55.join(env.CLAUDE_CONFIG_DIR || path55.join(env.HOME || os22.homedir(), ".claude"), "projects");
78881
78985
  const since = Date.now() - days * 86400000;
78882
78986
  const types = {};
78883
78987
  let agents = 0;
78884
78988
  const dirs = (dir) => {
78885
78989
  try {
78886
- return readdirSync13(dir).map((name) => path52.join(dir, name));
78990
+ return readdirSync13(dir).map((name) => path55.join(dir, name));
78887
78991
  } catch {
78888
78992
  return [];
78889
78993
  }
78890
78994
  };
78891
78995
  for (const project of dirs(projects))
78892
78996
  for (const session of dirs(project))
78893
- for (const meta of dirs(path52.join(session, "subagents")).filter((f) => f.endsWith(".meta.json"))) {
78997
+ for (const meta of dirs(path55.join(session, "subagents")).filter((f) => f.endsWith(".meta.json"))) {
78894
78998
  try {
78895
78999
  if (statSync9(meta).mtimeMs < since)
78896
79000
  continue;
78897
- const agentType = JSON.parse(readFileSync22(meta, "utf8")).agentType;
79001
+ const agentType = JSON.parse(readFileSync23(meta, "utf8")).agentType;
78898
79002
  const transcript = meta.replace(/\.meta\.json$/, ".jsonl");
78899
- if (typeof agentType !== "string" || !existsSync22(transcript))
79003
+ if (typeof agentType !== "string" || !existsSync24(transcript))
78900
79004
  continue;
78901
79005
  const ran = modelsOf(transcript);
78902
79006
  if (!ran.size)
@@ -79035,8 +79139,8 @@ var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock)
79035
79139
 
79036
79140
  // packages/workit-core/src/git/ops.ts
79037
79141
  import { spawnSync as spawnSync20 } from "node:child_process";
79038
- import { existsSync as existsSync23, readFileSync as readFileSync23 } from "node:fs";
79039
- import path53 from "node:path";
79142
+ import { existsSync as existsSync25, readFileSync as readFileSync24 } from "node:fs";
79143
+ import path56 from "node:path";
79040
79144
  function operationInProgress(cwd) {
79041
79145
  const markers = [
79042
79146
  ["rebase-merge", "rebase"],
@@ -79049,18 +79153,18 @@ function operationInProgress(cwd) {
79049
79153
  if (!run.ok)
79050
79154
  return null;
79051
79155
  const found = run.stdout.split(`
79052
- `).map((line) => path53.resolve(cwd, line.trim()));
79156
+ `).map((line) => path56.resolve(cwd, line.trim()));
79053
79157
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
79054
79158
  for (const [index, [, operation]] of markers.entries()) {
79055
79159
  const marker = found[index];
79056
- if (!marker || !existsSync23(marker))
79160
+ if (!marker || !existsSync25(marker))
79057
79161
  continue;
79058
- if (index === 1 && existsSync23(path53.join(marker, "applying")))
79162
+ if (index === 1 && existsSync25(path56.join(marker, "applying")))
79059
79163
  return { operation: "am", commitAllowed: false, branch: null };
79060
- if (index === 0 && existsSync23(path53.join(marker, "amend")) && !conflicted()) {
79164
+ if (index === 0 && existsSync25(path56.join(marker, "amend")) && !conflicted()) {
79061
79165
  let headName = "";
79062
79166
  try {
79063
- headName = readFileSync23(path53.join(marker, "head-name"), "utf8").trim();
79167
+ headName = readFileSync24(path56.join(marker, "head-name"), "utf8").trim();
79064
79168
  } catch {}
79065
79169
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
79066
79170
  return { operation, commitAllowed: branch !== null, branch };
@@ -79388,7 +79492,7 @@ function gitCommit(cwd, input) {
79388
79492
  if (paths.some((value) => value.startsWith("-") || !value.trim()))
79389
79493
  return failure3("invalid_input", "paths must not be empty or start with -");
79390
79494
  const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
79391
- const addable = paths.filter((value) => existsSync23(path53.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
79495
+ const addable = paths.filter((value) => existsSync25(path56.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
79392
79496
  if (addable.length) {
79393
79497
  const added = git5(cwd, ["add", "-A", "--", ...addable]);
79394
79498
  if (!added.ok)
@@ -79927,12 +80031,12 @@ var init_ops = __esm(() => {
79927
80031
 
79928
80032
  // packages/workit-core/src/verdict-cover.ts
79929
80033
  import fs26 from "node:fs";
79930
- import path54 from "node:path";
80034
+ import path57 from "node:path";
79931
80035
  function chainsAbove(cwd, branch) {
79932
80036
  const root = storeRoot(cwd);
79933
80037
  if (!root.ok)
79934
80038
  return [];
79935
- const dir = path54.join(root.value.root, "stacks");
80039
+ const dir = path57.join(root.value.root, "stacks");
79936
80040
  let names = [];
79937
80041
  try {
79938
80042
  names = fs26.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -79943,7 +80047,7 @@ function chainsAbove(cwd, branch) {
79943
80047
  for (const name of names.toSorted()) {
79944
80048
  let parsed;
79945
80049
  try {
79946
- parsed = JSON.parse(fs26.readFileSync(path54.join(dir, name), "utf8"));
80050
+ parsed = JSON.parse(fs26.readFileSync(path57.join(dir, name), "utf8"));
79947
80051
  } catch {
79948
80052
  continue;
79949
80053
  }
@@ -80038,10 +80142,10 @@ var init_pr_lane = __esm(() => {
80038
80142
 
80039
80143
  // packages/workit-core/src/forge/reruns.ts
80040
80144
  import fs27 from "node:fs";
80041
- import path55 from "node:path";
80145
+ import path58 from "node:path";
80042
80146
  function rerunLogPath(cwd) {
80043
80147
  const common = gitCommonDir(cwd);
80044
- return common ? path55.join(common, "workit", "ci-reruns.jsonl") : null;
80148
+ return common ? path58.join(common, "workit", "ci-reruns.jsonl") : null;
80045
80149
  }
80046
80150
  function rerunCounts(cwd, key) {
80047
80151
  const counts = new Map;
@@ -80076,7 +80180,7 @@ function recordReruns(cwd, rows) {
80076
80180
  if (!file)
80077
80181
  return false;
80078
80182
  try {
80079
- fs27.mkdirSync(path55.dirname(file), { recursive: true });
80183
+ fs27.mkdirSync(path58.dirname(file), { recursive: true });
80080
80184
  fs27.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
80081
80185
  `).join(""));
80082
80186
  return true;
@@ -80089,7 +80193,7 @@ function withRerunLock(cwd, fn) {
80089
80193
  if (!file)
80090
80194
  return fn();
80091
80195
  const lock = `${file.slice(0, -".jsonl".length)}.lock`;
80092
- fs27.mkdirSync(path55.dirname(lock), { recursive: true });
80196
+ fs27.mkdirSync(path58.dirname(lock), { recursive: true });
80093
80197
  const until = Date.now() + LOCK_WAIT_MS2;
80094
80198
  for (;; ) {
80095
80199
  try {
@@ -80506,7 +80610,7 @@ function recordAccepted(cwd, resolved, status, accepted, options) {
80506
80610
  return recordReruns(cwd, rows);
80507
80611
  }
80508
80612
  var MAX_LOG_TAILS = 5, GITLAB_MERGEABLE, GITHUB_MERGEABLE, NO_CHECKS_GRACE_MS = 90000;
80509
- var init_report = __esm(() => {
80613
+ var init_report2 = __esm(() => {
80510
80614
  init_rev();
80511
80615
  init_ledger();
80512
80616
  init_reruns();
@@ -81055,7 +81159,7 @@ var init_pr_ops = __esm(() => {
81055
81159
  init_ledger();
81056
81160
  init_verdict_cover();
81057
81161
  init_pr_lane();
81058
- init_report();
81162
+ init_report2();
81059
81163
  VERIFY_DELAYS_MS = [1000, 2000, 4000];
81060
81164
  AFTER_REVIEW = {
81061
81165
  commit: "stop here; review comes when the user asks",
@@ -81068,9 +81172,9 @@ var init_pr_ops = __esm(() => {
81068
81172
 
81069
81173
  // packages/workit-core/src/stack.ts
81070
81174
  import { spawnSync as spawnSync22 } from "node:child_process";
81071
- import { createHash as createHash17, randomBytes as randomBytes4 } from "node:crypto";
81175
+ import { createHash as createHash18, randomBytes as randomBytes4 } from "node:crypto";
81072
81176
  import fs28 from "node:fs";
81073
- import path56 from "node:path";
81177
+ import path59 from "node:path";
81074
81178
  function worktreeOf(cwd, branch) {
81075
81179
  const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
81076
81180
  if (!list.ok)
@@ -81119,7 +81223,7 @@ function gitRestack(cwd, input) {
81119
81223
  error: "no store root"
81120
81224
  };
81121
81225
  fs28.mkdirSync(scratch, { recursive: true });
81122
- dir = path56.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
81226
+ dir = path59.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
81123
81227
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
81124
81228
  if (!added.ok)
81125
81229
  return {
@@ -81175,7 +81279,7 @@ function pruneScratch(cwd) {
81175
81279
  if (!scratch || !fs28.existsSync(scratch))
81176
81280
  return;
81177
81281
  for (const name of fs28.readdirSync(scratch)) {
81178
- const dir = path56.join(scratch, name);
81282
+ const dir = path59.join(scratch, name);
81179
81283
  if (!rebaseInProgress(dir) && !trackedDirt(dir))
81180
81284
  gitRun3(cwd, ["worktree", "remove", "--force", dir]);
81181
81285
  }
@@ -81185,7 +81289,7 @@ function stacksDir(cwd) {
81185
81289
  const root = storeRoot(cwd);
81186
81290
  if (!root.ok)
81187
81291
  return stackFail(root.code, root.error, root.unblock);
81188
- return { ok: true, data: path56.join(root.value.root, "stacks") };
81292
+ return { ok: true, data: path59.join(root.value.root, "stacks") };
81189
81293
  }
81190
81294
  function parseStack(raw) {
81191
81295
  if (!isRecord12(raw) || !Array.isArray(raw.branches))
@@ -81237,7 +81341,7 @@ function readStack(cwd, name) {
81237
81341
  const dir = stacksDir(cwd);
81238
81342
  if (!dir.ok)
81239
81343
  return dir;
81240
- const file = path56.join(dir.data, fileFor(name));
81344
+ const file = path59.join(dir.data, fileFor(name));
81241
81345
  let text;
81242
81346
  try {
81243
81347
  text = fs28.readFileSync(file, "utf8");
@@ -81258,7 +81362,7 @@ function writeStack(cwd, stack, now = new Date) {
81258
81362
  const dir = stacksDir(cwd);
81259
81363
  if (!dir.ok)
81260
81364
  return dir;
81261
- const file = path56.join(dir.data, fileFor(stack.name));
81365
+ const file = path59.join(dir.data, fileFor(stack.name));
81262
81366
  const temp = `${file}.${process.pid}.tmp`;
81263
81367
  try {
81264
81368
  fs28.mkdirSync(dir.data, { recursive: true });
@@ -81286,7 +81390,7 @@ function listStacks(cwd) {
81286
81390
  for (const name of names) {
81287
81391
  let parsed = null;
81288
81392
  try {
81289
- parsed = parseStack(JSON.parse(fs28.readFileSync(path56.join(dir.data, name), "utf8")));
81393
+ parsed = parseStack(JSON.parse(fs28.readFileSync(path59.join(dir.data, name), "utf8")));
81290
81394
  } catch {
81291
81395
  parsed = null;
81292
81396
  }
@@ -81348,7 +81452,7 @@ function takeLock(lock) {
81348
81452
  host: localLockHost(),
81349
81453
  nonce: randomBytes4(8).toString("hex")
81350
81454
  };
81351
- fs28.writeFileSync(path56.join(lock, OWNER), JSON.stringify(owner));
81455
+ fs28.writeFileSync(path59.join(lock, OWNER), JSON.stringify(owner));
81352
81456
  return owner;
81353
81457
  }
81354
81458
  function reapLock(lock, observed) {
@@ -81385,7 +81489,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
81385
81489
  return dir;
81386
81490
  if (!validName(name))
81387
81491
  return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
81388
- const lock = path56.join(dir.data, `${fileFor(name)}.lock`);
81492
+ const lock = path59.join(dir.data, `${fileFor(name)}.lock`);
81389
81493
  try {
81390
81494
  fs28.mkdirSync(dir.data, { recursive: true });
81391
81495
  } catch (error) {
@@ -81415,7 +81519,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
81415
81519
  const touch = () => {
81416
81520
  try {
81417
81521
  const now = new Date;
81418
- fs28.utimesSync(path56.join(lock, OWNER), now, now);
81522
+ fs28.utimesSync(path59.join(lock, OWNER), now, now);
81419
81523
  } catch {}
81420
81524
  };
81421
81525
  const beat = setInterval(touch, HEARTBEAT_MS);
@@ -82316,10 +82420,10 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
82316
82420
  return !status.ok || status.stdout.trim() !== "";
82317
82421
  }, scratchDir = (cwd) => {
82318
82422
  const dir = stacksDir(cwd);
82319
- return dir.ok ? path56.join(dir.data, "worktrees") : null;
82423
+ return dir.ok ? path59.join(dir.data, "worktrees") : null;
82320
82424
  }, firstLine = (run) => (`${run.stderr}
82321
82425
  ${run.stdout}`.split(`
82322
- `).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash17("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
82426
+ `).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash18("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
82323
82427
  if (name.length === 0 || name.length > 200)
82324
82428
  return false;
82325
82429
  for (let index = 0;index < name.length; index += 1) {
@@ -82331,12 +82435,12 @@ ${run.stdout}`.split(`
82331
82435
  }, isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str3 = (value) => typeof value === "string" && value ? value : null, LOCK_WAIT_MS3 = 2000, HEARTBEAT_MS = 20000, HEARTBEAT_STALE_MS, OWNERLESS_STALE_MS = 30000, OWNER = "owner", readOwner = (lock) => {
82332
82436
  let raw = null;
82333
82437
  try {
82334
- raw = fs28.readFileSync(path56.join(lock, OWNER), "utf8");
82438
+ raw = fs28.readFileSync(path59.join(lock, OWNER), "utf8");
82335
82439
  } catch {
82336
82440
  raw = null;
82337
82441
  }
82338
82442
  let age = null;
82339
- for (const target of [path56.join(lock, OWNER), lock])
82443
+ for (const target of [path59.join(lock, OWNER), lock])
82340
82444
  try {
82341
82445
  age = Date.now() - fs28.statSync(target).mtimeMs;
82342
82446
  break;
@@ -82360,7 +82464,7 @@ var init_stack = __esm(() => {
82360
82464
  init_autonomy();
82361
82465
  init_store_lock();
82362
82466
  init_pr_ops();
82363
- init_report();
82467
+ init_report2();
82364
82468
  init_ops();
82365
82469
  init_rev();
82366
82470
  init_ledger();
@@ -82377,9 +82481,9 @@ var init_stack = __esm(() => {
82377
82481
 
82378
82482
  // packages/workit-core/src/fanout.ts
82379
82483
  import { spawnSync as spawnSync23 } from "node:child_process";
82380
- import { createHash as createHash18 } from "node:crypto";
82484
+ import { createHash as createHash19 } from "node:crypto";
82381
82485
  import fs29 from "node:fs";
82382
- import path57 from "node:path";
82486
+ import path60 from "node:path";
82383
82487
  function expandBraces2(pattern, limit = 64) {
82384
82488
  for (let index = 0;index < pattern.length; index += 1) {
82385
82489
  if (pattern[index] === "\\") {
@@ -82625,7 +82729,7 @@ function parsePlan(raw, defaults, name) {
82625
82729
  id,
82626
82730
  branch,
82627
82731
  base: optional("base") ?? "",
82628
- worktree: path57.resolve(defaults.root, optional("worktree") ?? path57.join(defaults.worktreeRoot, id)),
82732
+ worktree: path60.resolve(defaults.root, optional("worktree") ?? path60.join(defaults.worktreeRoot, id)),
82629
82733
  tier,
82630
82734
  reason,
82631
82735
  dependsOn: [...new Set(dependsOn ?? [])],
@@ -82754,12 +82858,12 @@ function regeneratedFiles(root, tracked) {
82754
82858
  const found = new Map;
82755
82859
  let scripts;
82756
82860
  try {
82757
- const manifest = JSON.parse(fs29.readFileSync(path57.join(root, "package.json"), "utf8"));
82861
+ const manifest = JSON.parse(fs29.readFileSync(path60.join(root, "package.json"), "utf8"));
82758
82862
  scripts = manifest.scripts ?? {};
82759
82863
  } catch {
82760
82864
  return found;
82761
82865
  }
82762
- const runner = fs29.existsSync(path57.join(root, "bun.lock")) ? "bun run" : fs29.existsSync(path57.join(root, "pnpm-lock.yaml")) ? "pnpm run" : fs29.existsSync(path57.join(root, "yarn.lock")) ? "yarn run" : "npm run";
82866
+ const runner = fs29.existsSync(path60.join(root, "bun.lock")) ? "bun run" : fs29.existsSync(path60.join(root, "pnpm-lock.yaml")) ? "pnpm run" : fs29.existsSync(path60.join(root, "yarn.lock")) ? "yarn run" : "npm run";
82763
82867
  const outputs = tracked.filter((file) => !CODE.test(file));
82764
82868
  for (const [name, command] of Object.entries(scripts)) {
82765
82869
  if (!name.startsWith("write:") || typeof command !== "string")
@@ -82767,7 +82871,7 @@ function regeneratedFiles(root, tracked) {
82767
82871
  for (const file of command.match(/[\w./-]+\.[cm]?[jt]s\b/gu) ?? []) {
82768
82872
  let source;
82769
82873
  try {
82770
- source = fs29.readFileSync(path57.join(root, file), "utf8");
82874
+ source = fs29.readFileSync(path60.join(root, file), "utf8");
82771
82875
  } catch {
82772
82876
  continue;
82773
82877
  }
@@ -82889,11 +82993,11 @@ function defaultTrunk(cwd) {
82889
82993
  }
82890
82994
  function mainCheckout(cwd) {
82891
82995
  const common = gitCommonDir(cwd);
82892
- return common && path57.basename(common) === ".git" ? path57.dirname(common) : path57.resolve(cwd);
82996
+ return common && path60.basename(common) === ".git" ? path60.dirname(common) : path60.resolve(cwd);
82893
82997
  }
82894
82998
  function worktreeRoot(cwd) {
82895
82999
  const main = mainCheckout(cwd);
82896
- return path57.join(path57.dirname(main), `${path57.basename(main)}-wt`);
83000
+ return path60.join(path60.dirname(main), `${path60.basename(main)}-wt`);
82897
83001
  }
82898
83002
  function sliceHash(slice) {
82899
83003
  const {
@@ -82907,13 +83011,13 @@ function sliceHash(slice) {
82907
83011
  issues: _issues,
82908
83012
  ...definition
82909
83013
  } = slice;
82910
- return createHash18("sha256").update(JSON.stringify(definition)).digest("hex").slice(0, 16);
83014
+ return createHash19("sha256").update(JSON.stringify(definition)).digest("hex").slice(0, 16);
82911
83015
  }
82912
83016
  function fanoutsDir(cwd) {
82913
83017
  const root = storeRoot(cwd);
82914
83018
  if (!root.ok)
82915
83019
  return fanoutFail(root.code, root.error, root.unblock);
82916
- return { ok: true, data: path57.join(root.value.root, "fanouts") };
83020
+ return { ok: true, data: path60.join(root.value.root, "fanouts") };
82917
83021
  }
82918
83022
  function parseStored(raw) {
82919
83023
  if (!isRecord13(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -82981,7 +83085,7 @@ function readFanout(cwd, name) {
82981
83085
  const dir = fanoutsDir(cwd);
82982
83086
  if (!dir.ok)
82983
83087
  return dir;
82984
- const file = path57.join(dir.data, stackFileName(name));
83088
+ const file = path60.join(dir.data, stackFileName(name));
82985
83089
  let parsed = null;
82986
83090
  try {
82987
83091
  parsed = parseStored(JSON.parse(fs29.readFileSync(file, "utf8")));
@@ -83006,7 +83110,7 @@ function listFanouts(cwd) {
83006
83110
  for (const entry of names) {
83007
83111
  let parsed = null;
83008
83112
  try {
83009
- parsed = parseStored(JSON.parse(fs29.readFileSync(path57.join(dir.data, entry), "utf8")));
83113
+ parsed = parseStored(JSON.parse(fs29.readFileSync(path60.join(dir.data, entry), "utf8")));
83010
83114
  } catch {
83011
83115
  parsed = null;
83012
83116
  }
@@ -83045,7 +83149,7 @@ function writeFanout(cwd, plan) {
83045
83149
  const dir = fanoutsDir(cwd);
83046
83150
  if (!dir.ok)
83047
83151
  return dir;
83048
- const file = path57.join(dir.data, stackFileName(plan.name));
83152
+ const file = path60.join(dir.data, stackFileName(plan.name));
83049
83153
  const temp = `${file}.${process.pid}.tmp`;
83050
83154
  try {
83051
83155
  fs29.mkdirSync(dir.data, { recursive: true });
@@ -83256,13 +83360,13 @@ var init_fanout = __esm(() => {
83256
83360
  // packages/workit-core/src/forge/exec.ts
83257
83361
  import { spawnSync as spawnSync24 } from "node:child_process";
83258
83362
  import fs30 from "node:fs";
83259
- import path58 from "node:path";
83363
+ import path61 from "node:path";
83260
83364
  function findExecutable(bin, env) {
83261
- const dirs = (env.PATH ?? env.Path ?? "").split(path58.delimiter).filter(Boolean);
83365
+ const dirs = (env.PATH ?? env.Path ?? "").split(path61.delimiter).filter(Boolean);
83262
83366
  const exts = process.platform === "win32" ? [".exe"] : [""];
83263
83367
  for (const dir of dirs)
83264
83368
  for (const ext of exts) {
83265
- const candidate = path58.join(dir, `${bin}${ext}`);
83369
+ const candidate = path61.join(dir, `${bin}${ext}`);
83266
83370
  try {
83267
83371
  const stat = fs30.statSync(candidate);
83268
83372
  if (stat.isFile())
@@ -84805,7 +84909,7 @@ var init_pr_edit = __esm(() => {
84805
84909
  init_rev();
84806
84910
  init_stack();
84807
84911
  init_pr_ops();
84808
- init_report();
84912
+ init_report2();
84809
84913
  });
84810
84914
 
84811
84915
  // packages/workit-core/src/core/live-session.ts
@@ -84825,7 +84929,7 @@ var init_live_session = __esm(() => {
84825
84929
  });
84826
84930
 
84827
84931
  // packages/workit-core/src/hooks/write-gate.ts
84828
- import path59 from "node:path";
84932
+ import path62 from "node:path";
84829
84933
  var CASE_INSENSITIVE, PREFIXES2, SCRIPT_SHELLS, cache3, MUTATING_GIT, DRY_N, MUTATING_WORKIT_GIT, CD2, PUSHD2, POPD2, UNKNOWN_DIR;
84830
84934
  var init_write_gate = __esm(() => {
84831
84935
  init_task_store();
@@ -84865,7 +84969,7 @@ var init_write_gate = __esm(() => {
84865
84969
  CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
84866
84970
  PUSHD2 = new Set(["pushd", "push-location"]);
84867
84971
  POPD2 = new Set(["popd", "pop-location"]);
84868
- UNKNOWN_DIR = path59.resolve(path59.parse(process.cwd()).root, ".workit-unknown-dir");
84972
+ UNKNOWN_DIR = path62.resolve(path62.parse(process.cwd()).root, ".workit-unknown-dir");
84869
84973
  });
84870
84974
 
84871
84975
  // packages/workit-core/src/hooks/worktree-confine.ts
@@ -84890,20 +84994,20 @@ var init_worktree_confine = __esm(() => {
84890
84994
 
84891
84995
  // packages/workit-core/src/worktree-deps.ts
84892
84996
  import fs31 from "node:fs";
84893
- import path60 from "node:path";
84997
+ import path63 from "node:path";
84894
84998
  function moduleDirs(main) {
84895
84999
  const out = [];
84896
85000
  const walk = (relative, depth) => {
84897
85001
  let entries;
84898
85002
  try {
84899
- entries = fs31.readdirSync(path60.join(main, relative), { withFileTypes: true });
85003
+ entries = fs31.readdirSync(path63.join(main, relative), { withFileTypes: true });
84900
85004
  } catch {
84901
85005
  return;
84902
85006
  }
84903
85007
  for (const entry of entries) {
84904
85008
  if (!entry.isDirectory())
84905
85009
  continue;
84906
- const child = path60.join(relative, entry.name);
85010
+ const child = path63.join(relative, entry.name);
84907
85011
  if (entry.name === MODULES)
84908
85012
  out.push(child);
84909
85013
  else if (depth < MAX_DEPTH2 && !entry.name.startsWith("."))
@@ -84917,8 +85021,8 @@ function linkEntry(main, worktree, source, target) {
84917
85021
  const stat = fs31.lstatSync(source);
84918
85022
  if (stat.isSymbolicLink()) {
84919
85023
  const link = fs31.readlinkSync(source);
84920
- const outside = path60.isAbsolute(link) && within(link, main) && !path60.relative(main, link).split(path60.sep).includes(MODULES);
84921
- const to = outside ? path60.join(worktree, path60.relative(main, link)) : link;
85024
+ const outside = path63.isAbsolute(link) && within(link, main) && !path63.relative(main, link).split(path63.sep).includes(MODULES);
85025
+ const to = outside ? path63.join(worktree, path63.relative(main, link)) : link;
84922
85026
  fs31.symlinkSync(to, target, process.platform === "win32" ? "junction" : undefined);
84923
85027
  return;
84924
85028
  }
@@ -84928,8 +85032,8 @@ function mirror(main, worktree, source, target, top) {
84928
85032
  fs31.mkdirSync(target);
84929
85033
  let count = 0;
84930
85034
  for (const entry of fs31.readdirSync(source, { withFileTypes: true })) {
84931
- const from = path60.join(source, entry.name);
84932
- const to = path60.join(target, entry.name);
85035
+ const from = path63.join(source, entry.name);
85036
+ const to = path63.join(target, entry.name);
84933
85037
  if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin"))
84934
85038
  count += mirror(main, worktree, from, to, false);
84935
85039
  else {
@@ -84945,16 +85049,16 @@ function linkWorktreeDependencies(main, worktree) {
84945
85049
  const dirs = [];
84946
85050
  let entries = 0;
84947
85051
  for (const relative of moduleDirs(main)) {
84948
- const target = path60.join(worktree, relative);
84949
- if (!fs31.existsSync(path60.dirname(target)) || fs31.existsSync(target))
85052
+ const target = path63.join(worktree, relative);
85053
+ if (!fs31.existsSync(path63.dirname(target)) || fs31.existsSync(target))
84950
85054
  continue;
84951
- if (!gitRun4(worktree, ["check-ignore", "-q", `${relative.split(path60.sep).join("/")}/`]).ok) {
84952
- notes.push(`${relative.split(path60.sep).join("/")} is not ignored by git, so it was not linked from ${main}; install dependencies in the worktree yourself`);
85055
+ if (!gitRun4(worktree, ["check-ignore", "-q", `${relative.split(path63.sep).join("/")}/`]).ok) {
85056
+ notes.push(`${relative.split(path63.sep).join("/")} is not ignored by git, so it was not linked from ${main}; install dependencies in the worktree yourself`);
84953
85057
  continue;
84954
85058
  }
84955
85059
  try {
84956
- entries += mirror(main, worktree, path60.join(main, relative), target, true);
84957
- dirs.push(relative.split(path60.sep).join("/"));
85060
+ entries += mirror(main, worktree, path63.join(main, relative), target, true);
85061
+ dirs.push(relative.split(path63.sep).join("/"));
84958
85062
  } catch (error) {
84959
85063
  fs31.rmSync(target, { recursive: true, force: true });
84960
85064
  notes.push(`could not link ${relative} from ${main} (${error.message}); install dependencies in the worktree yourself`);
@@ -84967,8 +85071,8 @@ function linkWorktreeDependencies(main, worktree) {
84967
85071
  return { linked: { from: main, dirs, entries, ms }, notes };
84968
85072
  }
84969
85073
  var MODULES = "node_modules", MAX_DEPTH2 = 2, within = (child, parent) => {
84970
- const relative = path60.relative(parent, child);
84971
- return relative === "" || !relative.startsWith("..") && !path60.isAbsolute(relative);
85074
+ const relative = path63.relative(parent, child);
85075
+ return relative === "" || !relative.startsWith("..") && !path63.isAbsolute(relative);
84972
85076
  };
84973
85077
  var init_worktree_deps = __esm(() => {
84974
85078
  init_fanout();
@@ -84977,7 +85081,7 @@ var init_worktree_deps = __esm(() => {
84977
85081
  // packages/workit-core/src/fanout-worktree.ts
84978
85082
  import { randomBytes as randomBytes5 } from "node:crypto";
84979
85083
  import fs32 from "node:fs";
84980
- import path61 from "node:path";
85084
+ import path64 from "node:path";
84981
85085
  function rowSample(lines) {
84982
85086
  const out = [];
84983
85087
  let bytes = 0;
@@ -85037,7 +85141,7 @@ function adminDir(worktree) {
85037
85141
  const file = dir.stdout.trim();
85038
85142
  let token = null;
85039
85143
  try {
85040
- token = fs32.readFileSync(path61.join(file, FANOUT_TOKEN_FILE), "utf8").trim() || null;
85144
+ token = fs32.readFileSync(path64.join(file, FANOUT_TOKEN_FILE), "utf8").trim() || null;
85041
85145
  } catch {
85042
85146
  token = null;
85043
85147
  }
@@ -85045,7 +85149,7 @@ function adminDir(worktree) {
85045
85149
  const stat = worktreeDeps.stat(file);
85046
85150
  return {
85047
85151
  path: file,
85048
- id: path61.basename(file),
85152
+ id: path64.basename(file),
85049
85153
  ino: stat.ino.toString(),
85050
85154
  token,
85051
85155
  birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
@@ -85061,7 +85165,7 @@ function markAdminDir(worktree) {
85061
85165
  return null;
85062
85166
  const token = randomBytes5(16).toString("hex");
85063
85167
  try {
85064
- fs32.writeFileSync(path61.join(admin.path, FANOUT_TOKEN_FILE), `${token}
85168
+ fs32.writeFileSync(path64.join(admin.path, FANOUT_TOKEN_FILE), `${token}
85065
85169
  `);
85066
85170
  } catch {
85067
85171
  return null;
@@ -85076,7 +85180,7 @@ function excludeScratch(cwd) {
85076
85180
  const common = gitCommonDir(cwd);
85077
85181
  if (!common)
85078
85182
  return;
85079
- const file = path61.join(common, "info", "exclude");
85183
+ const file = path64.join(common, "info", "exclude");
85080
85184
  let text = "";
85081
85185
  try {
85082
85186
  text = fs32.readFileSync(file, "utf8");
@@ -85085,7 +85189,7 @@ function excludeScratch(cwd) {
85085
85189
  }
85086
85190
  if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
85087
85191
  return;
85088
- fs32.mkdirSync(path61.dirname(file), { recursive: true });
85192
+ fs32.mkdirSync(path64.dirname(file), { recursive: true });
85089
85193
  fs32.appendFileSync(file, `${text && !text.endsWith(`
85090
85194
  `) ? `
85091
85195
  ` : ""}# workit fanout: per-worker scratch dirs
@@ -85094,13 +85198,13 @@ ${EXCLUDE_LINE}
85094
85198
  }
85095
85199
  function worktreeDependencies(main, worktree) {
85096
85200
  for (const [name, install] of LOCKFILES2) {
85097
- const ours = readBytes(path61.join(main, name));
85098
- const theirs = readBytes(path61.join(worktree, name));
85201
+ const ours = readBytes(path64.join(main, name));
85202
+ const theirs = readBytes(path64.join(worktree, name));
85099
85203
  if (ours === null && theirs === null)
85100
85204
  continue;
85101
85205
  if (ours !== null && theirs !== null && ours.equals(theirs))
85102
85206
  continue;
85103
- if (fs32.existsSync(path61.join(worktree, "node_modules")))
85207
+ if (fs32.existsSync(path64.join(worktree, "node_modules")))
85104
85208
  break;
85105
85209
  const command = install(worktree);
85106
85210
  return {
@@ -85114,7 +85218,7 @@ function worktreeDependencies(main, worktree) {
85114
85218
  return { ...linkWorktreeDependencies(main, worktree), install: null };
85115
85219
  }
85116
85220
  function ensureScratch(worktree) {
85117
- const scratch = path61.join(worktree, SCRATCH_DIR);
85221
+ const scratch = path64.join(worktree, SCRATCH_DIR);
85118
85222
  fs32.mkdirSync(scratch, { recursive: true });
85119
85223
  return scratch;
85120
85224
  }
@@ -85353,7 +85457,7 @@ function unpushedCount(cwd, branch) {
85353
85457
  return count.ok ? Number(count.stdout.trim()) : null;
85354
85458
  }
85355
85459
  function removeScratch(worktree) {
85356
- const scratch = path61.join(worktree, SCRATCH_DIR);
85460
+ const scratch = path64.join(worktree, SCRATCH_DIR);
85357
85461
  let stat;
85358
85462
  try {
85359
85463
  stat = fs32.lstatSync(scratch);
@@ -85369,16 +85473,16 @@ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000,
85369
85473
  try {
85370
85474
  return fs32.realpathSync.native(target);
85371
85475
  } catch {
85372
- return path61.resolve(target);
85476
+ return path64.resolve(target);
85373
85477
  }
85374
85478
  }, samePath = (a, b) => {
85375
85479
  const left = canonical2(a);
85376
85480
  const right = canonical2(b);
85377
85481
  return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
85378
85482
  }, inside3 = (child, parent) => {
85379
- const relative = path61.relative(canonical2(parent), canonical2(child));
85380
- return relative === "" || !relative.startsWith("..") && !path61.isAbsolute(relative);
85381
- }, slicePath = (cwd, slice) => path61.isAbsolute(slice.worktree) ? slice.worktree : path61.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeDeps, LOCKFILES2, readBytes = (file) => {
85483
+ const relative = path64.relative(canonical2(parent), canonical2(child));
85484
+ return relative === "" || !relative.startsWith("..") && !path64.isAbsolute(relative);
85485
+ }, slicePath = (cwd, slice) => path64.isAbsolute(slice.worktree) ? slice.worktree : path64.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeDeps, LOCKFILES2, readBytes = (file) => {
85382
85486
  try {
85383
85487
  return fs32.readFileSync(file);
85384
85488
  } catch {
@@ -85409,7 +85513,7 @@ var init_fanout_worktree = __esm(() => {
85409
85513
 
85410
85514
  // packages/workit-core/src/forge/merge-cleanup.ts
85411
85515
  import fs33 from "node:fs";
85412
- import path62 from "node:path";
85516
+ import path65 from "node:path";
85413
85517
  function cleanupMergedBranch(cwd, branch, mergedHead) {
85414
85518
  const out = { branchDeleted: false, worktreeRemoved: null, notes: [] };
85415
85519
  if (!branch || branch.startsWith("-"))
@@ -85469,13 +85573,13 @@ var SLOW2, isFanoutWorktree = (worktree) => {
85469
85573
  if (!dir.ok)
85470
85574
  return false;
85471
85575
  try {
85472
- return fs33.readFileSync(path62.join(dir.stdout.trim(), FANOUT_TOKEN_FILE), "utf8").trim() !== "";
85576
+ return fs33.readFileSync(path65.join(dir.stdout.trim(), FANOUT_TOKEN_FILE), "utf8").trim() !== "";
85473
85577
  } catch {
85474
85578
  return false;
85475
85579
  }
85476
85580
  }, inside4 = (child, parent) => {
85477
- const relative = path62.relative(fs33.realpathSync.native(parent), fs33.realpathSync.native(child));
85478
- return relative === "" || !relative.startsWith("..") && !path62.isAbsolute(relative);
85581
+ const relative = path65.relative(fs33.realpathSync.native(parent), fs33.realpathSync.native(child));
85582
+ return relative === "" || !relative.startsWith("..") && !path65.isAbsolute(relative);
85479
85583
  };
85480
85584
  var init_merge_cleanup = __esm(() => {
85481
85585
  init_fanout();
@@ -85756,7 +85860,7 @@ var init_duration = __esm(() => {
85756
85860
  });
85757
85861
 
85758
85862
  // packages/workit-cli/src/verbs/forge-common.ts
85759
- import { readFileSync as readFileSync24 } from "node:fs";
85863
+ import { readFileSync as readFileSync25 } from "node:fs";
85760
85864
  function parseFlags(argv, spec) {
85761
85865
  const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
85762
85866
  for (let index = 0;index < argv.length; index += 1) {
@@ -85911,7 +86015,7 @@ var init_forge_common = __esm(() => {
85911
86015
  forgeDeps = {
85912
86016
  runner: undefined,
85913
86017
  npm: undefined,
85914
- readStdin: () => readFileSync24(0, "utf8"),
86018
+ readStdin: () => readFileSync25(0, "utf8"),
85915
86019
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
85916
86020
  now: () => Date.now()
85917
86021
  };
@@ -85922,12 +86026,12 @@ var exports_pr = {};
85922
86026
  __export(exports_pr, {
85923
86027
  run: () => run15
85924
86028
  });
85925
- import { readFileSync as readFileSync25 } from "node:fs";
85926
- import path63 from "node:path";
86029
+ import { readFileSync as readFileSync26 } from "node:fs";
86030
+ import path66 from "node:path";
85927
86031
  function readBodyFile(io, file) {
85928
86032
  try {
85929
86033
  return {
85930
- body: file === "-" ? forgeDeps.readStdin() : readFileSync25(path63.resolve(io.cwd, file), "utf8")
86034
+ body: file === "-" ? forgeDeps.readStdin() : readFileSync26(path66.resolve(io.cwd, file), "utf8")
85931
86035
  };
85932
86036
  } catch {
85933
86037
  return `cannot read --body-file ${file}`;
@@ -86256,7 +86360,7 @@ async function merge3(argv, io) {
86256
86360
  const cleanup = result.data.deletedBranch === true ? cleanupMergedBranch(io.cwd, result.data.branch, result.data.head) : null;
86257
86361
  return emit(io, ok2({ ...result.data, localCleanup: cleanup }), (data) => [
86258
86362
  `merged ${connected.data.forge.kind === "github" ? "PR #" : "MR !"}${data.number} (${data.method}) at head ${data.head.slice(0, 12)}${data.mergeSha ? ` -> ${data.mergeSha.slice(0, 12)}` : ""}`,
86259
- data.unverified ? `UNVERIFIED: merged without a verdict (--unverified: ${data.unverified.reason}); ledger ${data.unverified.recorded}` : `verdict ${data.verdict.verdictId ?? "?"} accepted for that head${data.verdict.cover ? ` (stack verdict on ${data.verdict.cover.branch} @ ${data.verdict.cover.head.slice(0, 12)})` : data.verdict.carry ? ` (${carryLabel(data.verdict.carry)})` : ""}`,
86363
+ data.unverified ? `UNVERIFIED: merged without a verdict (--unverified: ${data.unverified.reason}); ledger ${data.unverified.recorded}` : mergedOnLine(data.verdict),
86260
86364
  ...data.baseCi.note ? [data.baseCi.note] : [],
86261
86365
  ...data.mergeBack.length ? [
86262
86366
  `merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
@@ -86424,13 +86528,21 @@ async function run15(argv, io) {
86424
86528
  return reply(rest, io);
86425
86529
  return usage4(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE9);
86426
86530
  }
86427
- var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--issue <#n|ID|url>]… [--refs] [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE9 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, NEED_TEXT, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : landed.lane === "none" && !landed.verdictId ? `merged at ${(landed.head ?? "?").slice(0, 12)} with no verdict (lane none)` : landed.review === "self-reviewed" ? `merged self-reviewed (lane self) with the author's verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, acceptedHow = (verdict) => verdict.cover ? `stack verdict ${verdict.cover.verdictId} on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)}` : verdict.carry ? carryLabel(verdict.carry) : verdict.basis, METHODS2;
86531
+ var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--issue <#n|ID|url>]… [--refs] [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE9 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, NEED_TEXT, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : landed.lane === "none" && !landed.verdictId ? `merged at ${(landed.head ?? "?").slice(0, 12)} with no verdict (lane none)` : landed.review === "self-reviewed" ? `merged self-reviewed (lane self) with the author's verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, mergedOnLine = (verdict) => {
86532
+ if (verdict.review === "self-reviewed")
86533
+ return `self-reviewed (lane ${verdict.lane}): the author's verdict ${verdict.verdictId ?? "?"}; no independent verdict accepted for that head`;
86534
+ if (!verdict.verdictId)
86535
+ return `no verdict required (lane ${verdict.lane})`;
86536
+ if (!verdict.accepted && !verdict.cover && !verdict.carry)
86537
+ return `verdict ${verdict.verdictId} satisfied lane ${verdict.lane}; not an accepted independent verdict for that head`;
86538
+ return `verdict ${verdict.verdictId} accepted for that head${verdict.cover ? ` (stack verdict on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)})` : verdict.carry ? ` (${carryLabel(verdict.carry)})` : ""}`;
86539
+ }, acceptedHow = (verdict) => verdict.cover ? `stack verdict ${verdict.cover.verdictId} on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)}` : verdict.carry ? carryLabel(verdict.carry) : verdict.basis, METHODS2;
86428
86540
  var init_pr = __esm(() => {
86429
86541
  init_autonomy();
86430
86542
  init_issue_link();
86431
86543
  init_pr_edit();
86432
86544
  init_pr_ops();
86433
- init_report();
86545
+ init_report2();
86434
86546
  init_rev();
86435
86547
  init_workspaces();
86436
86548
  init_merge_cleanup();
@@ -86684,7 +86796,7 @@ async function run16(argv, io) {
86684
86796
  }
86685
86797
  var WAIT_USAGE = "workit ci wait [--pr <n> | --branch <b>] [--head <sha>] [--timeout 20m] [--interval 30s] [--json]", RERUN_USAGE = "workit ci rerun [--pr <n> | --branch <b>] [--check <name>… | --failed] --reason flake|infra [--force] [--json]", LOGS_USAGE = "workit ci logs --failed [--pr <n> | --branch <b>] [--json]", LOG_LINES = 60, ONE_POLL_MS = 20000, MAX_POLL_ERRORS = 3, seconds = (ms) => `${Math.round(ms / 1000)}s`;
86686
86798
  var init_ci = __esm(() => {
86687
- init_report();
86799
+ init_report2();
86688
86800
  init_ledger();
86689
86801
  init_autonomy();
86690
86802
  init_output();
@@ -86723,8 +86835,8 @@ var exports_git = {};
86723
86835
  __export(exports_git, {
86724
86836
  run: () => run17
86725
86837
  });
86726
- import { readFileSync as readFileSync26 } from "node:fs";
86727
- import path64 from "node:path";
86838
+ import { readFileSync as readFileSync27 } from "node:fs";
86839
+ import path67 from "node:path";
86728
86840
  function kindHint(cwd, kind) {
86729
86841
  let kinds = [...BRANCH_KINDS];
86730
86842
  try {
@@ -86828,7 +86940,7 @@ function commitMessage(parsed, io) {
86828
86940
 
86829
86941
  `);
86830
86942
  try {
86831
- return file === "-" ? readFileSync26(0, "utf8") : readFileSync26(path64.resolve(io.cwd, file), "utf8");
86943
+ return file === "-" ? readFileSync27(0, "utf8") : readFileSync27(path67.resolve(io.cwd, file), "utf8");
86832
86944
  } catch (error) {
86833
86945
  return {
86834
86946
  error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
@@ -87242,7 +87354,7 @@ var init_verify = __esm(() => {
87242
87354
  init_exec();
87243
87355
  init_rev();
87244
87356
  init_ledger();
87245
- init_report();
87357
+ init_report2();
87246
87358
  init_redact();
87247
87359
  NPM_NAME = /^(?:@[A-Za-z0-9~][A-Za-z0-9-._~]*\/)?[A-Za-z0-9~][A-Za-z0-9-._~]*$/u;
87248
87360
  SEMVER2 = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;
@@ -88261,15 +88373,15 @@ var init_stack2 = __esm(() => {
88261
88373
  });
88262
88374
 
88263
88375
  // packages/workit-core/src/fanout-brief.ts
88264
- import { createHash as createHash19 } from "node:crypto";
88376
+ import { createHash as createHash20 } from "node:crypto";
88265
88377
  import fs35 from "node:fs";
88266
- import path65 from "node:path";
88378
+ import path68 from "node:path";
88267
88379
  function workerSession(lead, slice, attempt) {
88268
88380
  const tail = attempt === null ? "" : `+try${attempt}`;
88269
88381
  const full = `${lead}-w-${slice}${tail}`;
88270
88382
  if (full.length <= SESSION_MAX)
88271
88383
  return full;
88272
- const digest = createHash19("sha256").update(slice).digest("hex").slice(0, 12);
88384
+ const digest = createHash20("sha256").update(slice).digest("hex").slice(0, 12);
88273
88385
  return `${lead.slice(0, SESSION_MAX - 3 - digest.length - tail.length)}-w-${digest}${tail}`;
88274
88386
  }
88275
88387
  function renderBrief(cwd, plan, input) {
@@ -88345,7 +88457,7 @@ function renderBrief(cwd, plan, input) {
88345
88457
  "STANDING:",
88346
88458
  ...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
88347
88459
  exists ? `WORKTREE: ${planned} (made by \`workit fanout worktree create ${slice.id}\`; yours alone)` : `WORKTREE: ${planned} (not made yet: the lead runs \`workit fanout worktree create ${slice.id}\` before spawning, then renders this brief again)`,
88348
- ...exists && typeof made?.install === "string" && !fs35.existsSync(path65.join(planned, "node_modules")) ? [`INSTALL: \`${made.install}\` first: the main checkout's node_modules was not linked here`] : [],
88460
+ ...exists && typeof made?.install === "string" && !fs35.existsSync(path68.join(planned, "node_modules")) ? [`INSTALL: \`${made.install}\` first: the main checkout's node_modules was not linked here`] : [],
88349
88461
  ...identityLines(identity, minted),
88350
88462
  ...rules(mode, branch, slice.base, exists ? planned : null)
88351
88463
  ];
@@ -89075,7 +89187,7 @@ var DEFAULT_STUCK_MS, isoOf = (seconds) => {
89075
89187
  }, newest = (...values) => values.filter((value) => Boolean(value) && !Number.isNaN(Date.parse(value ?? ""))).reduce((best, value) => best === null || Date.parse(value) > Date.parse(best) ? value : best, null);
89076
89188
  var init_fanout_status = __esm(() => {
89077
89189
  init_duration();
89078
- init_report();
89190
+ init_report2();
89079
89191
  init_role_models();
89080
89192
  init_ledger();
89081
89193
  init_fanout_check();
@@ -89090,7 +89202,7 @@ __export(exports_fanout, {
89090
89202
  run: () => run21
89091
89203
  });
89092
89204
  import fs36 from "node:fs";
89093
- import path66 from "node:path";
89205
+ import path69 from "node:path";
89094
89206
  function forgeFor(io, offline) {
89095
89207
  if (offline)
89096
89208
  return { forge: null, error: "--offline" };
@@ -89151,7 +89263,7 @@ function plan2(argv, io) {
89151
89263
  return usage4(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
89152
89264
  if (flags.positionals.length !== 1)
89153
89265
  return usage4(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
89154
- const file = path66.resolve(io.cwd, flags.positionals[0]);
89266
+ const file = path69.resolve(io.cwd, flags.positionals[0]);
89155
89267
  let raw;
89156
89268
  try {
89157
89269
  raw = JSON.parse(fs36.readFileSync(file, "utf8"));
@@ -89527,6 +89639,8 @@ async function run22(argv, io) {
89527
89639
  if (rest.length !== 1)
89528
89640
  return usage5(io, `ledger verdict takes one result: ${VERDICT_RESULTS.join("|")}`);
89529
89641
  const claimed = values.self === true ? null : claimedSession(acting);
89642
+ if (values.self !== true && claimed === null)
89643
+ return emit(io, fail2("blocked", NO_SESSION_ERROR, { unblock: NO_SESSION_UNBLOCK }));
89530
89644
  const token = values.token ?? io.env.WORKIT_SESSION_TOKEN ?? null;
89531
89645
  const refusal = claimed === null ? null : sessionClaimRefusal(io.env, claimed, io.cwd, { token, flag: acting.source === "flag" });
89532
89646
  if (refusal)
@@ -89641,7 +89755,7 @@ var USAGE15 = "workit ledger decision|ruling|verdict|standing|list|check ... (wo
89641
89755
  };
89642
89756
  }
89643
89757
  return { actor, source: "env" };
89644
- }, claimedSession = (acting) => acting.source === "as" ? acting.derivedFrom ?? null : acting.actor.session, positiveInt2 = (value, flag) => {
89758
+ }, NO_SESSION_ERROR = "no_session: this verdict cannot bind a host session (WORKIT_SESSION_ID is unset or empty and no --session was named), so it would be stored as a self verdict that no gate accepts; nothing was recorded", NO_SESSION_UNBLOCK = "a Claude Code verifier or reviewer passes exactly the `--session <lead>:<agent id> --token=<token>` its SubagentStart hook put in its context; none there means that hook did not bind it (check `workit doctor`: hook_runs lists SubagentStart, hook_issued_sessions passes), so the lead spawns a fresh workit:verifier or workit:reviewer. On Codex, OpenCode and Pi record from the agent's own shell, where the host sets its session. The author records `--self` instead (never accepted as independent)", claimedSession = (acting) => acting.source === "as" ? acting.derivedFrom ?? null : acting.actor.session, positiveInt2 = (value, flag) => {
89645
89759
  if (value === undefined)
89646
89760
  return;
89647
89761
  const parsed = Number(value);
@@ -104692,7 +104806,7 @@ var init_ast = __esm(() => {
104692
104806
 
104693
104807
  // packages/workit-cli/src/test-audit/rules.ts
104694
104808
  import { builtinModules } from "node:module";
104695
- import path67 from "node:path";
104809
+ import path70 from "node:path";
104696
104810
  function testCallee(call) {
104697
104811
  let callee = call.callee;
104698
104812
  if (callee.type === "CallExpression")
@@ -105185,12 +105299,12 @@ function checkConstantsOnly(ctx, emit) {
105185
105299
  }
105186
105300
  function checkOverMocking(ctx, emit) {
105187
105301
  const { program } = ctx.parsed;
105188
- const unit = path67.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
105302
+ const unit = path70.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
105189
105303
  const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
105190
105304
  const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
105191
105305
  for (const call of mocked) {
105192
105306
  const spec = call.arguments[0].value;
105193
- const base = path67.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
105307
+ const base = path70.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
105194
105308
  if (base === unit)
105195
105309
  emit({
105196
105310
  rule: "over-mocking",
@@ -105579,8 +105693,8 @@ var init_rules = __esm(() => {
105579
105693
 
105580
105694
  // packages/workit-cli/src/test-audit/audit.ts
105581
105695
  import { spawnSync as spawnSync29 } from "node:child_process";
105582
- import { existsSync as existsSync24, readdirSync as readdirSync14, readFileSync as readFileSync27, statSync as statSync10 } from "node:fs";
105583
- import path68 from "node:path";
105696
+ import { existsSync as existsSync26, readdirSync as readdirSync14, readFileSync as readFileSync28, statSync as statSync10 } from "node:fs";
105697
+ import path71 from "node:path";
105584
105698
  function git7(cwd, args) {
105585
105699
  const run = spawnSync29("git", ["-c", "core.quotepath=off", ...args], {
105586
105700
  cwd,
@@ -105593,7 +105707,7 @@ function walkTests(dir, out) {
105593
105707
  for (const entry of readdirSync14(dir, { withFileTypes: true })) {
105594
105708
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
105595
105709
  continue;
105596
- const full = path68.join(dir, entry.name);
105710
+ const full = path71.join(dir, entry.name);
105597
105711
  if (entry.isDirectory()) {
105598
105712
  if (!SKIP_DIRS.has(entry.name))
105599
105713
  walkTests(full, out);
@@ -105605,8 +105719,8 @@ function expandPaths(cwd, inputs) {
105605
105719
  const files = [];
105606
105720
  const missing = [];
105607
105721
  for (const input of inputs.length > 0 ? inputs : ["."]) {
105608
- const full = path68.resolve(cwd, input);
105609
- if (!existsSync24(full))
105722
+ const full = path71.resolve(cwd, input);
105723
+ if (!existsSync26(full))
105610
105724
  missing.push(input);
105611
105725
  else if (statSync10(full).isDirectory())
105612
105726
  walkTests(full, files);
@@ -105642,11 +105756,11 @@ function changedLines(cwd, base) {
105642
105756
  for (let offset = 0;offset < count; offset += 1)
105643
105757
  lines.add(start + offset);
105644
105758
  }
105645
- changed.set(path68.join(root, file), lines);
105759
+ changed.set(path71.join(root, file), lines);
105646
105760
  }
105647
105761
  const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
105648
105762
  for (const file of untracked.stdout.split("\x00").filter(Boolean))
105649
- changed.set(path68.join(root, file), "all");
105763
+ changed.set(path71.join(root, file), "all");
105650
105764
  return changed;
105651
105765
  }
105652
105766
  function auditFiles(files, display) {
@@ -105659,7 +105773,7 @@ function auditFiles(files, display) {
105659
105773
  const shown = display(file);
105660
105774
  let parsed;
105661
105775
  try {
105662
- parsed = parseSource(readFileSync27(file, "utf8"), file);
105776
+ parsed = parseSource(readFileSync28(file, "utf8"), file);
105663
105777
  } catch (error) {
105664
105778
  parseErrors.push({
105665
105779
  file: shown,
@@ -105708,7 +105822,7 @@ function auditFiles(files, display) {
105708
105822
  durationMs: Math.round(performance.now() - started)
105709
105823
  };
105710
105824
  }
105711
- var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path68.sep).join("/"), SEVERITY_ORDER, ruleIds;
105825
+ var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path71.sep).join("/"), SEVERITY_ORDER, ruleIds;
105712
105826
  var init_audit = __esm(() => {
105713
105827
  init_ast();
105714
105828
  init_rules();
@@ -105722,12 +105836,12 @@ var init_audit = __esm(() => {
105722
105836
  // packages/workit-cli/src/test-audit/mutate.ts
105723
105837
  import { spawn as spawn2 } from "node:child_process";
105724
105838
  import {
105725
- existsSync as existsSync25,
105839
+ existsSync as existsSync27,
105726
105840
  lstatSync as lstatSync8,
105727
105841
  mkdirSync as mkdirSync12,
105728
105842
  mkdtempSync as mkdtempSync4,
105729
105843
  readdirSync as readdirSync15,
105730
- readFileSync as readFileSync28,
105844
+ readFileSync as readFileSync29,
105731
105845
  readlinkSync as readlinkSync3,
105732
105846
  realpathSync as realpathSync10,
105733
105847
  renameSync as renameSync7,
@@ -105737,8 +105851,8 @@ import {
105737
105851
  writeFileSync as writeFileSync12,
105738
105852
  copyFileSync as copyFileSync8
105739
105853
  } from "node:fs";
105740
- import os22 from "node:os";
105741
- import path69 from "node:path";
105854
+ import os23 from "node:os";
105855
+ import path72 from "node:path";
105742
105856
  function mutantsFor(source, file, lines) {
105743
105857
  const parsed = parseSource(source, file);
105744
105858
  const out = [];
@@ -105811,7 +105925,7 @@ function relatedTests(source, testFiles) {
105811
105925
  for (const match of text.matchAll(SPECIFIER)) {
105812
105926
  const spec = match[1];
105813
105927
  if (spec.startsWith(".")) {
105814
- if (stripExt(path69.posix.join(path69.posix.dirname(file), spec)) === target)
105928
+ if (stripExt(path72.posix.join(path72.posix.dirname(file), spec)) === target)
105815
105929
  return true;
105816
105930
  } else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
105817
105931
  return true;
@@ -105819,7 +105933,7 @@ function relatedTests(source, testFiles) {
105819
105933
  return false;
105820
105934
  }).map((entry) => entry.file);
105821
105935
  }
105822
- function removeStaleCopies(tmp = os22.tmpdir()) {
105936
+ function removeStaleCopies(tmp = os23.tmpdir()) {
105823
105937
  let removed = 0;
105824
105938
  let names = [];
105825
105939
  try {
@@ -105831,12 +105945,12 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
105831
105945
  for (const name of names) {
105832
105946
  if (!name.startsWith(TEMP_PREFIX))
105833
105947
  continue;
105834
- const dir = path69.join(tmp, name);
105948
+ const dir = path72.join(tmp, name);
105835
105949
  try {
105836
105950
  const stat = lstatSync8(dir);
105837
105951
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
105838
105952
  continue;
105839
- const pid = Number(readFileSync28(path69.join(dir, PID_FILE), "utf8"));
105953
+ const pid = Number(readFileSync29(path72.join(dir, PID_FILE), "utf8"));
105840
105954
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
105841
105955
  continue;
105842
105956
  } catch (error) {
@@ -105853,8 +105967,8 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
105853
105967
  function linkNodeModules(origin, target, repo, copy) {
105854
105968
  mkdirSync12(target, { recursive: true });
105855
105969
  for (const entry of readdirSync15(origin, { withFileTypes: true })) {
105856
- const from = path69.join(origin, entry.name);
105857
- const to = path69.join(target, entry.name);
105970
+ const from = path72.join(origin, entry.name);
105971
+ const to = path72.join(target, entry.name);
105858
105972
  if (entry.name.startsWith("@") && entry.isDirectory()) {
105859
105973
  linkNodeModules(from, to, repo, copy);
105860
105974
  continue;
@@ -105870,9 +105984,9 @@ function linkNodeModules(origin, target, repo, copy) {
105870
105984
  } catch {
105871
105985
  continue;
105872
105986
  }
105873
- const relative = path69.relative(repo, real);
105874
- if (!relative.startsWith("..") && !path69.isAbsolute(relative) && !relative.split(path69.sep).includes("node_modules")) {
105875
- symlinkSync(path69.join(copy, relative), to, "junction");
105987
+ const relative = path72.relative(repo, real);
105988
+ if (!relative.startsWith("..") && !path72.isAbsolute(relative) && !relative.split(path72.sep).includes("node_modules")) {
105989
+ symlinkSync(path72.join(copy, relative), to, "junction");
105876
105990
  continue;
105877
105991
  }
105878
105992
  }
@@ -105888,7 +106002,7 @@ function copySymlink(repo, tree, from, to) {
105888
106002
  }
105889
106003
  const realRepo = realpathSync10(repo);
105890
106004
  if (real && inside5(realRepo, real)) {
105891
- symlinkSync(path69.relative(path69.dirname(to), path69.join(tree, path69.relative(realRepo, real))), to);
106005
+ symlinkSync(path72.relative(path72.dirname(to), path72.join(tree, path72.relative(realRepo, real))), to);
105892
106006
  return;
105893
106007
  }
105894
106008
  if (real && statSync11(real).isFile())
@@ -105897,10 +106011,10 @@ function copySymlink(repo, tree, from, to) {
105897
106011
  symlinkSync(readlinkSync3(from), to);
105898
106012
  }
105899
106013
  function writeInside(copy, file, content) {
105900
- const target = path69.join(copy, file);
106014
+ const target = path72.join(copy, file);
105901
106015
  try {
105902
106016
  const stat = lstatSync8(target);
105903
- if (!stat.isFile() || !inside5(realpathSync10(copy), realpathSync10(path69.dirname(target))))
106017
+ if (!stat.isFile() || !inside5(realpathSync10(copy), realpathSync10(path72.dirname(target))))
105904
106018
  return false;
105905
106019
  } catch {
105906
106020
  return false;
@@ -105911,9 +106025,9 @@ function writeInside(copy, file, content) {
105911
106025
  return true;
105912
106026
  }
105913
106027
  function copyWorkingTree(repo) {
105914
- const dir = mkdtempSync4(path69.join(os22.tmpdir(), TEMP_PREFIX));
105915
- writeFileSync12(path69.join(dir, PID_FILE), String(process.pid));
105916
- const tree = path69.join(dir, "tree");
106028
+ const dir = mkdtempSync4(path72.join(os23.tmpdir(), TEMP_PREFIX));
106029
+ writeFileSync12(path72.join(dir, PID_FILE), String(process.pid));
106030
+ const tree = path72.join(dir, "tree");
105917
106031
  const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
105918
106032
  if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
105919
106033
  const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
@@ -105930,29 +106044,29 @@ function copyWorkingTree(repo) {
105930
106044
  for (const relative of listed) {
105931
106045
  if (!relative)
105932
106046
  continue;
105933
- const from = path69.join(repo, relative);
106047
+ const from = path72.join(repo, relative);
105934
106048
  let stat;
105935
106049
  try {
105936
106050
  stat = lstatSync8(from);
105937
106051
  } catch {
105938
106052
  continue;
105939
106053
  }
105940
- const to = path69.join(tree, relative);
105941
- mkdirSync12(path69.dirname(to), { recursive: true });
106054
+ const to = path72.join(tree, relative);
106055
+ mkdirSync12(path72.dirname(to), { recursive: true });
105942
106056
  if (stat.isSymbolicLink())
105943
106057
  copySymlink(repo, tree, from, to);
105944
106058
  else if (stat.isFile())
105945
106059
  copyFileSync8(from, to);
105946
- let parent = path69.dirname(relative);
106060
+ let parent = path72.dirname(relative);
105947
106061
  while (parent !== "." && !dirs.has(parent)) {
105948
106062
  dirs.add(parent);
105949
- parent = path69.dirname(parent);
106063
+ parent = path72.dirname(parent);
105950
106064
  }
105951
106065
  }
105952
106066
  for (const parent of dirs) {
105953
- const modules = path69.join(repo, parent, "node_modules");
105954
- if (existsSync25(modules) && !existsSync25(path69.join(tree, parent, "node_modules")))
105955
- linkNodeModules(modules, path69.join(tree, parent, "node_modules"), repo, tree);
106067
+ const modules = path72.join(repo, parent, "node_modules");
106068
+ if (existsSync27(modules) && !existsSync27(path72.join(tree, parent, "node_modules")))
106069
+ linkNodeModules(modules, path72.join(tree, parent, "node_modules"), repo, tree);
105956
106070
  }
105957
106071
  return { dir, tree };
105958
106072
  }
@@ -105991,20 +106105,20 @@ async function runMutation(options) {
105991
106105
  const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
105992
106106
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
105993
106107
  try {
105994
- return { file, text: readFileSync28(path69.join(repo, file), "utf8") };
106108
+ return { file, text: readFileSync29(path72.join(repo, file), "utf8") };
105995
106109
  } catch {
105996
106110
  return { file, text: "" };
105997
106111
  }
105998
106112
  });
105999
106113
  const candidates = [];
106000
106114
  for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
106001
- const relative = toPosix(path69.relative(repo, absolute));
106115
+ const relative = toPosix(path72.relative(repo, absolute));
106002
106116
  if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
106003
106117
  continue;
106004
- if (NOT_SOURCE.test(relative) || !existsSync25(absolute))
106118
+ if (NOT_SOURCE.test(relative) || !existsSync27(absolute))
106005
106119
  continue;
106006
106120
  try {
106007
- candidates.push(...mutantsFor(readFileSync28(absolute, "utf8"), relative, lines));
106121
+ candidates.push(...mutantsFor(readFileSync29(absolute, "utf8"), relative, lines));
106008
106122
  } catch {}
106009
106123
  }
106010
106124
  const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
@@ -106012,9 +106126,9 @@ async function runMutation(options) {
106012
106126
  for (const mutant of mutants)
106013
106127
  if (!related.has(mutant.file))
106014
106128
  related.set(mutant.file, relatedTests(mutant.file, testFiles));
106015
- const cwdRelative = path69.relative(repo, options.cwd);
106129
+ const cwdRelative = path72.relative(repo, options.cwd);
106016
106130
  const asArg = (file) => {
106017
- const relative = toPosix(path69.relative(cwdRelative, file));
106131
+ const relative = toPosix(path72.relative(cwdRelative, file));
106018
106132
  return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
106019
106133
  };
106020
106134
  const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
@@ -106050,7 +106164,7 @@ async function runMutation(options) {
106050
106164
  return finish3(options, 0, results, counts, started);
106051
106165
  removeStaleCopies();
106052
106166
  const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
106053
- const copyCwd = path69.join(copy, cwdRelative);
106167
+ const copyCwd = path72.join(copy, cwdRelative);
106054
106168
  let current = null;
106055
106169
  const restore = () => {
106056
106170
  if (current)
@@ -106085,7 +106199,7 @@ async function runMutation(options) {
106085
106199
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
106086
106200
  continue;
106087
106201
  }
106088
- const original = readFileSync28(path69.join(copy, mutant.file), "utf8");
106202
+ const original = readFileSync29(path72.join(copy, mutant.file), "utf8");
106089
106203
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
106090
106204
  current = { file: mutant.file, original };
106091
106205
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -106142,8 +106256,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
106142
106256
  return error.code === "EPERM";
106143
106257
  }
106144
106258
  }, inside5 = (root, file) => {
106145
- const relative = path69.relative(root, file);
106146
- return relative === "" || !relative.startsWith("..") && !path69.isAbsolute(relative);
106259
+ const relative = path72.relative(root, file);
106260
+ return relative === "" || !relative.startsWith("..") && !path72.isAbsolute(relative);
106147
106261
  }, active, killTree = (child) => {
106148
106262
  try {
106149
106263
  if (child.pid && process.platform !== "win32")
@@ -106191,8 +106305,8 @@ var exports_test_audit = {};
106191
106305
  __export(exports_test_audit, {
106192
106306
  run: () => run23
106193
106307
  });
106194
- import { existsSync as existsSync26 } from "node:fs";
106195
- import path70 from "node:path";
106308
+ import { existsSync as existsSync28 } from "node:fs";
106309
+ import path73 from "node:path";
106196
106310
  function parse8(argv) {
106197
106311
  const options = {
106198
106312
  paths: [],
@@ -106297,8 +106411,8 @@ async function run23(argv, io) {
106297
106411
  return emit(io, fail2("invalid_input", `${options}
106298
106412
  usage: ${USAGE16}`));
106299
106413
  const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
106300
- const repo = top.ok ? path70.resolve(top.stdout.trim()) : null;
106301
- const display = (file) => toPosix(path70.relative(io.cwd, file) || file);
106414
+ const repo = top.ok ? path73.resolve(top.stdout.trim()) : null;
106415
+ const display = (file) => toPosix(path73.relative(io.cwd, file) || file);
106302
106416
  let base = null;
106303
106417
  let changed = null;
106304
106418
  if (options.diff || options.mutate) {
@@ -106315,7 +106429,7 @@ usage: ${USAGE16}`));
106315
106429
  }
106316
106430
  let files;
106317
106431
  if (options.diff && changed)
106318
- files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync26(file)).toSorted();
106432
+ files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync28(file)).toSorted();
106319
106433
  else {
106320
106434
  const expanded = expandPaths(io.cwd, options.paths);
106321
106435
  if (expanded.missing.length > 0)
@@ -106436,8 +106550,8 @@ var exports_youtrack = {};
106436
106550
  __export(exports_youtrack, {
106437
106551
  run: () => run25
106438
106552
  });
106439
- import { readFileSync as readFileSync29 } from "node:fs";
106440
- import path71 from "node:path";
106553
+ import { readFileSync as readFileSync30 } from "node:fs";
106554
+ import path74 from "node:path";
106441
106555
  async function run25(argv, io) {
106442
106556
  const [sub, ...rest] = argv;
106443
106557
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
@@ -106483,7 +106597,7 @@ async function run25(argv, io) {
106483
106597
  let markdown = flags.values.markdown ?? "";
106484
106598
  if (flags.values.file)
106485
106599
  try {
106486
- markdown = readFileSync29(path71.resolve(io.cwd, flags.values.file), "utf8");
106600
+ markdown = readFileSync30(path74.resolve(io.cwd, flags.values.file), "utf8");
106487
106601
  } catch (error) {
106488
106602
  return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE18);
106489
106603
  }
@@ -106551,8 +106665,8 @@ var exports_changelog = {};
106551
106665
  __export(exports_changelog, {
106552
106666
  run: () => run26
106553
106667
  });
106554
- import { readFileSync as readFileSync30 } from "node:fs";
106555
- import path72 from "node:path";
106668
+ import { readFileSync as readFileSync31 } from "node:fs";
106669
+ import path75 from "node:path";
106556
106670
  async function run26(argv, io) {
106557
106671
  const [sub, ...rest] = argv;
106558
106672
  if (sub !== "apply")
@@ -106598,7 +106712,7 @@ async function run26(argv, io) {
106598
106712
  ]);
106599
106713
  }
106600
106714
  var USAGE19 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
106601
- const text = raw === "-" ? readFileSync30(0, "utf8") : raw.startsWith("@") ? readFileSync30(path72.resolve(cwd, raw.slice(1)), "utf8") : raw;
106715
+ const text = raw === "-" ? readFileSync31(0, "utf8") : raw.startsWith("@") ? readFileSync31(path75.resolve(cwd, raw.slice(1)), "utf8") : raw;
106602
106716
  return JSON.parse(text);
106603
106717
  };
106604
106718
  var init_changelog2 = __esm(() => {
@@ -106614,7 +106728,7 @@ __export(exports_handoff, {
106614
106728
  run: () => run27
106615
106729
  });
106616
106730
  import fs37 from "node:fs";
106617
- import path73 from "node:path";
106731
+ import path76 from "node:path";
106618
106732
  import { parseArgs as parseArgs2 } from "node:util";
106619
106733
  function render3(brief) {
106620
106734
  const lines = [
@@ -106654,9 +106768,9 @@ function bounded(brief, cwd) {
106654
106768
  let file = null;
106655
106769
  if (store.ok)
106656
106770
  try {
106657
- const dir = path73.join(store.value.root, "handoff");
106771
+ const dir = path76.join(store.value.root, "handoff");
106658
106772
  fs37.mkdirSync(dir, { recursive: true });
106659
- file = path73.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
106773
+ file = path76.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
106660
106774
  fs37.writeFileSync(file, `${[...body, ...tail].join(`
106661
106775
  `)}
106662
106776
  `, {
@@ -106733,7 +106847,7 @@ var init_handoff = __esm(() => {
106733
106847
  // packages/workit-cli/src/main.ts
106734
106848
  init_host_session();
106735
106849
  init_package();
106736
- import path74 from "node:path";
106850
+ import path77 from "node:path";
106737
106851
 
106738
106852
  // packages/workit-cli/src/verbs/registry.ts
106739
106853
  var TASK_FAMILY_NAMES = [
@@ -107645,7 +107759,7 @@ var defaultIo = (json) => ({
107645
107759
  });
107646
107760
  async function main(argv, overrides = {}, options = {}) {
107647
107761
  const parsed = parseGlobals(argv);
107648
- const cwd = path74.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
107762
+ const cwd = path77.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
107649
107763
  const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
107650
107764
  if (parsed.error)
107651
107765
  return emit(io, fail2("invalid_input", parsed.error));