@brainervirus/workit-codex 8.13.1 → 8.14.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workit",
3
- "version": "8.13.1",
3
+ "version": "8.14.0",
4
4
  "description": "Adaptive engineering workflow rails for Codex",
5
5
  "author": {
6
6
  "name": "BrainerVirus"
@@ -19602,6 +19602,8 @@ var workspaceConfigSchema = object({
19602
19602
  defaultEndpoint: unknown().optional(),
19603
19603
  verification: _enum(["self", "independent"]).optional(),
19604
19604
  trailers: boolean2().optional(),
19605
+ liveSessionGuard: boolean2().optional(),
19606
+ fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
19605
19607
  autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
19606
19608
  kind,
19607
19609
  union([boolean2(), literal("verified")]).optional()
@@ -19919,6 +19921,17 @@ var resolveRuntimeReleaseTracks = (cwd) => {
19919
19921
  const matched = matchRuntimeWorkspace(cwd);
19920
19922
  return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
19921
19923
  };
19924
+ var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
19925
+ var TIER_HOSTS = new Set([
19926
+ "claude_code",
19927
+ "opencode",
19928
+ "codex",
19929
+ "codex_cli",
19930
+ "codex_desktop",
19931
+ "cursor",
19932
+ "pi",
19933
+ "default"
19934
+ ]);
19922
19935
  var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
19923
19936
  const matched = matchRuntimeWorkspace(cwd);
19924
19937
  if (!matched)
@@ -28451,10 +28464,44 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
28451
28464
  var signals = new Map;
28452
28465
  var cache = new Map;
28453
28466
  var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
28467
+ // packages/workit-core/src/core/live-session.ts
28468
+ import { spawnSync as spawnSync15 } from "node:child_process";
28469
+ var LIVE_SESSION_TTL_MS = 15 * 60000;
28470
+ var liveProbe = {
28471
+ platform: process.platform,
28472
+ run: (args) => spawnSync15("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
28473
+ start: (pid) => processStartOf(pid)
28474
+ };
28475
+ var PIDS = new Map;
28476
+
28454
28477
  // packages/workit-core/src/hooks/write-gate.ts
28455
28478
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
28456
28479
  var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
28457
28480
  var cache2 = new Map;
28481
+ var MUTATING_GIT = new Set([
28482
+ "add",
28483
+ "am",
28484
+ "apply",
28485
+ "checkout",
28486
+ "cherry-pick",
28487
+ "clean",
28488
+ "commit",
28489
+ "merge",
28490
+ "mv",
28491
+ "pull",
28492
+ "rebase",
28493
+ "reset",
28494
+ "restore",
28495
+ "revert",
28496
+ "rm",
28497
+ "stash",
28498
+ "switch"
28499
+ ]);
28500
+ var DRY_N = new Set(["add", "clean", "rm", "mv"]);
28501
+ var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
28502
+ var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
28503
+ var PUSHD2 = new Set(["pushd", "push-location"]);
28504
+ var POPD2 = new Set(["popd", "pop-location"]);
28458
28505
  // packages/workit-core/src/core/skill-manifests.ts
28459
28506
  if (false) {}
28460
28507
 
@@ -12672,6 +12672,8 @@ var workspaceConfigSchema = object({
12672
12672
  defaultEndpoint: unknown().optional(),
12673
12673
  verification: _enum(["self", "independent"]).optional(),
12674
12674
  trailers: boolean2().optional(),
12675
+ liveSessionGuard: boolean2().optional(),
12676
+ fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
12675
12677
  autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
12676
12678
  kind,
12677
12679
  union([boolean2(), literal("verified")]).optional()
@@ -12989,24 +12991,39 @@ var resolveRuntimeReleaseTracks = (cwd) => {
12989
12991
  const matched = matchRuntimeWorkspace(cwd);
12990
12992
  return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
12991
12993
  };
12992
- var workspaceTrailers = (cwd, dir) => {
12994
+ var inheritedSetting = (cwd, dir, has) => {
12993
12995
  let matched;
12994
12996
  try {
12995
12997
  matched = matchRuntimeWorkspace(cwd, dir);
12996
12998
  } catch {
12997
- return { enabled: true, workspace: null, from: null };
12999
+ return null;
12998
13000
  }
12999
13001
  if (!matched)
13000
- return { enabled: true, workspace: null, from: null };
13002
+ return null;
13001
13003
  const { selected, matches } = matched;
13002
13004
  const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
13003
- const source = [selected, ...enclosing].find((entry) => typeof entry.trailers === "boolean");
13005
+ return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
13006
+ };
13007
+ var workspaceTrailers = (cwd, dir) => {
13008
+ const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
13004
13009
  return {
13005
- enabled: source?.trailers !== false,
13006
- workspace: selected.name,
13007
- from: source?.name ?? null
13010
+ enabled: found?.source?.trailers !== false,
13011
+ workspace: found?.workspace ?? null,
13012
+ from: found?.source?.name ?? null
13008
13013
  };
13009
13014
  };
13015
+ var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
13016
+ var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
13017
+ var TIER_HOSTS = new Set([
13018
+ "claude_code",
13019
+ "opencode",
13020
+ "codex",
13021
+ "codex_cli",
13022
+ "codex_desktop",
13023
+ "cursor",
13024
+ "pi",
13025
+ "default"
13026
+ ]);
13010
13027
  var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
13011
13028
  const matched = matchRuntimeWorkspace(cwd);
13012
13029
  if (!matched)
@@ -20218,27 +20235,231 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
20218
20235
  };
20219
20236
  // packages/workit-core/src/hooks/write-gate.ts
20220
20237
  import { realpathSync as realpathSync5 } from "node:fs";
20238
+ import os7 from "node:os";
20239
+ import path23 from "node:path";
20240
+
20241
+ // packages/workit-core/src/core/live-session.ts
20242
+ import { spawnSync as spawnSync13 } from "node:child_process";
20243
+ import { createHash as createHash15 } from "node:crypto";
20244
+ import fs17 from "node:fs";
20245
+ import os6 from "node:os";
20221
20246
  import path22 from "node:path";
20247
+ var LIVE_SESSION_TTL_MS = 15 * 60000;
20248
+ var HEARTBEAT_EVERY_MS = 30000;
20249
+ var liveProbe = {
20250
+ platform: process.platform,
20251
+ run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
20252
+ start: (pid) => processStartOf(pid)
20253
+ };
20254
+ var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
20255
+ var familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, "");
20256
+ var processAlive = (pid) => {
20257
+ try {
20258
+ process.kill(pid, 0);
20259
+ return true;
20260
+ } catch (error) {
20261
+ return error.code === "EPERM";
20262
+ }
20263
+ };
20264
+ var processInfo = (pid) => {
20265
+ try {
20266
+ if (liveProbe.platform === "linux") {
20267
+ const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
20268
+ const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
20269
+ const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
20270
+ return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
20271
+ }
20272
+ if (liveProbe.platform === "win32")
20273
+ return null;
20274
+ const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
20275
+ return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
20276
+ } catch {
20277
+ return null;
20278
+ }
20279
+ };
20280
+ var WRAPPER = /^(?:\S*\/)?(?:sh|bash|dash|zsh|ash|ksh|fish|env|timeout|npx|npm|bunx|cmd\.exe|pwsh|powershell)(?:\s|$)|launch\.mjs|npx-cli\.js/u;
20281
+ var PIDS = new Map;
20282
+ var hostPid = (transport) => {
20283
+ if (transport !== "hook-process")
20284
+ return process.pid;
20285
+ const key = `${liveProbe.platform}\x00${process.ppid}`;
20286
+ if (PIDS.has(key))
20287
+ return PIDS.get(key) ?? null;
20288
+ if (liveProbe.platform === "win32") {
20289
+ PIDS.set(key, null);
20290
+ return null;
20291
+ }
20292
+ let pid = process.ppid;
20293
+ for (let hop = 0;hop < 4; hop++) {
20294
+ const info = processInfo(pid);
20295
+ if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
20296
+ break;
20297
+ pid = info.ppid;
20298
+ }
20299
+ PIDS.set(key, pid);
20300
+ return pid;
20301
+ };
20302
+ var sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-");
20303
+ var liveOwner = (input, transport, env = process.env) => {
20304
+ const session = input.session.id?.trim();
20305
+ if (!session)
20306
+ return null;
20307
+ const leads = new Set([leadOf(sessionSafe(session))]);
20308
+ if (input.session.parentId)
20309
+ leads.add(leadOf(sessionSafe(input.session.parentId)));
20310
+ if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
20311
+ leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
20312
+ return {
20313
+ host: input.host,
20314
+ session: sessionSafe(session),
20315
+ leads: [...leads],
20316
+ pid: () => hostPid(transport)
20317
+ };
20318
+ };
20319
+ var checkoutOf = (target) => {
20320
+ let dir = path22.resolve(target);
20321
+ while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
20322
+ dir = path22.dirname(dir);
20323
+ if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
20324
+ dir = path22.dirname(dir);
20325
+ const parts = dir.split(path22.sep);
20326
+ const dotGit = parts.lastIndexOf(".git");
20327
+ if (dotGit > 0)
20328
+ dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
20329
+ const location = resolveStore(dir);
20330
+ if (location instanceof Error || !location.shared || !location.top)
20331
+ return null;
20332
+ let top = location.top;
20333
+ try {
20334
+ top = fs17.realpathSync.native(top);
20335
+ } catch {}
20336
+ return top;
20337
+ };
20338
+ var markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
20339
+ var checkoutKey = (top, platform = process.platform) => {
20340
+ let key = path22.resolve(top);
20341
+ try {
20342
+ key = fs17.realpathSync.native(key);
20343
+ } catch {}
20344
+ return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
20345
+ };
20346
+ var readMarker = (file) => {
20347
+ try {
20348
+ const value = JSON.parse(fs17.readFileSync(file, "utf8"));
20349
+ if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
20350
+ return null;
20351
+ return {
20352
+ version: 1,
20353
+ session: value.session,
20354
+ leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
20355
+ host: value.host,
20356
+ pid: typeof value.pid === "number" ? value.pid : null,
20357
+ pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
20358
+ machine: typeof value.machine === "string" ? value.machine : "",
20359
+ checkout: typeof value.checkout === "string" ? value.checkout : "",
20360
+ heartbeatAt: value.heartbeatAt
20361
+ };
20362
+ } catch {
20363
+ return null;
20364
+ }
20365
+ };
20366
+ var ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt);
20367
+ var isLive = (marker, deps) => {
20368
+ const age = ageOf2(marker, deps.now ?? new Date);
20369
+ if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
20370
+ return false;
20371
+ if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
20372
+ return true;
20373
+ if (!(deps.alive ?? processAlive)(marker.pid))
20374
+ return false;
20375
+ if (marker.pidStart === null)
20376
+ return true;
20377
+ const start = liveProbe.start(marker.pid);
20378
+ return start === null || start === marker.pidStart;
20379
+ };
20380
+ var sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead));
20381
+ var sameOwner = (marker, owner, machine) => {
20382
+ if (sameLead(marker, owner))
20383
+ return true;
20384
+ if (marker.machine !== machine)
20385
+ return false;
20386
+ const pid = owner.pid();
20387
+ if (pid === null || marker.pid === null || marker.pidStart === null)
20388
+ return familyOf(marker.host) === familyOf(owner.host);
20389
+ return marker.pid === pid;
20390
+ };
20391
+ var liveConflict = (target, owner, deps = {}) => {
20392
+ try {
20393
+ const checkout = checkoutOf(target);
20394
+ if (!checkout)
20395
+ return null;
20396
+ const file = markerFile2(checkout);
20397
+ const marker = readMarker(file);
20398
+ if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
20399
+ return null;
20400
+ if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
20401
+ return null;
20402
+ return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
20403
+ } catch {
20404
+ return null;
20405
+ }
20406
+ };
20407
+ var claimLiveSession = (target, owner, deps = {}) => {
20408
+ try {
20409
+ const checkout = checkoutOf(target);
20410
+ if (!checkout)
20411
+ return;
20412
+ const file = markerFile2(checkout);
20413
+ const now = deps.now ?? new Date;
20414
+ const machine = deps.machine ?? os6.hostname();
20415
+ const current = readMarker(file);
20416
+ if (current) {
20417
+ const age = ageOf2(current, now);
20418
+ if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
20419
+ return;
20420
+ if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
20421
+ return;
20422
+ }
20423
+ const pid = owner.pid();
20424
+ const marker = {
20425
+ version: 1,
20426
+ session: owner.session,
20427
+ leads: owner.leads,
20428
+ host: owner.host,
20429
+ pid,
20430
+ pidStart: pid === null ? null : liveProbe.start(pid),
20431
+ machine,
20432
+ checkout,
20433
+ heartbeatAt: now.toISOString()
20434
+ };
20435
+ fs17.mkdirSync(path22.dirname(file), { recursive: true });
20436
+ const temp = `${file}.${process.pid}.tmp`;
20437
+ fs17.writeFileSync(temp, JSON.stringify(marker));
20438
+ fs17.renameSync(temp, file);
20439
+ } catch {}
20440
+ };
20441
+
20442
+ // packages/workit-core/src/hooks/write-gate.ts
20222
20443
  var NONE3 = { kind: "none" };
20223
20444
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
20224
20445
  var canonical2 = (file) => {
20225
20446
  let rest = "";
20226
- for (let dir = path22.resolve(file);; dir = path22.dirname(dir)) {
20447
+ for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
20227
20448
  try {
20228
- return path22.join(realpathSync5(dir), rest);
20449
+ return path23.join(realpathSync5(dir), rest);
20229
20450
  } catch {
20230
- if (path22.dirname(dir) === dir)
20231
- return path22.resolve(file);
20232
- rest = rest ? path22.join(path22.basename(dir), rest) : path22.basename(dir);
20451
+ if (path23.dirname(dir) === dir)
20452
+ return path23.resolve(file);
20453
+ rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
20233
20454
  }
20234
20455
  }
20235
20456
  };
20236
20457
  var exempt = (cwd, root, file, plans) => {
20237
20458
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
20238
- const relative = path22.relative(fold(canonical2(root)), fold(canonical2(path22.resolve(cwd, file))));
20239
- if (relative.startsWith("..") || path22.isAbsolute(relative))
20459
+ const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
20460
+ if (relative.startsWith("..") || path23.isAbsolute(relative))
20240
20461
  return true;
20241
- const posix = relative.split(path22.sep).join("/");
20462
+ const posix = relative.split(path23.sep).join("/");
20242
20463
  const segments = posix.split("/");
20243
20464
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
20244
20465
  };
@@ -20336,7 +20557,7 @@ var shellWrites = (command) => {
20336
20557
  return { writes: true, targets: unknown ? null : targets };
20337
20558
  };
20338
20559
  var cache2 = new Map;
20339
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path22.resolve(root, file)) ?? "-").join("\x00");
20560
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00");
20340
20561
  var gateFor = (cwd) => {
20341
20562
  const store = new TaskStore(cwd);
20342
20563
  const listed = store.listTaskIndex();
@@ -20374,7 +20595,218 @@ var gateFor = (cwd) => {
20374
20595
  cache2.delete(cache2.keys().next().value);
20375
20596
  return gate;
20376
20597
  };
20377
- function writeGate(cwd, paths) {
20598
+ var MUTATING_GIT = new Set([
20599
+ "add",
20600
+ "am",
20601
+ "apply",
20602
+ "checkout",
20603
+ "cherry-pick",
20604
+ "clean",
20605
+ "commit",
20606
+ "merge",
20607
+ "mv",
20608
+ "pull",
20609
+ "rebase",
20610
+ "reset",
20611
+ "restore",
20612
+ "revert",
20613
+ "rm",
20614
+ "stash",
20615
+ "switch"
20616
+ ]);
20617
+ var DRY_N = new Set(["add", "clean", "rm", "mv"]);
20618
+ var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
20619
+ var DIR_TARGET = ".workit-live-target";
20620
+ var OVERRIDE = "WORKIT_LIVE_SESSION=override";
20621
+ var inert = (args, sub) => args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run" || DRY_N.has(sub) && /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg) && !arg.startsWith("--"));
20622
+ var gitMutationDirs = (cwd, command, dialect) => {
20623
+ let invocations;
20624
+ try {
20625
+ invocations = rawInvocations(command, dialect);
20626
+ } catch {
20627
+ return [];
20628
+ }
20629
+ return invocations.flatMap((invocation) => {
20630
+ if (invocation.tool !== "git" || invocation.dir === undefined)
20631
+ return [];
20632
+ const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
20633
+ const sub = invocation.args[at] ?? "";
20634
+ const rest = invocation.args.slice(at + 1);
20635
+ if (!MUTATING_GIT.has(sub) || inert(rest, sub))
20636
+ return [];
20637
+ if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
20638
+ return [];
20639
+ return [path23.resolve(cwd, invocation.dir ?? ".")];
20640
+ });
20641
+ };
20642
+ var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
20643
+ var PUSHD2 = new Set(["pushd", "push-location"]);
20644
+ var POPD2 = new Set(["popd", "pop-location"]);
20645
+ var cdInto = (dir, words) => {
20646
+ const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
20647
+ if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
20648
+ return;
20649
+ return target === undefined ? os7.homedir() : path23.resolve(dir, target);
20650
+ };
20651
+ var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
20652
+ var segmentTargets = (cwd, command, dialect) => {
20653
+ const targets = [];
20654
+ let dir = cwd;
20655
+ let stack = [];
20656
+ const groups = [];
20657
+ const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
20658
+ for (const segment of segmentsOf(command, dialect)) {
20659
+ if (segment.group === "open") {
20660
+ groups.push({ dir, stack: [...stack] });
20661
+ continue;
20662
+ }
20663
+ if (segment.group === "close") {
20664
+ const saved = groups.pop();
20665
+ if (saved)
20666
+ ({ dir, stack } = saved);
20667
+ continue;
20668
+ }
20669
+ const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
20670
+ const words = lead < 0 ? [] : segment.words.slice(lead);
20671
+ const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
20672
+ if (CD2.has(name)) {
20673
+ dir = cdInto(dir, words);
20674
+ continue;
20675
+ }
20676
+ if (PUSHD2.has(name)) {
20677
+ stack.push(dir);
20678
+ dir = cdInto(dir, words);
20679
+ continue;
20680
+ }
20681
+ if (POPD2.has(name)) {
20682
+ dir = stack.length ? stack.pop() : undefined;
20683
+ continue;
20684
+ }
20685
+ for (const file of segment.redirects)
20686
+ if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
20687
+ const resolved = at(file);
20688
+ if (resolved)
20689
+ targets.push(resolved);
20690
+ }
20691
+ const edits = editsOf(segment.words);
20692
+ if (edits === null) {
20693
+ if (dir !== undefined)
20694
+ targets.push(path23.join(dir, DIR_TARGET));
20695
+ } else
20696
+ for (const file of edits) {
20697
+ const resolved = at(file);
20698
+ if (resolved)
20699
+ targets.push(resolved);
20700
+ }
20701
+ if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
20702
+ targets.push(path23.join(dir, DIR_TARGET));
20703
+ }
20704
+ return targets;
20705
+ };
20706
+ var overridden = (command, dialect) => {
20707
+ const segments = segmentsOf(command, dialect);
20708
+ if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
20709
+ return false;
20710
+ const words = segments[0].words;
20711
+ const lead = words.findIndex((word) => !/^\w+=/.test(word));
20712
+ return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
20713
+ };
20714
+ var HOST_LABELS = {
20715
+ claude_code: "Claude Code",
20716
+ opencode: "OpenCode",
20717
+ codex_cli: "Codex CLI",
20718
+ codex_desktop: "Codex Desktop",
20719
+ cursor: "Cursor",
20720
+ pi: "Pi"
20721
+ };
20722
+ var ageText = (ms) => {
20723
+ const seconds = Math.max(0, Math.round(ms / 1000));
20724
+ return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
20725
+ };
20726
+ var liveDeny = ({ marker, checkout, ageMs, file }) => {
20727
+ const unblock = `Work in your own worktree (\`git worktree add <path> -b <branch>\`, or \`workit fanout worktree create <slice>\`). Only if the user confirms that session is finished: release it with \`rm '${file}'\`, or prefix one command with ${OVERRIDE}.`;
20728
+ return {
20729
+ kind: "deny",
20730
+ reason: `workit live-session guard: ${checkout} belongs to another live agent session, ${HOST_LABELS[marker.host] ?? marker.host} session ${marker.session}${marker.pid === null ? "" : ` (pid ${marker.pid})`}, last active ${ageText(ageMs)} ago. Do not edit a checkout another session is working in; read-only commands stay allowed. ${unblock}`,
20731
+ unblock
20732
+ };
20733
+ };
20734
+ var guarded = (targets) => {
20735
+ const seen = new Set;
20736
+ return targets.filter((target) => {
20737
+ const dir = path23.dirname(target);
20738
+ if (seen.has(dir))
20739
+ return false;
20740
+ seen.add(dir);
20741
+ return true;
20742
+ });
20743
+ };
20744
+ var liveGate = (owner, targets, deps) => {
20745
+ try {
20746
+ for (const target of guarded(targets)) {
20747
+ const conflict = liveConflict(target, owner, deps);
20748
+ if (conflict && workspaceLiveSessionGuard(conflict.checkout))
20749
+ return liveDeny(conflict);
20750
+ }
20751
+ } catch {}
20752
+ return NONE3;
20753
+ };
20754
+ var claimAll = (owner, targets, deps) => {
20755
+ for (const target of guarded(targets))
20756
+ claimLiveSession(target, owner, deps);
20757
+ };
20758
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)];
20759
+ function writeGate(cwd, paths, owner, deps) {
20760
+ if (!owner)
20761
+ return beforeWriteGate(cwd, paths);
20762
+ const targets = liveTargets(cwd, paths);
20763
+ const live = liveGate(owner, targets, deps);
20764
+ if (live.kind !== "none")
20765
+ return live;
20766
+ const decision = beforeWriteGate(cwd, paths);
20767
+ if (decision.kind === "none")
20768
+ claimAll(owner, targets, deps);
20769
+ return decision;
20770
+ }
20771
+ function shellGate(cwd, command, owner, dialect = "posix", deps) {
20772
+ const writes = shellWrites(command);
20773
+ let targets = [];
20774
+ if (owner) {
20775
+ try {
20776
+ targets = [
20777
+ ...segmentTargets(cwd, command, dialect),
20778
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
20779
+ ];
20780
+ } catch {
20781
+ targets = [];
20782
+ }
20783
+ }
20784
+ if (owner && targets.length) {
20785
+ const live = liveGate(owner, targets, deps);
20786
+ if (live.kind !== "none") {
20787
+ if (!overridden(command, dialect))
20788
+ return live;
20789
+ noteOverride(cwd, owner, command);
20790
+ }
20791
+ }
20792
+ const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
20793
+ if (owner && targets.length && decision.kind === "none")
20794
+ claimAll(owner, targets, deps);
20795
+ return decision;
20796
+ }
20797
+ var noteOverride = (cwd, owner, command) => {
20798
+ try {
20799
+ if (!inWorkitWorkspace(cwd))
20800
+ return;
20801
+ appendHookObserved(cwd, {
20802
+ type: "live_session.override",
20803
+ actor: { host: owner.host, session: owner.session, agentId: null },
20804
+ session: owner.session,
20805
+ command: command.slice(0, 200)
20806
+ });
20807
+ } catch {}
20808
+ };
20809
+ function beforeWriteGate(cwd, paths) {
20378
20810
  try {
20379
20811
  const gate = gateFor(cwd);
20380
20812
  if (!gate || gate.blockers.length === 0)
@@ -20392,7 +20824,7 @@ function writeGate(cwd, paths) {
20392
20824
  }
20393
20825
  }
20394
20826
  // packages/workit-core/src/hooks/stop.ts
20395
- import { spawnSync as spawnSync13 } from "node:child_process";
20827
+ import { spawnSync as spawnSync14 } from "node:child_process";
20396
20828
  var NONE4 = { kind: "none" };
20397
20829
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
20398
20830
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -20413,7 +20845,7 @@ var asksUser = (message) => {
20413
20845
  };
20414
20846
  var short = (sha) => (sha ?? "?").slice(0, 12);
20415
20847
  var gitList = (cwd, args) => {
20416
- const run = spawnSync13("git", args, {
20848
+ const run = spawnSync14("git", args, {
20417
20849
  cwd,
20418
20850
  encoding: "utf8",
20419
20851
  timeout: 3000,
@@ -20777,12 +21209,12 @@ ${line}` };
20777
21209
  };
20778
21210
  // packages/workit-core/src/hooks/hosts/fields.ts
20779
21211
  import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
20780
- import path23 from "node:path";
21212
+ import path24 from "node:path";
20781
21213
  var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
20782
21214
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
20783
21215
  var optionalText = (value) => typeof value === "string" ? value : null;
20784
21216
  var existingDirectory = (value) => {
20785
- if (!nonEmpty2(value) || !path23.isAbsolute(value) || !existsSync7(value))
21217
+ if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
20786
21218
  return null;
20787
21219
  try {
20788
21220
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -20927,8 +21359,7 @@ function handleHook(input, deps) {
20927
21359
  const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
20928
21360
  if (raw.kind === "deny")
20929
21361
  return raw;
20930
- const writes = usable2(descriptor.events["write.pre"].support) ? shellWrites(event.command) : { writes: false, targets: null };
20931
- const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE5;
21362
+ const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
20932
21363
  if (gate.kind === "deny")
20933
21364
  return gate;
20934
21365
  noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
@@ -20941,7 +21372,7 @@ function handleHook(input, deps) {
20941
21372
  rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
20942
21373
  return NONE5;
20943
21374
  case "write.pre":
20944
- return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE5;
21375
+ return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
20945
21376
  case "subagent.start":
20946
21377
  return {
20947
21378
  kind: "context",
@@ -21520,8 +21951,8 @@ var codexAdapter = {
21520
21951
  };
21521
21952
 
21522
21953
  // packages/workit-core/src/hooks/hosts/cursor.ts
21523
- import fs17 from "node:fs";
21524
- import path24 from "node:path";
21954
+ import fs18 from "node:fs";
21955
+ import path25 from "node:path";
21525
21956
  var undocumented = { support: "undocumented", native: null };
21526
21957
  var none = { support: "none", native: null };
21527
21958
  var CURSOR_DESCRIPTOR = {
@@ -21718,17 +22149,17 @@ var protocolEvent2 = (input) => {
21718
22149
  };
21719
22150
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
21720
22151
  var lastAssistantText = (file) => {
21721
- if (!file || !path24.isAbsolute(file))
22152
+ if (!file || !path25.isAbsolute(file))
21722
22153
  return null;
21723
22154
  try {
21724
- const size = fs17.statSync(file).size;
22155
+ const size = fs18.statSync(file).size;
21725
22156
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
21726
22157
  const buffer = Buffer.alloc(size - start);
21727
- const fd = fs17.openSync(file, "r");
22158
+ const fd = fs18.openSync(file, "r");
21728
22159
  try {
21729
- fs17.readSync(fd, buffer, 0, buffer.length, start);
22160
+ fs18.readSync(fd, buffer, 0, buffer.length, start);
21730
22161
  } finally {
21731
- fs17.closeSync(fd);
22162
+ fs18.closeSync(fd);
21732
22163
  }
21733
22164
  const lines = buffer.toString("utf8").split(`
21734
22165
  `);
@@ -21755,7 +22186,7 @@ var lastAssistantText = (file) => {
21755
22186
  return null;
21756
22187
  }
21757
22188
  };
21758
- var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path24.sep) ? root : `${root}${path24.sep}`);
22189
+ var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`);
21759
22190
  var hookCwd = (input) => {
21760
22191
  const cwd = existingDirectory(input.cwd);
21761
22192
  if (cwd && input.hook_event_name === "beforeShellExecution")
@@ -37,6 +37,6 @@ if (quiet)
37
37
  process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
38
38
  `);
39
39
  else {
40
- const { runCodexHook } = await import("./workit-hook-j1zd509p.js");
40
+ const { runCodexHook } = await import("./workit-hook-s8c0mzcq.js");
41
41
  await runCodexHook([payload]);
42
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-codex",
3
- "version": "8.13.1",
3
+ "version": "8.14.0",
4
4
  "private": false,
5
5
  "description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, method skills, and verifier/reviewer/implementer agents",
6
6
  "keywords": [
@@ -39,8 +39,8 @@
39
39
  "build": "bun scripts/build.ts"
40
40
  },
41
41
  "devDependencies": {
42
- "@brainervirus/workit-core": "^8.13.1",
43
- "@brainervirus/workit-mcp": "^8.13.1"
42
+ "@brainervirus/workit-core": "^8.14.0",
43
+ "@brainervirus/workit-mcp": "^8.14.0"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=24"
@@ -25,9 +25,9 @@ ceremony; do it yourself. Workit never spawns agents; you do.
25
25
  it; change the plan or the standing orders and render again.
26
26
  4. **Spawn in a rolling window**, in the background, each in its own worktree:
27
27
  keep 4-6 in flight and refill from `spawnable` in `workit fanout status` as
28
- each one finishes. Pick the model from TIER (`references/orchestration.md`).
29
- Claude Code: the `implementer` agent; elsewhere first `workit fanout
30
- worktree create <slice>`.
28
+ each one finishes. Model: the brief's `model:` line, else TIER
29
+ (`references/orchestration.md`). Claude Code: the `implementer` agent;
30
+ elsewhere first `workit fanout worktree create <slice>`.
31
31
  5. **Judge liveness by side effects only:** `workit fanout status` (head age,
32
32
  PR, CI, verdict, landed; STUCK past the TIMEBOX). Stop a stuck worker and
33
33
  observe that it exited (a timeout is not proof). Removing a worktree drops
@@ -53,7 +53,7 @@ ceremony; do it yourself. Workit never spawns agents; you do.
53
53
  status` order, release the worktrees you made, and `workit ledger standing
54
54
  clear`. Only you touch topology: workers never rebase, retarget or merge,
55
55
  except the integration-tip merge in integration mode. Then ship
56
- (read the `workit-ship` skill's SKILL.md and follow it).
56
+ (read the `workit-ship` skill's SKILL.md and follow it) up to `defaultEndpoint` from `workit grant show`.
57
57
 
58
58
  ## Example
59
59
 
@@ -5,8 +5,10 @@ and fans in. Workit records and checks; the host's own subagents run the work.
5
5
 
6
6
  ## TIER to model
7
7
 
8
- TIER is a field of each slice and of its brief. Map it where the host lets you
9
- pick a model per spawn; nothing in the workspace config routes it.
8
+ TIER is a field of each slice and of its brief. When the workspace sets
9
+ `fanout.tierModels`, the brief has a `model:` line under TIER and `workit
10
+ fanout status` names the model next to each spawnable slice: use that model.
11
+ Without one, map TIER where the host lets you pick a model per spawn:
10
12
 
11
13
  | TIER | Claude Code (`model` on the Agent call) | Hosts without a per-spawn model |
12
14
  | --- | --- | --- |