@brainervirus/workit-cli 8.33.0 → 8.35.0

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.
Files changed (2) hide show
  1. package/dist/index.js +904 -527
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -9286,7 +9286,7 @@ var package_default;
9286
9286
  var init_package = __esm(() => {
9287
9287
  package_default = {
9288
9288
  name: "@brainervirus/workit-cli",
9289
- version: "8.33.0",
9289
+ version: "8.35.0",
9290
9290
  private: false,
9291
9291
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
9292
9292
  keywords: [
@@ -9324,13 +9324,13 @@ var init_package = __esm(() => {
9324
9324
  },
9325
9325
  dependencies: {
9326
9326
  "@babel/parser": "7.29.9",
9327
- "@brainervirus/workit-cursor": "^8.33.0",
9327
+ "@brainervirus/workit-cursor": "^8.35.0",
9328
9328
  "@inkjs/ui": "2.0.0",
9329
9329
  ink: "7.1.1",
9330
9330
  react: "19.2.8"
9331
9331
  },
9332
9332
  devDependencies: {
9333
- "@brainervirus/workit-core": "^8.33.0"
9333
+ "@brainervirus/workit-core": "^8.35.0"
9334
9334
  },
9335
9335
  engines: {
9336
9336
  node: ">=24"
@@ -66607,6 +66607,35 @@ var init_heavy_words = __esm(() => {
66607
66607
  HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
66608
66608
  });
66609
66609
 
66610
+ // packages/workit-core/src/hooks/hook-runs.ts
66611
+ import { mkdirSync as mkdirSync6, readdirSync as readdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
66612
+ import path37 from "node:path";
66613
+ var HOOK_RUNS_DIR = "hook-runs", readHookRuns = (dir) => {
66614
+ const runs = [];
66615
+ let names = [];
66616
+ try {
66617
+ names = readdirSync8(path37.join(dir, HOOK_RUNS_DIR));
66618
+ } catch {
66619
+ return runs;
66620
+ }
66621
+ for (const name of names) {
66622
+ try {
66623
+ const row = JSON.parse(readFileSync15(path37.join(dir, HOOK_RUNS_DIR, name), "utf8"));
66624
+ if (typeof row.host === "string" && typeof row.event === "string" && typeof row.at === "string" && typeof row.count === "number")
66625
+ runs.push({
66626
+ host: row.host,
66627
+ event: row.event,
66628
+ at: row.at,
66629
+ count: row.count
66630
+ });
66631
+ } catch {}
66632
+ }
66633
+ return runs;
66634
+ };
66635
+ var init_hook_runs = __esm(() => {
66636
+ init_heavy_words();
66637
+ });
66638
+
66610
66639
  // packages/workit-core/src/hooks/delivery-verbs.ts
66611
66640
  var WORKIT_ALLOWED_VERBS, ALLOWED;
66612
66641
  var init_delivery_verbs = __esm(() => {
@@ -66635,14 +66664,15 @@ var init_delivery_verbs = __esm(() => {
66635
66664
  import {
66636
66665
  copyFileSync as copyFileSync4,
66637
66666
  existsSync as existsSync15,
66638
- mkdirSync as mkdirSync6,
66639
- readFileSync as readFileSync15,
66667
+ readdirSync as readdirSync9,
66668
+ mkdirSync as mkdirSync7,
66669
+ readFileSync as readFileSync16,
66640
66670
  realpathSync as realpathSync7,
66641
66671
  rmSync as rmSync3,
66642
66672
  statSync as statSync5
66643
66673
  } from "node:fs";
66644
66674
  import os11 from "node:os";
66645
- import path37 from "node:path";
66675
+ import path38 from "node:path";
66646
66676
  function mergeClaudeSettings(settings, shell, skip = {}) {
66647
66677
  const config = clone4(settings);
66648
66678
  const owned = emptyOwned();
@@ -66652,7 +66682,11 @@ function mergeClaudeSettings(settings, shell, skip = {}) {
66652
66682
  if (shell) {
66653
66683
  const env = objectAt(config, ["env"], owned);
66654
66684
  if (!env)
66655
- return { config: settings, owned: emptyOwned(), error: "env is not an object" };
66685
+ return {
66686
+ config: settings,
66687
+ owned: emptyOwned(),
66688
+ error: "env is not an object"
66689
+ };
66656
66690
  if (env.CLAUDE_CODE_SHELL === undefined) {
66657
66691
  env.CLAUDE_CODE_SHELL = shell;
66658
66692
  owned.env.CLAUDE_CODE_SHELL = shell;
@@ -66758,7 +66792,7 @@ function planHostPermissions(options) {
66758
66792
  plan.blocked.push(`${target.file} is not a JSON object; left untouched`);
66759
66793
  continue;
66760
66794
  }
66761
- const shellWanted = host === "claude-code" && options.shell !== false && path37.basename(paths.env.SHELL ?? "") === "zsh";
66795
+ const shellWanted = host === "claude-code" && options.shell !== false && path38.basename(paths.env.SHELL ?? "") === "zsh";
66762
66796
  const bash = shellWanted ? options.bash === undefined ? findBash(paths.env) : options.bash : null;
66763
66797
  const merged = host === "claude-code" ? mergeClaudeSettings(parsed, bash, skip) : mergeCursorCliConfig(parsed, skip);
66764
66798
  if (merged.error) {
@@ -66858,11 +66892,11 @@ function applyHostPermissions(plan, options = {}) {
66858
66892
  try {
66859
66893
  const target = change.before === null ? change.file : realpathSync7(change.file);
66860
66894
  if (change.before !== null) {
66861
- const backups = path37.join(paths.stateDir, "host-permissions-backup");
66862
- mkdirSync6(backups, { recursive: true, mode: 448 });
66863
- copyFileSync4(target, path37.join(backups, `${new Date().toISOString().replaceAll(":", "-")}-${change.host}-${path37.basename(change.file)}`));
66895
+ const backups = path38.join(paths.stateDir, "host-permissions-backup");
66896
+ mkdirSync7(backups, { recursive: true, mode: 448 });
66897
+ copyFileSync4(target, path38.join(backups, `${new Date().toISOString().replaceAll(":", "-")}-${change.host}-${path38.basename(change.file)}`));
66864
66898
  }
66865
- mkdirSync6(path37.dirname(target), { recursive: true });
66899
+ mkdirSync7(path38.dirname(target), { recursive: true });
66866
66900
  writeFileAtomic(target, change.after);
66867
66901
  const previous = record.hosts[change.host];
66868
66902
  record.hosts[change.host] = {
@@ -66892,7 +66926,10 @@ function permissionHealth(host, options = {}) {
66892
66926
  if (host === "codex") {
66893
66927
  if (text === null)
66894
66928
  return { state: "absent" };
66895
- return text === codexRulesText() ? { state: "ok" } : { state: "drift", detail: `${target.file} differs from this Workit version's rules` };
66929
+ return text === codexRulesText() ? { state: "ok" } : {
66930
+ state: "drift",
66931
+ detail: `${target.file} differs from this Workit version's rules`
66932
+ };
66896
66933
  }
66897
66934
  if (text === null)
66898
66935
  return { state: "absent" };
@@ -66915,7 +66952,37 @@ function permissionHealth(host, options = {}) {
66915
66952
  detail: `${target.file} lacks ${missing.length} Workit entr${missing.length === 1 ? "y" : "ies"} (${missing[0]}${missing.length > 1 ? ", …" : ""})`
66916
66953
  };
66917
66954
  }
66918
- var PERMISSION_HOSTS, CLAUDE_ALLOW, CLAUDE_ASK, AUTO_MODE_ENVIRONMENT = "Org-specific CLIs: workit — the user's delivery CLI. Its push, pr merge, release and PR-thread verbs run only under per-workspace grants and non-author verdicts the user configured in ~/.config/workit, outside the agent's reach.", AUTO_MODE_ALLOW = "Workit-gated delivery: `workit pr merge`, `workit git push --force-with-lease` on the session's own branch, `workit release` and `workit pr reply/resolve` on the session's repo are authorized by the user's standing Workit grants; not Merge Without Review, Git Destructive or External System Writes. Does NOT cover `--unverified`, `--admin` or any bypass flag.", DEFAULTS2 = "$defaults", CHECK_NAMES, LOCK_VERBS, LEASE_PREFIXES, CURSOR_ALLOW, CODEX_HEADER, codexRulesText = () => {
66955
+ function permissionCoverage(host, commands, options = {}) {
66956
+ const target = permissionTarget(host, options);
66957
+ if (!existsSync15(target.hostDir))
66958
+ return { state: "no-host" };
66959
+ const cwd = options.cwd ?? process.cwd();
66960
+ let missing;
66961
+ if (host === "codex") {
66962
+ const patterns = codexAllowPatterns(path38.dirname(target.file));
66963
+ missing = commands.filter((command) => {
66964
+ const words = command.split(" ");
66965
+ return !patterns.some((p) => p.every((word, i) => words[i] === word));
66966
+ });
66967
+ } else if (host === "cursor") {
66968
+ const rules = [target.file, path38.join(cwd, ".cursor", "cli.json")].flatMap((file) => stringList(valueFrom(readRecord(file), "permissions", "allow")));
66969
+ missing = commands.filter((command) => !rules.some((rule) => cursorRuleMatches(rule, command)));
66970
+ } else {
66971
+ const files = [
66972
+ target.file,
66973
+ path38.join(cwd, ".claude", "settings.json"),
66974
+ path38.join(cwd, ".claude", "settings.local.json")
66975
+ ].map(readRecord);
66976
+ const rules = files.flatMap((file) => stringList(valueFrom(file, "permissions", "allow")));
66977
+ missing = commands.filter((command) => !rules.some((rule) => claudeRuleMatches(rule, command)));
66978
+ const delivery = commands.some((command) => !LEASE_COMMANDS.includes(command));
66979
+ const auto = files.flatMap((file) => stringList(valueFrom(file, "autoMode", "allow")));
66980
+ if (delivery && !auto.some((line) => /\bworkit\b/iu.test(line)))
66981
+ missing.push("autoMode.allow line naming workit");
66982
+ }
66983
+ return missing.length === 0 ? { state: "ok" } : { state: "missing", file: target.file, missing };
66984
+ }
66985
+ var PERMISSION_HOSTS, CHECK_NAMES, LOCK_VERBS, LEASE_PREFIXES, CLAUDE_ALLOW, CLAUDE_ASK, AUTO_MODE_ENVIRONMENT = "Org-specific CLIs: workit — the user's delivery CLI. Its push, pr merge, release and PR-thread verbs run only under per-workspace grants and non-author verdicts the user configured in ~/.config/workit, outside the agent's reach.", AUTO_MODE_ALLOW = "Workit-gated delivery: `workit pr merge`, `workit git push --force-with-lease` on the session's own branch, `workit release` and `workit pr reply/resolve` on the session's repo are authorized by the user's standing Workit grants; not Merge Without Review, Git Destructive or External System Writes. Does NOT cover `--unverified`, `--admin` or any bypass flag.", DEFAULTS2 = "$defaults", CURSOR_ALLOW, CODEX_HEADER, codexRulesText = () => {
66919
66986
  const rules = [...WORKIT_ALLOWED_VERBS, "--version"].map((verb) => {
66920
66987
  const examples = verb === "git" ? `
66921
66988
  match = [["workit", "git", "push", "--cwd", "/w"]],
@@ -67007,30 +67074,34 @@ ${[...rules, ...lease, grant].join(`
67007
67074
  }, resolvePaths = (options) => {
67008
67075
  const env = options.env ?? process.env;
67009
67076
  const home = options.home ?? env.HOME ?? os11.homedir();
67010
- return { home, env, stateDir: options.stateDir ?? stateDir({ ...env, HOME: home }) };
67077
+ return {
67078
+ home,
67079
+ env,
67080
+ stateDir: options.stateDir ?? stateDir({ ...env, HOME: home })
67081
+ };
67011
67082
  }, permissionTarget = (host, options = {}) => {
67012
67083
  const { home, env } = resolvePaths(options);
67013
67084
  if (host === "claude-code") {
67014
67085
  const dir = claudeConfigDir(home, env);
67015
- return { file: path37.join(dir, "settings.json"), hostDir: dir };
67086
+ return { file: path38.join(dir, "settings.json"), hostDir: dir };
67016
67087
  }
67017
67088
  if (host === "codex") {
67018
- const dir = env.CODEX_HOME || path37.join(home, ".codex");
67019
- return { file: path37.join(dir, "rules", "workit.rules"), hostDir: dir };
67089
+ const dir = env.CODEX_HOME || path38.join(home, ".codex");
67090
+ return { file: path38.join(dir, "rules", "workit.rules"), hostDir: dir };
67020
67091
  }
67021
- const dir = path37.join(home, ".cursor");
67022
- return { file: path37.join(dir, "cli-config.json"), hostDir: dir };
67092
+ const dir = path38.join(home, ".cursor");
67093
+ return { file: path38.join(dir, "cli-config.json"), hostDir: dir };
67023
67094
  }, AGENT_SESSION_VARS, agentSessionVar = (env) => AGENT_SESSION_VARS.find((name) => (env[name] ?? "") !== "") ?? null, RECORD_FILE = "host-permissions.json", readOwnership = (options = {}) => {
67024
67095
  const { stateDir } = resolvePaths(options);
67025
67096
  try {
67026
- const value = JSON.parse(readFileSync15(path37.join(stateDir, RECORD_FILE), "utf8"));
67097
+ const value = JSON.parse(readFileSync16(path38.join(stateDir, RECORD_FILE), "utf8"));
67027
67098
  if (isRecord5(value) && isRecord5(value.hosts))
67028
67099
  return { version: 1, hosts: value.hosts };
67029
67100
  } catch {}
67030
67101
  return { version: 1, hosts: {} };
67031
67102
  }, writeOwnership = (record, stateDir) => {
67032
- mkdirSync6(stateDir, { recursive: true });
67033
- writeFileAtomic(path37.join(stateDir, RECORD_FILE), JSON.stringify(record, null, 2) + `
67103
+ mkdirSync7(stateDir, { recursive: true });
67104
+ writeFileAtomic(path38.join(stateDir, RECORD_FILE), JSON.stringify(record, null, 2) + `
67034
67105
  `, 384);
67035
67106
  }, unionLists = (a, b) => {
67036
67107
  const lists = { ...a };
@@ -67052,14 +67123,14 @@ ${[...rules, ...lease, grant].join(`
67052
67123
  writeOwnership(record, paths.stateDir);
67053
67124
  }, readText4 = (file) => {
67054
67125
  try {
67055
- return readFileSync15(file, "utf8");
67126
+ return readFileSync16(file, "utf8");
67056
67127
  } catch {
67057
67128
  return null;
67058
67129
  }
67059
67130
  }, findBash = (env) => {
67060
- const dirs = ["/usr/bin", "/bin", ...(env.PATH ?? "").split(path37.delimiter).filter(Boolean)];
67131
+ const dirs = ["/usr/bin", "/bin", ...(env.PATH ?? "").split(path38.delimiter).filter(Boolean)];
67061
67132
  for (const dir of dirs) {
67062
- const candidate = path37.join(dir, "bash");
67133
+ const candidate = path38.join(dir, "bash");
67063
67134
  try {
67064
67135
  if (statSync5(candidate).isFile())
67065
67136
  return candidate;
@@ -67068,10 +67139,60 @@ ${[...rules, ...lease, grant].join(`
67068
67139
  return null;
67069
67140
  }, setupCommand = (hosts) => `workit setup --hosts=${hosts.join(",")}`, removeCodexRules = (file, options = {}) => {
67070
67141
  const expected = permissionTarget("codex", options).file;
67071
- if (path37.resolve(file) !== path37.resolve(expected) || !existsSync15(expected))
67142
+ if (path38.resolve(file) !== path38.resolve(expected) || !existsSync15(expected))
67072
67143
  return false;
67073
67144
  rmSync3(expected);
67074
67145
  return true;
67146
+ }, GRANT_COMMANDS, LEASE_COMMANDS, grantedCommands = (kinds) => [
67147
+ ...kinds.flatMap((kind) => GRANT_COMMANDS[kind] ?? []),
67148
+ ...LEASE_COMMANDS
67149
+ ], claudeRuleMatches = (rule, command) => {
67150
+ const body = /^Bash\((.*)\)$/u.exec(rule)?.[1];
67151
+ if (rule === "Bash" || body === "*" || body === "")
67152
+ return true;
67153
+ if (body === undefined)
67154
+ return false;
67155
+ if (body.endsWith(":*") && !body.slice(0, -2).includes("*")) {
67156
+ const prefix = body.slice(0, -2);
67157
+ return command === prefix || command.startsWith(`${prefix} `);
67158
+ }
67159
+ const escaped = body.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
67160
+ const pattern = body.endsWith(" *") ? escaped.replace(/ \.\*$/u, "(?: .*)?") : escaped;
67161
+ return new RegExp(`^${pattern}$`, "u").test(command);
67162
+ }, cursorRuleMatches = (rule, command) => {
67163
+ const full = /^Shell\((.*)\)$/u.exec(rule)?.[1];
67164
+ return full !== undefined && (command === full || command.startsWith(`${full} `));
67165
+ }, stringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [], readRecord = (file) => {
67166
+ const text = readText4(file);
67167
+ if (text === null)
67168
+ return null;
67169
+ try {
67170
+ const value = JSON.parse(text);
67171
+ return isRecord5(value) ? value : null;
67172
+ } catch {
67173
+ return null;
67174
+ }
67175
+ }, codexAllowPatterns = (dir) => {
67176
+ const patterns = [];
67177
+ let names = [];
67178
+ try {
67179
+ names = readdirSync9(dir).filter((name) => name.endsWith(".rules"));
67180
+ } catch {
67181
+ return patterns;
67182
+ }
67183
+ for (const name of names)
67184
+ for (const m of (readText4(path38.join(dir, name)) ?? "").matchAll(/pattern\s*=\s*(\[[^\]]*\])\s*,\s*decision\s*=\s*"allow"/gu))
67185
+ try {
67186
+ const words = JSON.parse(m[1]);
67187
+ if (Array.isArray(words))
67188
+ patterns.push(words.map(String));
67189
+ } catch {}
67190
+ return patterns;
67191
+ }, valueFrom = (root, ...keys) => {
67192
+ let node = root;
67193
+ for (const key of keys)
67194
+ node = isRecord5(node) ? node[key] : undefined;
67195
+ return node;
67075
67196
  };
67076
67197
  var init_host_permissions = __esm(() => {
67077
67198
  init_heavy_words();
@@ -67083,20 +67204,21 @@ var init_host_permissions = __esm(() => {
67083
67204
  "codex",
67084
67205
  "cursor"
67085
67206
  ]);
67207
+ CHECK_NAMES = ["test", "lint", "typecheck", "build", "format", "docs"];
67208
+ LOCK_VERBS = ["status", "wait"];
67209
+ LEASE_PREFIXES = [
67210
+ ...CHECK_NAMES.map((name) => ["check", name]),
67211
+ ...LOCK_VERBS.map((verb) => ["lock", verb])
67212
+ ];
67086
67213
  CLAUDE_ALLOW = [
67087
67214
  ...WORKIT_ALLOWED_VERBS.map((verb) => `Bash(workit ${verb} *)`),
67215
+ ...LEASE_PREFIXES.map((rest) => `Bash(workit ${rest.join(" ")} *)`),
67088
67216
  "Bash(workit --version)"
67089
67217
  ];
67090
67218
  CLAUDE_ASK = [
67091
67219
  "Bash(workit * --unverified*)",
67092
67220
  "Bash(workit * --overwrite-unintegrated*)"
67093
67221
  ];
67094
- CHECK_NAMES = ["test", "lint", "typecheck", "build", "format", "docs"];
67095
- LOCK_VERBS = ["status", "wait"];
67096
- LEASE_PREFIXES = [
67097
- ...CHECK_NAMES.map((name) => ["check", name]),
67098
- ...LOCK_VERBS.map((verb) => ["lock", verb])
67099
- ];
67100
67222
  CURSOR_ALLOW = [
67101
67223
  ...WORKIT_ALLOWED_VERBS.map((verb) => `Shell(workit ${verb})`),
67102
67224
  ...LEASE_PREFIXES.map((rest) => `Shell(workit ${rest.join(" ")})`),
@@ -67115,7 +67237,11 @@ var init_host_permissions = __esm(() => {
67115
67237
  created: [],
67116
67238
  env: {}
67117
67239
  },
67118
- cursor: { lists: { "permissions.allow": [...CURSOR_ALLOW] }, created: [], env: {} }
67240
+ cursor: {
67241
+ lists: { "permissions.allow": [...CURSOR_ALLOW] },
67242
+ created: [],
67243
+ env: {}
67244
+ }
67119
67245
  };
67120
67246
  PRIOR_ENTRIES = {
67121
67247
  "claude-code": {},
@@ -67142,6 +67268,12 @@ var init_host_permissions = __esm(() => {
67142
67268
  "PI_SESSION_ID",
67143
67269
  "CURSOR_AGENT"
67144
67270
  ];
67271
+ GRANT_COMMANDS = {
67272
+ push: ["workit git push"],
67273
+ pr: ["workit pr create"],
67274
+ merge: ["workit pr merge"]
67275
+ };
67276
+ LEASE_COMMANDS = LEASE_PREFIXES.map((rest) => `workit ${rest.join(" ")}`);
67145
67277
  });
67146
67278
 
67147
67279
  // packages/workit-cli/src/admin/doctor.ts
@@ -67150,31 +67282,34 @@ import { createHash as createHash14 } from "node:crypto";
67150
67282
  import {
67151
67283
  existsSync as existsSync16,
67152
67284
  lstatSync as lstatSync5,
67153
- mkdirSync as mkdirSync7,
67285
+ mkdirSync as mkdirSync8,
67154
67286
  mkdtempSync,
67155
- readdirSync as readdirSync8,
67156
- readFileSync as readFileSync16,
67287
+ readdirSync as readdirSync10,
67288
+ readFileSync as readFileSync17,
67157
67289
  readlinkSync as readlinkSync2,
67158
67290
  renameSync as renameSync4,
67159
67291
  rmSync as rmSync4,
67160
67292
  statSync as statSync6,
67161
67293
  unlinkSync as unlinkSync3,
67162
- writeFileSync as writeFileSync7
67294
+ writeFileSync as writeFileSync8
67163
67295
  } from "node:fs";
67164
67296
  import os12 from "node:os";
67165
- import path38 from "node:path";
67297
+ import path39 from "node:path";
67166
67298
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67167
- let dir = path38.resolve(cwd);
67299
+ let dir = path39.resolve(cwd);
67168
67300
  while (true) {
67169
- if (existsSync16(path38.join(dir, "packages", "workit-core", "package.json")))
67301
+ if (existsSync16(path39.join(dir, "packages", "workit-core", "package.json")))
67170
67302
  return dir;
67171
- const parent = path38.dirname(dir);
67303
+ const parent = path39.dirname(dir);
67172
67304
  if (parent === dir)
67173
67305
  return null;
67174
67306
  dir = parent;
67175
67307
  }
67176
67308
  }, listClaudeProcesses = () => {
67177
- const r = spawnSync16("ps", ["-eo", "pid=,lstart=,args="], { encoding: "utf8", timeout: 5000 });
67309
+ const r = spawnSync16("ps", ["-eo", "pid=,lstart=,args="], {
67310
+ encoding: "utf8",
67311
+ timeout: 5000
67312
+ });
67178
67313
  if (r.status !== 0 || typeof r.stdout !== "string")
67179
67314
  return null;
67180
67315
  const found = [];
@@ -67191,8 +67326,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67191
67326
  }, resolve = (options) => {
67192
67327
  const env = options.env ?? process.env;
67193
67328
  const home = options.home ?? env.HOME ?? os12.homedir();
67194
- const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path38.join(home, ".config", "workit");
67195
- const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path38.join(home, ".local", "state", "workit");
67329
+ const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path39.join(home, ".config", "workit");
67330
+ const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path39.join(home, ".local", "state", "workit");
67196
67331
  const cwd = options.cwd ?? process.cwd();
67197
67332
  const dev = options.dev?.trim() || env.WORKFLOW_TOOLKIT_DEV?.trim() || findDevFromCwd(cwd);
67198
67333
  return {
@@ -67203,11 +67338,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67203
67338
  cwd,
67204
67339
  workspaceRoot: options.workspaceRoot ?? env.WORKFLOW_WORKSPACE_ROOT ?? cwd,
67205
67340
  dev,
67206
- opencodeConfig: options.opencodeConfig ?? path38.join(home, ".config", "opencode", "opencode.json"),
67341
+ opencodeConfig: options.opencodeConfig ?? path39.join(home, ".config", "opencode", "opencode.json"),
67207
67342
  opencodePackageCacheDir: options.opencodePackageCacheDir ?? null,
67208
- cursorSettings: options.cursorSettings ?? path38.join(home, ".cursor", "settings.json"),
67209
- cursorMcp: options.cursorMcp ?? path38.join(home, ".cursor", "mcp.json"),
67210
- cursorPluginDir: options.cursorPluginDir ?? path38.join(home, ".cursor", "plugins", "local", "workit"),
67343
+ cursorSettings: options.cursorSettings ?? path39.join(home, ".cursor", "settings.json"),
67344
+ cursorMcp: options.cursorMcp ?? path39.join(home, ".cursor", "mcp.json"),
67345
+ cursorPluginDir: options.cursorPluginDir ?? path39.join(home, ".cursor", "plugins", "local", "workit"),
67211
67346
  cursorHeartbeatDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
67212
67347
  hookStateDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
67213
67348
  codexHome: options.codexHome,
@@ -67220,14 +67355,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67220
67355
  };
67221
67356
  }, readJson = (p) => {
67222
67357
  try {
67223
- const parsed = JSON.parse(readFileSync16(p, "utf8"));
67358
+ const parsed = JSON.parse(readFileSync17(p, "utf8"));
67224
67359
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
67225
67360
  } catch {
67226
67361
  return null;
67227
67362
  }
67228
67363
  }, parsesAsConfigObject = (p) => {
67229
67364
  try {
67230
- return isConfigObject(JSON.parse(readFileSync16(p, "utf8")));
67365
+ return isConfigObject(JSON.parse(readFileSync17(p, "utf8")));
67231
67366
  } catch {
67232
67367
  return false;
67233
67368
  }
@@ -67237,12 +67372,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67237
67372
  ...new Set(entries.filter((entry) => typeof entry === "string" && isWorkitPlugin(entry)))
67238
67373
  ];
67239
67374
  }, commandOnPath = (name, env) => findOnPath(process.platform === "win32" ? [name, `${name}.exe`] : [name], env) !== null, pathextNames = (name, env) => process.platform === "win32" ? (env.PATHEXT ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean).map((ext) => `${name}${ext.toLowerCase()}`) : [name], findOnPath = (names, env) => {
67240
- const dirs = (env.PATH ?? process.env.PATH ?? "").split(path38.delimiter);
67375
+ const dirs = (env.PATH ?? process.env.PATH ?? "").split(path39.delimiter);
67241
67376
  for (const dir of dirs) {
67242
67377
  if (!dir)
67243
67378
  continue;
67244
67379
  for (const candidateName of names) {
67245
- const candidate = path38.join(dir, candidateName);
67380
+ const candidate = path39.join(dir, candidateName);
67246
67381
  try {
67247
67382
  const st = statSync6(candidate);
67248
67383
  if (process.platform !== "win32" && (st.mode & 73) === 0)
@@ -67271,7 +67406,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67271
67406
  if (env.BUN && existsSync16(env.BUN))
67272
67407
  return env.BUN;
67273
67408
  for (const candidate of [
67274
- path38.join(env.HOME || os12.homedir(), ".bun/bin/bun"),
67409
+ path39.join(env.HOME || os12.homedir(), ".bun/bin/bun"),
67275
67410
  "/usr/local/bin/bun",
67276
67411
  "/usr/bin/bun"
67277
67412
  ]) {
@@ -67326,7 +67461,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67326
67461
  detail: "installed package, not a dev checkout — version parity applies to checkouts only"
67327
67462
  };
67328
67463
  }
67329
- const corePkg = readJson(path38.join(res.dev, "packages/workit-core/package.json"));
67464
+ const corePkg = readJson(path39.join(res.dev, "packages/workit-core/package.json"));
67330
67465
  if (!corePkg) {
67331
67466
  return {
67332
67467
  id: "versions",
@@ -67336,7 +67471,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67336
67471
  }
67337
67472
  const refs = new Set;
67338
67473
  for (const name of ["workit-opencode", "workit-cursor", "workit-cli"]) {
67339
- const pkg = readJson(path38.join(res.dev, "packages", name, "package.json"));
67474
+ const pkg = readJson(path39.join(res.dev, "packages", name, "package.json"));
67340
67475
  const dep = pkg?.dependencies?.["@brainervirus/workit-core"] ?? pkg?.devDependencies?.["@brainervirus/workit-core"];
67341
67476
  if (typeof dep === "string")
67342
67477
  refs.add(dep);
@@ -67352,7 +67487,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67352
67487
  if (refs.size > 1) {
67353
67488
  problems.push(`adapters pin different core versions: ${[...refs].join(", ")}`);
67354
67489
  }
67355
- const opencodePkg = readJson(path38.join(res.dev, "packages/workit-opencode/package.json"));
67490
+ const opencodePkg = readJson(path39.join(res.dev, "packages/workit-opencode/package.json"));
67356
67491
  const sdk = opencodePkg?.dependencies?.["@opencode/plugin"] ?? opencodePkg?.devDependencies?.["@opencode/plugin"];
67357
67492
  const sdkVersion = typeof sdk === "string" ? (sdk.match(/^\d+(?:\.\d+){0,2}/) ?? [])[0] : null;
67358
67493
  if (sdkVersion && !semverAtLeast(sdkVersion, SUPPORT_MATRIX.opencode.minimum)) {
@@ -67379,14 +67514,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67379
67514
  status: "pass",
67380
67515
  detail: "opencode CLI not on PATH — skipping version check"
67381
67516
  };
67382
- const scratch = mkdtempSync(path38.join(os12.tmpdir(), "workit-doctor-opencode-"));
67517
+ const scratch = mkdtempSync(path39.join(os12.tmpdir(), "workit-doctor-opencode-"));
67383
67518
  let raw;
67384
67519
  try {
67385
67520
  raw = versionOf("opencode", {
67386
67521
  ...res.env,
67387
- XDG_DATA_HOME: path38.join(scratch, "data"),
67388
- XDG_STATE_HOME: path38.join(scratch, "state"),
67389
- XDG_CACHE_HOME: path38.join(scratch, "cache")
67522
+ XDG_DATA_HOME: path39.join(scratch, "data"),
67523
+ XDG_STATE_HOME: path39.join(scratch, "state"),
67524
+ XDG_CACHE_HOME: path39.join(scratch, "cache")
67390
67525
  }, 5000);
67391
67526
  } finally {
67392
67527
  rmSync4(scratch, { recursive: true, force: true });
@@ -67450,7 +67585,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67450
67585
  const tables = new Map;
67451
67586
  let text;
67452
67587
  try {
67453
- text = readFileSync16(file, "utf8");
67588
+ text = readFileSync17(file, "utf8");
67454
67589
  } catch {
67455
67590
  return tables;
67456
67591
  }
@@ -67488,19 +67623,19 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67488
67623
  current[unquote(pair[1])] = value === "true" ? true : value === "false" ? false : unquote(value);
67489
67624
  }
67490
67625
  return tables;
67491
- }, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path38.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
67626
+ }, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path39.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
67492
67627
  const codexHome = codexHomeOf(res);
67493
- const configFile = path38.join(codexHome, "config.toml");
67628
+ const configFile = path39.join(codexHome, "config.toml");
67494
67629
  const installs = [];
67495
67630
  for (const [key, table] of readTomlTables(configFile)) {
67496
67631
  const [kind, id, ...rest] = key.split("\x00");
67497
67632
  if (kind !== "plugins" || rest.length > 0 || !id?.startsWith("workit@") || table.enabled === false)
67498
67633
  continue;
67499
67634
  const marketplace = id.slice("workit@".length);
67500
- const dir = path38.join(codexHome, "plugins", "cache", marketplace, "workit");
67635
+ const dir = path39.join(codexHome, "plugins", "cache", marketplace, "workit");
67501
67636
  let versions = [];
67502
67637
  try {
67503
- versions = readdirSync8(dir).filter((v) => existsSync16(path38.join(dir, v, ".codex-plugin")));
67638
+ versions = readdirSync10(dir).filter((v) => existsSync16(path39.join(dir, v, ".codex-plugin")));
67504
67639
  } catch {
67505
67640
  versions = [];
67506
67641
  }
@@ -67508,7 +67643,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67508
67643
  installs.push({
67509
67644
  id,
67510
67645
  marketplace,
67511
- root: version ? path38.join(dir, version) : null,
67646
+ root: version ? path39.join(dir, version) : null,
67512
67647
  version,
67513
67648
  configFile
67514
67649
  });
@@ -67530,7 +67665,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67530
67665
  };
67531
67666
  return `sha256:${createHash14("sha256").update(JSON.stringify(body)).digest("hex")}`;
67532
67667
  }, codexHookTrust = (install, tables) => {
67533
- const hooks = install.root ? readJson(path38.join(install.root, "hooks", "hooks.json"))?.hooks : null;
67668
+ const hooks = install.root ? readJson(path39.join(install.root, "hooks", "hooks.json"))?.hooks : null;
67534
67669
  if (!hooks || typeof hooks !== "object")
67535
67670
  return null;
67536
67671
  const result = [];
@@ -67603,25 +67738,25 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67603
67738
  status: "pass",
67604
67739
  detail: "no Workit Codex plugin enabled — skipping"
67605
67740
  };
67606
- const agentsDir = path38.join(codexHomeOf(res), "agents");
67741
+ const agentsDir = path39.join(codexHomeOf(res), "agents");
67607
67742
  const problems = [];
67608
67743
  const fixes = [];
67609
67744
  const installed = [];
67610
67745
  for (const install of installs) {
67611
- const source = install.root ? path38.join(install.root, "agents") : null;
67612
- const files = source && existsSync16(source) ? readdirSync8(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
67746
+ const source = install.root ? path39.join(install.root, "agents") : null;
67747
+ const files = source && existsSync16(source) ? readdirSync10(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
67613
67748
  if (files.length === 0) {
67614
67749
  problems.push(`${install.id} ${install.version ?? "?"} bundles no agents`);
67615
67750
  fixes.push(`codex plugin remove ${install.id} && codex plugin add ${install.id}`);
67616
67751
  continue;
67617
67752
  }
67618
- const install_fix = `node ${JSON.stringify(path38.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
67619
- const pluginVersion = readJson(path38.join(install.root, "package.json"))?.version;
67753
+ const install_fix = `node ${JSON.stringify(path39.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
67754
+ const pluginVersion = readJson(path39.join(install.root, "package.json"))?.version;
67620
67755
  const version = typeof pluginVersion === "string" ? pluginVersion.match(SEMVER)?.[0] ?? null : null;
67621
67756
  let stale = false;
67622
67757
  for (const file of files) {
67623
- const dest = path38.join(agentsDir, file);
67624
- const want = stampCodexAgent(readFileSync16(path38.join(source, file), "utf8"), version);
67758
+ const dest = path39.join(agentsDir, file);
67759
+ const want = stampCodexAgent(readFileSync17(path39.join(source, file), "utf8"), version);
67625
67760
  let link = false;
67626
67761
  try {
67627
67762
  link = lstatSync5(dest).isSymbolicLink();
@@ -67635,7 +67770,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67635
67770
  }
67636
67771
  let have = null;
67637
67772
  try {
67638
- have = readFileSync16(dest, "utf8");
67773
+ have = readFileSync17(dest, "utf8");
67639
67774
  } catch {
67640
67775
  have = null;
67641
67776
  }
@@ -67674,7 +67809,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67674
67809
  const env = res.env.PI_CODING_AGENT_DIR;
67675
67810
  if (env)
67676
67811
  return env === "~" ? res.home : env.replace(/^~(?=\/|\\)/, res.home);
67677
- return path38.join(res.home, ".pi", "agent");
67812
+ return path39.join(res.home, ".pi", "agent");
67678
67813
  }, npmSpecName = (spec) => {
67679
67814
  const at = spec.indexOf("@", spec.startsWith("@") ? 1 : 0);
67680
67815
  return at > 0 ? { name: spec.slice(0, at), pinned: true } : { name: spec, pinned: false };
@@ -67682,10 +67817,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67682
67817
  const scopes = [
67683
67818
  {
67684
67819
  scope: "project",
67685
- settingsFile: path38.join(res.cwd, ".pi", "settings.json"),
67686
- base: path38.join(res.cwd, ".pi")
67820
+ settingsFile: path39.join(res.cwd, ".pi", "settings.json"),
67821
+ base: path39.join(res.cwd, ".pi")
67687
67822
  },
67688
- { scope: "user", settingsFile: path38.join(agentDir, "settings.json"), base: agentDir }
67823
+ {
67824
+ scope: "user",
67825
+ settingsFile: path39.join(agentDir, "settings.json"),
67826
+ base: agentDir
67827
+ }
67689
67828
  ];
67690
67829
  for (const { scope, settingsFile, base } of scopes) {
67691
67830
  const packages = readJson(settingsFile)?.packages;
@@ -67700,13 +67839,20 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67700
67839
  const spec = npmSpecName(source.slice(4).trim());
67701
67840
  if (spec.name !== PI_PACKAGE)
67702
67841
  continue;
67703
- const root = path38.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
67704
- return { scope, settingsFile, source, filter, root, npm: { pinned: spec.pinned } };
67842
+ const root = path39.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
67843
+ return {
67844
+ scope,
67845
+ settingsFile,
67846
+ source,
67847
+ filter,
67848
+ root,
67849
+ npm: { pinned: spec.pinned }
67850
+ };
67705
67851
  }
67706
67852
  if (/^(git:|https?:|ssh:|git@)/.test(source))
67707
67853
  continue;
67708
- const local = path38.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
67709
- if (readJson(path38.join(local, "package.json"))?.name === PI_PACKAGE)
67854
+ const local = path39.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
67855
+ if (readJson(path39.join(local, "package.json"))?.name === PI_PACKAGE)
67710
67856
  return { scope, settingsFile, source, filter, root: local, npm: null };
67711
67857
  }
67712
67858
  }
@@ -67717,7 +67863,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67717
67863
  const list = patterns.filter((p) => typeof p === "string");
67718
67864
  if (list.length === 0)
67719
67865
  return false;
67720
- const name = path38.posix.basename(file);
67866
+ const name = path39.posix.basename(file);
67721
67867
  const matches = (p) => {
67722
67868
  const re = globToRegExp3(p);
67723
67869
  return re.test(file) || re.test(name);
@@ -67735,19 +67881,23 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67735
67881
  }, checkPiExtension = (res) => {
67736
67882
  const agentDir = piAgentDirOf(res);
67737
67883
  if (!existsSync16(agentDir) && !commandOnPath("pi", res.env))
67738
- return { id: "pi_extension", status: "pass", detail: "Pi not installed — skipping" };
67884
+ return {
67885
+ id: "pi_extension",
67886
+ status: "pass",
67887
+ detail: "Pi not installed — skipping"
67888
+ };
67739
67889
  const installCmd = (scope, spec = `npm:${PI_PACKAGE}`) => `pi install ${spec}${scope === "project" ? " -l" : ""}`;
67740
67890
  const entry = findPiEntry(res, agentDir);
67741
67891
  if (!entry)
67742
67892
  return {
67743
67893
  id: "pi_extension",
67744
67894
  status: "warn",
67745
- detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path38.join(agentDir, "settings.json")} or ${path38.join(res.cwd, ".pi", "settings.json")}`,
67895
+ detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path39.join(agentDir, "settings.json")} or ${path39.join(res.cwd, ".pi", "settings.json")}`,
67746
67896
  fix: installCmd("user")
67747
67897
  };
67748
67898
  const where = `${entry.source} (${entry.scope}, ${entry.settingsFile})`;
67749
67899
  const rebuild = `cd ${JSON.stringify(entry.root)} && bun run build`;
67750
- const pkg = readJson(path38.join(entry.root, "package.json"));
67900
+ const pkg = readJson(path39.join(entry.root, "package.json"));
67751
67901
  if (!pkg)
67752
67902
  return {
67753
67903
  id: "pi_extension",
@@ -67756,7 +67906,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67756
67906
  fix: entry.npm ? installCmd(entry.scope, entry.source) : `restore ${entry.root}, then ${rebuild}`
67757
67907
  };
67758
67908
  const declared = Array.isArray(pkg.pi?.extensions) ? pkg.pi.extensions.some((e) => typeof e === "string" && e.replace(/^\.\//, "") === PI_EXTENSION) : false;
67759
- if (!declared || !existsSync16(path38.join(entry.root, PI_EXTENSION)))
67909
+ if (!declared || !existsSync16(path39.join(entry.root, PI_EXTENSION)))
67760
67910
  return {
67761
67911
  id: "pi_extension",
67762
67912
  status: "warn",
@@ -67809,21 +67959,21 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67809
67959
  detail: `Workit Pi extension ${version ?? "?"} from ${where} loads ${PI_EXTENSION}`
67810
67960
  };
67811
67961
  }, assetPathsFor = (host, dev) => {
67812
- const pkg = path38.join(dev, "packages", `workit-${host}`);
67962
+ const pkg = path39.join(dev, "packages", `workit-${host}`);
67813
67963
  switch (host) {
67814
67964
  case "opencode": {
67815
- const packaged = path38.join(pkg, "assets", "skills");
67816
- const root = existsSync16(packaged) ? packaged : path38.join(dev, "packages", "workit-core", "skills");
67817
- return WORKIT_METHOD_SKILLS.map((skill) => path38.join(root, skill, "SKILL.md"));
67965
+ const packaged = path39.join(pkg, "assets", "skills");
67966
+ const root = existsSync16(packaged) ? packaged : path39.join(dev, "packages", "workit-core", "skills");
67967
+ return WORKIT_METHOD_SKILLS.map((skill) => path39.join(root, skill, "SKILL.md"));
67818
67968
  }
67819
67969
  case "cursor":
67820
67970
  return [
67821
- path38.join(pkg, "assets", "templates", "workit-contract.md"),
67822
- path38.join(pkg, "mcp.json"),
67823
- path38.join(pkg, ".cursor-plugin")
67971
+ path39.join(pkg, "assets", "templates", "workit-contract.md"),
67972
+ path39.join(pkg, "mcp.json"),
67973
+ path39.join(pkg, ".cursor-plugin")
67824
67974
  ];
67825
67975
  case "cli":
67826
- return [path38.join(pkg, "assets", "templates", "spec-template.md")];
67976
+ return [path39.join(pkg, "assets", "templates", "spec-template.md")];
67827
67977
  }
67828
67978
  }, hostsFor = (host) => host === "cli" ? ["opencode", "cursor", "cli"] : [host], checkAssets = (res) => {
67829
67979
  const dev = res.dev;
@@ -67854,22 +68004,22 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67854
68004
  fix: "Reinstall or rebuild the workit package (missing assets under packages/workit-<host>)"
67855
68005
  };
67856
68006
  }, launcherSlotsFor = (host, dev) => {
67857
- const pkg = path38.join(dev, "packages", `workit-${host}`);
68007
+ const pkg = path39.join(dev, "packages", `workit-${host}`);
67858
68008
  switch (host) {
67859
68009
  case "opencode":
67860
- return [[path38.join(pkg, "src", "plugin.ts"), path38.join(pkg, "dist", "plugin.js")]];
68010
+ return [[path39.join(pkg, "src", "plugin.ts"), path39.join(pkg, "dist", "plugin.js")]];
67861
68011
  case "cursor":
67862
68012
  return [
67863
- [path38.join(pkg, "dist", "mcp-server.js")],
67864
- [path38.join(pkg, "dist", "cursor-session-start.js")]
68013
+ [path39.join(pkg, "dist", "mcp-server.js")],
68014
+ [path39.join(pkg, "dist", "cursor-session-start.js")]
67865
68015
  ];
67866
68016
  case "cli":
67867
- return [[path38.join(pkg, "src", "index.tsx"), path38.join(pkg, "dist", "index.js")]];
68017
+ return [[path39.join(pkg, "src", "index.tsx"), path39.join(pkg, "dist", "index.js")]];
67868
68018
  }
67869
68019
  }, validNodeEntry = (entry, runtime, env) => {
67870
68020
  try {
67871
68021
  const stat = statSync6(entry);
67872
- if (!stat.isFile() || stat.size === 0 || !readFileSync16(entry, "utf8").startsWith(`#!/usr/bin/env node
68022
+ if (!stat.isFile() || stat.size === 0 || !readFileSync17(entry, "utf8").startsWith(`#!/usr/bin/env node
67873
68023
  `)) {
67874
68024
  return false;
67875
68025
  }
@@ -67891,7 +68041,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67891
68041
  if (typeof command !== "string" || !Array.isArray(args) || typeof args[0] !== "string") {
67892
68042
  return "invalid";
67893
68043
  }
67894
- const executable = path38.basename(command).toLowerCase();
68044
+ const executable = path39.basename(command).toLowerCase();
67895
68045
  const canonical = cursorMcpServerEntry("").args;
67896
68046
  if (executable === "npx" || executable === "npx.exe" || executable === "npx.cmd") {
67897
68047
  if (args.length !== canonical.length || args.some((a, i) => a !== canonical[i]))
@@ -67903,10 +68053,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67903
68053
  return {
67904
68054
  kind: "node",
67905
68055
  runtime: command,
67906
- entry: path38.isAbsolute(args[0]) ? args[0] : path38.resolve(path38.dirname(res.cursorMcp), args[0])
68056
+ entry: path39.isAbsolute(args[0]) ? args[0] : path39.resolve(path39.dirname(res.cursorMcp), args[0])
67907
68057
  };
67908
68058
  }, canonicalCursorHook, isCanonicalCursorHook = (command, res) => command === canonicalCursorHook || command === cursorHooksEntry(res.cursorPluginDir).command, registeredCursorHook = (res) => {
67909
- const hooksFile = path38.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
68059
+ const hooksFile = path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
67910
68060
  if (!existsSync16(hooksFile))
67911
68061
  return "invalid";
67912
68062
  const config = readJson(hooksFile);
@@ -67924,9 +68074,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67924
68074
  if (!m)
67925
68075
  return false;
67926
68076
  const entry = m[1].trim();
67927
- if (!path38.isAbsolute(entry))
68077
+ if (!path39.isAbsolute(entry))
67928
68078
  return false;
67929
- if (path38.resolve(entry) !== path38.resolve(path38.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
68079
+ if (path39.resolve(entry) !== path39.resolve(path39.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
67930
68080
  return false;
67931
68081
  }
67932
68082
  return validNodeEntry(entry, "node", res.env);
@@ -67935,7 +68085,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67935
68085
  const hosts = hostsFor(res.host);
67936
68086
  const registered = hosts.includes("cursor") ? registeredCursorLauncher(res) : null;
67937
68087
  const runtime = registered && registered !== "invalid" && registered.kind === "node" ? registered.runtime : "node";
67938
- const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) => path38.join(res.cursorPluginDir, rel)).filter((p) => !validNodeEntry(p, runtime, res.env)).map((p) => `cursor: ${p}`) : [];
68088
+ const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) => path39.join(res.cursorPluginDir, rel)).filter((p) => !validNodeEntry(p, runtime, res.env)).map((p) => `cursor: ${p}`) : [];
67939
68089
  if (hosts.includes("cursor")) {
67940
68090
  if (registered === "invalid") {
67941
68091
  missing.push(`cursor: canonical workit MCP launcher in ${res.cursorMcp}`);
@@ -67944,9 +68094,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67944
68094
  }
67945
68095
  const hook = registeredCursorHook(res);
67946
68096
  if (hook === "invalid") {
67947
- missing.push(`cursor: canonical session-start hook in ${path38.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
68097
+ missing.push(`cursor: canonical session-start hook in ${path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
67948
68098
  } else if (hook !== null && !isCanonicalCursorHook(hook, res) && !validLocalDistHook(hook, res)) {
67949
- missing.push(`cursor: canonical session-start hook in ${path38.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
68099
+ missing.push(`cursor: canonical session-start hook in ${path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
67950
68100
  }
67951
68101
  }
67952
68102
  if (dev) {
@@ -67982,10 +68132,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67982
68132
  const normal = entry.replaceAll("\\", "/").replace(/\/+$/, "");
67983
68133
  if (!normal.endsWith("local/workit") && !normal.endsWith("local/workflow-toolkit"))
67984
68134
  continue;
67985
- const dir = entry.startsWith("~") ? path38.join(res.home, entry.slice(1)) : entry;
68135
+ const dir = entry.startsWith("~") ? path39.join(res.home, entry.slice(1)) : entry;
67986
68136
  let state = null;
67987
68137
  try {
67988
- state = readdirSync8(dir).length === 0 ? "empty" : null;
68138
+ state = readdirSync10(dir).length === 0 ? "empty" : null;
67989
68139
  } catch {
67990
68140
  state = "missing";
67991
68141
  }
@@ -68007,17 +68157,26 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68007
68157
  status: "pass",
68008
68158
  detail: "no registered Workit host plugin is missing its files"
68009
68159
  };
68010
- return { id: "plugin_files", status: "warn", detail: problems.join("; "), fix: fixes.join("; ") };
68160
+ return {
68161
+ id: "plugin_files",
68162
+ status: "warn",
68163
+ detail: problems.join("; "),
68164
+ fix: fixes.join("; ")
68165
+ };
68011
68166
  }, CURSOR_HOOK_FIX = "Run `workit init` and select Cursor to install the bundled hook", checkCursorHook = (res) => {
68012
68167
  if (!hostsFor(res.host).includes("cursor"))
68013
- return { id: "cursor_hook", status: "pass", detail: "cursor hook not inspected on this host" };
68168
+ return {
68169
+ id: "cursor_hook",
68170
+ status: "pass",
68171
+ detail: "cursor hook not inspected on this host"
68172
+ };
68014
68173
  if (!existsSync16(res.cursorPluginDir))
68015
68174
  return {
68016
68175
  id: "cursor_hook",
68017
68176
  status: "pass",
68018
68177
  detail: "no Cursor plugin install — hook launcher not inspected"
68019
68178
  };
68020
- const launcher = path38.join(res.cursorPluginDir, "hooks", "launch.mjs");
68179
+ const launcher = path39.join(res.cursorPluginDir, "hooks", "launch.mjs");
68021
68180
  if (!existsSync16(launcher))
68022
68181
  return {
68023
68182
  id: "cursor_hook",
@@ -68025,7 +68184,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68025
68184
  detail: `cursor hook launcher mode: missing — ${launcher} is absent (an install from before the pinned launcher)`,
68026
68185
  fix: CURSOR_HOOK_FIX
68027
68186
  };
68028
- const hooksFile = path38.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
68187
+ const hooksFile = path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
68029
68188
  const registered = readJson(hooksFile);
68030
68189
  const sessionCommand = registered?.hooks?.sessionStart?.[0]?.command;
68031
68190
  const offLauncher = [
@@ -68084,7 +68243,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68084
68243
  detail: `${label}; probe ${ms} ms; ${silent}`,
68085
68244
  fix: "Open a Cursor agent chat, then re-run `workit doctor`; if this persists, re-run `workit init` with Cursor (it writes absolute hook paths)"
68086
68245
  };
68087
- return { id: "cursor_hook", status: "pass", detail: `${label}; probe ${ms} ms` };
68246
+ return {
68247
+ id: "cursor_hook",
68248
+ status: "pass",
68249
+ detail: `${label}; probe ${ms} ms`
68250
+ };
68088
68251
  }, SILENT_HOOK_GRACE_MS, mtimeMs = (file) => {
68089
68252
  try {
68090
68253
  return statSync6(file).mtimeMs;
@@ -68092,10 +68255,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68092
68255
  return null;
68093
68256
  }
68094
68257
  }, silentCursorHooks = (res) => {
68095
- const session = mtimeMs(path38.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
68258
+ const session = mtimeMs(path39.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
68096
68259
  if (session === null || Date.now() - session < SILENT_HOOK_GRACE_MS)
68097
68260
  return null;
68098
- const hook = mtimeMs(path38.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
68261
+ const hook = mtimeMs(path39.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
68099
68262
  if (hook !== null && hook >= session)
68100
68263
  return null;
68101
68264
  return `Cursor started the Workit MCP server at ${new Date(session).toISOString()} but no Workit hook has run since${hook === null ? " (none ever)" : ""} — if the agent ran in that window, Cursor is not running the plugin hooks and Workit enforcement is off`;
@@ -68191,7 +68354,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68191
68354
  }
68192
68355
  return { id: "stale_pin", status: "pass", detail: "opencode pin resolves" };
68193
68356
  }, pluginMcpSelectors = (res) => {
68194
- const p = path38.join(res.cursorPluginDir, "mcp.json");
68357
+ const p = path39.join(res.cursorPluginDir, "mcp.json");
68195
68358
  if (!existsSync16(p))
68196
68359
  return null;
68197
68360
  const cfg = readJson(p);
@@ -68225,19 +68388,19 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68225
68388
  return null;
68226
68389
  }
68227
68390
  }, installedPluginVersion = (res) => {
68228
- const pkg = readJson(path38.join(res.cursorPluginDir, "package.json"));
68391
+ const pkg = readJson(path39.join(res.cursorPluginDir, "package.json"));
68229
68392
  return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
68230
68393
  }, installedOpenCodeCache = (res) => {
68231
68394
  if (res.opencodePackageCacheDir !== null) {
68232
- const pkg = readJson(path38.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
68395
+ const pkg = readJson(path39.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
68233
68396
  return typeof pkg?.version === "string" && pkg.version ? { version: pkg.version, dir: res.opencodePackageCacheDir } : null;
68234
68397
  }
68235
68398
  const found = openCodeInstalledPlugin(res.home, res.env);
68236
68399
  if (!found)
68237
68400
  return null;
68238
- let dir = path38.dirname(path38.dirname(path38.dirname(found.root)));
68239
- if (path38.basename(dir) !== "workit-opencode@latest")
68240
- dir = path38.dirname(dir);
68401
+ let dir = path39.dirname(path39.dirname(path39.dirname(found.root)));
68402
+ if (path39.basename(dir) !== "workit-opencode@latest")
68403
+ dir = path39.dirname(dir);
68241
68404
  return { version: found.version, dir };
68242
68405
  }, checkOpenCodePackageCache = (res) => {
68243
68406
  if (!existsSync16(res.opencodeConfig)) {
@@ -68341,7 +68504,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68341
68504
  fix: "Re-run install-cursor-plugin.sh — it rewrites the workit MCP entry to the canonical @latest selector"
68342
68505
  };
68343
68506
  }
68344
- const hooksFile = path38.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
68507
+ const hooksFile = path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
68345
68508
  const hookCmd = readJson(hooksFile)?.hooks?.sessionStart?.[0]?.command ?? null;
68346
68509
  const canonicalHook = canonicalCursorHook;
68347
68510
  const staleHook = hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && !hookCmd.startsWith("node ") && hookCmd.includes("@brainervirus/workit-cursor@");
@@ -68363,7 +68526,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68363
68526
  };
68364
68527
  }
68365
68528
  const installed = installedPluginVersion(res);
68366
- const source = res.dev ? readJson(path38.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path38.join(packageRoot(), "package.json"))?.version ?? null;
68529
+ const source = res.dev ? readJson(path39.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path39.join(packageRoot(), "package.json"))?.version ?? null;
68367
68530
  const localDist = mcpSelectors === null && hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && hookCmd.startsWith("node ");
68368
68531
  if (localDist && installed !== null && source !== null && !semverAtLeast(installed, source)) {
68369
68532
  return {
@@ -68375,7 +68538,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68375
68538
  }
68376
68539
  if (localDist && installed !== null && source !== null && res.dev) {
68377
68540
  const entry = /^node\s+(.+)$/.exec((hookCmd ?? "").trim())?.[1]?.trim() ?? "";
68378
- const sourceBundle = entry && path38.isAbsolute(entry) ? path38.join(res.dev, "packages", "workit-cursor", "dist", path38.basename(entry)) : "";
68541
+ const sourceBundle = entry && path39.isAbsolute(entry) ? path39.join(res.dev, "packages", "workit-cursor", "dist", path39.basename(entry)) : "";
68379
68542
  if (entry && sourceBundle) {
68380
68543
  const installedHash = bundleHashOfFile(entry);
68381
68544
  const sourceHash = bundleHashOfFile(sourceBundle);
@@ -68383,7 +68546,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68383
68546
  return {
68384
68547
  id: "stale_install",
68385
68548
  status: "fail",
68386
- detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path38.basename(entry)} hash mismatch)`,
68549
+ detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path39.basename(entry)} hash mismatch)`,
68387
68550
  fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory with the current build"
68388
68551
  };
68389
68552
  }
@@ -68483,7 +68646,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68483
68646
  }, checkMalformedConfig = (res) => {
68484
68647
  const files = [];
68485
68648
  for (const name of ["config.json", "youtrack.json", "vcs.json", "workspaces.json"]) {
68486
- const p = path38.join(res.configDir, name);
68649
+ const p = path39.join(res.configDir, name);
68487
68650
  if (existsSync16(p))
68488
68651
  files.push(p);
68489
68652
  }
@@ -68608,7 +68771,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68608
68771
  const slot = `${SESSION_HOOK_NAME} hook`;
68609
68772
  switch (hook.state) {
68610
68773
  case "not_git":
68611
- return { status: "pass", detail: `not inside a git work tree — skipping the ${slot}` };
68774
+ return {
68775
+ status: "pass",
68776
+ detail: `not inside a git work tree — skipping the ${slot}`
68777
+ };
68612
68778
  case "installed":
68613
68779
  return {
68614
68780
  status: "pass",
@@ -68701,17 +68867,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68701
68867
  return { ...installSessionHook(res.cwd, res.env), workspace: workspace.name };
68702
68868
  }, isPlaceholder = (p) => {
68703
68869
  try {
68704
- return readFileSync16(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
68870
+ return readFileSync17(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
68705
68871
  } catch {
68706
68872
  return false;
68707
68873
  }
68708
68874
  }, checkCredentialMetadata = (res) => {
68709
68875
  const tokenPaths = [];
68710
- const youtrackJson = readJson(path38.join(res.configDir, "youtrack.json"));
68876
+ const youtrackJson = readJson(path39.join(res.configDir, "youtrack.json"));
68711
68877
  if (youtrackJson) {
68712
- tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path38.join(res.configDir, "youtrack.token"));
68713
- } else if (existsSync16(path38.join(res.configDir, "youtrack.token"))) {
68714
- tokenPaths.push(path38.join(res.configDir, "youtrack.token"));
68878
+ tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path39.join(res.configDir, "youtrack.token"));
68879
+ } else if (existsSync16(path39.join(res.configDir, "youtrack.token"))) {
68880
+ tokenPaths.push(path39.join(res.configDir, "youtrack.token"));
68715
68881
  }
68716
68882
  if (tokenPaths.length === 0) {
68717
68883
  return {
@@ -68722,7 +68888,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68722
68888
  }
68723
68889
  const problems = [];
68724
68890
  for (const raw of tokenPaths) {
68725
- const p = path38.isAbsolute(raw) ? raw : path38.resolve(res.configDir, raw);
68891
+ const p = path39.isAbsolute(raw) ? raw : path39.resolve(res.configDir, raw);
68726
68892
  if (!existsSync16(p)) {
68727
68893
  problems.push(`${p} is missing`);
68728
68894
  continue;
@@ -68802,7 +68968,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68802
68968
  },
68803
68969
  ghLogin: (e) => {
68804
68970
  try {
68805
- const out = spawnSync16("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
68971
+ const out = spawnSync16("gh", ["api", "user", "--jq", ".login"], {
68972
+ encoding: "utf8",
68973
+ env: e
68974
+ });
68806
68975
  const login = (out.stdout ?? "").trim();
68807
68976
  return out.status === 0 && login ? login : null;
68808
68977
  } catch {
@@ -68837,8 +69006,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68837
69006
  }), checkGithubIdentity = (res, probes = defaultIdentityProbes(res.env)) => {
68838
69007
  const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
68839
69008
  if (!remoteHost)
68840
- return { id: "github_identity", status: "pass", detail: "no GitHub remote configured" };
68841
- const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path38.join(res.configDir, "vcs.json"));
69009
+ return {
69010
+ id: "github_identity",
69011
+ status: "pass",
69012
+ detail: "no GitHub remote configured"
69013
+ };
69014
+ const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path39.join(res.configDir, "vcs.json"));
68842
69015
  const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
68843
69016
  if (workspace.error)
68844
69017
  return {
@@ -68849,7 +69022,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68849
69022
  };
68850
69023
  const provider = identityProvider(remoteHost, vcsJson, workspace.provider);
68851
69024
  if (provider !== "github")
68852
- return { id: "github_identity", status: "pass", detail: "no GitHub remote configured" };
69025
+ return {
69026
+ id: "github_identity",
69027
+ status: "pass",
69028
+ detail: "no GitHub remote configured"
69029
+ };
68853
69030
  const cliHost = String(vcsJson?.github?.host ?? "github.com").toLowerCase();
68854
69031
  const cliLogin = probes.ghLogin({ ...res.env, GH_HOST: cliHost });
68855
69032
  if (!cliLogin)
@@ -68865,8 +69042,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68865
69042
  }, checkGitLabIdentity = (res, probes = defaultIdentityProbes(res.env)) => {
68866
69043
  const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
68867
69044
  if (!remoteHost)
68868
- return { id: "gitlab_identity", status: "pass", detail: "no GitLab remote configured" };
68869
- const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path38.join(res.configDir, "vcs.json"));
69045
+ return {
69046
+ id: "gitlab_identity",
69047
+ status: "pass",
69048
+ detail: "no GitLab remote configured"
69049
+ };
69050
+ const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path39.join(res.configDir, "vcs.json"));
68870
69051
  const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
68871
69052
  if (workspace.error)
68872
69053
  return {
@@ -68877,7 +69058,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68877
69058
  };
68878
69059
  const provider = identityProvider(remoteHost, vcsJson, workspace.provider);
68879
69060
  if (provider !== "gitlab")
68880
- return { id: "gitlab_identity", status: "pass", detail: "no GitLab remote configured" };
69061
+ return {
69062
+ id: "gitlab_identity",
69063
+ status: "pass",
69064
+ detail: "no GitLab remote configured"
69065
+ };
68881
69066
  const cliHost = String(vcsJson?.gitlab?.host ?? "gitlab.com").toLowerCase();
68882
69067
  const cliLogin = probes.glabLogin?.({ ...res.env, GITLAB_HOST: cliHost }) ?? null;
68883
69068
  if (!cliLogin)
@@ -68892,11 +69077,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68892
69077
  { surface: "SSH", login: probes.sshLogin(remoteHost) }
68893
69078
  ]);
68894
69079
  }, checkLogWritable = (res) => {
68895
- const logsDir = path38.join(res.stateDir, "logs");
68896
- const probe = path38.join(logsDir, "doctor-probe.tmp");
69080
+ const logsDir = path39.join(res.stateDir, "logs");
69081
+ const probe = path39.join(logsDir, "doctor-probe.tmp");
68897
69082
  try {
68898
- mkdirSync7(logsDir, { recursive: true, mode: 448 });
68899
- writeFileSync7(probe, `{"probe":true}
69083
+ mkdirSync8(logsDir, { recursive: true, mode: 448 });
69084
+ writeFileSync8(probe, `{"probe":true}
68900
69085
  `, { mode: 384 });
68901
69086
  return {
68902
69087
  id: "log_writable",
@@ -68926,7 +69111,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68926
69111
  fix
68927
69112
  };
68928
69113
  if (lock.state === "absent")
68929
- return { id: "workspace_lock", status: "pass", detail: "no metadata lock held" };
69114
+ return {
69115
+ id: "workspace_lock",
69116
+ status: "pass",
69117
+ detail: "no metadata lock held"
69118
+ };
68930
69119
  if (lock.state === "stale")
68931
69120
  return {
68932
69121
  id: "workspace_lock",
@@ -68971,7 +69160,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68971
69160
  const counts = { skip: 0, late: 0 };
68972
69161
  let text = "";
68973
69162
  try {
68974
- text = readFileSync16(path38.join(stateDir, HOOK_SKIPS_FILE), "utf8");
69163
+ text = readFileSync17(path39.join(stateDir, HOOK_SKIPS_FILE), "utf8");
68975
69164
  } catch {
68976
69165
  return counts;
68977
69166
  }
@@ -68989,7 +69178,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68989
69178
  }
68990
69179
  return counts;
68991
69180
  }, checkHookDeadline = (res) => {
68992
- const file = path38.join(res.hookStateDir, HOOK_SKIPS_FILE);
69181
+ const file = path39.join(res.hookStateDir, HOOK_SKIPS_FILE);
68993
69182
  const { skip, late } = countHookSkips(res.hookStateDir, Date.now() - DAY_MS);
68994
69183
  const lateText = late > 0 ? `; ${late} answered past it in full` : "";
68995
69184
  if (skip > 0)
@@ -69020,11 +69209,15 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69020
69209
  status: "warn",
69021
69210
  detail: line.split("; ")[0],
69022
69211
  fix: line.split("; ")[1]
69023
- } : { id: "grant_endpoint", status: "pass", detail: "grants and default endpoint agree" };
69212
+ } : {
69213
+ id: "grant_endpoint",
69214
+ status: "pass",
69215
+ detail: "grants and default endpoint agree"
69216
+ };
69024
69217
  }, checkChecksConfig = (res) => {
69025
69218
  const root = checkRoot(res.cwd);
69026
- const current = existsSync16(path38.join(root, CHECKS_FILE));
69027
- const legacy = existsSync16(path38.join(root, LEGACY_CHECKS_FILE));
69219
+ const current = existsSync16(path39.join(root, CHECKS_FILE));
69220
+ const legacy = existsSync16(path39.join(root, LEGACY_CHECKS_FILE));
69028
69221
  if (!legacy)
69029
69222
  return {
69030
69223
  id: "checks_config",
@@ -69039,8 +69232,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69039
69232
  };
69040
69233
  }, fixChecksConfig = (options = {}) => {
69041
69234
  const root = checkRoot(resolve(options).cwd);
69042
- const from = path38.join(root, LEGACY_CHECKS_FILE);
69043
- const to = path38.join(root, CHECKS_FILE);
69235
+ const from = path39.join(root, LEGACY_CHECKS_FILE);
69236
+ const to = path39.join(root, CHECKS_FILE);
69044
69237
  if (!existsSync16(from))
69045
69238
  return { action: "none", detail: `no ${LEGACY_CHECKS_FILE} at ${root}` };
69046
69239
  if (existsSync16(to))
@@ -69100,7 +69293,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69100
69293
  status: "info",
69101
69294
  detail: "no Workit Claude Code plugin install recorded — skipping"
69102
69295
  };
69103
- const registry = readJson(path38.join(claudeConfigDir(res.home, res.env), "plugins", "installed_plugins.json"));
69296
+ const registry = readJson(path39.join(claudeConfigDir(res.home, res.env), "plugins", "installed_plugins.json"));
69104
69297
  const recorded = (install) => {
69105
69298
  const entries = registry?.plugins?.[install.id];
69106
69299
  const entry = Array.isArray(entries) ? entries.find((e) => e?.installPath === install.installPath) : null;
@@ -69150,7 +69343,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69150
69343
  }, HOOK_ISSUED_MARKER = "workit-issued-sessions/3", hookRecordsIssued = (root) => {
69151
69344
  let files;
69152
69345
  try {
69153
- files = readdirSync8(path38.join(root, "dist")).filter((f) => f.endsWith(".js"));
69346
+ files = readdirSync10(path39.join(root, "dist")).filter((f) => f.endsWith(".js"));
69154
69347
  } catch {
69155
69348
  return null;
69156
69349
  }
@@ -69158,7 +69351,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69158
69351
  return null;
69159
69352
  return files.some((file) => {
69160
69353
  try {
69161
- return readFileSync16(path38.join(root, "dist", file), "utf8").includes(HOOK_ISSUED_MARKER);
69354
+ return readFileSync17(path39.join(root, "dist", file), "utf8").includes(HOOK_ISSUED_MARKER);
69162
69355
  } catch {
69163
69356
  return false;
69164
69357
  }
@@ -69214,7 +69407,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69214
69407
  env,
69215
69408
  timeout: WORKIT_PROBE_TIMEOUT_MS,
69216
69409
  shell: true
69217
- }) : spawnSync16(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
69410
+ }) : spawnSync16(bin, ["--version"], {
69411
+ encoding: "utf8",
69412
+ env,
69413
+ timeout: WORKIT_PROBE_TIMEOUT_MS
69414
+ });
69218
69415
  const timedOut = r.error !== undefined && "code" in r.error && r.error.code === "ETIMEDOUT";
69219
69416
  const version = r.status === 0 ? (r.stdout ?? "").match(SEMVER)?.[0] ?? null : null;
69220
69417
  const probe = { version: timedOut ? null : version, timedOut };
@@ -69227,14 +69424,22 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69227
69424
  version: i.version
69228
69425
  })),
69229
69426
  { label: "Cursor plugin", version: installedPluginVersion(res) },
69230
- { label: "OpenCode plugin", version: installedOpenCodeCache(res)?.version ?? null }
69427
+ {
69428
+ label: "OpenCode plugin",
69429
+ version: installedOpenCodeCache(res)?.version ?? null
69430
+ }
69231
69431
  ].filter((p) => p.version !== null);
69232
69432
  const newest = plugins.reduce((best, p) => best && semverAtLeast(best.version, p.version) ? best : p, null);
69233
69433
  const target = newest?.version ?? "latest";
69234
69434
  const fix = `npm i -g ${CLI_PACKAGE}@${target} (or run it without a global install: npx -y ${CLI_PACKAGE}@${target} <command>)`;
69235
69435
  const bin = findOnPath(pathextNames("workit", res.env), res.env);
69236
69436
  if (!bin) {
69237
- return { id: "workit_on_path", status: "warn", detail: "no workit on PATH", fix };
69437
+ return {
69438
+ id: "workit_on_path",
69439
+ status: "warn",
69440
+ detail: "no workit on PATH",
69441
+ fix
69442
+ };
69238
69443
  }
69239
69444
  const { version, timedOut } = workitVersionAt(bin, res.env);
69240
69445
  if (!version) {
@@ -69269,7 +69474,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69269
69474
  const file = permissionTarget(host, paths).file;
69270
69475
  switch (health.state) {
69271
69476
  case "no-host":
69272
- return { id, status: "info", detail: `${host} not found; no permission block needed` };
69477
+ return {
69478
+ id,
69479
+ status: "info",
69480
+ detail: `${host} not found; no permission block needed`
69481
+ };
69273
69482
  case "absent":
69274
69483
  return {
69275
69484
  id,
@@ -69277,10 +69486,79 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69277
69486
  detail: `no Workit permission block in ${file} (optional: \`${fix}\` lets agents run Workit's delivery verbs without a prompt)`
69278
69487
  };
69279
69488
  case "ok":
69280
- return { id, status: "pass", detail: `Workit's permission block is current in ${file}` };
69489
+ return {
69490
+ id,
69491
+ status: "pass",
69492
+ detail: `Workit's permission block is current in ${file}`
69493
+ };
69281
69494
  default:
69282
69495
  return { id, status: "warn", detail: health.detail, fix };
69283
69496
  }
69497
+ }, checkGrantRules = (host, id) => (res) => {
69498
+ const paths = {
69499
+ home: res.home,
69500
+ env: res.codexHome ? { ...res.env, CODEX_HOME: res.codexHome } : res.env,
69501
+ stateDir: res.stateDir,
69502
+ cwd: res.cwd
69503
+ };
69504
+ let entry = null;
69505
+ try {
69506
+ entry = resolveWorkspaceFrom(res.cwd, res.configDir);
69507
+ } catch {}
69508
+ if (!entry)
69509
+ return {
69510
+ id,
69511
+ status: "info",
69512
+ detail: "no Workit workspace here; no grants to cover"
69513
+ };
69514
+ const { grants } = configuredGrants(entry);
69515
+ const granted = ["push", "pr", "merge"].filter((kind) => kind === "merge" ? Boolean(grants.merge) : grants[kind] !== false);
69516
+ const coverage = permissionCoverage(host, grantedCommands(granted), paths);
69517
+ if (coverage.state === "no-host")
69518
+ return {
69519
+ id,
69520
+ status: "info",
69521
+ detail: `${host} not found; no rules to check`
69522
+ };
69523
+ if (coverage.state === "ok")
69524
+ return {
69525
+ id,
69526
+ status: "pass",
69527
+ detail: `${host} rules cover the granted Workit verbs (${granted.join(", ") || "none"}) and the check and lock verbs`
69528
+ };
69529
+ const shown = coverage.missing.slice(0, 4).join(", ");
69530
+ return {
69531
+ id,
69532
+ status: "warn",
69533
+ detail: `${host} rules in ${coverage.file} do not cover ${shown}${coverage.missing.length > 4 ? ", …" : ""}${existsSync16(coverage.file) ? "" : ` (${coverage.file} does not exist yet; \`workit setup\` writes it)`}: agents get a prompt (or an auto-mode denial) on verbs the workspace grants`,
69534
+ fix: `workit setup --hosts=${host}`
69535
+ };
69536
+ }, ago = (ms) => {
69537
+ const minutes = Math.max(0, Math.round(ms / 60000));
69538
+ if (minutes < 1)
69539
+ return "just now";
69540
+ if (minutes < 60)
69541
+ return `${minutes} min ago`;
69542
+ const hours = Math.round(minutes / 60);
69543
+ return hours < 48 ? `${hours} h ago` : `${Math.round(hours / 24)} d ago`;
69544
+ }, checkHookRuns = (res) => {
69545
+ const runs = readHookRuns(res.hookStateDir).filter((run) => run.event === "PreToolUse" || run.event === "SessionStart");
69546
+ if (runs.length === 0)
69547
+ return {
69548
+ id: "hook_runs",
69549
+ status: "info",
69550
+ detail: "no host hook run recorded yet (counted from this Workit version on; Claude Code launcher fast-path answers and Codex PostToolUse are not counted; start an agent session, then re-run `workit doctor`)"
69551
+ };
69552
+ const now = Date.now();
69553
+ const hosts = [...new Set(runs.map((run) => run.host))].toSorted();
69554
+ const parts = hosts.map((host) => {
69555
+ const seen = ["SessionStart", "PreToolUse"].map((event) => {
69556
+ const run = runs.find((r) => r.host === host && r.event === event);
69557
+ return `${event} ${run ? `${ago(now - Date.parse(run.at))} (${run.count})` : "never"}`;
69558
+ });
69559
+ return `${host}: ${seen.join(", ")}`;
69560
+ });
69561
+ return { id: "hook_runs", status: "pass", detail: parts.join("; ") };
69284
69562
  }, RUN_CHECKS, INSTALLER_REQUIRED, runDoctor = (options = {}) => {
69285
69563
  const res = resolve(options);
69286
69564
  const raw = RUN_CHECKS.map((fn) => fn(res));
@@ -69332,6 +69610,7 @@ var init_doctor = __esm(() => {
69332
69610
  init_host_install();
69333
69611
  init_skill_manifests();
69334
69612
  init_session_hook();
69613
+ init_hook_runs();
69335
69614
  init_host_permissions();
69336
69615
  OPENCODE_V1_FIX = `OpenCode < ${SUPPORT_MATRIX.opencode.minimum} cannot load Workit 3 (V2 plugin API only): upgrade OpenCode to ${SUPPORT_MATRIX.opencode.minimum}+, or stay on Workit 2.x by pinning "@brainervirus/workit-opencode@2" in the opencode.json "plugin" array`;
69337
69616
  AGENT_MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
@@ -69371,10 +69650,14 @@ var init_doctor = __esm(() => {
69371
69650
  checkHeavyScope,
69372
69651
  checkHookDeadline,
69373
69652
  checkGrantEndpoint,
69653
+ checkHookRuns,
69374
69654
  checkChecksConfig,
69375
69655
  checkHostPermissions("claude-code", "claude_permissions"),
69376
69656
  checkHostPermissions("codex", "codex_permissions"),
69377
- checkHostPermissions("cursor", "cursor_permissions")
69657
+ checkHostPermissions("cursor", "cursor_permissions"),
69658
+ checkGrantRules("claude-code", "claude_grant_rules"),
69659
+ checkGrantRules("codex", "codex_grant_rules"),
69660
+ checkGrantRules("cursor", "cursor_grant_rules")
69378
69661
  ];
69379
69662
  INSTALLER_REQUIRED = new Set([
69380
69663
  "runtime",
@@ -69394,19 +69677,19 @@ import {
69394
69677
  cpSync as cpSync4,
69395
69678
  existsSync as existsSync17,
69396
69679
  lstatSync as lstatSync6,
69397
- mkdirSync as mkdirSync8,
69680
+ mkdirSync as mkdirSync9,
69398
69681
  mkdtempSync as mkdtempSync2,
69399
- readFileSync as readFileSync17,
69400
- readdirSync as readdirSync9,
69682
+ readFileSync as readFileSync18,
69683
+ readdirSync as readdirSync11,
69401
69684
  realpathSync as realpathSync8,
69402
69685
  renameSync as renameSync5,
69403
69686
  rmSync as rmSync5,
69404
69687
  statSync as statSync7,
69405
- writeFileSync as writeFileSync8
69688
+ writeFileSync as writeFileSync9
69406
69689
  } from "node:fs";
69407
69690
  import { createRequire as createRequire2 } from "node:module";
69408
69691
  import os13 from "node:os";
69409
- import path39 from "node:path";
69692
+ import path40 from "node:path";
69410
69693
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
69411
69694
  function parseList(raw) {
69412
69695
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -69451,7 +69734,7 @@ function buildSetupPreview(values, opts = {}) {
69451
69734
  const current = readConfigFromDir(state.configDir);
69452
69735
  mutations.push({
69453
69736
  type: "merge-json",
69454
- path: path39.join(state.configDir, "config.json"),
69737
+ path: path40.join(state.configDir, "config.json"),
69455
69738
  value: {
69456
69739
  locale: values.locale,
69457
69740
  localeOptions: current.localeOptions,
@@ -69477,17 +69760,17 @@ function buildSetupPreview(values, opts = {}) {
69477
69760
  if (blocked.length === 0) {
69478
69761
  mutations.push({
69479
69762
  type: "update-workspaces",
69480
- path: path39.join(state.configDir, "workspaces.json"),
69763
+ path: path40.join(state.configDir, "workspaces.json"),
69481
69764
  entries: values.workspaces,
69482
69765
  expectedRevision: diskWorkspaceResult.revision
69483
69766
  });
69484
69767
  }
69485
69768
  }
69486
69769
  if (values.baseUrl.trim()) {
69487
- const ytPath = path39.join(state.configDir, "youtrack.json");
69770
+ const ytPath = path40.join(state.configDir, "youtrack.json");
69488
69771
  const ytExisting = readConfigRecord(ytPath);
69489
69772
  const ytConfigured = configuredTokenPath(ytExisting, "tokenFile");
69490
- const tokenPath = resolveTokenPath(ytConfigured, path39.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
69773
+ const tokenPath = resolveTokenPath(ytConfigured, path40.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
69491
69774
  const draft = youtrackDraft(values, tokenPath);
69492
69775
  if (ytConfigured !== null && tokenPath !== ytConfigured) {
69493
69776
  delete draft.tokenFile;
@@ -69515,7 +69798,7 @@ function buildSetupPreview(values, opts = {}) {
69515
69798
  });
69516
69799
  }
69517
69800
  if (values.vcsProvider !== "skip") {
69518
- const vcsPath = path39.join(state.configDir, "vcs.json");
69801
+ const vcsPath = path40.join(state.configDir, "vcs.json");
69519
69802
  mutations.push({
69520
69803
  type: "merge-json",
69521
69804
  path: vcsPath,
@@ -69544,7 +69827,7 @@ function buildSetupPreview(values, opts = {}) {
69544
69827
  mutations.push({
69545
69828
  type: "install-host",
69546
69829
  platform: "cursor",
69547
- path: path39.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
69830
+ path: path40.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
69548
69831
  commands
69549
69832
  });
69550
69833
  }
@@ -69557,7 +69840,7 @@ function buildSetupPreview(values, opts = {}) {
69557
69840
  mutations.push({
69558
69841
  type: "install-host",
69559
69842
  platform,
69560
- path: platform === "claude-code" ? claudeInstallRecord(paths.home, env) : platform === "codex" ? path39.join(env.CODEX_HOME ?? path39.join(paths.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : path39.join(env.PI_CODING_AGENT_DIR ?? path39.join(paths.home, ".pi", "agent"), "settings.json"),
69843
+ path: platform === "claude-code" ? claudeInstallRecord(paths.home, env) : platform === "codex" ? path40.join(env.CODEX_HOME ?? path40.join(paths.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : path40.join(env.PI_CODING_AGENT_DIR ?? path40.join(paths.home, ".pi", "agent"), "settings.json"),
69561
69844
  commands
69562
69845
  });
69563
69846
  }
@@ -69569,8 +69852,8 @@ function buildSetupPreview(values, opts = {}) {
69569
69852
  projectRefused = target.refused;
69570
69853
  if (target?.ok) {
69571
69854
  const root = target.root;
69572
- const gitignorePath = path39.join(root, ".gitignore");
69573
- const existing = existsSync17(gitignorePath) ? readFileSync17(gitignorePath, "utf8") : "";
69855
+ const gitignorePath = path40.join(root, ".gitignore");
69856
+ const existing = existsSync17(gitignorePath) ? readFileSync18(gitignorePath, "utf8") : "";
69574
69857
  const existingLines = new Set(existing.split(`
69575
69858
  `).map((l) => l.trim()).filter(Boolean));
69576
69859
  const entries = GITIGNORE_ENTRIES.filter((e) => e.trim() !== "" && !existingLines.has(e.trim()));
@@ -69607,10 +69890,10 @@ function deepMerge(base, patch) {
69607
69890
  return out;
69608
69891
  }
69609
69892
  function applyMutation(m) {
69610
- const dir = path39.dirname(m.path);
69893
+ const dir = path40.dirname(m.path);
69611
69894
  switch (m.type) {
69612
69895
  case "create-file": {
69613
- mkdirSync8(dir, { recursive: true });
69896
+ mkdirSync9(dir, { recursive: true });
69614
69897
  const res = writeFileExclusive(m.path, m.content, m.mode);
69615
69898
  return res === "created" ? { platform: "core", file: m.path, status: "Installed" } : {
69616
69899
  platform: "core",
@@ -69633,8 +69916,8 @@ function applyMutation(m) {
69633
69916
  if (existing.kind === "record" && JSON.stringify(existing.value) === JSON.stringify(merged)) {
69634
69917
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
69635
69918
  }
69636
- mkdirSync8(dir, { recursive: true });
69637
- writeFileSync8(m.path, JSON.stringify(merged, null, 2) + `
69919
+ mkdirSync9(dir, { recursive: true });
69920
+ writeFileSync9(m.path, JSON.stringify(merged, null, 2) + `
69638
69921
  `, "utf8");
69639
69922
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
69640
69923
  }
@@ -69675,10 +69958,10 @@ function applyMutation(m) {
69675
69958
  `;
69676
69959
  if (prev === next)
69677
69960
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
69678
- mkdirSync8(dir, { recursive: true });
69961
+ mkdirSync9(dir, { recursive: true });
69679
69962
  const tmp = `${m.path}.${process.pid}.tmp`;
69680
69963
  try {
69681
- writeFileSync8(tmp, next, "utf8");
69964
+ writeFileSync9(tmp, next, "utf8");
69682
69965
  if (workspacesRevision(readFileSafe(m.path)) !== m.expectedRevision) {
69683
69966
  rmSync5(tmp, { force: true });
69684
69967
  return {
@@ -69713,11 +69996,11 @@ function applyMutation(m) {
69713
69996
  detail: "all entries already present"
69714
69997
  };
69715
69998
  }
69716
- mkdirSync8(dir, { recursive: true });
69999
+ mkdirSync9(dir, { recursive: true });
69717
70000
  const separator = existing && !existing.endsWith(`
69718
70001
  `) ? `
69719
70002
  ` : "";
69720
- writeFileSync8(m.path, existing + separator + (existing ? `
70003
+ writeFileSync9(m.path, existing + separator + (existing ? `
69721
70004
  ` : "") + add.join(`
69722
70005
  `) + `
69723
70006
  `, "utf8");
@@ -69756,8 +70039,8 @@ function applyMutation(m) {
69756
70039
  detail: "already configured"
69757
70040
  };
69758
70041
  }
69759
- mkdirSync8(path39.dirname(m.path), { recursive: true });
69760
- writeFileSync8(m.path, JSON.stringify(merged, null, 2) + `
70042
+ mkdirSync9(path40.dirname(m.path), { recursive: true });
70043
+ writeFileSync9(m.path, JSON.stringify(merged, null, 2) + `
69761
70044
  `, "utf8");
69762
70045
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
69763
70046
  }
@@ -69765,11 +70048,11 @@ function applyMutation(m) {
69765
70048
  }
69766
70049
  function adapterRoot(platform, res, preferInstalledAdapter = false) {
69767
70050
  const candidates = [];
69768
- const stagedCursor = path39.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
70051
+ const stagedCursor = path40.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
69769
70052
  if (platform === "cursor" && preferInstalledAdapter)
69770
70053
  candidates.push(stagedCursor);
69771
70054
  if (res.dev) {
69772
- candidates.push(path39.join(res.dev, "packages", `workit-${platform}`));
70055
+ candidates.push(path40.join(res.dev, "packages", `workit-${platform}`));
69773
70056
  } else {
69774
70057
  candidates.push(...nodeResolvedAdapters(platform));
69775
70058
  if (platform === "cursor")
@@ -69779,10 +70062,10 @@ function adapterRoot(platform, res, preferInstalledAdapter = false) {
69779
70062
  if (cached)
69780
70063
  candidates.push(cached.root);
69781
70064
  }
69782
- let dir = path39.resolve(res.cwd);
70065
+ let dir = path40.resolve(res.cwd);
69783
70066
  while (true) {
69784
- candidates.push(path39.join(dir, "packages", `workit-${platform}`));
69785
- const parent = path39.dirname(dir);
70067
+ candidates.push(path40.join(dir, "packages", `workit-${platform}`));
70068
+ const parent = path40.dirname(dir);
69786
70069
  if (parent === dir)
69787
70070
  break;
69788
70071
  dir = parent;
@@ -69804,7 +70087,7 @@ function applyOpenCode(root, res) {
69804
70087
  detail: "workit-opencode package has no loadable plugin entry (src/plugin.ts or dist/plugin.js)"
69805
70088
  };
69806
70089
  }
69807
- mkdirSync8(path39.dirname(res.opencodeConfig), { recursive: true });
70090
+ mkdirSync9(path40.dirname(res.opencodeConfig), { recursive: true });
69808
70091
  const existing = readExisting(res.opencodeConfig);
69809
70092
  if (existing.kind === "malformed") {
69810
70093
  return {
@@ -69833,7 +70116,7 @@ function applyOpenCode(root, res) {
69833
70116
  detail: "already registered"
69834
70117
  };
69835
70118
  }
69836
- writeFileSync8(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
70119
+ writeFileSync9(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
69837
70120
  `, "utf8");
69838
70121
  return existing.kind === "record" ? {
69839
70122
  platform: "opencode",
@@ -69849,31 +70132,31 @@ function applyOpenCode(root, res) {
69849
70132
  }
69850
70133
  function copyPluginDir(src, dest) {
69851
70134
  const realSrc = realpathSync8(src);
69852
- const marker = path39.join(dest, ".workit-root");
70135
+ const marker = path40.join(dest, ".workit-root");
69853
70136
  const destIsLink = existsSync17(dest) && lstatSync6(dest).isSymbolicLink();
69854
70137
  const synced = !destIsLink && readFileSafe(marker)?.trim() === realSrc && samePluginContent(realSrc, dest, dest);
69855
70138
  if (synced)
69856
70139
  return "Skipped";
69857
70140
  const hadDir = existsSync17(dest);
69858
70141
  const rules = preservedCursorRules(realSrc, dest);
69859
- const parent = path39.dirname(dest);
69860
- mkdirSync8(parent, { recursive: true });
69861
- const swap = mkdtempSync2(path39.join(parent, `.${path39.basename(dest)}.swap-`));
69862
- const stage = path39.join(swap, "stage");
69863
- const backup = path39.join(swap, "backup");
70142
+ const parent = path40.dirname(dest);
70143
+ mkdirSync9(parent, { recursive: true });
70144
+ const swap = mkdtempSync2(path40.join(parent, `.${path40.basename(dest)}.swap-`));
70145
+ const stage = path40.join(swap, "stage");
70146
+ const backup = path40.join(swap, "backup");
69864
70147
  try {
69865
70148
  cpSync4(realSrc, stage, {
69866
70149
  recursive: true,
69867
- filter: (entry) => !path39.relative(realSrc, entry).split(path39.sep).includes("node_modules")
70150
+ filter: (entry) => !path40.relative(realSrc, entry).split(path40.sep).includes("node_modules")
69868
70151
  });
69869
- const stagedHooks = path39.join(stage, CURSOR_HOOKS_FILE);
70152
+ const stagedHooks = path40.join(stage, CURSOR_HOOKS_FILE);
69870
70153
  if (existsSync17(stagedHooks))
69871
- writeFileSync8(stagedHooks, localizedCursorHooks(readFileSync17(stagedHooks), dest));
70154
+ writeFileSync9(stagedHooks, localizedCursorHooks(readFileSync18(stagedHooks), dest));
69872
70155
  for (const [name, content] of rules) {
69873
- mkdirSync8(path39.join(stage, "rules"), { recursive: true });
69874
- writeFileSync8(path39.join(stage, "rules", name), content);
70156
+ mkdirSync9(path40.join(stage, "rules"), { recursive: true });
70157
+ writeFileSync9(path40.join(stage, "rules", name), content);
69875
70158
  }
69876
- writeFileSync8(path39.join(stage, ".workit-root"), realSrc + `
70159
+ writeFileSync9(path40.join(stage, ".workit-root"), realSrc + `
69877
70160
  `, "utf8");
69878
70161
  if (!samePluginContent(realSrc, stage, dest))
69879
70162
  throw new Error("staged adapter content is incomplete");
@@ -69896,7 +70179,7 @@ function copyPluginDir(src, dest) {
69896
70179
  return hadDir ? "Configured" : "Installed";
69897
70180
  }
69898
70181
  function applyCursorSettings(root, res) {
69899
- mkdirSync8(path39.dirname(res.cursorSettings), { recursive: true });
70182
+ mkdirSync9(path40.dirname(res.cursorSettings), { recursive: true });
69900
70183
  const settingsExisting = readExisting(res.cursorSettings);
69901
70184
  if (settingsExisting.kind === "malformed") {
69902
70185
  return {
@@ -69915,7 +70198,7 @@ function applyCursorSettings(root, res) {
69915
70198
  detail: "already registered"
69916
70199
  };
69917
70200
  }
69918
- writeFileSync8(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
70201
+ writeFileSync9(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
69919
70202
  `, "utf8");
69920
70203
  return {
69921
70204
  platform: "cursor",
@@ -69924,7 +70207,7 @@ function applyCursorSettings(root, res) {
69924
70207
  };
69925
70208
  }
69926
70209
  function applyCursorMcp(root, res) {
69927
- mkdirSync8(path39.dirname(res.cursorMcp), { recursive: true });
70210
+ mkdirSync9(path40.dirname(res.cursorMcp), { recursive: true });
69928
70211
  const mcpExisting = readExisting(res.cursorMcp);
69929
70212
  if (mcpExisting.kind === "malformed") {
69930
70213
  return {
@@ -69954,7 +70237,7 @@ function applyCursorMcp(root, res) {
69954
70237
  detail: "already registered"
69955
70238
  };
69956
70239
  }
69957
- writeFileSync8(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
70240
+ writeFileSync9(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
69958
70241
  `, "utf8");
69959
70242
  return {
69960
70243
  platform: "cursor",
@@ -70030,7 +70313,7 @@ function applySetupPreview(preview, options = {}) {
70030
70313
  let cursorCopyOk = false;
70031
70314
  for (const mutation of preview.mutations) {
70032
70315
  if (mutation.type === "install-host") {
70033
- const expectedPath = mutation.platform === "cursor" ? path39.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap") : mutation.platform === "codex" ? path39.join(res.env.CODEX_HOME ?? path39.join(res.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : mutation.platform === "pi" ? path39.join(res.env.PI_CODING_AGENT_DIR ?? path39.join(res.home, ".pi", "agent"), "settings.json") : mutation.platform === "claude-code" ? claudeInstallRecord(res.home, res.env) : "";
70316
+ const expectedPath = mutation.platform === "cursor" ? path40.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap") : mutation.platform === "codex" ? path40.join(res.env.CODEX_HOME ?? path40.join(res.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : mutation.platform === "pi" ? path40.join(res.env.PI_CODING_AGENT_DIR ?? path40.join(res.home, ".pi", "agent"), "settings.json") : mutation.platform === "claude-code" ? claudeInstallRecord(res.home, res.env) : "";
70034
70317
  if (mutation.path !== expectedPath) {
70035
70318
  entries.push({
70036
70319
  platform: mutation.platform,
@@ -70142,7 +70425,7 @@ function applySetupPreview(preview, options = {}) {
70142
70425
  if (preview.projectRefused) {
70143
70426
  entries.push({
70144
70427
  platform: "core",
70145
- file: path39.resolve(res.cwd),
70428
+ file: path40.resolve(res.cwd),
70146
70429
  status: "Skipped",
70147
70430
  detail: preview.projectRefused
70148
70431
  });
@@ -70223,12 +70506,12 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70223
70506
  return configured ?? fallback;
70224
70507
  }, readConfigRecord = (file) => {
70225
70508
  try {
70226
- const value = JSON.parse(readFileSync17(file, "utf8"));
70509
+ const value = JSON.parse(readFileSync18(file, "utf8"));
70227
70510
  return isRecord6(value) ? value : null;
70228
70511
  } catch {
70229
70512
  return null;
70230
70513
  }
70231
- }, claudeInstallRecord = (home, env) => path39.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
70514
+ }, claudeInstallRecord = (home, env) => path40.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
70232
70515
  const env = options.env ?? process.env;
70233
70516
  const home = options.home ?? env.HOME ?? os13.homedir();
70234
70517
  return {
@@ -70236,15 +70519,15 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70236
70519
  configDir: resolvedConfigDir,
70237
70520
  cwd: options.cwd ?? process.cwd(),
70238
70521
  env,
70239
- opencodeConfig: options.opencodeConfig ?? path39.join(home, ".config", "opencode", "opencode.json"),
70240
- cursorSettings: options.cursorSettings ?? path39.join(home, ".cursor", "settings.json"),
70241
- cursorMcp: options.cursorMcp ?? path39.join(home, ".cursor", "mcp.json"),
70242
- cursorPluginDir: options.cursorPluginDir ?? path39.join(home, ".cursor", "plugins", "local", "workit")
70522
+ opencodeConfig: options.opencodeConfig ?? path40.join(home, ".config", "opencode", "opencode.json"),
70523
+ cursorSettings: options.cursorSettings ?? path40.join(home, ".cursor", "settings.json"),
70524
+ cursorMcp: options.cursorMcp ?? path40.join(home, ".cursor", "mcp.json"),
70525
+ cursorPluginDir: options.cursorPluginDir ?? path40.join(home, ".cursor", "plugins", "local", "workit")
70243
70526
  };
70244
70527
  }, isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readExisting = (p) => {
70245
70528
  let raw;
70246
70529
  try {
70247
- raw = readFileSync17(p, "utf8");
70530
+ raw = readFileSync18(p, "utf8");
70248
70531
  } catch {
70249
70532
  if (existsSync17(p))
70250
70533
  return { kind: "malformed", error: `${p} is not readable` };
@@ -70261,7 +70544,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70261
70544
  return { kind: "malformed", error: `${p} is not a JSON object` };
70262
70545
  }, readFileSafe = (p) => {
70263
70546
  try {
70264
- return readFileSync17(p, "utf8");
70547
+ return readFileSync18(p, "utf8");
70265
70548
  } catch {
70266
70549
  return null;
70267
70550
  }
@@ -70276,15 +70559,15 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70276
70559
  }
70277
70560
  }, isAdapter = (root, platform) => {
70278
70561
  try {
70279
- const pkg = JSON.parse(readFileSync17(path39.join(root, "package.json"), "utf8"));
70562
+ const pkg = JSON.parse(readFileSync18(path40.join(root, "package.json"), "utf8"));
70280
70563
  return pkg.name === `@brainervirus/workit-${platform}`;
70281
70564
  } catch {
70282
70565
  return false;
70283
70566
  }
70284
70567
  }, nodeResolvedAdapters = (platform) => {
70285
70568
  const name = `@brainervirus/workit-${platform}`;
70286
- const require2 = createRequire2(path39.join(packageRoot(), "package.json"));
70287
- return (require2.resolve.paths(name) ?? []).map((dir) => path39.join(dir, name));
70569
+ const require2 = createRequire2(path40.join(packageRoot(), "package.json"));
70570
+ return (require2.resolve.paths(name) ?? []).map((dir) => path40.join(dir, name));
70288
70571
  }, realRoot = (root) => {
70289
70572
  try {
70290
70573
  return realpathSync8(root);
@@ -70300,7 +70583,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70300
70583
  if (!isDevOpenCodeAdapterRoot(root, opts.dev))
70301
70584
  return OPENCODE_NPM_PIN;
70302
70585
  for (const rel of ["src/plugin.ts", "dist/plugin.js"]) {
70303
- const entry = path39.join(root, rel);
70586
+ const entry = path40.join(root, rel);
70304
70587
  if (existsSync17(entry))
70305
70588
  return `file://${entry}`;
70306
70589
  }
@@ -70315,25 +70598,25 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70315
70598
  }
70316
70599
  }, samePluginContent = (src, dest, pluginDir, relative = "") => {
70317
70600
  try {
70318
- for (const entry of readdirSync9(src, { withFileTypes: true })) {
70601
+ for (const entry of readdirSync11(src, { withFileTypes: true })) {
70319
70602
  if (entry.name === "node_modules")
70320
70603
  continue;
70321
- const source = path39.join(src, entry.name);
70322
- const installed = path39.join(dest, entry.name);
70323
- const rel = path39.join(relative, entry.name);
70604
+ const source = path40.join(src, entry.name);
70605
+ const installed = path40.join(dest, entry.name);
70606
+ const rel = path40.join(relative, entry.name);
70324
70607
  if (entry.isDirectory()) {
70325
70608
  if (!statSync7(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
70326
70609
  return false;
70327
70610
  } else {
70328
- const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync17(source), pluginDir) : readFileSync17(source);
70329
- if (!expected.equals(readFileSync17(installed)))
70611
+ const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync18(source), pluginDir) : readFileSync18(source);
70612
+ if (!expected.equals(readFileSync18(installed)))
70330
70613
  return false;
70331
70614
  }
70332
70615
  }
70333
- for (const entry of readdirSync9(dest, { withFileTypes: true })) {
70334
- if (existsSync17(path39.join(src, entry.name)))
70616
+ for (const entry of readdirSync11(dest, { withFileTypes: true })) {
70617
+ if (existsSync17(path40.join(src, entry.name)))
70335
70618
  continue;
70336
- const rel = path39.join(relative, entry.name);
70619
+ const rel = path40.join(relative, entry.name);
70337
70620
  if (rel === ".workit-root")
70338
70621
  continue;
70339
70622
  if (relative === "rules" && entry.isFile() && entry.name.endsWith(".mdc"))
@@ -70346,32 +70629,32 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70346
70629
  }
70347
70630
  }, preservedCursorRules = (src, dest) => {
70348
70631
  const preserved = new Map;
70349
- const sourceRules = path39.join(src, "rules");
70350
- const installedRules = path39.join(dest, "rules");
70632
+ const sourceRules = path40.join(src, "rules");
70633
+ const installedRules = path40.join(dest, "rules");
70351
70634
  try {
70352
- for (const entry of readdirSync9(installedRules, { withFileTypes: true })) {
70635
+ for (const entry of readdirSync11(installedRules, { withFileTypes: true })) {
70353
70636
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
70354
70637
  continue;
70355
- if (existsSync17(path39.join(sourceRules, entry.name)))
70638
+ if (existsSync17(path40.join(sourceRules, entry.name)))
70356
70639
  continue;
70357
- preserved.set(entry.name, readFileSync17(path39.join(installedRules, entry.name)));
70640
+ preserved.set(entry.name, readFileSync18(path40.join(installedRules, entry.name)));
70358
70641
  }
70359
70642
  } catch {}
70360
70643
  return preserved;
70361
70644
  }, removeLegacyCursorDir = (res) => {
70362
- const legacy = path39.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
70645
+ const legacy = path40.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
70363
70646
  if (legacy === res.cursorPluginDir || !existsSync17(legacy))
70364
70647
  return;
70365
- const legacyRules = path39.join(legacy, "rules");
70648
+ const legacyRules = path40.join(legacy, "rules");
70366
70649
  if (existsSync17(legacyRules)) {
70367
70650
  try {
70368
- mkdirSync8(path39.join(res.cursorPluginDir, "rules"), { recursive: true });
70369
- for (const entry of readdirSync9(legacyRules, { withFileTypes: true })) {
70651
+ mkdirSync9(path40.join(res.cursorPluginDir, "rules"), { recursive: true });
70652
+ for (const entry of readdirSync11(legacyRules, { withFileTypes: true })) {
70370
70653
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
70371
70654
  continue;
70372
- const target = path39.join(res.cursorPluginDir, "rules", entry.name);
70655
+ const target = path40.join(res.cursorPluginDir, "rules", entry.name);
70373
70656
  if (!existsSync17(target))
70374
- copyFileSync5(path39.join(legacyRules, entry.name), target);
70657
+ copyFileSync5(path40.join(legacyRules, entry.name), target);
70375
70658
  }
70376
70659
  } catch {}
70377
70660
  }
@@ -70405,16 +70688,16 @@ var init_setup = __esm(() => {
70405
70688
  { envKey: "WORKFLOW_GITHUB_HOST", affects: "vcs.json github.host" }
70406
70689
  ];
70407
70690
  SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
70408
- CURSOR_HOOKS_FILE = path39.join("hooks", "hooks-cursor.json");
70691
+ CURSOR_HOOKS_FILE = path40.join("hooks", "hooks-cursor.json");
70409
70692
  });
70410
70693
 
70411
70694
  // packages/workit-core/src/core/workspace-branch-policy.ts
70412
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync18, renameSync as renameSync6, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "node:fs";
70413
- import path40 from "node:path";
70695
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync19, renameSync as renameSync6, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "node:fs";
70696
+ import path41 from "node:path";
70414
70697
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
70415
70698
  function applyWorkspaceBranchPolicy(opts) {
70416
70699
  const { workspace_root, env = process.env } = opts;
70417
- const dir = path40.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
70700
+ const dir = path41.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
70418
70701
  const {
70419
70702
  status,
70420
70703
  path: wsPath,
@@ -70426,7 +70709,7 @@ function applyWorkspaceBranchPolicy(opts) {
70426
70709
  if (status === "malformed" || status === "invalid")
70427
70710
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
70428
70711
  const detection = detectBranchPolicy(workspace_root);
70429
- const name = String(env.WORKFLOW_BP_NAME ?? path40.basename(workspace_root));
70712
+ const name = String(env.WORKFLOW_BP_NAME ?? path41.basename(workspace_root));
70430
70713
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
70431
70714
  const policy = {
70432
70715
  preset: detection.preset,
@@ -70475,10 +70758,10 @@ function applyWorkspaceBranchPolicy(opts) {
70475
70758
  ok: false,
70476
70759
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
70477
70760
  };
70478
- mkdirSync9(path40.dirname(wsPath), { recursive: true });
70761
+ mkdirSync10(path41.dirname(wsPath), { recursive: true });
70479
70762
  const tmp = `${wsPath}.${process.pid}.tmp`;
70480
70763
  try {
70481
- writeFileSync9(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
70764
+ writeFileSync10(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
70482
70765
  `, "utf8");
70483
70766
  if (workspacesRevision(readFileSafe2(wsPath)) !== revision) {
70484
70767
  rmSync6(tmp, { force: true });
@@ -70507,7 +70790,7 @@ function applyWorkspaceBranchPolicy(opts) {
70507
70790
  }
70508
70791
  var readFileSafe2 = (p) => {
70509
70792
  try {
70510
- return readFileSync18(p, "utf8");
70793
+ return readFileSync19(p, "utf8");
70511
70794
  } catch {
70512
70795
  return null;
70513
70796
  }
@@ -70760,7 +71043,7 @@ var init_logic = __esm(() => {
70760
71043
 
70761
71044
  // packages/workit-cli/src/wizard-state.ts
70762
71045
  import { existsSync as existsSync18, statSync as statSync8 } from "node:fs";
70763
- import path41 from "node:path";
71046
+ import path42 from "node:path";
70764
71047
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
70765
71048
  function resolveBasePath(values, env = process.env) {
70766
71049
  return env.WORKFLOW_WORKSPACE_ROOT?.trim() || values.basePath.trim();
@@ -71287,8 +71570,8 @@ var NEXT, PREV, skipsCustomBranch = (screen, preset) => (screen === "branchAllow
71287
71570
  const p = value.trim();
71288
71571
  if (!p)
71289
71572
  return "workspace root path is required";
71290
- return !path41.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
71291
- }, skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync18(path41.join(basePath, ".git")), decodeBranchPreset = (value, fallback) => value === "gitflow" || value === "github-flow" || value === "trunk-based" || value === "custom" ? value : fallback, decodeVcsProvider = (value, fallback) => value === "gitlab" || value === "github" || value === "skip" ? value : fallback, decodeIssueTracker = (value, fallback) => value === "youtrack" || value === "github" || value === "gitlab" || value === "none" ? value : fallback, GITHUB_ISSUES, withGithubIssues = (draft, tracker) => tracker === "github" ? { ...draft, issues: { ...GITHUB_ISSUES } } : draft;
71573
+ return !path42.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
71574
+ }, skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync18(path42.join(basePath, ".git")), decodeBranchPreset = (value, fallback) => value === "gitflow" || value === "github-flow" || value === "trunk-based" || value === "custom" ? value : fallback, decodeVcsProvider = (value, fallback) => value === "gitlab" || value === "github" || value === "skip" ? value : fallback, decodeIssueTracker = (value, fallback) => value === "youtrack" || value === "github" || value === "gitlab" || value === "none" ? value : fallback, GITHUB_ISSUES, withGithubIssues = (draft, tracker) => tracker === "github" ? { ...draft, issues: { ...GITHUB_ISSUES } } : draft;
71292
71575
  var init_wizard_state = __esm(() => {
71293
71576
  init_config();
71294
71577
  init_workspaces();
@@ -71347,9 +71630,9 @@ var init_wizard_state = __esm(() => {
71347
71630
  });
71348
71631
 
71349
71632
  // packages/workit-cli/src/admin/uninstall.ts
71350
- import { existsSync as existsSync19, readFileSync as readFileSync19, realpathSync as realpathSync9, rmSync as rmSync7, writeFileSync as writeFileSync10 } from "node:fs";
71633
+ import { existsSync as existsSync19, readFileSync as readFileSync20, realpathSync as realpathSync9, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
71351
71634
  import os14 from "node:os";
71352
- import path42 from "node:path";
71635
+ import path43 from "node:path";
71353
71636
  function cleanCursorSettings(settings, pluginDir) {
71354
71637
  const next = { ...settings };
71355
71638
  let changed = false;
@@ -71522,7 +71805,7 @@ function planUninstall(paths = {}) {
71522
71805
  plan.actions.push({
71523
71806
  kind: "edit-json-remove",
71524
71807
  path: file,
71525
- detail: `remove Workit's permission block from ${path42.basename(file)}`
71808
+ detail: `remove Workit's permission block from ${path43.basename(file)}`
71526
71809
  });
71527
71810
  }
71528
71811
  if (existsSync19(res.codexRules))
@@ -71556,8 +71839,8 @@ function applyUninstall(plan, paths = {}) {
71556
71839
  status = outcome.status;
71557
71840
  detail = outcome.detail;
71558
71841
  } else if (action.kind === "remove-dir") {
71559
- const resolved = path42.resolve(action.path);
71560
- const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path42.resolve(res.codexPluginDir) && path42.basename(resolved) === "workit" && path42.basename(path42.dirname(resolved)) === "plugins";
71842
+ const resolved = path43.resolve(action.path);
71843
+ const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path43.resolve(res.codexPluginDir) && path43.basename(resolved) === "workit" && path43.basename(path43.dirname(resolved)) === "plugins";
71561
71844
  if (!allowed) {
71562
71845
  status = "failed";
71563
71846
  detail = `refusing to remove non-canonical plugin directory: ${resolved}`;
@@ -71599,17 +71882,17 @@ var resolveUninstallPaths = (options = {}) => {
71599
71882
  claudeSettings: permissionTarget("claude-code", permissionPaths).file,
71600
71883
  cursorCliConfig: permissionTarget("cursor", permissionPaths).file,
71601
71884
  codexRules: permissionTarget("codex", permissionPaths).file,
71602
- opencodeConfig: options.opencodeConfig ?? path42.join(home, ".config", "opencode", "opencode.json"),
71603
- cursorSettings: options.cursorSettings ?? path42.join(home, ".cursor", "settings.json"),
71604
- cursorMcp: options.cursorMcp ?? path42.join(home, ".cursor", "mcp.json"),
71605
- cursorPluginDir: options.cursorPluginDir ?? path42.join(home, ".cursor", "plugins", "local", "workit"),
71606
- codexPluginDir: path42.join(home, ".codex", "plugins", "workit"),
71607
- piConfig: path42.join(home, ".pi", "config.json")
71885
+ opencodeConfig: options.opencodeConfig ?? path43.join(home, ".config", "opencode", "opencode.json"),
71886
+ cursorSettings: options.cursorSettings ?? path43.join(home, ".cursor", "settings.json"),
71887
+ cursorMcp: options.cursorMcp ?? path43.join(home, ".cursor", "mcp.json"),
71888
+ cursorPluginDir: options.cursorPluginDir ?? path43.join(home, ".cursor", "plugins", "local", "workit"),
71889
+ codexPluginDir: path43.join(home, ".codex", "plugins", "workit"),
71890
+ piConfig: path43.join(home, ".pi", "config.json")
71608
71891
  };
71609
71892
  }, isRecord7 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readJsonRecord = (file) => {
71610
71893
  let raw;
71611
71894
  try {
71612
- raw = readFileSync19(file, "utf8");
71895
+ raw = readFileSync20(file, "utf8");
71613
71896
  } catch {
71614
71897
  if (existsSync19(file))
71615
71898
  return { kind: "malformed", error: `${file} is not readable` };
@@ -71622,8 +71905,8 @@ var resolveUninstallPaths = (options = {}) => {
71622
71905
  return { kind: "malformed", error: `${file} is not valid JSON` };
71623
71906
  }
71624
71907
  }, stripTrailingSep = (p) => {
71625
- const j = path42.join(p);
71626
- return path42.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
71908
+ const j = path43.join(p);
71909
+ return path43.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
71627
71910
  }, CURSOR_LEGACY_IDENTITIES, permissionPathsOf = (paths) => {
71628
71911
  const env = paths.env ?? process.env;
71629
71912
  return {
@@ -71650,8 +71933,8 @@ var resolveUninstallPaths = (options = {}) => {
71650
71933
  return cleanPiConfig;
71651
71934
  return null;
71652
71935
  }, canonicalRemoveDirAllowed = (actionPath, res) => {
71653
- const expected = path42.resolve(res.cursorPluginDir);
71654
- if (!(actionPath === expected && path42.basename(expected) === "workit" && path42.basename(path42.dirname(expected)) === "local" && path42.basename(path42.dirname(path42.dirname(expected))) === "plugins")) {
71936
+ const expected = path43.resolve(res.cursorPluginDir);
71937
+ if (!(actionPath === expected && path43.basename(expected) === "workit" && path43.basename(path43.dirname(expected)) === "local" && path43.basename(path43.dirname(path43.dirname(expected))) === "plugins")) {
71655
71938
  return false;
71656
71939
  }
71657
71940
  try {
@@ -71674,7 +71957,7 @@ var resolveUninstallPaths = (options = {}) => {
71674
71957
  `;
71675
71958
  let current;
71676
71959
  try {
71677
- current = readFileSync19(target, "utf8");
71960
+ current = readFileSync20(target, "utf8");
71678
71961
  } catch (error) {
71679
71962
  return {
71680
71963
  status: "failed",
@@ -71685,7 +71968,7 @@ var resolveUninstallPaths = (options = {}) => {
71685
71968
  return { status: "skipped", detail: "already clean" };
71686
71969
  }
71687
71970
  try {
71688
- writeFileSync10(target, serialized, "utf8");
71971
+ writeFileSync11(target, serialized, "utf8");
71689
71972
  } catch (error) {
71690
71973
  return {
71691
71974
  status: "failed",
@@ -71703,7 +71986,7 @@ var resolveUninstallPaths = (options = {}) => {
71703
71986
  };
71704
71987
  const home = paths.home ?? paths.env?.HOME ?? os14.homedir();
71705
71988
  const env = paths.env ?? process.env;
71706
- const recorded = claudeWorkitInstalls(home, env, action.cwd).some((install) => install.id === id && install.scope === scope && (install.projectPath === null ? action.cwd === null : path42.resolve(install.projectPath) === path42.resolve(action.cwd ?? "")));
71989
+ const recorded = claudeWorkitInstalls(home, env, action.cwd).some((install) => install.id === id && install.scope === scope && (install.projectPath === null ? action.cwd === null : path43.resolve(install.projectPath) === path43.resolve(action.cwd ?? "")));
71707
71990
  if (!recorded)
71708
71991
  return { status: "skipped", detail: `${id} (${scope} scope) is no longer installed` };
71709
71992
  const executable = findHostExecutable("claude", { home, env });
@@ -74465,14 +74748,14 @@ import {
74465
74748
  existsSync as existsSync20,
74466
74749
  mkdtempSync as mkdtempSync3,
74467
74750
  rmSync as rmSync8,
74468
- readFileSync as readFileSync20,
74469
- mkdirSync as mkdirSync10,
74751
+ readFileSync as readFileSync21,
74752
+ mkdirSync as mkdirSync11,
74470
74753
  copyFileSync as copyFileSync6,
74471
74754
  chmodSync as chmodSync2,
74472
74755
  cpSync as cpSync5,
74473
74756
  lstatSync as lstatSync7
74474
74757
  } from "node:fs";
74475
- import path43 from "node:path";
74758
+ import path44 from "node:path";
74476
74759
  import os16 from "node:os";
74477
74760
  import { createHash as createHash15 } from "node:crypto";
74478
74761
  function previewUpgrade(hosts = HOSTS2, deps = {}) {
@@ -74496,9 +74779,9 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
74496
74779
  for (const file of [
74497
74780
  paths.opencodeConfig,
74498
74781
  paths.opencodeConfig + "c",
74499
- path43.join(home, ".cursor/settings.json"),
74500
- path43.join(home, ".cursor/mcp.json"),
74501
- path43.join(home, ".cursor/plugins/local/workit/package.json"),
74782
+ path44.join(home, ".cursor/settings.json"),
74783
+ path44.join(home, ".cursor/mcp.json"),
74784
+ path44.join(home, ".cursor/plugins/local/workit/package.json"),
74502
74785
  paths.codexConfig,
74503
74786
  paths.piSettings
74504
74787
  ])
@@ -74513,16 +74796,16 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
74513
74796
  });
74514
74797
  const installedSources = sources(paths, run, hosts);
74515
74798
  if (installedSources.some((source) => source.host === "opencode")) {
74516
- const scratch = mkdtempSync3(path43.join(os16.tmpdir(), "workit-upgrade-opencode-"));
74799
+ const scratch = mkdtempSync3(path44.join(os16.tmpdir(), "workit-upgrade-opencode-"));
74517
74800
  let probe;
74518
74801
  try {
74519
74802
  probe = runner({
74520
74803
  ...deps,
74521
74804
  env: {
74522
74805
  ...deps.env,
74523
- XDG_DATA_HOME: path43.join(scratch, "data"),
74524
- XDG_STATE_HOME: path43.join(scratch, "state"),
74525
- XDG_CACHE_HOME: path43.join(scratch, "cache")
74806
+ XDG_DATA_HOME: path44.join(scratch, "data"),
74807
+ XDG_STATE_HOME: path44.join(scratch, "state"),
74808
+ XDG_CACHE_HOME: path44.join(scratch, "cache")
74526
74809
  }
74527
74810
  })("opencode", ["--version"]);
74528
74811
  } finally {
@@ -74568,7 +74851,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
74568
74851
  env: deps.env,
74569
74852
  cwd: home,
74570
74853
  packageVersion: latest.stdout.trim(),
74571
- targetDir: path43.join(home, ".cursor/plugins/local/workit")
74854
+ targetDir: path44.join(home, ".cursor/plugins/local/workit")
74572
74855
  });
74573
74856
  plan.entries.push({
74574
74857
  host: source.host,
@@ -74598,11 +74881,11 @@ function applyUpgrade(plan, deps = {}) {
74598
74881
  ok: false,
74599
74882
  error: "Stop the selected running hosts before upgrading their loaded plugins"
74600
74883
  };
74601
- const backup = path43.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
74602
- mkdirSync10(backup, { recursive: true, mode: 448 });
74884
+ const backup = path44.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
74885
+ mkdirSync11(backup, { recursive: true, mode: 448 });
74603
74886
  for (const file of Object.keys(plan.configDigests))
74604
74887
  if (existsSync20(file)) {
74605
- const target = path43.join(backup, createHash15("sha256").update(file).digest("hex").slice(0, 12) + "-" + path43.basename(file));
74888
+ const target = path44.join(backup, createHash15("sha256").update(file).digest("hex").slice(0, 12) + "-" + path44.basename(file));
74606
74889
  copyFileSync6(file, target);
74607
74890
  chmodSync2(target, 384);
74608
74891
  }
@@ -74619,9 +74902,9 @@ function applyUpgrade(plan, deps = {}) {
74619
74902
  `, 384);
74620
74903
  }
74621
74904
  const run = runner(deps);
74622
- const cursorRoot = path43.join(home, ".cursor/plugins/local/workit");
74905
+ const cursorRoot = path44.join(home, ".cursor/plugins/local/workit");
74623
74906
  if (plan.entries.some((entry) => entry.host === "cursor") && existsSync20(cursorRoot))
74624
- cpSync5(cursorRoot, path43.join(backup, "cursor-package"), { recursive: true });
74907
+ cpSync5(cursorRoot, path44.join(backup, "cursor-package"), { recursive: true });
74625
74908
  for (const entry of plan.entries) {
74626
74909
  if (entry.host === "opencode")
74627
74910
  return {
@@ -74641,7 +74924,7 @@ function applyUpgrade(plan, deps = {}) {
74641
74924
  {
74642
74925
  type: "install-host",
74643
74926
  platform: "cursor",
74644
- path: path43.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
74927
+ path: path44.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
74645
74928
  commands: entry.commands.map((command) => ({
74646
74929
  ...command,
74647
74930
  purpose: command.purpose ?? "Upgrade Cursor"
@@ -74650,7 +74933,7 @@ function applyUpgrade(plan, deps = {}) {
74650
74933
  {
74651
74934
  type: "install-adapter",
74652
74935
  platform: "cursor",
74653
- path: path43.join(home, ".cursor/plugins/local/workit")
74936
+ path: path44.join(home, ".cursor/plugins/local/workit")
74654
74937
  }
74655
74938
  ]
74656
74939
  };
@@ -74664,7 +74947,7 @@ function applyUpgrade(plan, deps = {}) {
74664
74947
  return { exitCode: outcome.status ?? 1, stdout: outcome.stdout, stderr: "" };
74665
74948
  }
74666
74949
  });
74667
- const pkg = object2(path43.join(home, ".cursor/plugins/local/workit/package.json"));
74950
+ const pkg = object2(path44.join(home, ".cursor/plugins/local/workit/package.json"));
74668
74951
  if (!result.ok || pkg?.version !== entry.latest)
74669
74952
  return {
74670
74953
  ok: false,
@@ -74698,15 +74981,15 @@ function applyUpgrade(plan, deps = {}) {
74698
74981
  const currentRoot = run("npm", ["root", "--global"]);
74699
74982
  if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== plan.cli.root)
74700
74983
  return { ok: false, backup, error: "Global npm location changed; preview again" };
74701
- const cliBackup = path43.join(backup, "cli-package");
74702
- cpSync5(path43.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
74984
+ const cliBackup = path44.join(backup, "cli-package");
74985
+ cpSync5(path44.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
74703
74986
  if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${plan.cli.latest}`]).status !== 0)
74704
74987
  return {
74705
74988
  ok: false,
74706
74989
  backup,
74707
- error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path43.join(cliBackup, "dist/index.js")}`
74990
+ error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path44.join(cliBackup, "dist/index.js")}`
74708
74991
  };
74709
- const installed = object2(path43.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
74992
+ const installed = object2(path44.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
74710
74993
  if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== plan.cli.latest)
74711
74994
  return {
74712
74995
  ok: false,
@@ -74731,12 +75014,12 @@ async function runUpgradeCommand(argv, deps = {}) {
74731
75014
  if (flags.has("--cli")) {
74732
75015
  const run = runner(deps);
74733
75016
  const root = run("npm", ["root", "--global"]);
74734
- if (root.status !== 0 || !path43.isAbsolute(root.stdout.trim())) {
75017
+ if (root.status !== 0 || !path44.isAbsolute(root.stdout.trim())) {
74735
75018
  plan.blocked.push("Global npm location could not be verified");
74736
75019
  } else {
74737
- const packageFile = path43.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
75020
+ const packageFile = path44.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
74738
75021
  const pkg = object2(packageFile);
74739
- if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(path43.dirname(packageFile)).isSymbolicLink()) {
75022
+ if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(path44.dirname(packageFile)).isSymbolicLink()) {
74740
75023
  plan.skipped.push({
74741
75024
  host: "cli",
74742
75025
  reason: "No managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
@@ -74859,37 +75142,37 @@ var HOSTS2, upgradePaths = (deps) => {
74859
75142
  ...process.env,
74860
75143
  ...deps.home || deps.env?.HOME ? {
74861
75144
  HOME: home,
74862
- CODEX_HOME: path43.join(home, ".codex"),
74863
- PI_CODING_AGENT_DIR: path43.join(home, ".pi/agent"),
74864
- XDG_CONFIG_HOME: path43.join(home, ".config"),
75145
+ CODEX_HOME: path44.join(home, ".codex"),
75146
+ PI_CODING_AGENT_DIR: path44.join(home, ".pi/agent"),
75147
+ XDG_CONFIG_HOME: path44.join(home, ".config"),
74865
75148
  WORKFLOW_TOOLKIT_CONFIG: undefined,
74866
75149
  WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
74867
75150
  } : {},
74868
75151
  ...deps.env,
74869
75152
  HOME: home
74870
75153
  };
74871
- const xdg = env.XDG_CONFIG_HOME || path43.join(home, ".config");
75154
+ const xdg = env.XDG_CONFIG_HOME || path44.join(home, ".config");
74872
75155
  return {
74873
75156
  home,
74874
75157
  env,
74875
- configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path43.join(xdg, "workit"),
74876
- opencodeConfig: path43.join(xdg, "opencode/opencode.json"),
74877
- piSettings: path43.join(env.PI_CODING_AGENT_DIR ?? path43.join(home, ".pi/agent"), "settings.json"),
74878
- codexConfig: path43.join(env.CODEX_HOME ?? path43.join(home, ".codex"), "config.toml")
75158
+ configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path44.join(xdg, "workit"),
75159
+ opencodeConfig: path44.join(xdg, "opencode/opencode.json"),
75160
+ piSettings: path44.join(env.PI_CODING_AGENT_DIR ?? path44.join(home, ".pi/agent"), "settings.json"),
75161
+ codexConfig: path44.join(env.CODEX_HOME ?? path44.join(home, ".codex"), "config.toml")
74879
75162
  };
74880
75163
  }, object2 = (file) => {
74881
75164
  if (!existsSync20(file))
74882
75165
  return null;
74883
75166
  let data;
74884
75167
  try {
74885
- data = JSON.parse(readFileSync20(file, "utf8"));
75168
+ data = JSON.parse(readFileSync21(file, "utf8"));
74886
75169
  } catch {
74887
75170
  throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
74888
75171
  }
74889
75172
  if (!data || typeof data !== "object" || Array.isArray(data))
74890
75173
  throw new Error(`${file} is not a configuration object`);
74891
75174
  return data;
74892
- }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync20(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
75175
+ }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync21(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
74893
75176
  const { home, env } = upgradePaths(deps);
74894
75177
  let step;
74895
75178
  try {
@@ -74915,9 +75198,9 @@ var HOSTS2, upgradePaths = (deps) => {
74915
75198
  for (const pin of plugins)
74916
75199
  if (typeof pin === "string" && isWorkitPlugin(pin))
74917
75200
  result.push(classify("opencode", pin));
74918
- const cursor = hosts.includes("cursor") ? object2(path43.join(home, ".cursor/plugins/local/workit/package.json")) : null;
75201
+ const cursor = hosts.includes("cursor") ? object2(path44.join(home, ".cursor/plugins/local/workit/package.json")) : null;
74919
75202
  if (cursor?.name === "@brainervirus/workit-cursor") {
74920
- const mcp = object2(path43.join(home, ".cursor/mcp.json"));
75203
+ const mcp = object2(path44.join(home, ".cursor/mcp.json"));
74921
75204
  const args = mcp?.mcpServers?.workit?.args ?? [];
74922
75205
  const selector = args.find((arg) => typeof arg === "string" && arg.startsWith("--package="));
74923
75206
  result.push(classify("cursor", typeof selector === "string" ? selector.slice(10) : "local"));
@@ -74930,7 +75213,7 @@ var HOSTS2, upgradePaths = (deps) => {
74930
75213
  if (typeof source !== "string")
74931
75214
  continue;
74932
75215
  const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
74933
- const local = path43.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path43.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
75216
+ const local = path44.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path44.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
74934
75217
  if (npm || local)
74935
75218
  result.push(classify("pi", source));
74936
75219
  }
@@ -74977,9 +75260,9 @@ var HOSTS2, upgradePaths = (deps) => {
74977
75260
  }, piInstalledVersion = (listing, selector) => {
74978
75261
  const lines = listing.split(/\r?\n/).map((line) => line.trim());
74979
75262
  const index = lines.indexOf(selector);
74980
- if (index < 0 || !lines[index + 1] || !path43.isAbsolute(lines[index + 1]))
75263
+ if (index < 0 || !lines[index + 1] || !path44.isAbsolute(lines[index + 1]))
74981
75264
  return;
74982
- const pkg = object2(path43.join(lines[index + 1], "package.json"));
75265
+ const pkg = object2(path44.join(lines[index + 1], "package.json"));
74983
75266
  return pkg?.name === "@brainervirus/workit-pi" ? pkg.version : undefined;
74984
75267
  }, versionAtLeast = (installed, latest) => {
74985
75268
  if (!/^\d+\.\d+\.\d+$/.test(installed))
@@ -75033,7 +75316,7 @@ var init_launch = __esm(() => {
75033
75316
  // packages/workit-core/src/knowledge.ts
75034
75317
  import { spawnSync as spawnSync18 } from "node:child_process";
75035
75318
  import fs20 from "node:fs";
75036
- import path44 from "node:path";
75319
+ import path45 from "node:path";
75037
75320
  function lintKnowledge(root) {
75038
75321
  const seen = new Set;
75039
75322
  const unreadable = [];
@@ -75137,7 +75420,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
75137
75420
  const end = lines.indexOf("---", 1);
75138
75421
  return end === -1 ? lines : [...lines.slice(0, end + 1).map(() => ""), ...lines.slice(end + 1)];
75139
75422
  }, read = (root, file) => {
75140
- const full = path44.join(root, file);
75423
+ const full = path45.join(root, file);
75141
75424
  try {
75142
75425
  fs20.lstatSync(full);
75143
75426
  } catch {
@@ -75178,7 +75461,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
75178
75461
  }
75179
75462
  }, brokenLinks = (root, doc, tracked) => {
75180
75463
  const findings = [];
75181
- const base = path44.dirname(path44.join(root, doc.file));
75464
+ const base = path45.dirname(path45.join(root, doc.file));
75182
75465
  const missing = (target, line, how) => findings.push({
75183
75466
  rule: "broken-link",
75184
75467
  file: doc.file,
@@ -75197,7 +75480,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
75197
75480
  const local = localTarget(target);
75198
75481
  if (!local)
75199
75482
  continue;
75200
- const resolved = local.startsWith("/") ? path44.join(root, local) : path44.resolve(base, local);
75483
+ const resolved = local.startsWith("/") ? path45.join(root, local) : path45.resolve(base, local);
75201
75484
  if (!exists2(resolved))
75202
75485
  missing(target, index + 1, "link");
75203
75486
  }
@@ -75207,9 +75490,9 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
75207
75490
  continue;
75208
75491
  const relative = /^\.\.?\//.test(candidate);
75209
75492
  const first = candidate.split("/")[0];
75210
- if (!relative && !(tracked ? tracked.has(first) : exists2(path44.join(root, first))))
75493
+ if (!relative && !(tracked ? tracked.has(first) : exists2(path45.join(root, first))))
75211
75494
  continue;
75212
- const resolved = relative ? path44.resolve(base, candidate) : path44.join(root, candidate);
75495
+ const resolved = relative ? path45.resolve(base, candidate) : path45.join(root, candidate);
75213
75496
  if (!exists2(resolved))
75214
75497
  missing(span, index + 1, "pointer");
75215
75498
  }
@@ -75535,7 +75818,7 @@ import { spawn, spawnSync as spawnSync19 } from "node:child_process";
75535
75818
  import { createHash as createHash16 } from "node:crypto";
75536
75819
  import fs21 from "node:fs";
75537
75820
  import os17 from "node:os";
75538
- import path45 from "node:path";
75821
+ import path46 from "node:path";
75539
75822
  import { StringDecoder } from "node:string_decoder";
75540
75823
  function tailLines(text, lines = TAIL_LINES) {
75541
75824
  const all = text.replace(/\r\n?/gu, `
@@ -75655,7 +75938,7 @@ function planSpawn(argv, options) {
75655
75938
  return { file: argv[0], args: [], verbatim: false, shell: true };
75656
75939
  if (options.platform !== "win32")
75657
75940
  return { file: argv[0], args: argv.slice(1), verbatim: false, shell: false };
75658
- const win = path45.win32;
75941
+ const win = path46.win32;
75659
75942
  const isFile = options.isFile ?? ((file) => {
75660
75943
  try {
75661
75944
  return fs21.statSync(file).isFile();
@@ -75817,10 +76100,10 @@ function runCheckCommand(argv, options) {
75817
76100
  function storeLog(storeRoot, log) {
75818
76101
  const digest = logDigest(log);
75819
76102
  const ref = `blobs/logs/${digest.slice("sha256:".length)}.log`;
75820
- const file = path45.join(storeRoot, ref);
76103
+ const file = path46.join(storeRoot, ref);
75821
76104
  try {
75822
76105
  if (!fs21.existsSync(file)) {
75823
- fs21.mkdirSync(path45.dirname(file), { recursive: true });
76106
+ fs21.mkdirSync(path46.dirname(file), { recursive: true });
75824
76107
  const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
75825
76108
  fs21.writeFileSync(temp, log, { mode: 384 });
75826
76109
  fs21.renameSync(temp, file);
@@ -75833,7 +76116,7 @@ function storeLog(storeRoot, log) {
75833
76116
  function pruneCheckLogs(storeRoot, options = {}) {
75834
76117
  const limits = { ...LOG_RETENTION, ...options.retention };
75835
76118
  const now = options.now ?? Date.now();
75836
- const dir = path45.join(storeRoot, "blobs", "logs");
76119
+ const dir = path46.join(storeRoot, "blobs", "logs");
75837
76120
  const report = { removed: 0, removedBytes: 0, kept: 0 };
75838
76121
  let names;
75839
76122
  try {
@@ -75843,7 +76126,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
75843
76126
  }
75844
76127
  const files = names.flatMap((name) => {
75845
76128
  try {
75846
- const stat = fs21.statSync(path45.join(dir, name));
76129
+ const stat = fs21.statSync(path46.join(dir, name));
75847
76130
  return stat.isFile() ? [{ name, size: stat.size, mtimeMs: stat.mtimeMs }] : [];
75848
76131
  } catch {
75849
76132
  return [];
@@ -75862,7 +76145,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
75862
76145
  continue;
75863
76146
  if (!options.dryRun)
75864
76147
  try {
75865
- fs21.rmSync(path45.join(dir, file.name), { force: true });
76148
+ fs21.rmSync(path46.join(dir, file.name), { force: true });
75866
76149
  } catch {
75867
76150
  continue;
75868
76151
  }
@@ -76413,12 +76696,12 @@ var init_workspace = __esm(() => {
76413
76696
 
76414
76697
  // packages/workit-core/src/core/heavy-rules.ts
76415
76698
  import fs22 from "node:fs";
76416
- import path46 from "node:path";
76699
+ import path47 from "node:path";
76417
76700
  function gitTop(cwd) {
76418
- for (let dir = path46.resolve(cwd);; dir = path46.dirname(dir)) {
76419
- if (fs22.existsSync(path46.join(dir, ".git")))
76701
+ for (let dir = path47.resolve(cwd);; dir = path47.dirname(dir)) {
76702
+ if (fs22.existsSync(path47.join(dir, ".git")))
76420
76703
  return dir;
76421
- if (path46.dirname(dir) === dir)
76704
+ if (path47.dirname(dir) === dir)
76422
76705
  return null;
76423
76706
  }
76424
76707
  }
@@ -76460,18 +76743,18 @@ var init_heavy_rules = () => {};
76460
76743
  // packages/workit-core/src/core/heavy.ts
76461
76744
  import fs23 from "node:fs";
76462
76745
  import os18 from "node:os";
76463
- import path47 from "node:path";
76746
+ import path48 from "node:path";
76464
76747
  function stateDir2(env, platform = process.platform) {
76465
76748
  if (env.WORKFLOW_TOOLKIT_STATE)
76466
76749
  return env.WORKFLOW_TOOLKIT_STATE;
76467
76750
  if (env.XDG_STATE_HOME)
76468
- return path47.join(env.XDG_STATE_HOME, "workit");
76751
+ return path48.join(env.XDG_STATE_HOME, "workit");
76469
76752
  const home = env.HOME || os18.homedir();
76470
76753
  if (platform === "darwin")
76471
- return path47.join(home, "Library", "Application Support", "workit");
76754
+ return path48.join(home, "Library", "Application Support", "workit");
76472
76755
  if (platform === "win32")
76473
- return path47.join(env.LOCALAPPDATA ?? path47.join(home, "AppData", "Local"), "workit");
76474
- return path47.join(home, ".local", "state", "workit");
76756
+ return path48.join(env.LOCALAPPDATA ?? path48.join(home, "AppData", "Local"), "workit");
76757
+ return path48.join(home, ".local", "state", "workit");
76475
76758
  }
76476
76759
  function positional(words, from, values = PM_VALUES) {
76477
76760
  let index = from;
@@ -76608,10 +76891,10 @@ function parsePattern(pattern) {
76608
76891
  }
76609
76892
  function machinePatterns(env) {
76610
76893
  const value = readJson2(heavyCommandsFile(env));
76611
- return isRecord8(value) ? stringList(value.patterns) : [];
76894
+ return isRecord8(value) ? stringList2(value.patterns) : [];
76612
76895
  }
76613
76896
  function repoPatterns(cwd) {
76614
- return repoHeavyCommands(gitTop(cwd) ?? path47.resolve(cwd));
76897
+ return repoHeavyCommands(gitTop(cwd) ?? path48.resolve(cwd));
76615
76898
  }
76616
76899
  function heavyLines(lines, options) {
76617
76900
  const found = [];
@@ -76690,13 +76973,13 @@ function leaseRewrite(command) {
76690
76973
  return null;
76691
76974
  return `${prefix}workit check --heavy -- ${rest}`;
76692
76975
  }
76693
- var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path47.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) => {
76976
+ 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) => {
76694
76977
  try {
76695
76978
  return JSON.parse(fs23.readFileSync(file, "utf8"));
76696
76979
  } catch {
76697
76980
  return;
76698
76981
  }
76699
- }, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], linesOf = (command, dialect) => {
76982
+ }, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), stringList2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], linesOf = (command, dialect) => {
76700
76983
  try {
76701
76984
  return commandLines(command, dialect);
76702
76985
  } catch {
@@ -76837,7 +77120,7 @@ var init_heavy = __esm(() => {
76837
77120
  import { randomUUID as randomUUID5 } from "node:crypto";
76838
77121
  import fs24 from "node:fs";
76839
77122
  import os19 from "node:os";
76840
- import path48 from "node:path";
77123
+ import path49 from "node:path";
76841
77124
  function memoryValue(value, host) {
76842
77125
  const text = typeof value === "number" ? String(value) : typeof value === "string" ? value : "";
76843
77126
  const percent = PERCENT.exec(text.trim());
@@ -77018,7 +77301,7 @@ function tryOnce(options, record, reclaimed) {
77018
77301
  const text = `${JSON.stringify(record)}
77019
77302
  `;
77020
77303
  for (let slot = 0;slot < options.slots; slot += 1) {
77021
- const file = path48.join(options.dir, `slot-${slot}.json`);
77304
+ const file = path49.join(options.dir, `slot-${slot}.json`);
77022
77305
  for (let attempt = 0;attempt < 2; attempt += 1) {
77023
77306
  try {
77024
77307
  const fd = fs24.openSync(file, "wx", 384);
@@ -77143,7 +77426,7 @@ function heldLease(dir, id, options = {}) {
77143
77426
  for (const name of names) {
77144
77427
  if (!/^slot-\d+\.json$/u.test(name))
77145
77428
  continue;
77146
- const lease = parseLease(readText5(path48.join(dir, name)));
77429
+ const lease = parseLease(readText5(path49.join(dir, name)));
77147
77430
  if (lease?.id === id && !staleReason(lease, options.probe) && (options.ancestor ?? isAncestor)(lease.pid))
77148
77431
  return lease;
77149
77432
  }
@@ -77198,10 +77481,10 @@ async function runScoped(run, wrapper, sinks) {
77198
77481
  }
77199
77482
  function firstNotice(env, session, key, now = Date.now()) {
77200
77483
  const id = (session ?? `ppid-${process.ppid}`).replace(/[^A-Za-z0-9_.-]/gu, "_").slice(0, 120);
77201
- const dir = path48.join(stateDir2(env), "notices");
77484
+ const dir = path49.join(stateDir2(env), "notices");
77202
77485
  try {
77203
77486
  fs24.mkdirSync(dir, { recursive: true, mode: 448 });
77204
- fs24.writeFileSync(path48.join(dir, `${key}-${id}`), `${new Date(now).toISOString()}
77487
+ fs24.writeFileSync(path49.join(dir, `${key}-${id}`), `${new Date(now).toISOString()}
77205
77488
  `, {
77206
77489
  flag: "wx",
77207
77490
  mode: 384
@@ -77211,7 +77494,7 @@ function firstNotice(env, session, key, now = Date.now()) {
77211
77494
  }
77212
77495
  try {
77213
77496
  for (const name of fs24.readdirSync(dir)) {
77214
- const file = path48.join(dir, name);
77497
+ const file = path49.join(dir, name);
77215
77498
  if (now - fs24.statSync(file).mtimeMs > NOTICE_TTL_MS)
77216
77499
  fs24.rmSync(file, { force: true });
77217
77500
  }
@@ -77221,7 +77504,7 @@ function firstNotice(env, session, key, now = Date.now()) {
77221
77504
  function leaseSlots(dir, slots, probe = {}) {
77222
77505
  const states = [];
77223
77506
  for (let slot = 0;slot < slots; slot += 1) {
77224
- const lease = parseLease(readText5(path48.join(dir, `slot-${slot}.json`)));
77507
+ const lease = parseLease(readText5(path49.join(dir, `slot-${slot}.json`)));
77225
77508
  states.push({
77226
77509
  slot,
77227
77510
  holder: lease ? holderOf(lease) : null,
@@ -77233,7 +77516,7 @@ function leaseSlots(dir, slots, probe = {}) {
77233
77516
  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 = () => ({
77234
77517
  totalMemBytes: os19.totalmem(),
77235
77518
  cpus: Math.max(1, os19.availableParallelism?.() ?? os19.cpus().length)
77236
- }), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path48.join(stateDir2(env), "leases"), readText5 = (file) => {
77519
+ }), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path49.join(stateDir2(env), "leases"), readText5 = (file) => {
77237
77520
  try {
77238
77521
  return fs24.readFileSync(file, "utf8");
77239
77522
  } catch {
@@ -77288,7 +77571,7 @@ __export(exports_check, {
77288
77571
  workspaceResources: () => workspaceResources
77289
77572
  });
77290
77573
  import os20 from "node:os";
77291
- import path49 from "node:path";
77574
+ import path50 from "node:path";
77292
77575
  function parse6(argv) {
77293
77576
  const split = argv.indexOf("--");
77294
77577
  const head = split >= 0 ? argv.slice(0, split) : [...argv];
@@ -77486,7 +77769,7 @@ function hostToolWaitCapMs(env) {
77486
77769
  }
77487
77770
  async function workspaceResources(io) {
77488
77771
  const env = io.env;
77489
- const dir = env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path49.join(env.XDG_CONFIG_HOME || path49.join(env.HOME || os20.homedir(), ".config"), "workit");
77772
+ 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");
77490
77773
  try {
77491
77774
  await Promise.resolve().then(() => init_workspaces());
77492
77775
  const workspace = resolveWorkspaceFrom(io.cwd, dir, env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
@@ -77605,7 +77888,7 @@ async function run12(argv, io) {
77605
77888
  if (options.shell && command.length !== 1)
77606
77889
  return emit(io, fail2("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
77607
77890
  }
77608
- const relCwd = posix(path49.relative(top, runCwd));
77891
+ const relCwd = posix(path50.relative(top, runCwd));
77609
77892
  const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
77610
77893
  const shadowed = !configured && name !== null ? config.checks.find((item) => item.name === name) : undefined;
77611
77894
  if (shadowed)
@@ -77667,7 +77950,7 @@ async function run12(argv, io) {
77667
77950
  const modifiedWorktree = key.tree !== null && treeAfter !== key.tree;
77668
77951
  const store = storeRoot(io.cwd);
77669
77952
  const blob = store.ok ? storeLog(store.value.root, result.log) : null;
77670
- const logPath = store.ok && blob ? path49.join(store.value.root, blob.ref) : null;
77953
+ const logPath = store.ok && blob ? path50.join(store.value.root, blob.ref) : null;
77671
77954
  const tail = tailLines(result.log, TAIL_LINES);
77672
77955
  const passed = result.exitCode === 0 && !result.timedOut;
77673
77956
  const bounded = boundedArgv(command);
@@ -77780,7 +78063,7 @@ async function run12(argv, io) {
77780
78063
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
77781
78064
  return 0;
77782
78065
  }
77783
- var USAGE5 = '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(path49.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, HOST_TOOL_WAIT_CAP_MS = 1e5;
78066
+ var USAGE5 = '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, HOST_TOOL_WAIT_CAP_MS = 1e5;
77784
78067
  var init_check = __esm(() => {
77785
78068
  init_check_config();
77786
78069
  init_checks3();
@@ -77806,7 +78089,7 @@ __export(exports_lock, {
77806
78089
  run: () => run13
77807
78090
  });
77808
78091
  import fs25 from "node:fs";
77809
- import path50 from "node:path";
78092
+ import path51 from "node:path";
77810
78093
  async function settingsFor(io) {
77811
78094
  const workspace = await workspaceResources(io);
77812
78095
  const config = loadCheckConfig(io.cwd);
@@ -77888,7 +78171,7 @@ function readPatterns(file) {
77888
78171
  return { patterns: [] };
77889
78172
  }
77890
78173
  function writePatterns(file, value) {
77891
- fs25.mkdirSync(path50.dirname(file), { recursive: true, mode: 448 });
78174
+ fs25.mkdirSync(path51.dirname(file), { recursive: true, mode: 448 });
77892
78175
  const temp = `${file}.${process.pid}.tmp`;
77893
78176
  fs25.writeFileSync(temp, `${JSON.stringify(value, null, 2)}
77894
78177
  `, { mode: 384 });
@@ -77974,9 +78257,9 @@ var exports_models = {};
77974
78257
  __export(exports_models, {
77975
78258
  run: () => run14
77976
78259
  });
77977
- import { existsSync as existsSync22, readdirSync as readdirSync10, readFileSync as readFileSync21, statSync as statSync9 } from "node:fs";
78260
+ import { existsSync as existsSync22, readdirSync as readdirSync12, readFileSync as readFileSync22, statSync as statSync9 } from "node:fs";
77978
78261
  import os21 from "node:os";
77979
- import path51 from "node:path";
78262
+ import path52 from "node:path";
77980
78263
  async function run14(argv, io) {
77981
78264
  const [sub, ...args] = argv.filter((arg) => arg !== "--json");
77982
78265
  if (sub !== "show")
@@ -78037,7 +78320,7 @@ var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78037
78320
  const found = new Set;
78038
78321
  let text = "";
78039
78322
  try {
78040
- text = readFileSync21(file, "utf8");
78323
+ text = readFileSync22(file, "utf8");
78041
78324
  } catch {
78042
78325
  return found;
78043
78326
  }
@@ -78051,24 +78334,24 @@ var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78051
78334
  } catch {}
78052
78335
  return found;
78053
78336
  }, matches3 = (ran, configured) => ran.toLowerCase().includes(configured.toLowerCase()), usageReport = (cwd, env, days) => {
78054
- const projects = path51.join(env.CLAUDE_CONFIG_DIR || path51.join(env.HOME || os21.homedir(), ".claude"), "projects");
78337
+ const projects = path52.join(env.CLAUDE_CONFIG_DIR || path52.join(env.HOME || os21.homedir(), ".claude"), "projects");
78055
78338
  const since = Date.now() - days * 86400000;
78056
78339
  const types = {};
78057
78340
  let agents = 0;
78058
78341
  const dirs = (dir) => {
78059
78342
  try {
78060
- return readdirSync10(dir).map((name) => path51.join(dir, name));
78343
+ return readdirSync12(dir).map((name) => path52.join(dir, name));
78061
78344
  } catch {
78062
78345
  return [];
78063
78346
  }
78064
78347
  };
78065
78348
  for (const project of dirs(projects))
78066
78349
  for (const session of dirs(project))
78067
- for (const meta of dirs(path51.join(session, "subagents")).filter((f) => f.endsWith(".meta.json"))) {
78350
+ for (const meta of dirs(path52.join(session, "subagents")).filter((f) => f.endsWith(".meta.json"))) {
78068
78351
  try {
78069
78352
  if (statSync9(meta).mtimeMs < since)
78070
78353
  continue;
78071
- const agentType = JSON.parse(readFileSync21(meta, "utf8")).agentType;
78354
+ const agentType = JSON.parse(readFileSync22(meta, "utf8")).agentType;
78072
78355
  const transcript = meta.replace(/\.meta\.json$/, ".jsonl");
78073
78356
  if (typeof agentType !== "string" || !existsSync22(transcript))
78074
78357
  continue;
@@ -78209,8 +78492,8 @@ var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock)
78209
78492
 
78210
78493
  // packages/workit-core/src/git/ops.ts
78211
78494
  import { spawnSync as spawnSync20 } from "node:child_process";
78212
- import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
78213
- import path52 from "node:path";
78495
+ import { existsSync as existsSync23, readFileSync as readFileSync23 } from "node:fs";
78496
+ import path53 from "node:path";
78214
78497
  function operationInProgress(cwd) {
78215
78498
  const markers = [
78216
78499
  ["rebase-merge", "rebase"],
@@ -78223,18 +78506,18 @@ function operationInProgress(cwd) {
78223
78506
  if (!run.ok)
78224
78507
  return null;
78225
78508
  const found = run.stdout.split(`
78226
- `).map((line) => path52.resolve(cwd, line.trim()));
78509
+ `).map((line) => path53.resolve(cwd, line.trim()));
78227
78510
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
78228
78511
  for (const [index, [, operation]] of markers.entries()) {
78229
78512
  const marker = found[index];
78230
78513
  if (!marker || !existsSync23(marker))
78231
78514
  continue;
78232
- if (index === 1 && existsSync23(path52.join(marker, "applying")))
78515
+ if (index === 1 && existsSync23(path53.join(marker, "applying")))
78233
78516
  return { operation: "am", commitAllowed: false, branch: null };
78234
- if (index === 0 && existsSync23(path52.join(marker, "amend")) && !conflicted()) {
78517
+ if (index === 0 && existsSync23(path53.join(marker, "amend")) && !conflicted()) {
78235
78518
  let headName = "";
78236
78519
  try {
78237
- headName = readFileSync22(path52.join(marker, "head-name"), "utf8").trim();
78520
+ headName = readFileSync23(path53.join(marker, "head-name"), "utf8").trim();
78238
78521
  } catch {}
78239
78522
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
78240
78523
  return { operation, commitAllowed: branch !== null, branch };
@@ -78562,7 +78845,7 @@ function gitCommit(cwd, input) {
78562
78845
  if (paths.some((value) => value.startsWith("-") || !value.trim()))
78563
78846
  return failure3("invalid_input", "paths must not be empty or start with -");
78564
78847
  const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
78565
- const addable = paths.filter((value) => existsSync23(path52.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
78848
+ const addable = paths.filter((value) => existsSync23(path53.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
78566
78849
  if (addable.length) {
78567
78850
  const added = git5(cwd, ["add", "-A", "--", ...addable]);
78568
78851
  if (!added.ok)
@@ -79101,12 +79384,12 @@ var init_ops = __esm(() => {
79101
79384
 
79102
79385
  // packages/workit-core/src/verdict-cover.ts
79103
79386
  import fs26 from "node:fs";
79104
- import path53 from "node:path";
79387
+ import path54 from "node:path";
79105
79388
  function chainsAbove(cwd, branch) {
79106
79389
  const root = storeRoot(cwd);
79107
79390
  if (!root.ok)
79108
79391
  return [];
79109
- const dir = path53.join(root.value.root, "stacks");
79392
+ const dir = path54.join(root.value.root, "stacks");
79110
79393
  let names = [];
79111
79394
  try {
79112
79395
  names = fs26.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -79117,7 +79400,7 @@ function chainsAbove(cwd, branch) {
79117
79400
  for (const name of names.toSorted()) {
79118
79401
  let parsed;
79119
79402
  try {
79120
- parsed = JSON.parse(fs26.readFileSync(path53.join(dir, name), "utf8"));
79403
+ parsed = JSON.parse(fs26.readFileSync(path54.join(dir, name), "utf8"));
79121
79404
  } catch {
79122
79405
  continue;
79123
79406
  }
@@ -79212,10 +79495,10 @@ var init_pr_lane = __esm(() => {
79212
79495
 
79213
79496
  // packages/workit-core/src/forge/reruns.ts
79214
79497
  import fs27 from "node:fs";
79215
- import path54 from "node:path";
79498
+ import path55 from "node:path";
79216
79499
  function rerunLogPath(cwd) {
79217
79500
  const common = gitCommonDir(cwd);
79218
- return common ? path54.join(common, "workit", "ci-reruns.jsonl") : null;
79501
+ return common ? path55.join(common, "workit", "ci-reruns.jsonl") : null;
79219
79502
  }
79220
79503
  function rerunCounts(cwd, key) {
79221
79504
  const counts = new Map;
@@ -79250,7 +79533,7 @@ function recordReruns(cwd, rows) {
79250
79533
  if (!file)
79251
79534
  return false;
79252
79535
  try {
79253
- fs27.mkdirSync(path54.dirname(file), { recursive: true });
79536
+ fs27.mkdirSync(path55.dirname(file), { recursive: true });
79254
79537
  fs27.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
79255
79538
  `).join(""));
79256
79539
  return true;
@@ -79263,7 +79546,7 @@ function withRerunLock(cwd, fn) {
79263
79546
  if (!file)
79264
79547
  return fn();
79265
79548
  const lock = `${file.slice(0, -".jsonl".length)}.lock`;
79266
- fs27.mkdirSync(path54.dirname(lock), { recursive: true });
79549
+ fs27.mkdirSync(path55.dirname(lock), { recursive: true });
79267
79550
  const until = Date.now() + LOCK_WAIT_MS2;
79268
79551
  for (;; ) {
79269
79552
  try {
@@ -79952,6 +80235,22 @@ function implicitDeleteSkip(cwd, resolved, doc, branch) {
79952
80235
  return `open PR ${children.data.map((n) => `#${n}`).join(", ")} targets ${branch} (a stack)`;
79953
80236
  return null;
79954
80237
  }
80238
+ function baseCiState(cwd, resolved, base) {
80239
+ if (!isProtectedTarget(cwd, base))
80240
+ return { ok: true, note: null };
80241
+ const read = resolved.forge.branchChecks(base);
80242
+ if (!read.ok)
80243
+ return { ok: true, note: `base ${base} CI not read (${read.error}); merging anyway` };
80244
+ const { requiredKnown } = read.data;
80245
+ const failing = read.data.checks.filter((check) => check.state === "failing" && (requiredKnown ? check.required === true : check.required !== false && check.conclusion === "failure"));
80246
+ if (failing.length)
80247
+ return { ok: false, failing, sha: read.data.sha, requiredKnown };
80248
+ const pending = read.data.checks.filter((check) => check.state === "pending");
80249
+ return {
80250
+ ok: true,
80251
+ note: pending.length ? `base ${base} CI is still running (${pending.map((check) => check.name).join(", ")}); merging without waiting` : null
80252
+ };
80253
+ }
79955
80254
  async function mergePullRequest(cwd, resolved, input, sleep) {
79956
80255
  const grant = requireGrant(cwd, "merge");
79957
80256
  if (!grant.allowed)
@@ -79961,6 +80260,9 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
79961
80260
  return failure3("invalid_input", "--unverified needs --reason <why the user asked for it>");
79962
80261
  if (bypass && !grant.allowUnverified)
79963
80262
  return refuse(`unverified_refused: workspace ${grant.workspace ? `"${grant.workspace}"` : "(none)"} grants merge: "verified", which never merges without an accepted independent verdict`, "a non-author session verifies the head (workit-review), then merge without --unverified", { reason: "unverified_refused" });
80263
+ const baseRed = input.baseRedOk ?? null;
80264
+ if (baseRed && !baseRed.reason.trim())
80265
+ return failure3("invalid_input", "--base-red-ok needs --reason <why merging onto a red base is fine>");
79964
80266
  const number = selectPr(cwd, resolved, { pr: input.pr });
79965
80267
  if (!number.ok)
79966
80268
  return number;
@@ -80045,6 +80347,15 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
80045
80347
  const lane = gate.verification === "proportional" ? `, ${laneLabel(gate)}` : "";
80046
80348
  return refuse(gate.need === "self" ? `NEEDS_VERDICT: ${label} has no verdict for ${head.slice(0, 12)}${lane}: the author's own --self verdict is enough (${check.accepted.reasons.join(", ")})` : `NEEDS_VERDICT: ${label} has no accepted independent verdict for ${head.slice(0, 12)}${lane} (${check.accepted.reasons.join(", ")})`, gate.need === "self" ? `after an observed passing \`workit check test\`, the author runs: workit ledger verdict tests-verified --self --how "<what you ran>" --branch ${branch} (or a verifier records one)${bypassHint}` : `an independent session verifies the head and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${branch}${bypassHint}`, { reason: "needs_verdict", verdict: summary });
80047
80349
  }
80350
+ const baseCi = baseCiState(cwd, resolved, doc.base);
80351
+ let baseRedOverride = null;
80352
+ if (!baseCi.ok) {
80353
+ const names = baseCi.failing.map((failed) => failed.name).join(", ");
80354
+ const url = baseCi.failing.find((failed) => failed.url)?.url ?? null;
80355
+ if (!baseRed)
80356
+ return refuse(`base_red: ${doc.base} at ${baseCi.sha?.slice(0, 12) ?? "its head"} has failing CI (${names}${baseCi.requiredKnown ? "" : "; the required checks could not be read, so every failed check counts"})${url ? `: ${url}` : ""}; merging ${label} onto it would land on a red base`, `fix ${doc.base} first, or, if the user accepts it: workit pr merge${input.pr ? ` --pr ${doc.number}` : ""} --base-red-ok --reason "<why>"`, { reason: "base_red", blockers: baseCi.failing.map((failed) => failed.name) });
80357
+ baseRedOverride = baseRed;
80358
+ }
80048
80359
  const squashMessage = squashMessageFor(cwd, resolved, input, doc);
80049
80360
  if (squashMessage && !squashMessage.ok)
80050
80361
  return squashMessage;
@@ -80060,6 +80371,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
80060
80371
  review: verdict.review,
80061
80372
  lane: verdict.lane,
80062
80373
  ...unverified ? { unverified: unverified.recorded } : {},
80374
+ ...baseRedOverride ? { baseRedOk: baseRedOverride.reason } : {},
80063
80375
  grant: grant.source
80064
80376
  });
80065
80377
  if (!intent.ok)
@@ -80088,6 +80400,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
80088
80400
  review: verdict.review,
80089
80401
  lane: verdict.lane,
80090
80402
  ...unverified ? { unverified: unverified.recorded } : {},
80403
+ ...baseRedOverride ? { baseRedOk: baseRedOverride.reason } : {},
80091
80404
  grant: grant.source,
80092
80405
  intent: intentId,
80093
80406
  verified: false
@@ -80144,6 +80457,10 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
80144
80457
  mergeSha: merged.data.mergeSha,
80145
80458
  verdict,
80146
80459
  unverified,
80460
+ baseCi: {
80461
+ note: baseRedOverride ? `base ${doc.base} CI is red; merged anyway (--base-red-ok: ${baseRedOverride.reason})` : baseCi.ok ? baseCi.note : null,
80462
+ override: baseRedOverride
80463
+ },
80147
80464
  grant: { source: grant.source },
80148
80465
  deletedBranch,
80149
80466
  recorded,
@@ -80210,7 +80527,7 @@ var init_pr_ops = __esm(() => {
80210
80527
  import { spawnSync as spawnSync22 } from "node:child_process";
80211
80528
  import { createHash as createHash17, randomBytes as randomBytes4 } from "node:crypto";
80212
80529
  import fs28 from "node:fs";
80213
- import path55 from "node:path";
80530
+ import path56 from "node:path";
80214
80531
  function worktreeOf(cwd, branch) {
80215
80532
  const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
80216
80533
  if (!list.ok)
@@ -80254,7 +80571,7 @@ function gitRestack(cwd, input) {
80254
80571
  if (!scratch)
80255
80572
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
80256
80573
  fs28.mkdirSync(scratch, { recursive: true });
80257
- dir = path55.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
80574
+ dir = path56.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
80258
80575
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
80259
80576
  if (!added.ok)
80260
80577
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -80295,7 +80612,7 @@ function pruneScratch(cwd) {
80295
80612
  if (!scratch || !fs28.existsSync(scratch))
80296
80613
  return;
80297
80614
  for (const name of fs28.readdirSync(scratch)) {
80298
- const dir = path55.join(scratch, name);
80615
+ const dir = path56.join(scratch, name);
80299
80616
  if (!rebaseInProgress(dir) && !trackedDirt(dir))
80300
80617
  gitRun3(cwd, ["worktree", "remove", "--force", dir]);
80301
80618
  }
@@ -80305,7 +80622,7 @@ function stacksDir(cwd) {
80305
80622
  const root = storeRoot(cwd);
80306
80623
  if (!root.ok)
80307
80624
  return stackFail(root.code, root.error, root.unblock);
80308
- return { ok: true, data: path55.join(root.value.root, "stacks") };
80625
+ return { ok: true, data: path56.join(root.value.root, "stacks") };
80309
80626
  }
80310
80627
  function parseStack(raw) {
80311
80628
  if (!isRecord12(raw) || !Array.isArray(raw.branches))
@@ -80357,7 +80674,7 @@ function readStack(cwd, name) {
80357
80674
  const dir = stacksDir(cwd);
80358
80675
  if (!dir.ok)
80359
80676
  return dir;
80360
- const file = path55.join(dir.data, fileFor(name));
80677
+ const file = path56.join(dir.data, fileFor(name));
80361
80678
  let text;
80362
80679
  try {
80363
80680
  text = fs28.readFileSync(file, "utf8");
@@ -80378,7 +80695,7 @@ function writeStack(cwd, stack, now = new Date) {
80378
80695
  const dir = stacksDir(cwd);
80379
80696
  if (!dir.ok)
80380
80697
  return dir;
80381
- const file = path55.join(dir.data, fileFor(stack.name));
80698
+ const file = path56.join(dir.data, fileFor(stack.name));
80382
80699
  const temp = `${file}.${process.pid}.tmp`;
80383
80700
  try {
80384
80701
  fs28.mkdirSync(dir.data, { recursive: true });
@@ -80406,7 +80723,7 @@ function listStacks(cwd) {
80406
80723
  for (const name of names) {
80407
80724
  let parsed = null;
80408
80725
  try {
80409
- parsed = parseStack(JSON.parse(fs28.readFileSync(path55.join(dir.data, name), "utf8")));
80726
+ parsed = parseStack(JSON.parse(fs28.readFileSync(path56.join(dir.data, name), "utf8")));
80410
80727
  } catch {
80411
80728
  parsed = null;
80412
80729
  }
@@ -80465,7 +80782,7 @@ function takeLock(lock) {
80465
80782
  host: localLockHost(),
80466
80783
  nonce: randomBytes4(8).toString("hex")
80467
80784
  };
80468
- fs28.writeFileSync(path55.join(lock, OWNER), JSON.stringify(owner));
80785
+ fs28.writeFileSync(path56.join(lock, OWNER), JSON.stringify(owner));
80469
80786
  return owner;
80470
80787
  }
80471
80788
  function reapLock(lock, observed) {
@@ -80502,7 +80819,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
80502
80819
  return dir;
80503
80820
  if (!validName(name))
80504
80821
  return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
80505
- const lock = path55.join(dir.data, `${fileFor(name)}.lock`);
80822
+ const lock = path56.join(dir.data, `${fileFor(name)}.lock`);
80506
80823
  try {
80507
80824
  fs28.mkdirSync(dir.data, { recursive: true });
80508
80825
  } catch (error) {
@@ -80532,7 +80849,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
80532
80849
  const touch = () => {
80533
80850
  try {
80534
80851
  const now = new Date;
80535
- fs28.utimesSync(path55.join(lock, OWNER), now, now);
80852
+ fs28.utimesSync(path56.join(lock, OWNER), now, now);
80536
80853
  } catch {}
80537
80854
  };
80538
80855
  const beat = setInterval(touch, HEARTBEAT_MS);
@@ -81383,7 +81700,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
81383
81700
  return !status.ok || status.stdout.trim() !== "";
81384
81701
  }, scratchDir = (cwd) => {
81385
81702
  const dir = stacksDir(cwd);
81386
- return dir.ok ? path55.join(dir.data, "worktrees") : null;
81703
+ return dir.ok ? path56.join(dir.data, "worktrees") : null;
81387
81704
  }, firstLine = (run) => (`${run.stderr}
81388
81705
  ${run.stdout}`.split(`
81389
81706
  `).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) => {
@@ -81398,12 +81715,12 @@ ${run.stdout}`.split(`
81398
81715
  }, 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) => {
81399
81716
  let raw = null;
81400
81717
  try {
81401
- raw = fs28.readFileSync(path55.join(lock, OWNER), "utf8");
81718
+ raw = fs28.readFileSync(path56.join(lock, OWNER), "utf8");
81402
81719
  } catch {
81403
81720
  raw = null;
81404
81721
  }
81405
81722
  let age = null;
81406
- for (const target of [path55.join(lock, OWNER), lock])
81723
+ for (const target of [path56.join(lock, OWNER), lock])
81407
81724
  try {
81408
81725
  age = Date.now() - fs28.statSync(target).mtimeMs;
81409
81726
  break;
@@ -81446,7 +81763,7 @@ var init_stack = __esm(() => {
81446
81763
  import { spawnSync as spawnSync23 } from "node:child_process";
81447
81764
  import { createHash as createHash18 } from "node:crypto";
81448
81765
  import fs29 from "node:fs";
81449
- import path56 from "node:path";
81766
+ import path57 from "node:path";
81450
81767
  function expandBraces2(pattern, limit = 64) {
81451
81768
  for (let index = 0;index < pattern.length; index += 1) {
81452
81769
  if (pattern[index] === "\\") {
@@ -81692,7 +82009,7 @@ function parsePlan(raw, defaults, name) {
81692
82009
  id,
81693
82010
  branch,
81694
82011
  base: optional("base") ?? "",
81695
- worktree: path56.resolve(defaults.root, optional("worktree") ?? path56.join(defaults.worktreeRoot, id)),
82012
+ worktree: path57.resolve(defaults.root, optional("worktree") ?? path57.join(defaults.worktreeRoot, id)),
81696
82013
  tier,
81697
82014
  reason,
81698
82015
  dependsOn: [...new Set(dependsOn ?? [])],
@@ -81821,12 +82138,12 @@ function regeneratedFiles(root, tracked) {
81821
82138
  const found = new Map;
81822
82139
  let scripts;
81823
82140
  try {
81824
- const manifest = JSON.parse(fs29.readFileSync(path56.join(root, "package.json"), "utf8"));
82141
+ const manifest = JSON.parse(fs29.readFileSync(path57.join(root, "package.json"), "utf8"));
81825
82142
  scripts = manifest.scripts ?? {};
81826
82143
  } catch {
81827
82144
  return found;
81828
82145
  }
81829
- const runner = fs29.existsSync(path56.join(root, "bun.lock")) ? "bun run" : fs29.existsSync(path56.join(root, "pnpm-lock.yaml")) ? "pnpm run" : fs29.existsSync(path56.join(root, "yarn.lock")) ? "yarn run" : "npm run";
82146
+ 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";
81830
82147
  const outputs = tracked.filter((file) => !CODE.test(file));
81831
82148
  for (const [name, command] of Object.entries(scripts)) {
81832
82149
  if (!name.startsWith("write:") || typeof command !== "string")
@@ -81834,7 +82151,7 @@ function regeneratedFiles(root, tracked) {
81834
82151
  for (const file of command.match(/[\w./-]+\.[cm]?[jt]s\b/gu) ?? []) {
81835
82152
  let source;
81836
82153
  try {
81837
- source = fs29.readFileSync(path56.join(root, file), "utf8");
82154
+ source = fs29.readFileSync(path57.join(root, file), "utf8");
81838
82155
  } catch {
81839
82156
  continue;
81840
82157
  }
@@ -81956,11 +82273,11 @@ function defaultTrunk(cwd) {
81956
82273
  }
81957
82274
  function mainCheckout(cwd) {
81958
82275
  const common = gitCommonDir(cwd);
81959
- return common && path56.basename(common) === ".git" ? path56.dirname(common) : path56.resolve(cwd);
82276
+ return common && path57.basename(common) === ".git" ? path57.dirname(common) : path57.resolve(cwd);
81960
82277
  }
81961
82278
  function worktreeRoot(cwd) {
81962
82279
  const main = mainCheckout(cwd);
81963
- return path56.join(path56.dirname(main), `${path56.basename(main)}-wt`);
82280
+ return path57.join(path57.dirname(main), `${path57.basename(main)}-wt`);
81964
82281
  }
81965
82282
  function sliceHash(slice) {
81966
82283
  const {
@@ -81980,7 +82297,7 @@ function fanoutsDir(cwd) {
81980
82297
  const root = storeRoot(cwd);
81981
82298
  if (!root.ok)
81982
82299
  return fanoutFail(root.code, root.error, root.unblock);
81983
- return { ok: true, data: path56.join(root.value.root, "fanouts") };
82300
+ return { ok: true, data: path57.join(root.value.root, "fanouts") };
81984
82301
  }
81985
82302
  function parseStored(raw) {
81986
82303
  if (!isRecord13(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -82048,7 +82365,7 @@ function readFanout(cwd, name) {
82048
82365
  const dir = fanoutsDir(cwd);
82049
82366
  if (!dir.ok)
82050
82367
  return dir;
82051
- const file = path56.join(dir.data, stackFileName(name));
82368
+ const file = path57.join(dir.data, stackFileName(name));
82052
82369
  let parsed = null;
82053
82370
  try {
82054
82371
  parsed = parseStored(JSON.parse(fs29.readFileSync(file, "utf8")));
@@ -82073,7 +82390,7 @@ function listFanouts(cwd) {
82073
82390
  for (const entry of names) {
82074
82391
  let parsed = null;
82075
82392
  try {
82076
- parsed = parseStored(JSON.parse(fs29.readFileSync(path56.join(dir.data, entry), "utf8")));
82393
+ parsed = parseStored(JSON.parse(fs29.readFileSync(path57.join(dir.data, entry), "utf8")));
82077
82394
  } catch {
82078
82395
  parsed = null;
82079
82396
  }
@@ -82112,7 +82429,7 @@ function writeFanout(cwd, plan) {
82112
82429
  const dir = fanoutsDir(cwd);
82113
82430
  if (!dir.ok)
82114
82431
  return dir;
82115
- const file = path56.join(dir.data, stackFileName(plan.name));
82432
+ const file = path57.join(dir.data, stackFileName(plan.name));
82116
82433
  const temp = `${file}.${process.pid}.tmp`;
82117
82434
  try {
82118
82435
  fs29.mkdirSync(dir.data, { recursive: true });
@@ -82323,13 +82640,13 @@ var init_fanout = __esm(() => {
82323
82640
  // packages/workit-core/src/forge/exec.ts
82324
82641
  import { spawnSync as spawnSync24 } from "node:child_process";
82325
82642
  import fs30 from "node:fs";
82326
- import path57 from "node:path";
82643
+ import path58 from "node:path";
82327
82644
  function findExecutable(bin, env) {
82328
- const dirs = (env.PATH ?? env.Path ?? "").split(path57.delimiter).filter(Boolean);
82645
+ const dirs = (env.PATH ?? env.Path ?? "").split(path58.delimiter).filter(Boolean);
82329
82646
  const exts = process.platform === "win32" ? [".exe"] : [""];
82330
82647
  for (const dir of dirs)
82331
82648
  for (const ext of exts) {
82332
- const candidate = path57.join(dir, `${bin}${ext}`);
82649
+ const candidate = path58.join(dir, `${bin}${ext}`);
82333
82650
  try {
82334
82651
  const stat = fs30.statSync(candidate);
82335
82652
  if (stat.isFile())
@@ -82699,6 +83016,30 @@ function createGitHubForge(options) {
82699
83016
  };
82700
83017
  return success2(status);
82701
83018
  },
83019
+ branchChecks(branch) {
83020
+ const contexts = [];
83021
+ let sha = null;
83022
+ let cursor = null;
83023
+ for (let page = 1;page <= maxPages; page += 1) {
83024
+ const data = graphql(BRANCH_CHECKS_QUERY, { owner, name, ref: `refs/heads/${branch}`, contexts: cursor }, `checks on ${branch}`);
83025
+ if (!data.ok)
83026
+ return data;
83027
+ const commit = data.data.repository?.object ?? null;
83028
+ if (!commit)
83029
+ return failure3("not_found", `branch ${branch} was not found`);
83030
+ sha = commit.oid ?? sha;
83031
+ const rollup = commit.statusCheckRollup;
83032
+ if (!rollup)
83033
+ break;
83034
+ contexts.push(...rollup.contexts.nodes);
83035
+ if (!rollup.contexts.pageInfo.hasNextPage)
83036
+ break;
83037
+ cursor = rollup.contexts.pageInfo.endCursor;
83038
+ }
83039
+ const required = requiredContexts(branch);
83040
+ const checks = latestPerName(contexts.map(checkFromContext).filter((check) => check !== null)).map((check) => required ? { ...check, required: required.includes(check.context) } : check);
83041
+ return success2({ sha, checks, requiredKnown: required !== null });
83042
+ },
82702
83043
  jobLogTail(job, lines) {
82703
83044
  const text = apiText(runner, "gh", apiHost, ["api", `repos/${repo}/actions/jobs/${job.jobId}/logs`], `job ${job.jobId} log`, FORGE_TIMEOUTS.log);
82704
83045
  return text.ok ? success2(logTail(text.data, lines)) : text;
@@ -82896,7 +83237,7 @@ function createGitHubForge(options) {
82896
83237
  }
82897
83238
  };
82898
83239
  }
82899
- var GITHUB_MAX_PAGES = 10, PR_FIELDS = `number url state isDraft isInMergeQueue mergeable mergeStateStatus baseRefName baseRefOid baseRef { target { oid } } headRefName headRefOid reviewDecision`, THREADS = `reviewThreads(first: 100, after: $threads) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line comments(first: 1) { nodes { author { login __typename } body url } } } }`, CONTEXTS = `contexts(first: 100, after: $contexts) { pageInfo { hasNextPage endCursor } nodes { __typename ... on CheckRun { name status conclusion detailsUrl databaseId isRequired(pullRequestNumber: $number) checkSuite { workflowRun { databaseId workflow { name } } } } ... on StatusContext { context state targetUrl isRequired(pullRequestNumber: $number) } } }`, PR_STATUS_QUERY, FIND_PR_QUERY = `query($owner: String!, $name: String!, $head: String!) { repository(owner: $owner, name: $name) { pullRequests(headRefName: $head, first: 20, orderBy: { field: CREATED_AT, direction: DESC }) { nodes { number url state baseRefName headRefName headRefOid headRepositoryOwner { login } } } } }`, READY_MUTATION = `mutation($id: ID!) { markPullRequestReadyForReview(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, DRAFT_MUTATION = `mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, REPLY_MUTATION = `mutation($thread: ID!, $body: String!) { addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $thread, body: $body }) { comment { url } } }`, RESOLVE_MUTATION = `mutation($thread: ID!) { resolveReviewThread(input: { threadId: $thread }) { thread { isResolved } } }`, THREAD_QUERY = `query($thread: ID!) { node(id: $thread) { ... on PullRequestReviewThread { isResolved pullRequest { number } } } }`, prState = (value) => value === "MERGED" ? "merged" : value === "CLOSED" ? "closed" : "open", FAILING2, safeUrl = (value) => value ? redactText(value) : null, encodeRef = (ref) => ref.split("/").map(encodeURIComponent).join("/");
83240
+ var GITHUB_MAX_PAGES = 10, PR_FIELDS = `number url state isDraft isInMergeQueue mergeable mergeStateStatus baseRefName baseRefOid baseRef { target { oid } } headRefName headRefOid reviewDecision`, THREADS = `reviewThreads(first: 100, after: $threads) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line comments(first: 1) { nodes { author { login __typename } body url } } } }`, CONTEXTS = `contexts(first: 100, after: $contexts) { pageInfo { hasNextPage endCursor } nodes { __typename ... on CheckRun { name status conclusion detailsUrl databaseId isRequired(pullRequestNumber: $number) checkSuite { workflowRun { databaseId workflow { name } } } } ... on StatusContext { context state targetUrl isRequired(pullRequestNumber: $number) } } }`, PR_STATUS_QUERY, FIND_PR_QUERY = `query($owner: String!, $name: String!, $head: String!) { repository(owner: $owner, name: $name) { pullRequests(headRefName: $head, first: 20, orderBy: { field: CREATED_AT, direction: DESC }) { nodes { number url state baseRefName headRefName headRefOid headRepositoryOwner { login } } } } }`, BRANCH_CHECKS_QUERY = `query($owner: String!, $name: String!, $ref: String!, $contexts: String) { repository(owner: $owner, name: $name) { object(expression: $ref) { ... on Commit { oid statusCheckRollup { contexts(first: 100, after: $contexts) { pageInfo { hasNextPage endCursor } nodes { __typename ... on CheckRun { name status conclusion detailsUrl databaseId checkSuite { workflowRun { databaseId workflow { name } } } } ... on StatusContext { context state targetUrl } } } } } } } }`, READY_MUTATION = `mutation($id: ID!) { markPullRequestReadyForReview(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, DRAFT_MUTATION = `mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, REPLY_MUTATION = `mutation($thread: ID!, $body: String!) { addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $thread, body: $body }) { comment { url } } }`, RESOLVE_MUTATION = `mutation($thread: ID!) { resolveReviewThread(input: { threadId: $thread }) { thread { isResolved } } }`, THREAD_QUERY = `query($thread: ID!) { node(id: $thread) { ... on PullRequestReviewThread { isResolved pullRequest { number } } } }`, prState = (value) => value === "MERGED" ? "merged" : value === "CLOSED" ? "closed" : "open", FAILING2, safeUrl = (value) => value ? redactText(value) : null, encodeRef = (ref) => ref.split("/").map(encodeURIComponent).join("/");
82900
83241
  var init_github = __esm(() => {
82901
83242
  init_exec();
82902
83243
  init_redact();
@@ -83097,6 +83438,35 @@ function createGitLabForge(options) {
83097
83438
  };
83098
83439
  return success2(status);
83099
83440
  },
83441
+ branchChecks(branch) {
83442
+ const tip = baseTip(branch);
83443
+ if (!tip)
83444
+ return failure3("not_found", `branch ${branch} was not found`);
83445
+ const latest = apiJson(runner, "glab", apiHost, api(`${project}/pipelines?ref=${encodeURIComponent(branch)}&sha=${tip}&order_by=id&sort=desc&per_page=1`), `pipelines on ${branch}`);
83446
+ if (!latest.ok)
83447
+ return latest;
83448
+ const pipeline = Array.isArray(latest.data) ? latest.data[0] : undefined;
83449
+ if (!pipeline)
83450
+ return success2({ sha: tip, checks: [], requiredKnown: true });
83451
+ const scope = String(pipeline.project_id ?? encodeURIComponent(repo));
83452
+ const jobs = pages(`projects/${scope}/pipelines/${pipeline.id}/jobs`, `pipeline ${pipeline.id} jobs`);
83453
+ if (!jobs.ok)
83454
+ return jobs;
83455
+ const bridges = pages(`projects/${scope}/pipelines/${pipeline.id}/bridges`, `pipeline ${pipeline.id} bridges`);
83456
+ if (!bridges.ok)
83457
+ return bridges;
83458
+ const checks = [
83459
+ ...jobs.data.items.map((job) => checkFromJob(job, pipeline.id, scope)),
83460
+ ...bridges.data.items.map((bridge) => checkFromBridge(bridge, pipeline.id, scope))
83461
+ ];
83462
+ if (stateOf(pipeline.status, false) === "failing" && !checks.some((check) => check.state === "failing"))
83463
+ checks.push(pipelineCheck("failing", pipeline.status, {
83464
+ id: pipeline.id,
83465
+ project_id: pipeline.project_id ?? 0,
83466
+ web_url: pipeline.web_url
83467
+ }));
83468
+ return success2({ sha: tip, checks, requiredKnown: true });
83469
+ },
83100
83470
  jobLogTail(job, lines) {
83101
83471
  const scope = job.scope ?? encodeURIComponent(repo);
83102
83472
  const text = apiText(runner, "glab", apiHost, ["api", `projects/${scope}/jobs/${job.jobId}/trace`], `job ${job.jobId} trace`, FORGE_TIMEOUTS.log);
@@ -83839,7 +84209,7 @@ var init_live_session = __esm(() => {
83839
84209
  });
83840
84210
 
83841
84211
  // packages/workit-core/src/hooks/write-gate.ts
83842
- import path58 from "node:path";
84212
+ import path59 from "node:path";
83843
84213
  var CASE_INSENSITIVE, PREFIXES2, SCRIPT_SHELLS, cache3, MUTATING_GIT, DRY_N, MUTATING_WORKIT_GIT, CD2, PUSHD2, POPD2, UNKNOWN_DIR;
83844
84214
  var init_write_gate = __esm(() => {
83845
84215
  init_task_store();
@@ -83879,7 +84249,7 @@ var init_write_gate = __esm(() => {
83879
84249
  CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
83880
84250
  PUSHD2 = new Set(["pushd", "push-location"]);
83881
84251
  POPD2 = new Set(["popd", "pop-location"]);
83882
- UNKNOWN_DIR = path58.resolve(path58.parse(process.cwd()).root, ".workit-unknown-dir");
84252
+ UNKNOWN_DIR = path59.resolve(path59.parse(process.cwd()).root, ".workit-unknown-dir");
83883
84253
  });
83884
84254
 
83885
84255
  // packages/workit-core/src/hooks/worktree-confine.ts
@@ -83904,20 +84274,20 @@ var init_worktree_confine = __esm(() => {
83904
84274
 
83905
84275
  // packages/workit-core/src/worktree-deps.ts
83906
84276
  import fs31 from "node:fs";
83907
- import path59 from "node:path";
84277
+ import path60 from "node:path";
83908
84278
  function moduleDirs(main) {
83909
84279
  const out = [];
83910
84280
  const walk = (relative, depth) => {
83911
84281
  let entries;
83912
84282
  try {
83913
- entries = fs31.readdirSync(path59.join(main, relative), { withFileTypes: true });
84283
+ entries = fs31.readdirSync(path60.join(main, relative), { withFileTypes: true });
83914
84284
  } catch {
83915
84285
  return;
83916
84286
  }
83917
84287
  for (const entry of entries) {
83918
84288
  if (!entry.isDirectory())
83919
84289
  continue;
83920
- const child = path59.join(relative, entry.name);
84290
+ const child = path60.join(relative, entry.name);
83921
84291
  if (entry.name === MODULES)
83922
84292
  out.push(child);
83923
84293
  else if (depth < MAX_DEPTH2 && !entry.name.startsWith("."))
@@ -83931,8 +84301,8 @@ function linkEntry(main, worktree, source, target) {
83931
84301
  const stat = fs31.lstatSync(source);
83932
84302
  if (stat.isSymbolicLink()) {
83933
84303
  const link = fs31.readlinkSync(source);
83934
- const outside = path59.isAbsolute(link) && within(link, main) && !path59.relative(main, link).split(path59.sep).includes(MODULES);
83935
- const to = outside ? path59.join(worktree, path59.relative(main, link)) : link;
84304
+ const outside = path60.isAbsolute(link) && within(link, main) && !path60.relative(main, link).split(path60.sep).includes(MODULES);
84305
+ const to = outside ? path60.join(worktree, path60.relative(main, link)) : link;
83936
84306
  fs31.symlinkSync(to, target, process.platform === "win32" ? "junction" : undefined);
83937
84307
  return;
83938
84308
  }
@@ -83942,8 +84312,8 @@ function mirror(main, worktree, source, target, top) {
83942
84312
  fs31.mkdirSync(target);
83943
84313
  let count = 0;
83944
84314
  for (const entry of fs31.readdirSync(source, { withFileTypes: true })) {
83945
- const from = path59.join(source, entry.name);
83946
- const to = path59.join(target, entry.name);
84315
+ const from = path60.join(source, entry.name);
84316
+ const to = path60.join(target, entry.name);
83947
84317
  if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin"))
83948
84318
  count += mirror(main, worktree, from, to, false);
83949
84319
  else {
@@ -83959,16 +84329,16 @@ function linkWorktreeDependencies(main, worktree) {
83959
84329
  const dirs = [];
83960
84330
  let entries = 0;
83961
84331
  for (const relative of moduleDirs(main)) {
83962
- const target = path59.join(worktree, relative);
83963
- if (!fs31.existsSync(path59.dirname(target)) || fs31.existsSync(target))
84332
+ const target = path60.join(worktree, relative);
84333
+ if (!fs31.existsSync(path60.dirname(target)) || fs31.existsSync(target))
83964
84334
  continue;
83965
- if (!gitRun4(worktree, ["check-ignore", "-q", `${relative.split(path59.sep).join("/")}/`]).ok) {
83966
- notes.push(`${relative.split(path59.sep).join("/")} is not ignored by git, so it was not linked from ${main}; install dependencies in the worktree yourself`);
84335
+ if (!gitRun4(worktree, ["check-ignore", "-q", `${relative.split(path60.sep).join("/")}/`]).ok) {
84336
+ 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`);
83967
84337
  continue;
83968
84338
  }
83969
84339
  try {
83970
- entries += mirror(main, worktree, path59.join(main, relative), target, true);
83971
- dirs.push(relative.split(path59.sep).join("/"));
84340
+ entries += mirror(main, worktree, path60.join(main, relative), target, true);
84341
+ dirs.push(relative.split(path60.sep).join("/"));
83972
84342
  } catch (error) {
83973
84343
  fs31.rmSync(target, { recursive: true, force: true });
83974
84344
  notes.push(`could not link ${relative} from ${main} (${error.message}); install dependencies in the worktree yourself`);
@@ -83981,8 +84351,8 @@ function linkWorktreeDependencies(main, worktree) {
83981
84351
  return { linked: { from: main, dirs, entries, ms }, notes };
83982
84352
  }
83983
84353
  var MODULES = "node_modules", MAX_DEPTH2 = 2, within = (child, parent) => {
83984
- const relative = path59.relative(parent, child);
83985
- return relative === "" || !relative.startsWith("..") && !path59.isAbsolute(relative);
84354
+ const relative = path60.relative(parent, child);
84355
+ return relative === "" || !relative.startsWith("..") && !path60.isAbsolute(relative);
83986
84356
  };
83987
84357
  var init_worktree_deps = __esm(() => {
83988
84358
  init_fanout();
@@ -83991,7 +84361,7 @@ var init_worktree_deps = __esm(() => {
83991
84361
  // packages/workit-core/src/fanout-worktree.ts
83992
84362
  import { randomBytes as randomBytes5 } from "node:crypto";
83993
84363
  import fs32 from "node:fs";
83994
- import path60 from "node:path";
84364
+ import path61 from "node:path";
83995
84365
  function rowSample(lines) {
83996
84366
  const out = [];
83997
84367
  let bytes = 0;
@@ -84051,7 +84421,7 @@ function adminDir(worktree) {
84051
84421
  const file = dir.stdout.trim();
84052
84422
  let token = null;
84053
84423
  try {
84054
- token = fs32.readFileSync(path60.join(file, FANOUT_TOKEN_FILE), "utf8").trim() || null;
84424
+ token = fs32.readFileSync(path61.join(file, FANOUT_TOKEN_FILE), "utf8").trim() || null;
84055
84425
  } catch {
84056
84426
  token = null;
84057
84427
  }
@@ -84059,7 +84429,7 @@ function adminDir(worktree) {
84059
84429
  const stat = worktreeDeps.stat(file);
84060
84430
  return {
84061
84431
  path: file,
84062
- id: path60.basename(file),
84432
+ id: path61.basename(file),
84063
84433
  ino: stat.ino.toString(),
84064
84434
  token,
84065
84435
  birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
@@ -84075,7 +84445,7 @@ function markAdminDir(worktree) {
84075
84445
  return null;
84076
84446
  const token = randomBytes5(16).toString("hex");
84077
84447
  try {
84078
- fs32.writeFileSync(path60.join(admin.path, FANOUT_TOKEN_FILE), `${token}
84448
+ fs32.writeFileSync(path61.join(admin.path, FANOUT_TOKEN_FILE), `${token}
84079
84449
  `);
84080
84450
  } catch {
84081
84451
  return null;
@@ -84090,7 +84460,7 @@ function excludeScratch(cwd) {
84090
84460
  const common = gitCommonDir(cwd);
84091
84461
  if (!common)
84092
84462
  return;
84093
- const file = path60.join(common, "info", "exclude");
84463
+ const file = path61.join(common, "info", "exclude");
84094
84464
  let text = "";
84095
84465
  try {
84096
84466
  text = fs32.readFileSync(file, "utf8");
@@ -84099,7 +84469,7 @@ function excludeScratch(cwd) {
84099
84469
  }
84100
84470
  if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
84101
84471
  return;
84102
- fs32.mkdirSync(path60.dirname(file), { recursive: true });
84472
+ fs32.mkdirSync(path61.dirname(file), { recursive: true });
84103
84473
  fs32.appendFileSync(file, `${text && !text.endsWith(`
84104
84474
  `) ? `
84105
84475
  ` : ""}# workit fanout: per-worker scratch dirs
@@ -84108,13 +84478,13 @@ ${EXCLUDE_LINE}
84108
84478
  }
84109
84479
  function worktreeDependencies(main, worktree) {
84110
84480
  for (const [name, install] of LOCKFILES2) {
84111
- const ours = readBytes(path60.join(main, name));
84112
- const theirs = readBytes(path60.join(worktree, name));
84481
+ const ours = readBytes(path61.join(main, name));
84482
+ const theirs = readBytes(path61.join(worktree, name));
84113
84483
  if (ours === null && theirs === null)
84114
84484
  continue;
84115
84485
  if (ours !== null && theirs !== null && ours.equals(theirs))
84116
84486
  continue;
84117
- if (fs32.existsSync(path60.join(worktree, "node_modules")))
84487
+ if (fs32.existsSync(path61.join(worktree, "node_modules")))
84118
84488
  break;
84119
84489
  const command = install(worktree);
84120
84490
  return {
@@ -84128,7 +84498,7 @@ function worktreeDependencies(main, worktree) {
84128
84498
  return { ...linkWorktreeDependencies(main, worktree), install: null };
84129
84499
  }
84130
84500
  function ensureScratch(worktree) {
84131
- const scratch = path60.join(worktree, SCRATCH_DIR);
84501
+ const scratch = path61.join(worktree, SCRATCH_DIR);
84132
84502
  fs32.mkdirSync(scratch, { recursive: true });
84133
84503
  return scratch;
84134
84504
  }
@@ -84367,7 +84737,7 @@ function unpushedCount(cwd, branch) {
84367
84737
  return count.ok ? Number(count.stdout.trim()) : null;
84368
84738
  }
84369
84739
  function removeScratch(worktree) {
84370
- const scratch = path60.join(worktree, SCRATCH_DIR);
84740
+ const scratch = path61.join(worktree, SCRATCH_DIR);
84371
84741
  let stat;
84372
84742
  try {
84373
84743
  stat = fs32.lstatSync(scratch);
@@ -84383,16 +84753,16 @@ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000,
84383
84753
  try {
84384
84754
  return fs32.realpathSync.native(target);
84385
84755
  } catch {
84386
- return path60.resolve(target);
84756
+ return path61.resolve(target);
84387
84757
  }
84388
84758
  }, samePath = (a, b) => {
84389
84759
  const left = canonical2(a);
84390
84760
  const right = canonical2(b);
84391
84761
  return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
84392
84762
  }, inside3 = (child, parent) => {
84393
- const relative = path60.relative(canonical2(parent), canonical2(child));
84394
- return relative === "" || !relative.startsWith("..") && !path60.isAbsolute(relative);
84395
- }, slicePath = (cwd, slice) => path60.isAbsolute(slice.worktree) ? slice.worktree : path60.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeDeps, LOCKFILES2, readBytes = (file) => {
84763
+ const relative = path61.relative(canonical2(parent), canonical2(child));
84764
+ return relative === "" || !relative.startsWith("..") && !path61.isAbsolute(relative);
84765
+ }, slicePath = (cwd, slice) => path61.isAbsolute(slice.worktree) ? slice.worktree : path61.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeDeps, LOCKFILES2, readBytes = (file) => {
84396
84766
  try {
84397
84767
  return fs32.readFileSync(file);
84398
84768
  } catch {
@@ -84423,7 +84793,7 @@ var init_fanout_worktree = __esm(() => {
84423
84793
 
84424
84794
  // packages/workit-core/src/forge/merge-cleanup.ts
84425
84795
  import fs33 from "node:fs";
84426
- import path61 from "node:path";
84796
+ import path62 from "node:path";
84427
84797
  function cleanupMergedBranch(cwd, branch, mergedHead) {
84428
84798
  const out = { branchDeleted: false, worktreeRemoved: null, notes: [] };
84429
84799
  if (!branch || branch.startsWith("-"))
@@ -84483,13 +84853,13 @@ var SLOW2, isFanoutWorktree = (worktree) => {
84483
84853
  if (!dir.ok)
84484
84854
  return false;
84485
84855
  try {
84486
- return fs33.readFileSync(path61.join(dir.stdout.trim(), FANOUT_TOKEN_FILE), "utf8").trim() !== "";
84856
+ return fs33.readFileSync(path62.join(dir.stdout.trim(), FANOUT_TOKEN_FILE), "utf8").trim() !== "";
84487
84857
  } catch {
84488
84858
  return false;
84489
84859
  }
84490
84860
  }, inside4 = (child, parent) => {
84491
- const relative = path61.relative(fs33.realpathSync.native(parent), fs33.realpathSync.native(child));
84492
- return relative === "" || !relative.startsWith("..") && !path61.isAbsolute(relative);
84861
+ const relative = path62.relative(fs33.realpathSync.native(parent), fs33.realpathSync.native(child));
84862
+ return relative === "" || !relative.startsWith("..") && !path62.isAbsolute(relative);
84493
84863
  };
84494
84864
  var init_merge_cleanup = __esm(() => {
84495
84865
  init_fanout();
@@ -84770,7 +85140,7 @@ var init_duration = __esm(() => {
84770
85140
  });
84771
85141
 
84772
85142
  // packages/workit-cli/src/verbs/forge-common.ts
84773
- import { readFileSync as readFileSync23 } from "node:fs";
85143
+ import { readFileSync as readFileSync24 } from "node:fs";
84774
85144
  function parseFlags(argv, spec) {
84775
85145
  const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
84776
85146
  for (let index = 0;index < argv.length; index += 1) {
@@ -84925,7 +85295,7 @@ var init_forge_common = __esm(() => {
84925
85295
  forgeDeps = {
84926
85296
  runner: undefined,
84927
85297
  npm: undefined,
84928
- readStdin: () => readFileSync23(0, "utf8"),
85298
+ readStdin: () => readFileSync24(0, "utf8"),
84929
85299
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
84930
85300
  now: () => Date.now()
84931
85301
  };
@@ -84936,12 +85306,12 @@ var exports_pr = {};
84936
85306
  __export(exports_pr, {
84937
85307
  run: () => run15
84938
85308
  });
84939
- import { readFileSync as readFileSync24 } from "node:fs";
84940
- import path62 from "node:path";
85309
+ import { readFileSync as readFileSync25 } from "node:fs";
85310
+ import path63 from "node:path";
84941
85311
  function readBodyFile(io, file) {
84942
85312
  try {
84943
85313
  return {
84944
- body: file === "-" ? forgeDeps.readStdin() : readFileSync24(path62.resolve(io.cwd, file), "utf8")
85314
+ body: file === "-" ? forgeDeps.readStdin() : readFileSync25(path63.resolve(io.cwd, file), "utf8")
84945
85315
  };
84946
85316
  } catch {
84947
85317
  return `cannot read --body-file ${file}`;
@@ -85204,6 +85574,7 @@ async function merge3(argv, io) {
85204
85574
  "delete-branch": "boolean",
85205
85575
  "keep-branch": "boolean",
85206
85576
  unverified: "boolean",
85577
+ "base-red-ok": "boolean",
85207
85578
  reason: "value",
85208
85579
  subject: "value",
85209
85580
  body: "value",
@@ -85211,7 +85582,11 @@ async function merge3(argv, io) {
85211
85582
  });
85212
85583
  if (typeof flags === "string")
85213
85584
  return usage4(io, flags, MERGE_USAGE);
85214
- const unverified = unverifiedFlag(flags);
85585
+ const baseRedOk = flags.booleans.has("base-red-ok");
85586
+ const baseRedReason = flags.values.reason?.trim();
85587
+ if (baseRedOk && !baseRedReason)
85588
+ return usage4(io, "--base-red-ok needs --reason <why merging onto a red base is fine>", MERGE_USAGE);
85589
+ const unverified = baseRedOk && !flags.booleans.has("unverified") ? null : unverifiedFlag(flags);
85215
85590
  if (typeof unverified === "string")
85216
85591
  return usage4(io, unverified, MERGE_USAGE);
85217
85592
  if (flags.positionals.length)
@@ -85253,6 +85628,7 @@ async function merge3(argv, io) {
85253
85628
  implicitDelete: !flags.booleans.has("keep-branch") && workspaceDeleteBranch(io.cwd),
85254
85629
  actor: actorFromEnv(io.env),
85255
85630
  ...unverified ? { unverified } : {},
85631
+ ...baseRedOk && baseRedReason ? { baseRedOk: { reason: baseRedReason } } : {},
85256
85632
  ...subject !== undefined ? { subject } : {},
85257
85633
  ...body !== undefined ? { body } : {}
85258
85634
  }, forgeDeps.sleep);
@@ -85265,6 +85641,7 @@ async function merge3(argv, io) {
85265
85641
  return emit(io, ok2({ ...result.data, localCleanup: cleanup }), (data) => [
85266
85642
  `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)}` : ""}`,
85267
85643
  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)})` : ""}`,
85644
+ ...data.baseCi.note ? [data.baseCi.note] : [],
85268
85645
  ...data.mergeBack.length ? [
85269
85646
  `merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
85270
85647
  ] : [],
@@ -85431,7 +85808,7 @@ async function run15(argv, io) {
85431
85808
  return reply(rest, io);
85432
85809
  return usage4(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE8);
85433
85810
  }
85434
- 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>] [--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]", USAGE8 = "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;
85811
+ 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]", USAGE8 = "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;
85435
85812
  var init_pr = __esm(() => {
85436
85813
  init_autonomy();
85437
85814
  init_issue_link();
@@ -85730,8 +86107,8 @@ var exports_git = {};
85730
86107
  __export(exports_git, {
85731
86108
  run: () => run17
85732
86109
  });
85733
- import { readFileSync as readFileSync25 } from "node:fs";
85734
- import path63 from "node:path";
86110
+ import { readFileSync as readFileSync26 } from "node:fs";
86111
+ import path64 from "node:path";
85735
86112
  function kindHint(cwd, kind) {
85736
86113
  let kinds = [...BRANCH_KINDS];
85737
86114
  try {
@@ -85835,7 +86212,7 @@ function commitMessage(parsed, io) {
85835
86212
 
85836
86213
  `);
85837
86214
  try {
85838
- return file === "-" ? readFileSync25(0, "utf8") : readFileSync25(path63.resolve(io.cwd, file), "utf8");
86215
+ return file === "-" ? readFileSync26(0, "utf8") : readFileSync26(path64.resolve(io.cwd, file), "utf8");
85839
86216
  } catch (error) {
85840
86217
  return {
85841
86218
  error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
@@ -87245,7 +87622,7 @@ var init_stack2 = __esm(() => {
87245
87622
  // packages/workit-core/src/fanout-brief.ts
87246
87623
  import { createHash as createHash19 } from "node:crypto";
87247
87624
  import fs35 from "node:fs";
87248
- import path64 from "node:path";
87625
+ import path65 from "node:path";
87249
87626
  function workerSession(lead, slice, attempt) {
87250
87627
  const tail = attempt === null ? "" : `+try${attempt}`;
87251
87628
  const full = `${lead}-w-${slice}${tail}`;
@@ -87327,7 +87704,7 @@ function renderBrief(cwd, plan, input) {
87327
87704
  "STANDING:",
87328
87705
  ...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
87329
87706
  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)`,
87330
- ...exists && typeof made?.install === "string" && !fs35.existsSync(path64.join(planned, "node_modules")) ? [`INSTALL: \`${made.install}\` first: the main checkout's node_modules was not linked here`] : [],
87707
+ ...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`] : [],
87331
87708
  ...identityLines(identity, minted),
87332
87709
  ...rules(mode, branch, slice.base, exists ? planned : null)
87333
87710
  ];
@@ -88068,7 +88445,7 @@ __export(exports_fanout, {
88068
88445
  run: () => run21
88069
88446
  });
88070
88447
  import fs36 from "node:fs";
88071
- import path65 from "node:path";
88448
+ import path66 from "node:path";
88072
88449
  function forgeFor(io, offline) {
88073
88450
  if (offline)
88074
88451
  return { forge: null, error: "--offline" };
@@ -88129,7 +88506,7 @@ function plan2(argv, io) {
88129
88506
  return usage4(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
88130
88507
  if (flags.positionals.length !== 1)
88131
88508
  return usage4(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
88132
- const file = path65.resolve(io.cwd, flags.positionals[0]);
88509
+ const file = path66.resolve(io.cwd, flags.positionals[0]);
88133
88510
  let raw;
88134
88511
  try {
88135
88512
  raw = JSON.parse(fs36.readFileSync(file, "utf8"));
@@ -103670,7 +104047,7 @@ var init_ast = __esm(() => {
103670
104047
 
103671
104048
  // packages/workit-cli/src/test-audit/rules.ts
103672
104049
  import { builtinModules } from "node:module";
103673
- import path66 from "node:path";
104050
+ import path67 from "node:path";
103674
104051
  function testCallee(call) {
103675
104052
  let callee = call.callee;
103676
104053
  if (callee.type === "CallExpression")
@@ -104163,12 +104540,12 @@ function checkConstantsOnly(ctx, emit) {
104163
104540
  }
104164
104541
  function checkOverMocking(ctx, emit) {
104165
104542
  const { program } = ctx.parsed;
104166
- const unit = path66.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
104543
+ const unit = path67.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
104167
104544
  const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
104168
104545
  const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
104169
104546
  for (const call of mocked) {
104170
104547
  const spec = call.arguments[0].value;
104171
- const base = path66.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
104548
+ const base = path67.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
104172
104549
  if (base === unit)
104173
104550
  emit({
104174
104551
  rule: "over-mocking",
@@ -104557,8 +104934,8 @@ var init_rules = __esm(() => {
104557
104934
 
104558
104935
  // packages/workit-cli/src/test-audit/audit.ts
104559
104936
  import { spawnSync as spawnSync29 } from "node:child_process";
104560
- import { existsSync as existsSync24, readdirSync as readdirSync11, readFileSync as readFileSync26, statSync as statSync10 } from "node:fs";
104561
- import path67 from "node:path";
104937
+ import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync27, statSync as statSync10 } from "node:fs";
104938
+ import path68 from "node:path";
104562
104939
  function git7(cwd, args) {
104563
104940
  const run = spawnSync29("git", ["-c", "core.quotepath=off", ...args], {
104564
104941
  cwd,
@@ -104568,10 +104945,10 @@ function git7(cwd, args) {
104568
104945
  return { ok: run.status === 0, stdout: run.stdout ?? "" };
104569
104946
  }
104570
104947
  function walkTests(dir, out) {
104571
- for (const entry of readdirSync11(dir, { withFileTypes: true })) {
104948
+ for (const entry of readdirSync13(dir, { withFileTypes: true })) {
104572
104949
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
104573
104950
  continue;
104574
- const full = path67.join(dir, entry.name);
104951
+ const full = path68.join(dir, entry.name);
104575
104952
  if (entry.isDirectory()) {
104576
104953
  if (!SKIP_DIRS.has(entry.name))
104577
104954
  walkTests(full, out);
@@ -104583,7 +104960,7 @@ function expandPaths(cwd, inputs) {
104583
104960
  const files = [];
104584
104961
  const missing = [];
104585
104962
  for (const input of inputs.length > 0 ? inputs : ["."]) {
104586
- const full = path67.resolve(cwd, input);
104963
+ const full = path68.resolve(cwd, input);
104587
104964
  if (!existsSync24(full))
104588
104965
  missing.push(input);
104589
104966
  else if (statSync10(full).isDirectory())
@@ -104620,11 +104997,11 @@ function changedLines(cwd, base) {
104620
104997
  for (let offset = 0;offset < count; offset += 1)
104621
104998
  lines.add(start + offset);
104622
104999
  }
104623
- changed.set(path67.join(root, file), lines);
105000
+ changed.set(path68.join(root, file), lines);
104624
105001
  }
104625
105002
  const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
104626
105003
  for (const file of untracked.stdout.split("\x00").filter(Boolean))
104627
- changed.set(path67.join(root, file), "all");
105004
+ changed.set(path68.join(root, file), "all");
104628
105005
  return changed;
104629
105006
  }
104630
105007
  function auditFiles(files, display) {
@@ -104637,7 +105014,7 @@ function auditFiles(files, display) {
104637
105014
  const shown = display(file);
104638
105015
  let parsed;
104639
105016
  try {
104640
- parsed = parseSource(readFileSync26(file, "utf8"), file);
105017
+ parsed = parseSource(readFileSync27(file, "utf8"), file);
104641
105018
  } catch (error) {
104642
105019
  parseErrors.push({
104643
105020
  file: shown,
@@ -104686,7 +105063,7 @@ function auditFiles(files, display) {
104686
105063
  durationMs: Math.round(performance.now() - started)
104687
105064
  };
104688
105065
  }
104689
- var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path67.sep).join("/"), SEVERITY_ORDER, ruleIds;
105066
+ var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path68.sep).join("/"), SEVERITY_ORDER, ruleIds;
104690
105067
  var init_audit = __esm(() => {
104691
105068
  init_ast();
104692
105069
  init_rules();
@@ -104702,21 +105079,21 @@ import { spawn as spawn2 } from "node:child_process";
104702
105079
  import {
104703
105080
  existsSync as existsSync25,
104704
105081
  lstatSync as lstatSync8,
104705
- mkdirSync as mkdirSync11,
105082
+ mkdirSync as mkdirSync12,
104706
105083
  mkdtempSync as mkdtempSync4,
104707
- readdirSync as readdirSync12,
104708
- readFileSync as readFileSync27,
105084
+ readdirSync as readdirSync14,
105085
+ readFileSync as readFileSync28,
104709
105086
  readlinkSync as readlinkSync3,
104710
105087
  realpathSync as realpathSync10,
104711
105088
  renameSync as renameSync7,
104712
105089
  rmSync as rmSync9,
104713
105090
  statSync as statSync11,
104714
105091
  symlinkSync,
104715
- writeFileSync as writeFileSync11,
105092
+ writeFileSync as writeFileSync12,
104716
105093
  copyFileSync as copyFileSync8
104717
105094
  } from "node:fs";
104718
105095
  import os22 from "node:os";
104719
- import path68 from "node:path";
105096
+ import path69 from "node:path";
104720
105097
  function mutantsFor(source, file, lines) {
104721
105098
  const parsed = parseSource(source, file);
104722
105099
  const out = [];
@@ -104789,7 +105166,7 @@ function relatedTests(source, testFiles) {
104789
105166
  for (const match of text.matchAll(SPECIFIER)) {
104790
105167
  const spec = match[1];
104791
105168
  if (spec.startsWith(".")) {
104792
- if (stripExt(path68.posix.join(path68.posix.dirname(file), spec)) === target)
105169
+ if (stripExt(path69.posix.join(path69.posix.dirname(file), spec)) === target)
104793
105170
  return true;
104794
105171
  } else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
104795
105172
  return true;
@@ -104801,7 +105178,7 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
104801
105178
  let removed = 0;
104802
105179
  let names = [];
104803
105180
  try {
104804
- names = readdirSync12(tmp);
105181
+ names = readdirSync14(tmp);
104805
105182
  } catch {
104806
105183
  return 0;
104807
105184
  }
@@ -104809,12 +105186,12 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
104809
105186
  for (const name of names) {
104810
105187
  if (!name.startsWith(TEMP_PREFIX))
104811
105188
  continue;
104812
- const dir = path68.join(tmp, name);
105189
+ const dir = path69.join(tmp, name);
104813
105190
  try {
104814
105191
  const stat = lstatSync8(dir);
104815
105192
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
104816
105193
  continue;
104817
- const pid = Number(readFileSync27(path68.join(dir, PID_FILE), "utf8"));
105194
+ const pid = Number(readFileSync28(path69.join(dir, PID_FILE), "utf8"));
104818
105195
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
104819
105196
  continue;
104820
105197
  } catch (error) {
@@ -104829,16 +105206,16 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
104829
105206
  return removed;
104830
105207
  }
104831
105208
  function linkNodeModules(origin, target, repo, copy) {
104832
- mkdirSync11(target, { recursive: true });
104833
- for (const entry of readdirSync12(origin, { withFileTypes: true })) {
104834
- const from = path68.join(origin, entry.name);
104835
- const to = path68.join(target, entry.name);
105209
+ mkdirSync12(target, { recursive: true });
105210
+ for (const entry of readdirSync14(origin, { withFileTypes: true })) {
105211
+ const from = path69.join(origin, entry.name);
105212
+ const to = path69.join(target, entry.name);
104836
105213
  if (entry.name.startsWith("@") && entry.isDirectory()) {
104837
105214
  linkNodeModules(from, to, repo, copy);
104838
105215
  continue;
104839
105216
  }
104840
105217
  if (entry.name.startsWith(".") && entry.name !== ".bin" && entry.isDirectory()) {
104841
- mkdirSync11(to, { recursive: true });
105218
+ mkdirSync12(to, { recursive: true });
104842
105219
  continue;
104843
105220
  }
104844
105221
  if (entry.isSymbolicLink()) {
@@ -104848,9 +105225,9 @@ function linkNodeModules(origin, target, repo, copy) {
104848
105225
  } catch {
104849
105226
  continue;
104850
105227
  }
104851
- const relative = path68.relative(repo, real);
104852
- if (!relative.startsWith("..") && !path68.isAbsolute(relative) && !relative.split(path68.sep).includes("node_modules")) {
104853
- symlinkSync(path68.join(copy, relative), to, "junction");
105228
+ const relative = path69.relative(repo, real);
105229
+ if (!relative.startsWith("..") && !path69.isAbsolute(relative) && !relative.split(path69.sep).includes("node_modules")) {
105230
+ symlinkSync(path69.join(copy, relative), to, "junction");
104854
105231
  continue;
104855
105232
  }
104856
105233
  }
@@ -104866,7 +105243,7 @@ function copySymlink(repo, tree, from, to) {
104866
105243
  }
104867
105244
  const realRepo = realpathSync10(repo);
104868
105245
  if (real && inside5(realRepo, real)) {
104869
- symlinkSync(path68.relative(path68.dirname(to), path68.join(tree, path68.relative(realRepo, real))), to);
105246
+ symlinkSync(path69.relative(path69.dirname(to), path69.join(tree, path69.relative(realRepo, real))), to);
104870
105247
  return;
104871
105248
  }
104872
105249
  if (real && statSync11(real).isFile())
@@ -104875,23 +105252,23 @@ function copySymlink(repo, tree, from, to) {
104875
105252
  symlinkSync(readlinkSync3(from), to);
104876
105253
  }
104877
105254
  function writeInside(copy, file, content) {
104878
- const target = path68.join(copy, file);
105255
+ const target = path69.join(copy, file);
104879
105256
  try {
104880
105257
  const stat = lstatSync8(target);
104881
- if (!stat.isFile() || !inside5(realpathSync10(copy), realpathSync10(path68.dirname(target))))
105258
+ if (!stat.isFile() || !inside5(realpathSync10(copy), realpathSync10(path69.dirname(target))))
104882
105259
  return false;
104883
105260
  } catch {
104884
105261
  return false;
104885
105262
  }
104886
105263
  const temp = `${target}.workit-mutant-${process.pid}`;
104887
- writeFileSync11(temp, content);
105264
+ writeFileSync12(temp, content);
104888
105265
  renameSync7(temp, target);
104889
105266
  return true;
104890
105267
  }
104891
105268
  function copyWorkingTree(repo) {
104892
- const dir = mkdtempSync4(path68.join(os22.tmpdir(), TEMP_PREFIX));
104893
- writeFileSync11(path68.join(dir, PID_FILE), String(process.pid));
104894
- const tree = path68.join(dir, "tree");
105269
+ const dir = mkdtempSync4(path69.join(os22.tmpdir(), TEMP_PREFIX));
105270
+ writeFileSync12(path69.join(dir, PID_FILE), String(process.pid));
105271
+ const tree = path69.join(dir, "tree");
104895
105272
  const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
104896
105273
  if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
104897
105274
  const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
@@ -104902,35 +105279,35 @@ function copyWorkingTree(repo) {
104902
105279
  git7(tree, ["update-ref", "--no-deref", "HEAD", head]);
104903
105280
  git7(tree, ["reset", "-q"]);
104904
105281
  } else
104905
- mkdirSync11(tree, { recursive: true });
105282
+ mkdirSync12(tree, { recursive: true });
104906
105283
  const listed = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00");
104907
105284
  const dirs = new Set([""]);
104908
105285
  for (const relative of listed) {
104909
105286
  if (!relative)
104910
105287
  continue;
104911
- const from = path68.join(repo, relative);
105288
+ const from = path69.join(repo, relative);
104912
105289
  let stat;
104913
105290
  try {
104914
105291
  stat = lstatSync8(from);
104915
105292
  } catch {
104916
105293
  continue;
104917
105294
  }
104918
- const to = path68.join(tree, relative);
104919
- mkdirSync11(path68.dirname(to), { recursive: true });
105295
+ const to = path69.join(tree, relative);
105296
+ mkdirSync12(path69.dirname(to), { recursive: true });
104920
105297
  if (stat.isSymbolicLink())
104921
105298
  copySymlink(repo, tree, from, to);
104922
105299
  else if (stat.isFile())
104923
105300
  copyFileSync8(from, to);
104924
- let parent = path68.dirname(relative);
105301
+ let parent = path69.dirname(relative);
104925
105302
  while (parent !== "." && !dirs.has(parent)) {
104926
105303
  dirs.add(parent);
104927
- parent = path68.dirname(parent);
105304
+ parent = path69.dirname(parent);
104928
105305
  }
104929
105306
  }
104930
105307
  for (const parent of dirs) {
104931
- const modules = path68.join(repo, parent, "node_modules");
104932
- if (existsSync25(modules) && !existsSync25(path68.join(tree, parent, "node_modules")))
104933
- linkNodeModules(modules, path68.join(tree, parent, "node_modules"), repo, tree);
105308
+ const modules = path69.join(repo, parent, "node_modules");
105309
+ if (existsSync25(modules) && !existsSync25(path69.join(tree, parent, "node_modules")))
105310
+ linkNodeModules(modules, path69.join(tree, parent, "node_modules"), repo, tree);
104934
105311
  }
104935
105312
  return { dir, tree };
104936
105313
  }
@@ -104969,20 +105346,20 @@ async function runMutation(options) {
104969
105346
  const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
104970
105347
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
104971
105348
  try {
104972
- return { file, text: readFileSync27(path68.join(repo, file), "utf8") };
105349
+ return { file, text: readFileSync28(path69.join(repo, file), "utf8") };
104973
105350
  } catch {
104974
105351
  return { file, text: "" };
104975
105352
  }
104976
105353
  });
104977
105354
  const candidates = [];
104978
105355
  for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
104979
- const relative = toPosix(path68.relative(repo, absolute));
105356
+ const relative = toPosix(path69.relative(repo, absolute));
104980
105357
  if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
104981
105358
  continue;
104982
105359
  if (NOT_SOURCE.test(relative) || !existsSync25(absolute))
104983
105360
  continue;
104984
105361
  try {
104985
- candidates.push(...mutantsFor(readFileSync27(absolute, "utf8"), relative, lines));
105362
+ candidates.push(...mutantsFor(readFileSync28(absolute, "utf8"), relative, lines));
104986
105363
  } catch {}
104987
105364
  }
104988
105365
  const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
@@ -104990,9 +105367,9 @@ async function runMutation(options) {
104990
105367
  for (const mutant of mutants)
104991
105368
  if (!related.has(mutant.file))
104992
105369
  related.set(mutant.file, relatedTests(mutant.file, testFiles));
104993
- const cwdRelative = path68.relative(repo, options.cwd);
105370
+ const cwdRelative = path69.relative(repo, options.cwd);
104994
105371
  const asArg = (file) => {
104995
- const relative = toPosix(path68.relative(cwdRelative, file));
105372
+ const relative = toPosix(path69.relative(cwdRelative, file));
104996
105373
  return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
104997
105374
  };
104998
105375
  const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
@@ -105028,7 +105405,7 @@ async function runMutation(options) {
105028
105405
  return finish3(options, 0, results, counts, started);
105029
105406
  removeStaleCopies();
105030
105407
  const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
105031
- const copyCwd = path68.join(copy, cwdRelative);
105408
+ const copyCwd = path69.join(copy, cwdRelative);
105032
105409
  let current = null;
105033
105410
  const restore = () => {
105034
105411
  if (current)
@@ -105063,7 +105440,7 @@ async function runMutation(options) {
105063
105440
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
105064
105441
  continue;
105065
105442
  }
105066
- const original = readFileSync27(path68.join(copy, mutant.file), "utf8");
105443
+ const original = readFileSync28(path69.join(copy, mutant.file), "utf8");
105067
105444
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
105068
105445
  current = { file: mutant.file, original };
105069
105446
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -105120,8 +105497,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
105120
105497
  return error.code === "EPERM";
105121
105498
  }
105122
105499
  }, inside5 = (root, file) => {
105123
- const relative = path68.relative(root, file);
105124
- return relative === "" || !relative.startsWith("..") && !path68.isAbsolute(relative);
105500
+ const relative = path69.relative(root, file);
105501
+ return relative === "" || !relative.startsWith("..") && !path69.isAbsolute(relative);
105125
105502
  }, active, killTree = (child) => {
105126
105503
  try {
105127
105504
  if (child.pid && process.platform !== "win32")
@@ -105170,7 +105547,7 @@ __export(exports_test_audit, {
105170
105547
  run: () => run23
105171
105548
  });
105172
105549
  import { existsSync as existsSync26 } from "node:fs";
105173
- import path69 from "node:path";
105550
+ import path70 from "node:path";
105174
105551
  function parse8(argv) {
105175
105552
  const options = {
105176
105553
  paths: [],
@@ -105275,8 +105652,8 @@ async function run23(argv, io) {
105275
105652
  return emit(io, fail2("invalid_input", `${options}
105276
105653
  usage: ${USAGE15}`));
105277
105654
  const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
105278
- const repo = top.ok ? path69.resolve(top.stdout.trim()) : null;
105279
- const display = (file) => toPosix(path69.relative(io.cwd, file) || file);
105655
+ const repo = top.ok ? path70.resolve(top.stdout.trim()) : null;
105656
+ const display = (file) => toPosix(path70.relative(io.cwd, file) || file);
105280
105657
  let base = null;
105281
105658
  let changed = null;
105282
105659
  if (options.diff || options.mutate) {
@@ -105414,8 +105791,8 @@ var exports_youtrack = {};
105414
105791
  __export(exports_youtrack, {
105415
105792
  run: () => run25
105416
105793
  });
105417
- import { readFileSync as readFileSync28 } from "node:fs";
105418
- import path70 from "node:path";
105794
+ import { readFileSync as readFileSync29 } from "node:fs";
105795
+ import path71 from "node:path";
105419
105796
  async function run25(argv, io) {
105420
105797
  const [sub, ...rest] = argv;
105421
105798
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
@@ -105461,7 +105838,7 @@ async function run25(argv, io) {
105461
105838
  let markdown = flags.values.markdown ?? "";
105462
105839
  if (flags.values.file)
105463
105840
  try {
105464
- markdown = readFileSync28(path70.resolve(io.cwd, flags.values.file), "utf8");
105841
+ markdown = readFileSync29(path71.resolve(io.cwd, flags.values.file), "utf8");
105465
105842
  } catch (error) {
105466
105843
  return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE17);
105467
105844
  }
@@ -105529,8 +105906,8 @@ var exports_changelog = {};
105529
105906
  __export(exports_changelog, {
105530
105907
  run: () => run26
105531
105908
  });
105532
- import { readFileSync as readFileSync29 } from "node:fs";
105533
- import path71 from "node:path";
105909
+ import { readFileSync as readFileSync30 } from "node:fs";
105910
+ import path72 from "node:path";
105534
105911
  async function run26(argv, io) {
105535
105912
  const [sub, ...rest] = argv;
105536
105913
  if (sub !== "apply")
@@ -105576,7 +105953,7 @@ async function run26(argv, io) {
105576
105953
  ]);
105577
105954
  }
105578
105955
  var USAGE18 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
105579
- const text = raw === "-" ? readFileSync29(0, "utf8") : raw.startsWith("@") ? readFileSync29(path71.resolve(cwd, raw.slice(1)), "utf8") : raw;
105956
+ const text = raw === "-" ? readFileSync30(0, "utf8") : raw.startsWith("@") ? readFileSync30(path72.resolve(cwd, raw.slice(1)), "utf8") : raw;
105580
105957
  return JSON.parse(text);
105581
105958
  };
105582
105959
  var init_changelog2 = __esm(() => {
@@ -105592,7 +105969,7 @@ __export(exports_handoff, {
105592
105969
  run: () => run27
105593
105970
  });
105594
105971
  import fs37 from "node:fs";
105595
- import path72 from "node:path";
105972
+ import path73 from "node:path";
105596
105973
  import { parseArgs as parseArgs2 } from "node:util";
105597
105974
  function render3(brief) {
105598
105975
  const lines = [
@@ -105632,9 +106009,9 @@ function bounded(brief, cwd) {
105632
106009
  let file = null;
105633
106010
  if (store.ok)
105634
106011
  try {
105635
- const dir = path72.join(store.value.root, "handoff");
106012
+ const dir = path73.join(store.value.root, "handoff");
105636
106013
  fs37.mkdirSync(dir, { recursive: true });
105637
- file = path72.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
106014
+ file = path73.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
105638
106015
  fs37.writeFileSync(file, `${[...body, ...tail].join(`
105639
106016
  `)}
105640
106017
  `, { mode: 384 });
@@ -105706,7 +106083,7 @@ var init_handoff = __esm(() => {
105706
106083
  // packages/workit-cli/src/main.ts
105707
106084
  init_host_session();
105708
106085
  init_package();
105709
- import path73 from "node:path";
106086
+ import path74 from "node:path";
105710
106087
 
105711
106088
  // packages/workit-cli/src/verbs/registry.ts
105712
106089
  var TASK_FAMILY_NAMES = [
@@ -106034,7 +106411,7 @@ var VERBS = [
106034
106411
  },
106035
106412
  {
106036
106413
  name: "merge",
106037
- 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>]",
106414
+ 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>]",
106038
106415
  summary: "Merge when READY, verified by a non-author and granted; --subject/--body set the squash message"
106039
106416
  }
106040
106417
  ],
@@ -106612,7 +106989,7 @@ var defaultIo = (json) => ({
106612
106989
  });
106613
106990
  async function main(argv, overrides = {}, options = {}) {
106614
106991
  const parsed = parseGlobals(argv);
106615
- const cwd = path73.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
106992
+ const cwd = path74.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
106616
106993
  const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
106617
106994
  if (parsed.error)
106618
106995
  return emit(io, fail2("invalid_input", parsed.error));