@brainervirus/workit-claude-code 8.38.3 → 8.38.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workit.js CHANGED
@@ -9317,7 +9317,7 @@ var package_default;
9317
9317
  var init_package = __esm(() => {
9318
9318
  package_default = {
9319
9319
  name: "@brainervirus/workit-cli",
9320
- version: "8.38.3",
9320
+ version: "8.38.5",
9321
9321
  private: false,
9322
9322
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
9323
9323
  keywords: [
@@ -9355,13 +9355,13 @@ var init_package = __esm(() => {
9355
9355
  },
9356
9356
  dependencies: {
9357
9357
  "@babel/parser": "7.29.9",
9358
- "@brainervirus/workit-cursor": "^8.38.3",
9358
+ "@brainervirus/workit-cursor": "^8.38.5",
9359
9359
  "@inkjs/ui": "2.0.0",
9360
9360
  ink: "7.1.1",
9361
9361
  react: "19.2.8"
9362
9362
  },
9363
9363
  devDependencies: {
9364
- "@brainervirus/workit-core": "^8.38.3"
9364
+ "@brainervirus/workit-core": "^8.38.5"
9365
9365
  },
9366
9366
  engines: {
9367
9367
  node: ">=24"
@@ -16832,6 +16832,79 @@ var init_workers = __esm(() => {
16832
16832
  UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
16833
16833
  });
16834
16834
 
16835
+ // packages/workit-core/src/core/task-delivery.ts
16836
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
16837
+ function closeDeliveredTasks(store, now, options = {}) {
16838
+ let closedCount = 0;
16839
+ const index = store.listStoreIndex();
16840
+ if (!index.ok)
16841
+ return 0;
16842
+ const open = index.data.filter((entry) => entry.status !== "closed" && entry.branch !== null);
16843
+ if (open.length === 0)
16844
+ return 0;
16845
+ const file = options.cached ? ledgerPath(store.root) : null;
16846
+ const signature = file?.ok ? `${fileSignature(file.value) ?? "-"}|${open.map((entry) => entry.id).join(",")}` : null;
16847
+ const marker = file?.ok ? `${file.value}.delivery-sweep` : null;
16848
+ if (signature && marker) {
16849
+ try {
16850
+ if (readFileSync6(marker, "utf8") === signature)
16851
+ return 0;
16852
+ } catch {}
16853
+ }
16854
+ const ledger = readLedger(store.root);
16855
+ if (!ledger.ok)
16856
+ return 0;
16857
+ const rows = ledger.value.rows;
16858
+ const after = (row, since) => Date.parse(row.at) >= Date.parse(since);
16859
+ for (const entry of open) {
16860
+ const merged = rows.findLast((row) => row.type === "pr.merged" && row.branch === entry.branch && typeof row.pr === "number" && after(row, entry.createdAt));
16861
+ if (!merged)
16862
+ continue;
16863
+ const pr = merged.pr;
16864
+ const verified = rows.findLast((row) => row.type === "delivery.verified" && row.expect === "merged" && row.branch === entry.branch && (row.pr === undefined || row.pr === pr));
16865
+ if (!verified) {
16866
+ const next = `PR #${pr} is merged but delivery is not verified: run \`workit verify-delivery merged\``;
16867
+ if (entry.progress.nextAction !== next) {
16868
+ store.mutateTask(entry.id, entry.revision, (task, context) => success(context.revision, null, {
16869
+ ...task,
16870
+ progress: { ...task.progress, nextAction: next }
16871
+ }));
16872
+ }
16873
+ continue;
16874
+ }
16875
+ const task = store.readTask(entry.id);
16876
+ if (!task.ok || task.data.status === "closed")
16877
+ continue;
16878
+ if (task.data.workers.some((worker) => workerBlocksTransition("close", worker.data.state)))
16879
+ continue;
16880
+ const closed = store.mutateTask(entry.id, task.data.revision, (value, context) => success(context.revision, null, {
16881
+ ...value,
16882
+ status: "closed",
16883
+ closure: {
16884
+ outcome: "verified",
16885
+ at: context.now,
16886
+ summary: `delivered: PR #${pr} merged (pr.merged ${String(merged.id)}) and delivery verified (delivery.verified ${String(verified.id)})`,
16887
+ decisionIds: [],
16888
+ evidenceIds: []
16889
+ }
16890
+ }), now);
16891
+ if (closed.ok)
16892
+ closedCount++;
16893
+ }
16894
+ if (signature && marker && closedCount === 0 && !racySignature(signature.split("|")[0] ?? "")) {
16895
+ try {
16896
+ writeFileSync4(marker, signature);
16897
+ } catch {}
16898
+ }
16899
+ return closedCount;
16900
+ }
16901
+ var init_task_delivery = __esm(() => {
16902
+ init_ledger();
16903
+ init_task_store();
16904
+ init_task_contract();
16905
+ init_workers();
16906
+ });
16907
+
16835
16908
  // packages/workit-core/src/core/task-evaluation.ts
16836
16909
  import { createHash as createHash11 } from "node:crypto";
16837
16910
  import * as fs14 from "node:fs";
@@ -18804,10 +18877,10 @@ var init_route_intent = __esm(() => {
18804
18877
 
18805
18878
  // packages/workit-core/src/core/runtime-identity.ts
18806
18879
  import { createHash as createHash12 } from "node:crypto";
18807
- import { readFileSync as readFileSync7 } from "node:fs";
18880
+ import { readFileSync as readFileSync8 } from "node:fs";
18808
18881
  var sha256Hex = (bytes) => createHash12("sha256").update(bytes).digest("hex"), bundleHashOfFile = (file) => {
18809
18882
  try {
18810
- return sha256Hex(readFileSync7(file));
18883
+ return sha256Hex(readFileSync8(file));
18811
18884
  } catch {
18812
18885
  return null;
18813
18886
  }
@@ -21199,7 +21272,7 @@ var init_core3 = __esm(() => {
21199
21272
  });
21200
21273
 
21201
21274
  // packages/workit-cli/src/task.ts
21202
- import { readFileSync as readFileSync8 } from "node:fs";
21275
+ import { readFileSync as readFileSync9 } from "node:fs";
21203
21276
  import path27 from "node:path";
21204
21277
  async function readInput(input) {
21205
21278
  if (typeof input === "string")
@@ -21216,7 +21289,7 @@ async function payloadValue(raw, deps) {
21216
21289
  return { ok: true, value: {} };
21217
21290
  let source;
21218
21291
  try {
21219
- source = raw === "-" ? await readInput(deps.stdin ?? process.stdin) : raw.startsWith("@") ? decodeUtf8(readFileSync8(raw.slice(1))) : raw;
21292
+ source = raw === "-" ? await readInput(deps.stdin ?? process.stdin) : raw.startsWith("@") ? decodeUtf8(readFileSync9(raw.slice(1))) : raw;
21220
21293
  } catch (error) {
21221
21294
  return {
21222
21295
  ok: false,
@@ -21504,7 +21577,12 @@ async function runTaskCommand(argv, deps = {}) {
21504
21577
  return parsed.usage ? 2 : 1;
21505
21578
  }
21506
21579
  const root = workspaceRootFor(deps);
21507
- const core = new WorkitCore(new TaskStore(root), contextFor(root, parsed.parsed.actor !== undefined ? { ...deps, actor: parsed.parsed.actor } : deps));
21580
+ const store = new TaskStore(root);
21581
+ try {
21582
+ const now = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
21583
+ closeDeliveredTasks(store, now, { cached: parsed.parsed.action !== "list" });
21584
+ } catch {}
21585
+ const core = new WorkitCore(store, contextFor(root, parsed.parsed.actor !== undefined ? { ...deps, actor: parsed.parsed.actor } : deps));
21508
21586
  let result;
21509
21587
  if (parsed.parsed.handoff) {
21510
21588
  const exported = dispatch(core, "state", parsed.parsed.request);
@@ -21600,6 +21678,7 @@ var init_task = __esm(() => {
21600
21678
  init_current_judgment();
21601
21679
  init_judgment();
21602
21680
  init_lane();
21681
+ init_task_delivery();
21603
21682
  init_task_store();
21604
21683
  init_core3();
21605
21684
  init_task_contract();
@@ -65285,13 +65364,13 @@ var init_changelog = __esm(() => {
65285
65364
 
65286
65365
  // packages/workit-core/src/core/hygiene.ts
65287
65366
  import { spawnSync as spawnSync12 } from "node:child_process";
65288
- import { existsSync as existsSync10, readFileSync as readFileSync10, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
65367
+ import { existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
65289
65368
  import path29 from "node:path";
65290
65369
  var repoRoot2, templatesDir = () => path29.join(repoRoot2, "templates", "hygiene"), packageJson = (root) => {
65291
65370
  if (!existsSync10(path29.join(root, "package.json")))
65292
65371
  return null;
65293
65372
  try {
65294
- return JSON.parse(readFileSync10(path29.join(root, "package.json"), "utf8"));
65373
+ return JSON.parse(readFileSync11(path29.join(root, "package.json"), "utf8"));
65295
65374
  } catch {
65296
65375
  return null;
65297
65376
  }
@@ -65344,7 +65423,7 @@ var repoRoot2, templatesDir = () => path29.join(repoRoot2, "templates", "hygiene
65344
65423
  const tpl = path29.join(templatesDir(), file);
65345
65424
  if (!existsSync10(tpl))
65346
65425
  continue;
65347
- const content = readFileSync10(tpl, "utf8").replace(/<PROJECT>/g, path29.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
65426
+ const content = readFileSync11(tpl, "utf8").replace(/<PROJECT>/g, path29.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
65348
65427
  planned.push({ path: path29.join(root, file), content });
65349
65428
  }
65350
65429
  return planned;
@@ -65356,13 +65435,13 @@ var init_hygiene = __esm(() => {
65356
65435
  });
65357
65436
 
65358
65437
  // packages/workit-cli/src/admin/setup-state.ts
65359
- import { existsSync as existsSync11, readFileSync as readFileSync11 } from "node:fs";
65438
+ import { existsSync as existsSync11, readFileSync as readFileSync12 } from "node:fs";
65360
65439
  import path30 from "node:path";
65361
65440
  var classifySetupFile = (dir, name) => {
65362
65441
  const file = path30.join(dir, name);
65363
65442
  let raw;
65364
65443
  try {
65365
- raw = readFileSync11(file, "utf8");
65444
+ raw = readFileSync12(file, "utf8");
65366
65445
  } catch {
65367
65446
  if (existsSync11(file)) {
65368
65447
  return { file, status: "malformed", error: `${file} is not readable` };
@@ -65803,7 +65882,7 @@ import {
65803
65882
  accessSync,
65804
65883
  constants as constants2,
65805
65884
  existsSync as existsSync12,
65806
- readFileSync as readFileSync12,
65885
+ readFileSync as readFileSync13,
65807
65886
  readdirSync as readdirSync6,
65808
65887
  realpathSync as realpathSync6,
65809
65888
  statSync as statSync2
@@ -66093,7 +66172,7 @@ function runHostCommand(step, context = {}) {
66093
66172
  }
66094
66173
  var resolveWindowsShimScript = (shim) => {
66095
66174
  try {
66096
- const contents = readFileSync12(shim, "utf8");
66175
+ const contents = readFileSync13(shim, "utf8");
66097
66176
  return resolveWindowsShimEntry(contents, shim);
66098
66177
  } catch {
66099
66178
  return null;
@@ -66114,7 +66193,7 @@ var resolveWindowsShimScript = (shim) => {
66114
66193
  }, isPiWorkitInstalled = (home, env = process.env) => {
66115
66194
  const settings = path33.join(env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent"), "settings.json");
66116
66195
  try {
66117
- const document2 = JSON.parse(readFileSync12(settings, "utf8"));
66196
+ const document2 = JSON.parse(readFileSync13(settings, "utf8"));
66118
66197
  return (document2.packages ?? []).some((entry) => {
66119
66198
  const source = entry && typeof entry === "object" && !Array.isArray(entry) ? entry.source : entry;
66120
66199
  const value = String(source).replaceAll("\\", "/").replace(/\/+$/, "");
@@ -66125,7 +66204,7 @@ var resolveWindowsShimScript = (shim) => {
66125
66204
  }
66126
66205
  }, OPENCODE_PLUGIN_SCOPE = "@brainervirus", OPENCODE_PLUGIN_NAME = "workit-opencode", packageVersionAt = (root, name) => {
66127
66206
  try {
66128
- const pkg = JSON.parse(readFileSync12(path33.join(root, "package.json"), "utf8"));
66207
+ const pkg = JSON.parse(readFileSync13(path33.join(root, "package.json"), "utf8"));
66129
66208
  return pkg.name === name && typeof pkg.version === "string" ? pkg.version : null;
66130
66209
  } catch {
66131
66210
  return null;
@@ -66163,7 +66242,7 @@ var resolveWindowsShimScript = (shim) => {
66163
66242
  path33.join(root, "package.json")
66164
66243
  ]) {
66165
66244
  try {
66166
- const manifest = JSON.parse(readFileSync12(file, "utf8"));
66245
+ const manifest = JSON.parse(readFileSync13(file, "utf8"));
66167
66246
  const repository = typeof manifest.repository === "string" ? manifest.repository : manifest.repository?.url;
66168
66247
  if ((manifest.name === "workit" || manifest.name === "@brainervirus/workit-codex") && repository?.replace(/\.git$/, "").toLowerCase() === "https://github.com/brainervirus/workit")
66169
66248
  return true;
@@ -66204,7 +66283,7 @@ var resolveWindowsShimScript = (shim) => {
66204
66283
  return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
66205
66284
  }, isCanonicalClaudePlugin = (root) => {
66206
66285
  try {
66207
- const manifest = JSON.parse(readFileSync12(path33.join(root, ".claude-plugin", "plugin.json"), "utf8"));
66286
+ const manifest = JSON.parse(readFileSync13(path33.join(root, ".claude-plugin", "plugin.json"), "utf8"));
66208
66287
  const repository = typeof manifest.repository === "string" ? manifest.repository : manifest.repository?.url;
66209
66288
  return manifest.name === "workit" && repository?.replace(/\.git$/, "").toLowerCase() === "https://github.com/brainervirus/workit";
66210
66289
  } catch {
@@ -66214,7 +66293,7 @@ var resolveWindowsShimScript = (shim) => {
66214
66293
  const file = path33.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json");
66215
66294
  let registry;
66216
66295
  try {
66217
- registry = JSON.parse(readFileSync12(file, "utf8"));
66296
+ registry = JSON.parse(readFileSync13(file, "utf8"));
66218
66297
  } catch {
66219
66298
  return [];
66220
66299
  }
@@ -66263,7 +66342,7 @@ var init_host_install = __esm(() => {
66263
66342
  });
66264
66343
 
66265
66344
  // packages/workit-core/src/core/skill-manifests.ts
66266
- import { cpSync as cpSync3, existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync7, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
66345
+ import { cpSync as cpSync3, existsSync as existsSync13, readFileSync as readFileSync14, readdirSync as readdirSync7, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
66267
66346
  import path34 from "node:path";
66268
66347
  var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync13(root) ? readdirSync7(root).filter((name) => existsSync13(path34.join(root, name, "SKILL.md"))).toSorted() : [], validateSkillManifests = (root, expected, label) => {
66269
66348
  const actual = skillManifestNames(root);
@@ -66281,7 +66360,7 @@ var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync13(root) ? re
66281
66360
  const file = path34.join(dir, entry.name);
66282
66361
  if (entry.isDirectory())
66283
66362
  pending.push(file);
66284
- else if ((statSync3(file).mode & 73) !== 0 || readFileSync13(file).subarray(0, 2).toString("latin1") === "#!") {
66363
+ else if ((statSync3(file).mode & 73) !== 0 || readFileSync14(file).subarray(0, 2).toString("latin1") === "#!") {
66285
66364
  return `Cursor vendor contains active file: ${file}`;
66286
66365
  }
66287
66366
  }
@@ -66314,11 +66393,11 @@ import {
66314
66393
  existsSync as existsSync14,
66315
66394
  lstatSync as lstatSync4,
66316
66395
  mkdirSync as mkdirSync5,
66317
- readFileSync as readFileSync14,
66396
+ readFileSync as readFileSync15,
66318
66397
  renameSync as renameSync3,
66319
66398
  rmSync as rmSync2,
66320
66399
  statSync as statSync4,
66321
- writeFileSync as writeFileSync6
66400
+ writeFileSync as writeFileSync7
66322
66401
  } from "node:fs";
66323
66402
  import path35 from "node:path";
66324
66403
  function inspectSessionHook(cwd, env = process.env) {
@@ -66517,7 +66596,7 @@ exit 0
66517
66596
  return rel === "" || !rel.startsWith("..") && !path35.isAbsolute(rel);
66518
66597
  }, readText3 = (file) => {
66519
66598
  try {
66520
- return readFileSync14(file, "utf8");
66599
+ return readFileSync15(file, "utf8");
66521
66600
  } catch {
66522
66601
  return null;
66523
66602
  }
@@ -66621,7 +66700,7 @@ exit 0
66621
66700
  mkdirSync5(path35.dirname(file), { recursive: true });
66622
66701
  const staged = `${file}.workit-new`;
66623
66702
  try {
66624
- writeFileSync6(staged, content, { mode: 493 });
66703
+ writeFileSync7(staged, content, { mode: 493 });
66625
66704
  chmodSync(staged, 493);
66626
66705
  renameSync3(staged, file);
66627
66706
  } finally {
@@ -66684,7 +66763,7 @@ var init_heavy_words = __esm(() => {
66684
66763
  });
66685
66764
 
66686
66765
  // packages/workit-core/src/hooks/hook-runs.ts
66687
- import { mkdirSync as mkdirSync6, readdirSync as readdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
66766
+ import { mkdirSync as mkdirSync6, readdirSync as readdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "node:fs";
66688
66767
  import path37 from "node:path";
66689
66768
  var HOOK_RUNS_DIR = "hook-runs", readHookRuns = (dir) => {
66690
66769
  const runs = [];
@@ -66696,7 +66775,7 @@ var HOOK_RUNS_DIR = "hook-runs", readHookRuns = (dir) => {
66696
66775
  }
66697
66776
  for (const name of names) {
66698
66777
  try {
66699
- const row = JSON.parse(readFileSync15(path37.join(dir, HOOK_RUNS_DIR, name), "utf8"));
66778
+ const row = JSON.parse(readFileSync16(path37.join(dir, HOOK_RUNS_DIR, name), "utf8"));
66700
66779
  if (typeof row.host === "string" && typeof row.event === "string" && typeof row.at === "string" && typeof row.count === "number")
66701
66780
  runs.push({
66702
66781
  host: row.host,
@@ -66742,7 +66821,7 @@ import {
66742
66821
  existsSync as existsSync15,
66743
66822
  readdirSync as readdirSync9,
66744
66823
  mkdirSync as mkdirSync7,
66745
- readFileSync as readFileSync16,
66824
+ readFileSync as readFileSync17,
66746
66825
  realpathSync as realpathSync7,
66747
66826
  rmSync as rmSync3,
66748
66827
  statSync as statSync5
@@ -67170,7 +67249,7 @@ ${[...rules, ...lease, grant].join(`
67170
67249
  }, AGENT_SESSION_VARS, agentSessionVar = (env) => AGENT_SESSION_VARS.find((name) => (env[name] ?? "") !== "") ?? null, RECORD_FILE = "host-permissions.json", readOwnership = (options = {}) => {
67171
67250
  const { stateDir } = resolvePaths(options);
67172
67251
  try {
67173
- const value = JSON.parse(readFileSync16(path38.join(stateDir, RECORD_FILE), "utf8"));
67252
+ const value = JSON.parse(readFileSync17(path38.join(stateDir, RECORD_FILE), "utf8"));
67174
67253
  if (isRecord5(value) && isRecord5(value.hosts))
67175
67254
  return { version: 1, hosts: value.hosts };
67176
67255
  } catch {}
@@ -67199,7 +67278,7 @@ ${[...rules, ...lease, grant].join(`
67199
67278
  writeOwnership(record, paths.stateDir);
67200
67279
  }, readText4 = (file) => {
67201
67280
  try {
67202
- return readFileSync16(file, "utf8");
67281
+ return readFileSync17(file, "utf8");
67203
67282
  } catch {
67204
67283
  return null;
67205
67284
  }
@@ -67361,13 +67440,13 @@ import {
67361
67440
  mkdirSync as mkdirSync8,
67362
67441
  mkdtempSync,
67363
67442
  readdirSync as readdirSync10,
67364
- readFileSync as readFileSync17,
67443
+ readFileSync as readFileSync18,
67365
67444
  readlinkSync as readlinkSync2,
67366
67445
  renameSync as renameSync4,
67367
67446
  rmSync as rmSync4,
67368
67447
  statSync as statSync6,
67369
67448
  unlinkSync as unlinkSync3,
67370
- writeFileSync as writeFileSync8
67449
+ writeFileSync as writeFileSync9
67371
67450
  } from "node:fs";
67372
67451
  import os12 from "node:os";
67373
67452
  import path39 from "node:path";
@@ -67431,14 +67510,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67431
67510
  };
67432
67511
  }, readJson = (p) => {
67433
67512
  try {
67434
- const parsed = JSON.parse(readFileSync17(p, "utf8"));
67513
+ const parsed = JSON.parse(readFileSync18(p, "utf8"));
67435
67514
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
67436
67515
  } catch {
67437
67516
  return null;
67438
67517
  }
67439
67518
  }, parsesAsConfigObject = (p) => {
67440
67519
  try {
67441
- return isConfigObject(JSON.parse(readFileSync17(p, "utf8")));
67520
+ return isConfigObject(JSON.parse(readFileSync18(p, "utf8")));
67442
67521
  } catch {
67443
67522
  return false;
67444
67523
  }
@@ -67661,7 +67740,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67661
67740
  const tables = new Map;
67662
67741
  let text;
67663
67742
  try {
67664
- text = readFileSync17(file, "utf8");
67743
+ text = readFileSync18(file, "utf8");
67665
67744
  } catch {
67666
67745
  return tables;
67667
67746
  }
@@ -67832,7 +67911,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67832
67911
  let stale = false;
67833
67912
  for (const file of files) {
67834
67913
  const dest = path39.join(agentsDir, file);
67835
- const want = stampCodexAgent(readFileSync17(path39.join(source, file), "utf8"), version);
67914
+ const want = stampCodexAgent(readFileSync18(path39.join(source, file), "utf8"), version);
67836
67915
  let link = false;
67837
67916
  try {
67838
67917
  link = lstatSync5(dest).isSymbolicLink();
@@ -67846,7 +67925,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67846
67925
  }
67847
67926
  let have = null;
67848
67927
  try {
67849
- have = readFileSync17(dest, "utf8");
67928
+ have = readFileSync18(dest, "utf8");
67850
67929
  } catch {
67851
67930
  have = null;
67852
67931
  }
@@ -68095,7 +68174,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68095
68174
  }, validNodeEntry = (entry, runtime, env) => {
68096
68175
  try {
68097
68176
  const stat = statSync6(entry);
68098
- if (!stat.isFile() || stat.size === 0 || !readFileSync17(entry, "utf8").startsWith(`#!/usr/bin/env node
68177
+ if (!stat.isFile() || stat.size === 0 || !readFileSync18(entry, "utf8").startsWith(`#!/usr/bin/env node
68099
68178
  `)) {
68100
68179
  return false;
68101
68180
  }
@@ -68951,7 +69030,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68951
69030
  return { ...installSessionHook(res.cwd, res.env), workspace: workspace.name };
68952
69031
  }, isPlaceholder = (p) => {
68953
69032
  try {
68954
- return readFileSync17(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
69033
+ return readFileSync18(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
68955
69034
  } catch {
68956
69035
  return false;
68957
69036
  }
@@ -69165,7 +69244,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69165
69244
  const probe = path39.join(logsDir, "doctor-probe.tmp");
69166
69245
  try {
69167
69246
  mkdirSync8(logsDir, { recursive: true, mode: 448 });
69168
- writeFileSync8(probe, `{"probe":true}
69247
+ writeFileSync9(probe, `{"probe":true}
69169
69248
  `, { mode: 384 });
69170
69249
  return {
69171
69250
  id: "log_writable",
@@ -69244,7 +69323,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69244
69323
  const counts = { skip: 0, late: 0 };
69245
69324
  let text = "";
69246
69325
  try {
69247
- text = readFileSync17(path39.join(stateDir, HOOK_SKIPS_FILE), "utf8");
69326
+ text = readFileSync18(path39.join(stateDir, HOOK_SKIPS_FILE), "utf8");
69248
69327
  } catch {
69249
69328
  return counts;
69250
69329
  }
@@ -69435,7 +69514,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69435
69514
  return null;
69436
69515
  return files.some((file) => {
69437
69516
  try {
69438
- return readFileSync17(path39.join(root, "dist", file), "utf8").includes(HOOK_ISSUED_MARKER);
69517
+ return readFileSync18(path39.join(root, "dist", file), "utf8").includes(HOOK_ISSUED_MARKER);
69439
69518
  } catch {
69440
69519
  return false;
69441
69520
  }
@@ -69779,13 +69858,13 @@ import {
69779
69858
  lstatSync as lstatSync6,
69780
69859
  mkdirSync as mkdirSync9,
69781
69860
  mkdtempSync as mkdtempSync2,
69782
- readFileSync as readFileSync18,
69861
+ readFileSync as readFileSync19,
69783
69862
  readdirSync as readdirSync11,
69784
69863
  realpathSync as realpathSync8,
69785
69864
  renameSync as renameSync5,
69786
69865
  rmSync as rmSync5,
69787
69866
  statSync as statSync7,
69788
- writeFileSync as writeFileSync9
69867
+ writeFileSync as writeFileSync10
69789
69868
  } from "node:fs";
69790
69869
  import { createRequire as createRequire2 } from "node:module";
69791
69870
  import os13 from "node:os";
@@ -69953,7 +70032,7 @@ function buildSetupPreview(values, opts = {}) {
69953
70032
  if (target?.ok) {
69954
70033
  const root = target.root;
69955
70034
  const gitignorePath = path40.join(root, ".gitignore");
69956
- const existing = existsSync17(gitignorePath) ? readFileSync18(gitignorePath, "utf8") : "";
70035
+ const existing = existsSync17(gitignorePath) ? readFileSync19(gitignorePath, "utf8") : "";
69957
70036
  const existingLines = new Set(existing.split(`
69958
70037
  `).map((l) => l.trim()).filter(Boolean));
69959
70038
  const entries = GITIGNORE_ENTRIES.filter((e) => e.trim() !== "" && !existingLines.has(e.trim()));
@@ -70017,7 +70096,7 @@ function applyMutation(m) {
70017
70096
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
70018
70097
  }
70019
70098
  mkdirSync9(dir, { recursive: true });
70020
- writeFileSync9(m.path, JSON.stringify(merged, null, 2) + `
70099
+ writeFileSync10(m.path, JSON.stringify(merged, null, 2) + `
70021
70100
  `, "utf8");
70022
70101
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
70023
70102
  }
@@ -70061,7 +70140,7 @@ function applyMutation(m) {
70061
70140
  mkdirSync9(dir, { recursive: true });
70062
70141
  const tmp = `${m.path}.${process.pid}.tmp`;
70063
70142
  try {
70064
- writeFileSync9(tmp, next, "utf8");
70143
+ writeFileSync10(tmp, next, "utf8");
70065
70144
  if (workspacesRevision(readFileSafe(m.path)) !== m.expectedRevision) {
70066
70145
  rmSync5(tmp, { force: true });
70067
70146
  return {
@@ -70100,7 +70179,7 @@ function applyMutation(m) {
70100
70179
  const separator = existing && !existing.endsWith(`
70101
70180
  `) ? `
70102
70181
  ` : "";
70103
- writeFileSync9(m.path, existing + separator + (existing ? `
70182
+ writeFileSync10(m.path, existing + separator + (existing ? `
70104
70183
  ` : "") + add.join(`
70105
70184
  `) + `
70106
70185
  `, "utf8");
@@ -70140,7 +70219,7 @@ function applyMutation(m) {
70140
70219
  };
70141
70220
  }
70142
70221
  mkdirSync9(path40.dirname(m.path), { recursive: true });
70143
- writeFileSync9(m.path, JSON.stringify(merged, null, 2) + `
70222
+ writeFileSync10(m.path, JSON.stringify(merged, null, 2) + `
70144
70223
  `, "utf8");
70145
70224
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
70146
70225
  }
@@ -70212,7 +70291,7 @@ function applyOpenCode(root, res) {
70212
70291
  detail: "already registered"
70213
70292
  };
70214
70293
  }
70215
- writeFileSync9(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
70294
+ writeFileSync10(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
70216
70295
  `, "utf8");
70217
70296
  return existing.kind === "record" ? {
70218
70297
  platform: "opencode",
@@ -70247,12 +70326,12 @@ function copyPluginDir(src, dest) {
70247
70326
  });
70248
70327
  const stagedHooks = path40.join(stage, CURSOR_HOOKS_FILE);
70249
70328
  if (existsSync17(stagedHooks))
70250
- writeFileSync9(stagedHooks, localizedCursorHooks(readFileSync18(stagedHooks), dest));
70329
+ writeFileSync10(stagedHooks, localizedCursorHooks(readFileSync19(stagedHooks), dest));
70251
70330
  for (const [name, content] of rules) {
70252
70331
  mkdirSync9(path40.join(stage, "rules"), { recursive: true });
70253
- writeFileSync9(path40.join(stage, "rules", name), content);
70332
+ writeFileSync10(path40.join(stage, "rules", name), content);
70254
70333
  }
70255
- writeFileSync9(path40.join(stage, ".workit-root"), realSrc + `
70334
+ writeFileSync10(path40.join(stage, ".workit-root"), realSrc + `
70256
70335
  `, "utf8");
70257
70336
  if (!samePluginContent(realSrc, stage, dest))
70258
70337
  throw new Error("staged adapter content is incomplete");
@@ -70294,7 +70373,7 @@ function applyCursorSettings(root, res) {
70294
70373
  detail: "already registered"
70295
70374
  };
70296
70375
  }
70297
- writeFileSync9(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
70376
+ writeFileSync10(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
70298
70377
  `, "utf8");
70299
70378
  return {
70300
70379
  platform: "cursor",
@@ -70333,7 +70412,7 @@ function applyCursorMcp(root, res) {
70333
70412
  detail: "already registered"
70334
70413
  };
70335
70414
  }
70336
- writeFileSync9(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
70415
+ writeFileSync10(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
70337
70416
  `, "utf8");
70338
70417
  return {
70339
70418
  platform: "cursor",
@@ -70602,7 +70681,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70602
70681
  return configured ?? fallback;
70603
70682
  }, readConfigRecord = (file) => {
70604
70683
  try {
70605
- const value = JSON.parse(readFileSync18(file, "utf8"));
70684
+ const value = JSON.parse(readFileSync19(file, "utf8"));
70606
70685
  return isRecord6(value) ? value : null;
70607
70686
  } catch {
70608
70687
  return null;
@@ -70623,7 +70702,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70623
70702
  }, isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readExisting = (p) => {
70624
70703
  let raw;
70625
70704
  try {
70626
- raw = readFileSync18(p, "utf8");
70705
+ raw = readFileSync19(p, "utf8");
70627
70706
  } catch {
70628
70707
  if (existsSync17(p))
70629
70708
  return { kind: "malformed", error: `${p} is not readable` };
@@ -70640,7 +70719,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70640
70719
  return { kind: "malformed", error: `${p} is not a JSON object` };
70641
70720
  }, readFileSafe = (p) => {
70642
70721
  try {
70643
- return readFileSync18(p, "utf8");
70722
+ return readFileSync19(p, "utf8");
70644
70723
  } catch {
70645
70724
  return null;
70646
70725
  }
@@ -70655,7 +70734,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70655
70734
  }
70656
70735
  }, isAdapter = (root, platform) => {
70657
70736
  try {
70658
- const pkg = JSON.parse(readFileSync18(path40.join(root, "package.json"), "utf8"));
70737
+ const pkg = JSON.parse(readFileSync19(path40.join(root, "package.json"), "utf8"));
70659
70738
  return pkg.name === `@brainervirus/workit-${platform}`;
70660
70739
  } catch {
70661
70740
  return false;
@@ -70704,8 +70783,8 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70704
70783
  if (!statSync7(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
70705
70784
  return false;
70706
70785
  } else {
70707
- const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync18(source), pluginDir) : readFileSync18(source);
70708
- if (!expected.equals(readFileSync18(installed)))
70786
+ const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync19(source), pluginDir) : readFileSync19(source);
70787
+ if (!expected.equals(readFileSync19(installed)))
70709
70788
  return false;
70710
70789
  }
70711
70790
  }
@@ -70733,7 +70812,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
70733
70812
  continue;
70734
70813
  if (existsSync17(path40.join(sourceRules, entry.name)))
70735
70814
  continue;
70736
- preserved.set(entry.name, readFileSync18(path40.join(installedRules, entry.name)));
70815
+ preserved.set(entry.name, readFileSync19(path40.join(installedRules, entry.name)));
70737
70816
  }
70738
70817
  } catch {}
70739
70818
  return preserved;
@@ -70788,7 +70867,7 @@ var init_setup = __esm(() => {
70788
70867
  });
70789
70868
 
70790
70869
  // packages/workit-core/src/core/workspace-branch-policy.ts
70791
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync19, renameSync as renameSync6, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "node:fs";
70870
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync20, renameSync as renameSync6, rmSync as rmSync6, writeFileSync as writeFileSync11 } from "node:fs";
70792
70871
  import path41 from "node:path";
70793
70872
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
70794
70873
  function applyWorkspaceBranchPolicy(opts) {
@@ -70857,7 +70936,7 @@ function applyWorkspaceBranchPolicy(opts) {
70857
70936
  mkdirSync10(path41.dirname(wsPath), { recursive: true });
70858
70937
  const tmp = `${wsPath}.${process.pid}.tmp`;
70859
70938
  try {
70860
- writeFileSync10(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
70939
+ writeFileSync11(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
70861
70940
  `, "utf8");
70862
70941
  if (workspacesRevision(readFileSafe2(wsPath)) !== revision) {
70863
70942
  rmSync6(tmp, { force: true });
@@ -70886,7 +70965,7 @@ function applyWorkspaceBranchPolicy(opts) {
70886
70965
  }
70887
70966
  var readFileSafe2 = (p) => {
70888
70967
  try {
70889
- return readFileSync19(p, "utf8");
70968
+ return readFileSync20(p, "utf8");
70890
70969
  } catch {
70891
70970
  return null;
70892
70971
  }
@@ -71726,7 +71805,7 @@ var init_wizard_state = __esm(() => {
71726
71805
  });
71727
71806
 
71728
71807
  // packages/workit-cli/src/admin/uninstall.ts
71729
- import { existsSync as existsSync19, readFileSync as readFileSync20, realpathSync as realpathSync9, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
71808
+ import { existsSync as existsSync19, readFileSync as readFileSync21, realpathSync as realpathSync9, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "node:fs";
71730
71809
  import os14 from "node:os";
71731
71810
  import path43 from "node:path";
71732
71811
  function cleanCursorSettings(settings, pluginDir) {
@@ -71988,7 +72067,7 @@ var resolveUninstallPaths = (options = {}) => {
71988
72067
  }, isRecord7 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readJsonRecord = (file) => {
71989
72068
  let raw;
71990
72069
  try {
71991
- raw = readFileSync20(file, "utf8");
72070
+ raw = readFileSync21(file, "utf8");
71992
72071
  } catch {
71993
72072
  if (existsSync19(file))
71994
72073
  return { kind: "malformed", error: `${file} is not readable` };
@@ -72053,7 +72132,7 @@ var resolveUninstallPaths = (options = {}) => {
72053
72132
  `;
72054
72133
  let current;
72055
72134
  try {
72056
- current = readFileSync20(target, "utf8");
72135
+ current = readFileSync21(target, "utf8");
72057
72136
  } catch (error) {
72058
72137
  return {
72059
72138
  status: "failed",
@@ -72064,7 +72143,7 @@ var resolveUninstallPaths = (options = {}) => {
72064
72143
  return { status: "skipped", detail: "already clean" };
72065
72144
  }
72066
72145
  try {
72067
- writeFileSync11(target, serialized, "utf8");
72146
+ writeFileSync12(target, serialized, "utf8");
72068
72147
  } catch (error) {
72069
72148
  return {
72070
72149
  status: "failed",
@@ -74839,7 +74918,7 @@ var init_setup2 = __esm(() => {
74839
74918
  });
74840
74919
 
74841
74920
  // packages/workit-cli/src/upgrade/shared.ts
74842
- import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
74921
+ import { existsSync as existsSync20, readFileSync as readFileSync22 } from "node:fs";
74843
74922
  import path44 from "node:path";
74844
74923
  import os16 from "node:os";
74845
74924
  import { createHash as createHash15 } from "node:crypto";
@@ -74876,7 +74955,7 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
74876
74955
  return null;
74877
74956
  let data;
74878
74957
  try {
74879
- data = JSON.parse(readFileSync21(file, "utf8"));
74958
+ data = JSON.parse(readFileSync22(file, "utf8"));
74880
74959
  } catch {
74881
74960
  throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
74882
74961
  }
@@ -74889,7 +74968,7 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
74889
74968
  } catch {
74890
74969
  return null;
74891
74970
  }
74892
- }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync21(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
74971
+ }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync22(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
74893
74972
  const { home, env } = upgradePaths(deps);
74894
74973
  let step;
74895
74974
  try {
@@ -75350,7 +75429,7 @@ var init_apply = __esm(() => {
75350
75429
  });
75351
75430
 
75352
75431
  // packages/workit-cli/src/upgrade/plan.ts
75353
- import { existsSync as existsSync22, mkdtempSync as mkdtempSync3, rmSync as rmSync8, lstatSync as lstatSync7, readFileSync as readFileSync22 } from "node:fs";
75432
+ import { existsSync as existsSync22, mkdtempSync as mkdtempSync3, rmSync as rmSync8, lstatSync as lstatSync7, readFileSync as readFileSync23 } from "node:fs";
75354
75433
  import path47 from "node:path";
75355
75434
  import os17 from "node:os";
75356
75435
  function previewUpgrade(hosts = HOSTS2, deps = {}) {
@@ -75504,7 +75583,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
75504
75583
  var codexRegistered = (configFile) => {
75505
75584
  let text;
75506
75585
  try {
75507
- text = readFileSync22(configFile, "utf8");
75586
+ text = readFileSync23(configFile, "utf8");
75508
75587
  } catch {
75509
75588
  return false;
75510
75589
  }
@@ -77790,6 +77869,7 @@ function resolveResourceSettings(sources, host = hostShape()) {
77790
77869
  const settings = {
77791
77870
  slots: DEFAULT_SLOTS,
77792
77871
  heavy: [...DEFAULT_HEAVY_CHECKS],
77872
+ light: [...DEFAULT_LIGHT_CHECKS],
77793
77873
  memoryMax: "",
77794
77874
  cpuQuota: "",
77795
77875
  waitMs: DEFAULT_WAIT_S * 1000,
@@ -77831,8 +77911,21 @@ function resolveResourceSettings(sources, host = hostShape()) {
77831
77911
  const heavy = isRecord9(value.heavy) ? Object.keys(value.heavy) : value.heavy;
77832
77912
  if (!Array.isArray(heavy) || !heavy.every((item) => typeof item === "string"))
77833
77913
  warn("heavy", "must be a list of check names");
77834
- else
77914
+ else {
77835
77915
  settings.heavy = repo ? [...new Set([...settings.heavy, ...heavy])] : heavy;
77916
+ settings.light = settings.light.filter((name) => !heavy.includes(name));
77917
+ }
77918
+ }
77919
+ if (value.light !== undefined) {
77920
+ const light = value.light;
77921
+ if (repo)
77922
+ machineOnly("light");
77923
+ else if (!Array.isArray(light) || !light.every((item) => typeof item === "string"))
77924
+ warn("light", "must be a list of check names");
77925
+ else {
77926
+ settings.light = light;
77927
+ settings.heavy = settings.heavy.filter((name) => !light.includes(name));
77928
+ }
77836
77929
  }
77837
77930
  if (value.memoryMax !== undefined) {
77838
77931
  const next = memoryValue(value.memoryMax, host);
@@ -77853,10 +77946,11 @@ function resolveResourceSettings(sources, host = hostShape()) {
77853
77946
  cpu = next;
77854
77947
  }
77855
77948
  if (value.wait !== undefined) {
77856
- if (typeof value.wait === "number" && value.wait >= 0)
77857
- settings.waitMs = value.wait * 1000;
77949
+ const wait = typeof value.wait === "number" ? value.wait >= 0 ? value.wait * 1000 : null : typeof value.wait === "string" ? parseWait(value.wait) : null;
77950
+ if (wait !== null)
77951
+ settings.waitMs = wait;
77858
77952
  else
77859
- warn("wait", "must be a number of seconds");
77953
+ warn("wait", 'must be a number of seconds or a duration ("30m")');
77860
77954
  }
77861
77955
  }
77862
77956
  settings.memoryMax = String(memory);
@@ -77974,7 +78068,65 @@ function removeOwn(file, id) {
77974
78068
  if (parseLease(readText5(file))?.id === id)
77975
78069
  fs24.rmSync(file, { force: true });
77976
78070
  }
78071
+ function leaseQueue(dir, probe = {}) {
78072
+ let names;
78073
+ try {
78074
+ names = fs24.readdirSync(queueDir(dir));
78075
+ } catch {
78076
+ return [];
78077
+ }
78078
+ const entries = [];
78079
+ for (const name of names.filter((item) => item.endsWith(".json")).toSorted()) {
78080
+ const file = path52.join(queueDir(dir), name);
78081
+ const record = parseLease(readText5(file));
78082
+ if (!record)
78083
+ continue;
78084
+ entries.push({
78085
+ file,
78086
+ holder: holderOf(record),
78087
+ id: record.id,
78088
+ stale: staleReason(record, probe)
78089
+ });
78090
+ }
78091
+ return entries;
78092
+ }
78093
+ function waitersAhead(options, own) {
78094
+ let ahead = 0;
78095
+ for (const entry of leaseQueue(options.dir, options.probe)) {
78096
+ if (path52.basename(entry.file) >= own)
78097
+ break;
78098
+ if (!TICKET.test(path52.basename(entry.file)))
78099
+ continue;
78100
+ if (entry.stale && reclaim(entry.file, (current) => !current || staleReason(current, options.probe) !== null))
78101
+ continue;
78102
+ ahead += 1;
78103
+ }
78104
+ return ahead;
78105
+ }
78106
+ function liveHolders(options) {
78107
+ return leaseSlots(options.dir, options.slots, options.probe).filter((slot) => slot.holder && !slot.stale).map((slot) => slot.holder);
78108
+ }
78109
+ function writeTicket(file, record) {
78110
+ fs24.mkdirSync(path52.dirname(file), { recursive: true, mode: 448 });
78111
+ fs24.writeFileSync(file, `${JSON.stringify(record)}
78112
+ `, { mode: 384 });
78113
+ }
78114
+ function parseWait(text) {
78115
+ const match = /^(\d+(?:\.\d+)?)([smh]?)$/u.exec(text.trim());
78116
+ if (!match)
78117
+ return null;
78118
+ const unit = { "": 1000, s: 1000, m: 60000, h: 3600000 }[match[2]] ?? 1000;
78119
+ return Math.round(Number(match[1]) * unit);
78120
+ }
78121
+ function formatWait(ms) {
78122
+ const seconds = Math.max(0, Math.round(ms / 1000));
78123
+ if (seconds < 60)
78124
+ return `${seconds}s`;
78125
+ const minutes = Math.floor(seconds / 60);
78126
+ return seconds % 60 ? `${minutes}m ${seconds % 60}s` : `${minutes}m`;
78127
+ }
77977
78128
  async function acquireLease(options) {
78129
+ const clock = options.clock ?? realClock;
77978
78130
  const pid = options.pid ?? process.pid;
77979
78131
  const boot = bootId();
77980
78132
  const record = {
@@ -77988,7 +78140,7 @@ async function acquireLease(options) {
77988
78140
  check: options.check,
77989
78141
  cwd: options.cwd,
77990
78142
  session: options.session,
77991
- startedAt: new Date().toISOString(),
78143
+ startedAt: new Date(clock.now()).toISOString(),
77992
78144
  scope: null
77993
78145
  };
77994
78146
  try {
@@ -77996,48 +78148,104 @@ async function acquireLease(options) {
77996
78148
  } catch (error) {
77997
78149
  return { ok: false, reason: "unavailable", error: error.message };
77998
78150
  }
77999
- const started = Date.now();
78151
+ const started = clock.now();
78000
78152
  const reclaimed = [];
78001
78153
  let waitedOn = [];
78002
- for (;; ) {
78003
- let outcome;
78004
- try {
78005
- outcome = tryOnce(options, record, reclaimed);
78006
- } catch (error) {
78007
- return { ok: false, reason: "unavailable", error: error.message };
78008
- }
78009
- if ("slot" in outcome) {
78010
- const { file, slot } = outcome;
78011
- let released = false;
78012
- const release = () => {
78013
- if (released)
78014
- return;
78015
- released = true;
78016
- process.off("exit", release);
78017
- removeOwn(file, record.id);
78018
- };
78019
- process.on("exit", release);
78020
- return {
78021
- ok: true,
78022
- lease: {
78023
- record,
78024
- slot,
78025
- file,
78026
- waitedOn,
78027
- waitedMs: waitedOn.length ? Date.now() - started : 0,
78028
- reclaimed,
78029
- release
78154
+ const name = ticketName(started, record.id);
78155
+ const ticket = path52.join(queueDir(options.dir), name);
78156
+ const ticketRecord = {
78157
+ ...record,
78158
+ maxAgeMs: options.waitMs + 60000
78159
+ };
78160
+ let queued = false;
78161
+ const onSignal = (signal) => {
78162
+ leave();
78163
+ process.exit(signal === "SIGINT" ? 130 : 143);
78164
+ };
78165
+ const leave = () => {
78166
+ process.off("exit", leave);
78167
+ process.off("SIGINT", onSignal);
78168
+ process.off("SIGTERM", onSignal);
78169
+ if (queued)
78170
+ removeOwn(ticket, record.id);
78171
+ queued = false;
78172
+ };
78173
+ let nextStatus = 0;
78174
+ try {
78175
+ for (;; ) {
78176
+ const waited = clock.now() - started;
78177
+ const ahead = queued ? waitersAhead(options, name) : waitersAhead(options, "~");
78178
+ const free = options.slots - liveHolders(options).length;
78179
+ if (ahead < free) {
78180
+ const outcome = tryOnce(options, record, reclaimed);
78181
+ if ("slot" in outcome) {
78182
+ const waitedMs = queued ? waited : 0;
78183
+ leave();
78184
+ const { file, slot } = outcome;
78185
+ let released = false;
78186
+ const release = () => {
78187
+ if (released)
78188
+ return;
78189
+ released = true;
78190
+ process.off("exit", release);
78191
+ removeOwn(file, record.id);
78192
+ };
78193
+ process.on("exit", release);
78194
+ return {
78195
+ ok: true,
78196
+ lease: {
78197
+ record,
78198
+ slot,
78199
+ file,
78200
+ waitedOn,
78201
+ waitedMs,
78202
+ reclaimed,
78203
+ release
78204
+ }
78205
+ };
78030
78206
  }
78031
- };
78032
- }
78033
- const waited = Date.now() - started;
78034
- if (!waitedOn.length) {
78035
- waitedOn = outcome.holders;
78036
- options.onWait?.(outcome.holders);
78207
+ }
78208
+ const holders = liveHolders(options);
78209
+ if (!waitedOn.length) {
78210
+ waitedOn = holders;
78211
+ options.onWait?.(holders);
78212
+ }
78213
+ if (waited >= options.waitMs)
78214
+ return {
78215
+ ok: false,
78216
+ reason: "timeout",
78217
+ holders,
78218
+ waitedMs: waited,
78219
+ position: ahead + 1
78220
+ };
78221
+ if (!queued) {
78222
+ writeTicket(ticket, ticketRecord);
78223
+ queued = true;
78224
+ process.on("exit", leave);
78225
+ process.on("SIGINT", onSignal);
78226
+ process.on("SIGTERM", onSignal);
78227
+ } else if (!fs24.existsSync(ticket)) {
78228
+ writeTicket(ticket, ticketRecord);
78229
+ }
78230
+ if (waited >= nextStatus) {
78231
+ options.onStatus?.({
78232
+ position: ahead + 1,
78233
+ ahead,
78234
+ holders,
78235
+ waitedMs: waited
78236
+ });
78237
+ nextStatus = waited + (options.statusEveryMs ?? 30000);
78238
+ }
78239
+ await clock.sleep(Math.min(options.pollMs ?? 2000, Math.max(1, options.waitMs - waited)));
78037
78240
  }
78038
- if (waited >= options.waitMs)
78039
- return { ok: false, reason: "timeout", holders: outcome.holders, waitedMs: waited };
78040
- await sleep(Math.min(options.pollMs ?? 2000, Math.max(1, options.waitMs - waited)));
78241
+ } catch (error) {
78242
+ return {
78243
+ ok: false,
78244
+ reason: "unavailable",
78245
+ error: error.message
78246
+ };
78247
+ } finally {
78248
+ leave();
78041
78249
  }
78042
78250
  }
78043
78251
  function updateLease(lease, patch) {
@@ -78150,7 +78358,7 @@ function leaseSlots(dir, slots, probe = {}) {
78150
78358
  }
78151
78359
  return states;
78152
78360
  }
78153
- var DEFAULT_HEAVY_CHECKS, DEFAULT_SLOTS = 1, DEFAULT_WAIT_S = 600, DEFAULT_MEMORY_SHARE = 0.4, DEFAULT_CPU_SHARE = 0.7, MAX_SLOTS = 64, UNREADABLE_TTL_MS = 30000, GUARD_TTL_MS = 30000, hostShape = () => ({
78361
+ var DEFAULT_HEAVY_CHECKS, DEFAULT_LIGHT_CHECKS, DEFAULT_SLOTS = 1, DEFAULT_WAIT_S = 1800, DEFAULT_MEMORY_SHARE = 0.4, DEFAULT_CPU_SHARE = 0.7, MAX_SLOTS = 64, UNREADABLE_TTL_MS = 30000, GUARD_TTL_MS = 30000, hostShape = () => ({
78154
78362
  totalMemBytes: os20.totalmem(),
78155
78363
  cpus: Math.max(1, os20.availableParallelism?.() ?? os20.cpus().length)
78156
78364
  }), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path52.join(stateDir2(env), "leases"), readText5 = (file) => {
@@ -78184,16 +78392,22 @@ var DEFAULT_HEAVY_CHECKS, DEFAULT_SLOTS = 1, DEFAULT_WAIT_S = 600, DEFAULT_MEMOR
78184
78392
  cwd: lease.cwd,
78185
78393
  session: lease.session,
78186
78394
  startedAt: lease.startedAt
78187
- }), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), SCOPE_START_FAILURE, NOTICE_TTL_MS;
78395
+ }), realClock, queueDir = (dir) => path52.join(dir, "queue"), TICKET, ticketName = (now, id) => `${String(now).padStart(15, "0")}-${String(process.hrtime.bigint() % 10n ** 16n).padStart(16, "0")}-${id}.json`, SCOPE_START_FAILURE, NOTICE_TTL_MS;
78188
78396
  var init_resources = __esm(() => {
78189
78397
  init_checks3();
78190
78398
  init_heavy();
78191
78399
  init_heavy_scope();
78192
- DEFAULT_HEAVY_CHECKS = ["test", "build", "typecheck"];
78400
+ DEFAULT_HEAVY_CHECKS = ["test", "build"];
78401
+ DEFAULT_LIGHT_CHECKS = ["lint", "typecheck", "format"];
78193
78402
  SIZE = /^(\d+(?:\.\d+)?)([KMGT]?)$/iu;
78194
78403
  PERCENT = /^(\d+(?:\.\d+)?)%$/u;
78195
78404
  UNITS = { "": 1, K: 1024, M: 1024 ** 2, G: 1024 ** 3, T: 1024 ** 4 };
78196
78405
  SCOPE_UNIT = /^[\w.@-]+\.scope$/u;
78406
+ realClock = {
78407
+ now: () => Date.now(),
78408
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
78409
+ };
78410
+ TICKET = /^\d{15}-\d{16}-[\w-]+\.json$/u;
78197
78411
  SCOPE_START_FAILURE = /^Failed to (?:connect to bus|create bus connection|start transient scope unit|parse \S+?=)/u;
78198
78412
  NOTICE_TTL_MS = 7 * 86400000;
78199
78413
  });
@@ -78204,7 +78418,6 @@ __export(exports_check, {
78204
78418
  OUTPUT_LINES: () => OUTPUT_LINES,
78205
78419
  boundedOutput: () => boundedOutput,
78206
78420
  countWarnings: () => countWarnings,
78207
- hostToolWaitCapMs: () => hostToolWaitCapMs,
78208
78421
  run: () => run12,
78209
78422
  workspaceResources: () => workspaceResources
78210
78423
  });
@@ -78227,6 +78440,7 @@ function parse6(argv) {
78227
78440
  positionals: [],
78228
78441
  command: split >= 0 ? argv.slice(split + 1) : null
78229
78442
  };
78443
+ let noWait = false;
78230
78444
  for (let index = 0;index < head.length; index += 1) {
78231
78445
  const arg = head[index];
78232
78446
  const [flag, inline] = arg.startsWith("--") && arg.includes("=") ? arg.split(/=(.*)/su) : [arg];
@@ -78250,6 +78464,13 @@ function parse6(argv) {
78250
78464
  options.noLease = true;
78251
78465
  continue;
78252
78466
  }
78467
+ if (flag === "--no-wait") {
78468
+ if (options.waitMs !== null && !noWait)
78469
+ return new Error("--no-wait goes without --wait");
78470
+ noWait = true;
78471
+ options.waitMs = 0;
78472
+ continue;
78473
+ }
78253
78474
  if (flag === "--name" || flag === "--task" || flag === "--base" || flag === "--timeout" || flag === "--wait" || flag === "--reason") {
78254
78475
  const got = value();
78255
78476
  if (got instanceof Error)
@@ -78259,10 +78480,12 @@ function parse6(argv) {
78259
78480
  else if (flag === "--reason")
78260
78481
  options.reason = got.trim() || null;
78261
78482
  else if (flag === "--wait") {
78262
- const seconds = Number(got);
78263
- if (!Number.isFinite(seconds) || seconds < 0)
78264
- return new Error("--wait must be a number of seconds");
78265
- options.waitMs = Math.round(seconds * 1000);
78483
+ if (noWait)
78484
+ return new Error("--no-wait goes without --wait");
78485
+ const waitMs = parseWait(got);
78486
+ if (waitMs === null)
78487
+ return new Error('--wait must be seconds or a duration ("90", "90s", "30m", "1h")');
78488
+ options.waitMs = waitMs;
78266
78489
  } else if (flag === "--task")
78267
78490
  options.task = got;
78268
78491
  else if (flag === "--base")
@@ -78392,22 +78615,30 @@ function countWarnings(log) {
78392
78615
  return redactLog(log).replace(ANSI_COLOR, "").split(`
78393
78616
  `).filter((line) => WARNING_LINE.test(line) && !NO_WARNINGS.test(line)).length;
78394
78617
  }
78395
- function boundedOutput(log, lines) {
78618
+ function boundedOutput(log, lines, stderr = "") {
78396
78619
  const all = tailLines(log, Number.MAX_SAFE_INTEGER);
78397
78620
  if (all.length === 0)
78398
78621
  return "";
78399
- const shown = all.slice(-lines);
78622
+ let shown = all.slice(-lines);
78623
+ const errors = tailLines(redactLog(stderr), STDERR_TAIL_LINES);
78624
+ let missing = [];
78625
+ if (errors.length) {
78626
+ const tail = new Set(shown);
78627
+ if (errors.some((line) => !tail.has(line))) {
78628
+ shown = all.slice(-Math.max(1, lines - errors.length - 2));
78629
+ const kept = new Set(shown);
78630
+ missing = errors.filter((line) => !kept.has(line));
78631
+ }
78632
+ }
78400
78633
  const omitted = all.length - shown.length;
78634
+ const footer = missing.length ? `… the last stderr lines, which the merged log holds earlier:
78635
+ ${missing.join(`
78636
+ `)}
78637
+ ` : "";
78401
78638
  return `${omitted ? `… ${omitted} earlier line${omitted === 1 ? "" : "s"} omitted (the full log is named below; --full streams it)
78402
78639
  ` : ""}${shown.join(`
78403
78640
  `)}
78404
- `;
78405
- }
78406
- function hostToolWaitCapMs(env) {
78407
- if (!env.CLAUDECODE && !env.OPENCODE && !env.OPENCODE_SESSION_ID)
78408
- return null;
78409
- const override = Number(env.WORKIT_HOST_WAIT_CAP_MS);
78410
- return Number.isFinite(override) && override >= 0 && env.WORKIT_HOST_WAIT_CAP_MS ? override : HOST_TOOL_WAIT_CAP_MS;
78641
+ ${footer}`;
78411
78642
  }
78412
78643
  async function workspaceResources(io) {
78413
78644
  const env = io.env;
@@ -78441,14 +78672,26 @@ async function prepareHeavy(io, settings, label, runCwd, timeoutMs) {
78441
78672
  waitMs: settings.waitMs,
78442
78673
  pollMs: 1000,
78443
78674
  maxAgeMs: (timeoutMs || MAX_UNVERIFIED_RUN_MS) + 60000,
78444
- onWait: (holders) => io.stderr(`workit: waiting for the heavy-check lease (${settings.slots} slot${settings.slots === 1 ? "" : "s"}): held by ${holders.map((holder) => describeHolder(holder)).join("; ")}
78445
- `)
78675
+ onStatus: (status) => {
78676
+ const slots = `${settings.slots} slot${settings.slots === 1 ? "" : "s"}`;
78677
+ const holders = status.holders.map((holder) => describeHolder(holder)).join("; ");
78678
+ io.stderr(status.waitedMs === 0 ? `workit: waiting for the heavy-check lease (${slots}): held by ${holders || "nobody; waiters ahead"}; queued at position ${status.position}, waiting up to ${formatWait(settings.waitMs)} (--wait <dur> sets it)
78679
+ ` : `workit: still queued for the heavy-check lease: position ${status.position}, ${formatWait(status.waitedMs)} of ${formatWait(settings.waitMs)} waited; held by ${holders || "nobody; waiters ahead"}
78680
+ `);
78681
+ }
78446
78682
  });
78447
78683
  if (!acquired.ok && acquired.reason === "timeout") {
78448
78684
  const holders = acquired.holders.map((holder) => describeHolder(holder)).join("; ");
78449
- emit(io, fail2("busy", `the heavy-check lease stayed busy for ${Math.round(acquired.waitedMs / 1000)} s`, {
78450
- data: { lease: { waitedMs: acquired.waitedMs, holders: acquired.holders } },
78451
- unblock: `wait for ${holders || "the holders"} to finish, then run it again`
78685
+ const waited = formatWait(acquired.waitedMs);
78686
+ emit(io, fail2("busy", settings.waitMs === 0 ? `the heavy-check lease is busy (--no-wait): held by ${holders || "nobody; waiters ahead"}` : `the heavy-check lease stayed busy for ${waited} (position ${acquired.position} in the queue)`, {
78687
+ data: {
78688
+ lease: {
78689
+ waitedMs: acquired.waitedMs,
78690
+ holders: acquired.holders,
78691
+ position: acquired.position
78692
+ }
78693
+ },
78694
+ unblock: `wait for ${holders || "the waiters ahead"} to finish (each holder's age is shown; a full test suite takes minutes), then run it again, or queue longer with --wait 30m`
78452
78695
  }));
78453
78696
  return 4;
78454
78697
  }
@@ -78548,11 +78791,8 @@ async function run12(argv, io) {
78548
78791
  { label: "workspaces.json", trust: "machine", value: workspace.value },
78549
78792
  { label: CHECKS_FILE, trust: "repo", value: config.resources }
78550
78793
  ]);
78551
- const cap = hostToolWaitCapMs(io.env);
78552
78794
  if (options.waitMs !== null)
78553
78795
  settings.waitMs = options.waitMs;
78554
- else if (cap !== null)
78555
- settings.waitMs = Math.min(settings.waitMs, cap);
78556
78796
  for (const warning of [workspace.warning, ...warnings])
78557
78797
  if (warning)
78558
78798
  io.stderr(`workit: resources: ${warning}
@@ -78569,6 +78809,7 @@ async function run12(argv, io) {
78569
78809
  `);
78570
78810
  const lease = heavy?.lease ?? null;
78571
78811
  let result;
78812
+ let stderrTail = "";
78572
78813
  try {
78573
78814
  const scoped = await runScoped((wrapper, sinks) => runCheckCommand(command, {
78574
78815
  cwd: runCwd,
@@ -78578,7 +78819,12 @@ async function run12(argv, io) {
78578
78819
  ...sinks,
78579
78820
  ...wrapper ? { wrapper } : {},
78580
78821
  ...lease ? { onSpawn: (pid) => updateLease(lease, { pgid: pid }) } : {}
78581
- }), heavy?.wrapper ?? null, options.full ? { onStdout: io.json ? io.stderr : io.stdout, onStderr: io.stderr } : { onStdout: () => {}, onStderr: () => {} });
78822
+ }), heavy?.wrapper ?? null, options.full ? { onStdout: io.json ? io.stderr : io.stdout, onStderr: io.stderr } : {
78823
+ onStdout: () => {},
78824
+ onStderr: (text) => {
78825
+ stderrTail = (stderrTail + text).slice(-STDERR_KEEP_CHARS);
78826
+ }
78827
+ });
78582
78828
  result = scoped.result;
78583
78829
  if (heavy && scoped.fallback) {
78584
78830
  io.stderr(`workit: the memory/CPU scope could not start (${scoped.fallback}); ran without it
@@ -78690,7 +78936,7 @@ async function run12(argv, io) {
78690
78936
  `);
78691
78937
  else {
78692
78938
  if (!options.full)
78693
- io.stdout(boundedOutput(result.log, OUTPUT_LINES));
78939
+ io.stdout(boundedOutput(result.log, OUTPUT_LINES, stderrTail));
78694
78940
  io.stderr(`${summary}
78695
78941
  `);
78696
78942
  if (recordError && passed)
@@ -78707,7 +78953,7 @@ async function run12(argv, io) {
78707
78953
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
78708
78954
  return 0;
78709
78955
  }
78710
- var USAGE6 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path53.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, ANSI_COLOR, WARNING_LINE, NO_WARNINGS, HOST_TOOL_WAIT_CAP_MS = 1e5;
78956
+ var USAGE6 = 'workit check <name> [--full] [--wait <dur> | --no-wait] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <dur> | --no-wait] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path53.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, ANSI_COLOR, WARNING_LINE, NO_WARNINGS, STDERR_TAIL_LINES = 10, STDERR_KEEP_CHARS = 65536;
78711
78957
  var init_check = __esm(() => {
78712
78958
  init_check_config();
78713
78959
  init_checks3();
@@ -78745,25 +78991,35 @@ async function settingsFor(io) {
78745
78991
  { label: CHECKS_FILE, trust: "repo", value: config.resources }
78746
78992
  ]).settings;
78747
78993
  }
78748
- function slotLines(slots) {
78994
+ function slotLines(slots, queue = []) {
78749
78995
  const busy = held(slots).length;
78996
+ const waiting = queue.filter((entry) => !entry.stale);
78750
78997
  return [
78751
78998
  `heavy-check lease: ${busy} of ${slots.length} slot${slots.length === 1 ? "" : "s"} held`,
78752
- ...slots.map((slot) => slot.holder ? ` slot ${slot.slot}: ${describeHolder(slot.holder)}${slot.stale ? ` (stale: ${slot.stale}; the next heavy check reclaims it)` : ""}` : ` slot ${slot.slot}: free`)
78999
+ ...slots.map((slot) => slot.holder ? ` slot ${slot.slot}: ${describeHolder(slot.holder)}${slot.stale ? ` (stale: ${slot.stale}; the next heavy check reclaims it)` : ""}` : ` slot ${slot.slot}: free`),
79000
+ `queue: ${waiting.length ? `${waiting.length} waiting, first come, first served` : "empty"}`,
79001
+ ...waiting.map((entry, index) => ` ${index + 1}. ${describeHolder(entry.holder)}`)
78753
79002
  ];
78754
79003
  }
78755
79004
  async function status(io) {
78756
79005
  const settings = await settingsFor(io);
78757
79006
  const slots = leaseSlots(leaseDir(io.env), settings.slots);
78758
- return emit(io, ok2({ dir: leaseDir(io.env), slots, waitMs: settings.waitMs }), (data) => slotLines(data.slots));
79007
+ const queue = leaseQueue(leaseDir(io.env));
79008
+ const waiting = queue.filter((entry) => !entry.stale).map((entry) => entry.holder);
79009
+ return emit(io, ok2({
79010
+ dir: leaseDir(io.env),
79011
+ slots,
79012
+ queue: waiting,
79013
+ waitMs: settings.waitMs,
79014
+ light: settings.light
79015
+ }), () => slotLines(slots, queue));
78759
79016
  }
78760
79017
  function timeoutOf(args) {
78761
79018
  const at = args.findIndex((arg) => arg === "--timeout" || arg.startsWith("--timeout="));
78762
79019
  if (at < 0)
78763
79020
  return null;
78764
79021
  const raw = args[at].includes("=") ? args[at].split("=")[1] : args[at + 1];
78765
- const seconds = Number(raw);
78766
- return Number.isFinite(seconds) && seconds >= 0 ? Math.round(seconds * 1000) : new Error("--timeout must be a number of seconds");
79022
+ return parseWait(raw ?? "") ?? new Error('--timeout must be seconds or a duration ("90", "90s", "30m", "1h")');
78767
79023
  }
78768
79024
  async function wait(args, io) {
78769
79025
  const timeout = timeoutOf(args);
@@ -78780,10 +79036,10 @@ async function wait(args, io) {
78780
79036
  const waitedMs = Date.now() - started;
78781
79037
  if (busy.length < slots.length)
78782
79038
  return emit(io, ok2({ free: true, waitedMs, slots }), () => [
78783
- `workit: the heavy-check lease has a free slot${waitedMs ? ` after ${Math.round(waitedMs / 1000)} s` : ""}`
79039
+ `workit: the heavy-check lease has a free slot${waitedMs ? ` after ${formatWait(waitedMs)}` : ""}`
78784
79040
  ]);
78785
79041
  if (waitedMs >= limit)
78786
- return emit(io, fail2("busy", `the heavy-check lease stayed busy for ${Math.round(waitedMs / 1000)} s`, {
79042
+ return emit(io, fail2("busy", `the heavy-check lease stayed busy for ${formatWait(waitedMs)}`, {
78787
79043
  data: { free: false, waitedMs, slots },
78788
79044
  unblock: "workit lock status"
78789
79045
  }));
@@ -78792,16 +79048,16 @@ async function wait(args, io) {
78792
79048
  io.stderr(`workit: waiting for the heavy-check lease: held by ${busy.flatMap((slot) => slot.holder ? [describeHolder(slot.holder)] : []).join("; ")}
78793
79049
  `);
78794
79050
  }
78795
- await sleep2(Math.min(1000, Math.max(1, limit - waitedMs)));
79051
+ await sleep(Math.min(1000, Math.max(1, limit - waitedMs)));
78796
79052
  }
78797
79053
  }
78798
79054
  async function runHeavy(args, io) {
78799
79055
  const split = args.indexOf("--");
78800
79056
  const head = split < 0 ? args : args.slice(0, split);
78801
- const unknown = head.find((arg, index) => arg !== "--shell" && arg !== "--json" && !arg.startsWith("--timeout") && !arg.startsWith("--wait") && !((head[index - 1] === "--timeout" || head[index - 1] === "--wait") && !arg.startsWith("-")));
79057
+ const unknown = head.find((arg, index) => arg !== "--shell" && arg !== "--json" && arg !== "--no-wait" && !arg.startsWith("--timeout") && !arg.startsWith("--wait") && !((head[index - 1] === "--timeout" || head[index - 1] === "--wait") && !arg.startsWith("-")));
78802
79058
  if (split < 0 || unknown)
78803
79059
  return emit(io, fail2("invalid_input", unknown ? `unexpected ${unknown}` : "no command after --", {
78804
- unblock: "workit lock run [--shell] [--wait <s>] [--timeout <s>] -- <cmd…>"
79060
+ unblock: "workit lock run [--shell] [--wait <dur> | --no-wait] [--timeout <s>] -- <cmd…>"
78805
79061
  }));
78806
79062
  const check = await Promise.resolve().then(() => (init_check(), {}));
78807
79063
  return run12([`--heavy`, ...head, ...args.slice(split)], io);
@@ -78887,7 +79143,7 @@ async function run13(argv, io) {
78887
79143
  return heavy(rest, io);
78888
79144
  return emit(io, fail2("invalid_input", `unknown lock subcommand ${sub ?? "(none)"}`, { unblock: USAGE7 }));
78889
79145
  }
78890
- var USAGE7 = "workit lock status | wait [--timeout <s>] | run [--shell] [--wait <s>] [--timeout <s>] -- <cmd…> | heavy list|add|remove|test", held = (slots) => slots.filter((slot) => slot.holder && !slot.stale), sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), commandText = (args) => {
79146
+ var USAGE7 = "workit lock status | wait [--timeout <dur>] | run [--shell] [--wait <dur> | --no-wait] [--timeout <s>] -- <cmd…> | heavy list|add|remove|test", held = (slots) => slots.filter((slot) => slot.holder && !slot.stale), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), commandText = (args) => {
78891
79147
  const split = args.indexOf("--");
78892
79148
  return (split < 0 ? args : args.slice(split + 1)).join(" ").trim();
78893
79149
  };
@@ -78904,7 +79160,7 @@ var exports_models = {};
78904
79160
  __export(exports_models, {
78905
79161
  run: () => run14
78906
79162
  });
78907
- import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync23, statSync as statSync9 } from "node:fs";
79163
+ import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync24, statSync as statSync9 } from "node:fs";
78908
79164
  import os22 from "node:os";
78909
79165
  import path55 from "node:path";
78910
79166
  async function run14(argv, io) {
@@ -78967,7 +79223,7 @@ var USAGE8 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78967
79223
  const found = new Set;
78968
79224
  let text = "";
78969
79225
  try {
78970
- text = readFileSync23(file, "utf8");
79226
+ text = readFileSync24(file, "utf8");
78971
79227
  } catch {
78972
79228
  return found;
78973
79229
  }
@@ -78998,7 +79254,7 @@ var USAGE8 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78998
79254
  try {
78999
79255
  if (statSync9(meta).mtimeMs < since)
79000
79256
  continue;
79001
- const agentType = JSON.parse(readFileSync23(meta, "utf8")).agentType;
79257
+ const agentType = JSON.parse(readFileSync24(meta, "utf8")).agentType;
79002
79258
  const transcript = meta.replace(/\.meta\.json$/, ".jsonl");
79003
79259
  if (typeof agentType !== "string" || !existsSync24(transcript))
79004
79260
  continue;
@@ -79139,7 +79395,7 @@ var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock)
79139
79395
 
79140
79396
  // packages/workit-core/src/git/ops.ts
79141
79397
  import { spawnSync as spawnSync20 } from "node:child_process";
79142
- import { existsSync as existsSync25, readFileSync as readFileSync24 } from "node:fs";
79398
+ import { existsSync as existsSync25, readFileSync as readFileSync25 } from "node:fs";
79143
79399
  import path56 from "node:path";
79144
79400
  function operationInProgress(cwd) {
79145
79401
  const markers = [
@@ -79164,7 +79420,7 @@ function operationInProgress(cwd) {
79164
79420
  if (index === 0 && existsSync25(path56.join(marker, "amend")) && !conflicted()) {
79165
79421
  let headName = "";
79166
79422
  try {
79167
- headName = readFileSync24(path56.join(marker, "head-name"), "utf8").trim();
79423
+ headName = readFileSync25(path56.join(marker, "head-name"), "utf8").trim();
79168
79424
  } catch {}
79169
79425
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
79170
79426
  return { operation, commitAllowed: branch !== null, branch };
@@ -85860,7 +86116,7 @@ var init_duration = __esm(() => {
85860
86116
  });
85861
86117
 
85862
86118
  // packages/workit-cli/src/verbs/forge-common.ts
85863
- import { readFileSync as readFileSync25 } from "node:fs";
86119
+ import { readFileSync as readFileSync26 } from "node:fs";
85864
86120
  function parseFlags(argv, spec) {
85865
86121
  const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
85866
86122
  for (let index = 0;index < argv.length; index += 1) {
@@ -86015,7 +86271,7 @@ var init_forge_common = __esm(() => {
86015
86271
  forgeDeps = {
86016
86272
  runner: undefined,
86017
86273
  npm: undefined,
86018
- readStdin: () => readFileSync25(0, "utf8"),
86274
+ readStdin: () => readFileSync26(0, "utf8"),
86019
86275
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
86020
86276
  now: () => Date.now()
86021
86277
  };
@@ -86026,12 +86282,12 @@ var exports_pr = {};
86026
86282
  __export(exports_pr, {
86027
86283
  run: () => run15
86028
86284
  });
86029
- import { readFileSync as readFileSync26 } from "node:fs";
86285
+ import { readFileSync as readFileSync27 } from "node:fs";
86030
86286
  import path66 from "node:path";
86031
86287
  function readBodyFile(io, file) {
86032
86288
  try {
86033
86289
  return {
86034
- body: file === "-" ? forgeDeps.readStdin() : readFileSync26(path66.resolve(io.cwd, file), "utf8")
86290
+ body: file === "-" ? forgeDeps.readStdin() : readFileSync27(path66.resolve(io.cwd, file), "utf8")
86035
86291
  };
86036
86292
  } catch {
86037
86293
  return `cannot read --body-file ${file}`;
@@ -86835,7 +87091,7 @@ var exports_git = {};
86835
87091
  __export(exports_git, {
86836
87092
  run: () => run17
86837
87093
  });
86838
- import { readFileSync as readFileSync27 } from "node:fs";
87094
+ import { readFileSync as readFileSync28 } from "node:fs";
86839
87095
  import path67 from "node:path";
86840
87096
  function kindHint(cwd, kind) {
86841
87097
  let kinds = [...BRANCH_KINDS];
@@ -86940,7 +87196,7 @@ function commitMessage(parsed, io) {
86940
87196
 
86941
87197
  `);
86942
87198
  try {
86943
- return file === "-" ? readFileSync27(0, "utf8") : readFileSync27(path67.resolve(io.cwd, file), "utf8");
87199
+ return file === "-" ? readFileSync28(0, "utf8") : readFileSync28(path67.resolve(io.cwd, file), "utf8");
86944
87200
  } catch (error) {
86945
87201
  return {
86946
87202
  error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
@@ -88101,6 +88357,11 @@ async function run19(argv, io) {
88101
88357
  if (!result.ok)
88102
88358
  return forgeFail(io, result);
88103
88359
  const report = result.data;
88360
+ if (report.delivered && expect === "merged") {
88361
+ try {
88362
+ closeDeliveredTasks(new TaskStore(io.cwd), new Date().toISOString().replace(/\.\d{3}Z$/, "Z"));
88363
+ } catch {}
88364
+ }
88104
88365
  if (report.delivered)
88105
88366
  return emit(io, ok2(report), render2);
88106
88367
  const failed = report.observations.filter((entry) => !entry.ok);
@@ -88119,6 +88380,8 @@ var USAGE12 = "workit verify-delivery [push|pr|merge|release] [--expect pushed|p
88119
88380
  ];
88120
88381
  var init_verify_delivery = __esm(() => {
88121
88382
  init_verify();
88383
+ init_task_store();
88384
+ init_task_delivery();
88122
88385
  init_ledger();
88123
88386
  init_output();
88124
88387
  init_forge_common();
@@ -105693,7 +105956,7 @@ var init_rules = __esm(() => {
105693
105956
 
105694
105957
  // packages/workit-cli/src/test-audit/audit.ts
105695
105958
  import { spawnSync as spawnSync29 } from "node:child_process";
105696
- import { existsSync as existsSync26, readdirSync as readdirSync14, readFileSync as readFileSync28, statSync as statSync10 } from "node:fs";
105959
+ import { existsSync as existsSync26, readdirSync as readdirSync14, readFileSync as readFileSync29, statSync as statSync10 } from "node:fs";
105697
105960
  import path71 from "node:path";
105698
105961
  function git7(cwd, args) {
105699
105962
  const run = spawnSync29("git", ["-c", "core.quotepath=off", ...args], {
@@ -105773,7 +106036,7 @@ function auditFiles(files, display) {
105773
106036
  const shown = display(file);
105774
106037
  let parsed;
105775
106038
  try {
105776
- parsed = parseSource(readFileSync28(file, "utf8"), file);
106039
+ parsed = parseSource(readFileSync29(file, "utf8"), file);
105777
106040
  } catch (error) {
105778
106041
  parseErrors.push({
105779
106042
  file: shown,
@@ -105841,14 +106104,14 @@ import {
105841
106104
  mkdirSync as mkdirSync12,
105842
106105
  mkdtempSync as mkdtempSync4,
105843
106106
  readdirSync as readdirSync15,
105844
- readFileSync as readFileSync29,
106107
+ readFileSync as readFileSync30,
105845
106108
  readlinkSync as readlinkSync3,
105846
106109
  realpathSync as realpathSync10,
105847
106110
  renameSync as renameSync7,
105848
106111
  rmSync as rmSync9,
105849
106112
  statSync as statSync11,
105850
106113
  symlinkSync,
105851
- writeFileSync as writeFileSync12,
106114
+ writeFileSync as writeFileSync13,
105852
106115
  copyFileSync as copyFileSync8
105853
106116
  } from "node:fs";
105854
106117
  import os23 from "node:os";
@@ -105950,7 +106213,7 @@ function removeStaleCopies(tmp = os23.tmpdir()) {
105950
106213
  const stat = lstatSync8(dir);
105951
106214
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
105952
106215
  continue;
105953
- const pid = Number(readFileSync29(path72.join(dir, PID_FILE), "utf8"));
106216
+ const pid = Number(readFileSync30(path72.join(dir, PID_FILE), "utf8"));
105954
106217
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
105955
106218
  continue;
105956
106219
  } catch (error) {
@@ -106020,13 +106283,13 @@ function writeInside(copy, file, content) {
106020
106283
  return false;
106021
106284
  }
106022
106285
  const temp = `${target}.workit-mutant-${process.pid}`;
106023
- writeFileSync12(temp, content);
106286
+ writeFileSync13(temp, content);
106024
106287
  renameSync7(temp, target);
106025
106288
  return true;
106026
106289
  }
106027
106290
  function copyWorkingTree(repo) {
106028
106291
  const dir = mkdtempSync4(path72.join(os23.tmpdir(), TEMP_PREFIX));
106029
- writeFileSync12(path72.join(dir, PID_FILE), String(process.pid));
106292
+ writeFileSync13(path72.join(dir, PID_FILE), String(process.pid));
106030
106293
  const tree = path72.join(dir, "tree");
106031
106294
  const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
106032
106295
  if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
@@ -106105,7 +106368,7 @@ async function runMutation(options) {
106105
106368
  const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
106106
106369
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
106107
106370
  try {
106108
- return { file, text: readFileSync29(path72.join(repo, file), "utf8") };
106371
+ return { file, text: readFileSync30(path72.join(repo, file), "utf8") };
106109
106372
  } catch {
106110
106373
  return { file, text: "" };
106111
106374
  }
@@ -106118,7 +106381,7 @@ async function runMutation(options) {
106118
106381
  if (NOT_SOURCE.test(relative) || !existsSync27(absolute))
106119
106382
  continue;
106120
106383
  try {
106121
- candidates.push(...mutantsFor(readFileSync29(absolute, "utf8"), relative, lines));
106384
+ candidates.push(...mutantsFor(readFileSync30(absolute, "utf8"), relative, lines));
106122
106385
  } catch {}
106123
106386
  }
106124
106387
  const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
@@ -106199,7 +106462,7 @@ async function runMutation(options) {
106199
106462
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
106200
106463
  continue;
106201
106464
  }
106202
- const original = readFileSync29(path72.join(copy, mutant.file), "utf8");
106465
+ const original = readFileSync30(path72.join(copy, mutant.file), "utf8");
106203
106466
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
106204
106467
  current = { file: mutant.file, original };
106205
106468
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -106550,7 +106813,7 @@ var exports_youtrack = {};
106550
106813
  __export(exports_youtrack, {
106551
106814
  run: () => run25
106552
106815
  });
106553
- import { readFileSync as readFileSync30 } from "node:fs";
106816
+ import { readFileSync as readFileSync31 } from "node:fs";
106554
106817
  import path74 from "node:path";
106555
106818
  async function run25(argv, io) {
106556
106819
  const [sub, ...rest] = argv;
@@ -106597,7 +106860,7 @@ async function run25(argv, io) {
106597
106860
  let markdown = flags.values.markdown ?? "";
106598
106861
  if (flags.values.file)
106599
106862
  try {
106600
- markdown = readFileSync30(path74.resolve(io.cwd, flags.values.file), "utf8");
106863
+ markdown = readFileSync31(path74.resolve(io.cwd, flags.values.file), "utf8");
106601
106864
  } catch (error) {
106602
106865
  return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE18);
106603
106866
  }
@@ -106665,7 +106928,7 @@ var exports_changelog = {};
106665
106928
  __export(exports_changelog, {
106666
106929
  run: () => run26
106667
106930
  });
106668
- import { readFileSync as readFileSync31 } from "node:fs";
106931
+ import { readFileSync as readFileSync32 } from "node:fs";
106669
106932
  import path75 from "node:path";
106670
106933
  async function run26(argv, io) {
106671
106934
  const [sub, ...rest] = argv;
@@ -106712,7 +106975,7 @@ async function run26(argv, io) {
106712
106975
  ]);
106713
106976
  }
106714
106977
  var USAGE19 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
106715
- const text = raw === "-" ? readFileSync31(0, "utf8") : raw.startsWith("@") ? readFileSync31(path75.resolve(cwd, raw.slice(1)), "utf8") : raw;
106978
+ const text = raw === "-" ? readFileSync32(0, "utf8") : raw.startsWith("@") ? readFileSync32(path75.resolve(cwd, raw.slice(1)), "utf8") : raw;
106716
106979
  return JSON.parse(text);
106717
106980
  };
106718
106981
  var init_changelog2 = __esm(() => {
@@ -106955,9 +107218,10 @@ function withSubcommands(entry, options = {}) {
106955
107218
  };
106956
107219
  }
106957
107220
  var LEASE_NOTES = [
106958
- "The heavy-check lease: heavy work (tests, builds, lint, typecheck, installs, image builds) shares one machine-wide lease, so parallel agents and worktrees never exhaust this machine's memory and CPU. `slots` (default 1) heavy checks run at once; on Linux each also runs in a systemd scope with MemoryMax and CPUQuota.",
106959
- "Waiting: a heavy check that finds every slot held prints one `waiting for the heavy-check lease` line naming the holder (check, pid, session, age, directory), polls once a second, and exits 4 (busy) after `--wait <s>`, else `resources.wait` seconds (default 600, capped at 100 inside a Claude Code or OpenCode shell). `workit lock status` shows the holders; `workit lock wait` waits for a free slot.",
106960
- "--heavy: an ad-hoc `workit check -- <cmd>` takes the lease only with --heavy; the named checks test, build and typecheck always do. The shell hook runs a raw heavy command (`npx jest`, `npm run test:ci`, `cargo test`, `docker build`) as `workit check --heavy -- <cmd>` on Claude Code, OpenCode and Pi, and denies it with that exact command on Codex and Cursor.",
107221
+ "The heavy-check lease: heavy work (tests, builds, installs, image builds) shares one machine-wide lease, so parallel agents and worktrees never exhaust this machine's memory and CPU. `slots` (default 1) heavy checks run at once; on Linux each also runs in a systemd scope with MemoryMax and CPUQuota.",
107222
+ "Queueing: a heavy check that finds every slot held joins a first-come, first-served queue and prints `waiting for the heavy-check lease` naming the holder (check, pid, session, age, directory) and its position, then a status line every 30 s. It waits `--wait <dur>` (90, 90s, 30m, 1h), else `resources.wait` (default 30 min), and exits 4 (busy) only past that wait or at once with --no-wait. A waiter that is killed gives up its place. A wait longer than your shell tool's timeout belongs in a background call. `workit lock status` shows the holders and the queue.",
107223
+ "Light checks: the named checks lint, typecheck and format (`resources.light`, set in workspaces.json) never take the lease, so they run at once beside a long suite.",
107224
+ "--heavy: an ad-hoc `workit check -- <cmd>` takes the lease only with --heavy; the named checks test and build always do. The shell hook runs a raw heavy command (`npx jest`, `npm run test:ci`, `cargo test`, `docker build`) as `workit check --heavy -- <cmd>` on Claude Code, OpenCode and Pi, and denies it with that exact command on Codex and Cursor.",
106961
107225
  '--no-lease skips the lease and needs --reason "<why>". Never hand-roll a flock: the lease already queues every agent on the machine.',
106962
107226
  "Output: a check prints the last 60 lines of its output and one summary line with `log: <path>` to the full log; --full streams all of it."
106963
107227
  ];
@@ -107074,7 +107338,7 @@ var VERBS = [
107074
107338
  {
107075
107339
  name: "check",
107076
107340
  group: "delivery",
107077
- usage: 'workit check <name> [--full] [--task <id>] [--base <ref>] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--full] [--timeout <s>] [--task <id>] [--base <ref>] [--heavy [--wait <s>]] [--no-lease --reason "<why>"] -- <cmd…>',
107341
+ usage: 'workit check <name> [--full] [--task <id>] [--base <ref>] [--wait <dur> | --no-wait] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--full] [--timeout <s>] [--task <id>] [--base <ref>] [--heavy [--wait <dur> | --no-wait]] [--no-lease --reason "<why>"] -- <cmd…>',
107078
107342
  flagHints: {
107079
107343
  list: "workit check <name> runs a configured check; a name that is not configured prints the valid ones (workit check test|lint|typecheck|check)"
107080
107344
  },
@@ -107082,6 +107346,7 @@ var VERBS = [
107082
107346
  examples: [
107083
107347
  "workit check test",
107084
107348
  "workit check --heavy -- npx jest",
107349
+ "workit check test --wait 45m",
107085
107350
  "workit check --name unit -- bun test test/unit",
107086
107351
  'workit check test --no-lease --reason "machine is idle, debugging a hang"'
107087
107352
  ],
@@ -107096,16 +107361,16 @@ var VERBS = [
107096
107361
  {
107097
107362
  name: "status",
107098
107363
  usage: "workit lock status",
107099
- summary: "Show each lease slot: free, held (check, pid, session, age, directory) or stale"
107364
+ summary: "Show each lease slot (free, held by check, pid, session, age, directory, or stale) and the queue of waiting checks in arrival order"
107100
107365
  },
107101
107366
  {
107102
107367
  name: "wait",
107103
- usage: "workit lock wait [--timeout <s>]",
107368
+ usage: "workit lock wait [--timeout <dur>]",
107104
107369
  summary: "Wait until a lease slot is free, without taking it (exit 0 free, 4 still busy at the timeout)"
107105
107370
  },
107106
107371
  {
107107
107372
  name: "run",
107108
- usage: "workit lock run [--shell] [--wait <s>] [--timeout <s>] -- <cmd…>",
107373
+ usage: "workit lock run [--shell] [--wait <dur> | --no-wait] [--timeout <s>] -- <cmd…>",
107109
107374
  summary: "Run a command under the lease: the same as workit check --heavy -- <cmd…>"
107110
107375
  },
107111
107376
  {