@brainervirus/workit-claude-code 8.40.8 → 8.41.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,7 +1,7 @@
1
1
  {
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
- "version": "8.40.8",
4
+ "version": "8.41.0",
5
5
  "description": "Workflow rails for agentic coding: task context on every session, branch policy on shell commands, and the workit method skills",
6
6
  "author": {
7
7
  "name": "BrainerVirus"
package/dist/workit.js CHANGED
@@ -9417,7 +9417,7 @@ var package_default;
9417
9417
  var init_package = __esm(() => {
9418
9418
  package_default = {
9419
9419
  name: "@brainervirus/workit-cli",
9420
- version: "8.40.8",
9420
+ version: "8.41.0",
9421
9421
  private: false,
9422
9422
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
9423
9423
  keywords: [
@@ -9455,13 +9455,13 @@ var init_package = __esm(() => {
9455
9455
  },
9456
9456
  dependencies: {
9457
9457
  "@babel/parser": "7.29.9",
9458
- "@brainervirus/workit-cursor": "^8.40.8",
9458
+ "@brainervirus/workit-cursor": "^8.41.0",
9459
9459
  "@inkjs/ui": "2.0.0",
9460
9460
  ink: "7.1.1",
9461
9461
  react: "19.2.8"
9462
9462
  },
9463
9463
  devDependencies: {
9464
- "@brainervirus/workit-core": "^8.40.8"
9464
+ "@brainervirus/workit-core": "^8.41.0"
9465
9465
  },
9466
9466
  engines: {
9467
9467
  node: ">=24"
@@ -75218,7 +75218,124 @@ var init_setup2 = __esm(() => {
75218
75218
  ALL_HOSTS = ["claude-code", "codex", "cursor", "opencode", "pi"];
75219
75219
  });
75220
75220
 
75221
+ // packages/workit-cli/src/progress.ts
75222
+ function createProgress(options = {}) {
75223
+ const env = options.env ?? process.env;
75224
+ const mode = modeFor(options, env);
75225
+ const stream = options.stream;
75226
+ const now = options.now ?? Date.now;
75227
+ const every = options.setInterval ?? ((tick, ms) => setInterval(tick, ms));
75228
+ const stop = options.clearInterval ?? ((timer) => clearInterval(timer));
75229
+ const proc = options.proc ?? process;
75230
+ const ascii = env.TERM === "dumb";
75231
+ const glyphs = mode === "spinner" ? { done: "\x1B[32m✓\x1B[0m", failed: "\x1B[31m✗\x1B[0m", skipped: "–" } : ascii ? { done: "ok", failed: "x", skipped: "-" } : { done: "✓", failed: "✗", skipped: "–" };
75232
+ const start = (initial) => {
75233
+ if (mode === "off" || !stream)
75234
+ return NOOP_STEP;
75235
+ let text = initial;
75236
+ let finished = false;
75237
+ const began = now();
75238
+ let frame = 0;
75239
+ const draw = () => {
75240
+ const seconds = Math.floor((now() - began) / 1000);
75241
+ let line = `${FRAMES[frame % FRAMES.length]} ${text} (${seconds}s)`;
75242
+ if (stream.columns && line.length >= stream.columns)
75243
+ line = `${line.slice(0, Math.max(1, stream.columns - 2))}…`;
75244
+ stream.write(`${CLEAR}${line}`);
75245
+ };
75246
+ let timer;
75247
+ if (mode === "spinner") {
75248
+ draw();
75249
+ timer = every(() => {
75250
+ frame += 1;
75251
+ draw();
75252
+ }, FRAME_MS);
75253
+ }
75254
+ const handlers = [];
75255
+ const release = () => {
75256
+ for (const [event, handler] of handlers)
75257
+ proc.off(event, handler);
75258
+ handlers.length = 0;
75259
+ };
75260
+ if (mode === "spinner") {
75261
+ const onSignal = (signal) => () => {
75262
+ if (!finished) {
75263
+ finished = true;
75264
+ if (timer !== undefined)
75265
+ stop(timer);
75266
+ stream.write(CLEAR);
75267
+ }
75268
+ release();
75269
+ proc.kill(proc.pid, signal);
75270
+ };
75271
+ const onExit = () => {
75272
+ if (!finished)
75273
+ stream.write(CLEAR);
75274
+ };
75275
+ handlers.push(["SIGINT", onSignal("SIGINT")], ["SIGTERM", onSignal("SIGTERM")]);
75276
+ handlers.push(["exit", onExit]);
75277
+ for (const [event, handler] of handlers)
75278
+ proc.on(event, handler);
75279
+ }
75280
+ return {
75281
+ label: (next) => {
75282
+ text = next;
75283
+ if (mode === "spinner" && !finished)
75284
+ draw();
75285
+ },
75286
+ end: (status, note) => {
75287
+ if (finished)
75288
+ return;
75289
+ finished = true;
75290
+ if (timer !== undefined)
75291
+ stop(timer);
75292
+ release();
75293
+ stream.write(`${mode === "spinner" ? CLEAR : ""}${glyphs[status]} ${text}${note ? `: ${note}` : ""}
75294
+ `);
75295
+ }
75296
+ };
75297
+ };
75298
+ return {
75299
+ mode,
75300
+ start,
75301
+ async run(label, fn, classify) {
75302
+ const step = start(label);
75303
+ try {
75304
+ const value = await fn();
75305
+ step.end(classify ? classify(value) : "done");
75306
+ return value;
75307
+ } catch (error) {
75308
+ step.end("failed");
75309
+ throw error;
75310
+ }
75311
+ },
75312
+ runSync(label, fn, classify) {
75313
+ const step = start(label);
75314
+ try {
75315
+ const value = fn();
75316
+ step.end(classify ? classify(value) : "done");
75317
+ return value;
75318
+ } catch (error) {
75319
+ step.end("failed");
75320
+ throw error;
75321
+ }
75322
+ }
75323
+ };
75324
+ }
75325
+ var FRAMES, FRAME_MS = 120, CLEAR = "\r\x1B[2K", truthy = (value) => value !== undefined && value !== "" && value !== "0" && value.toLowerCase() !== "false", modeFor = (options, env) => {
75326
+ if (options.json || !options.stream?.isTTY || truthy(env.CI))
75327
+ return "off";
75328
+ if (truthy(env.NO_COLOR) || env.TERM === "dumb")
75329
+ return "plain";
75330
+ return "spinner";
75331
+ }, NOOP_STEP;
75332
+ var init_progress = __esm(() => {
75333
+ FRAMES = ["·", "✢", "✳", "∗", "✻", "✽"];
75334
+ NOOP_STEP = { label: () => {}, end: () => {} };
75335
+ });
75336
+
75221
75337
  // packages/workit-cli/src/upgrade/shared.ts
75338
+ import { spawn } from "node:child_process";
75222
75339
  import { existsSync as existsSync21, readFileSync as readFileSync22 } from "node:fs";
75223
75340
  import path44 from "node:path";
75224
75341
  import os16 from "node:os";
@@ -75279,7 +75396,61 @@ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-o
75279
75396
  }
75280
75397
  const result = runHostCommand(step, { home, env });
75281
75398
  return { status: result.exitCode, stdout: result.stdout };
75282
- }), errorText = (error) => error instanceof Error ? error.message : "installed source could not be read", piInstalledVersion = (listing, selector) => {
75399
+ }), COMMAND_TIMEOUT_MS = 60000, asyncRunner = (deps) => {
75400
+ if (deps.runAsync)
75401
+ return deps.runAsync;
75402
+ const injected = deps.run;
75403
+ if (injected)
75404
+ return async (command, args) => injected(command, args);
75405
+ return (command, args) => {
75406
+ const { home, env } = upgradePaths(deps);
75407
+ let step;
75408
+ try {
75409
+ step = hostCommand(command, args, { home, env });
75410
+ } catch {
75411
+ return Promise.resolve({ status: 1, stdout: "" });
75412
+ }
75413
+ if (process.platform === "win32" && /\.(?:cmd|bat)$/i.test(step.command))
75414
+ return Promise.resolve({ status: 1, stdout: "" });
75415
+ const childEnv = { ...process.env, ...env, HOME: home };
75416
+ childEnv.CODEX_HOME = env.CODEX_HOME ?? path44.join(home, ".codex");
75417
+ childEnv.PI_CODING_AGENT_DIR = env.PI_CODING_AGENT_DIR ?? path44.join(home, ".pi", "agent");
75418
+ childEnv.CLAUDE_CONFIG_DIR = env.CLAUDE_CONFIG_DIR ?? path44.join(home, ".claude");
75419
+ childEnv.XDG_CONFIG_HOME = env.XDG_CONFIG_HOME ?? path44.join(home, ".config");
75420
+ return new Promise((resolve) => {
75421
+ let stdout = "";
75422
+ let settled = false;
75423
+ const finish = (status) => {
75424
+ if (settled)
75425
+ return;
75426
+ settled = true;
75427
+ clearTimeout(timer);
75428
+ resolve({ status, stdout });
75429
+ };
75430
+ let child;
75431
+ try {
75432
+ child = spawn(step.command, step.args, {
75433
+ cwd: step.cwd,
75434
+ env: childEnv,
75435
+ stdio: ["ignore", "pipe", "ignore"],
75436
+ windowsHide: true,
75437
+ shell: false
75438
+ });
75439
+ } catch {
75440
+ resolve({ status: 1, stdout: "" });
75441
+ return;
75442
+ }
75443
+ const timer = setTimeout(() => {
75444
+ child.kill("SIGKILL");
75445
+ finish(1);
75446
+ }, COMMAND_TIMEOUT_MS);
75447
+ child.stdout?.setEncoding("utf8");
75448
+ child.stdout?.on("data", (chunk) => stdout += chunk);
75449
+ child.on("error", () => finish(1));
75450
+ child.on("close", (code) => finish(code ?? 1));
75451
+ });
75452
+ };
75453
+ }, errorText = (error) => error instanceof Error ? error.message : "installed source could not be read", piInstalledVersion = (listing, selector) => {
75283
75454
  const lines = listing.split(/\r?\n/).map((line) => line.trim());
75284
75455
  const index = lines.indexOf(selector);
75285
75456
  if (index < 0 || !lines[index + 1] || !path44.isAbsolute(lines[index + 1]))
@@ -75368,7 +75539,7 @@ var init_processes = __esm(() => {
75368
75539
  import { existsSync as existsSync22, readdirSync as readdirSync12, mkdirSync as mkdirSync11, copyFileSync as copyFileSync6, chmodSync as chmodSync2, cpSync as cpSync5 } from "node:fs";
75369
75540
  import path46 from "node:path";
75370
75541
  import { createHash as createHash16 } from "node:crypto";
75371
- function applyUpgrade(plan, deps = {}) {
75542
+ async function applyUpgrade(plan, deps = {}) {
75372
75543
  const hosts = [...plan.failed];
75373
75544
  const done = (backup) => {
75374
75545
  const failures = hosts.filter((entry) => entry.status === "failed");
@@ -75423,6 +75594,9 @@ function applyUpgrade(plan, deps = {}) {
75423
75594
  `, 384);
75424
75595
  }
75425
75596
  const run = runner(deps);
75597
+ const runAsync = asyncRunner(deps);
75598
+ const progress = deps.progress ?? createProgress({ json: true });
75599
+ const statusOf = (outcome) => outcome?.status === "failed" ? "failed" : outcome?.status === "updated" ? "done" : "skipped";
75426
75600
  let cursorBackupError = null;
75427
75601
  if (plan.entries.some((entry) => entry.host === "cursor") && existsSync22(paths.cursorRoot))
75428
75602
  try {
@@ -75435,96 +75609,104 @@ function applyUpgrade(plan, deps = {}) {
75435
75609
  const listed = plan.entries.length ? listProcesses(deps) : [];
75436
75610
  const running = (listed ?? []).flatMap((entry) => classifyProcess(entry) ?? []);
75437
75611
  for (const entry of plan.entries) {
75438
- if (entry.host === "cursor" && cursorBackupError) {
75439
- hosts.push({
75440
- host: "cursor",
75441
- status: "failed",
75442
- reason: cursorBackupError,
75443
- fix: entry.fix
75444
- });
75445
- continue;
75446
- }
75447
- if (!LIVE_HOSTS.has(entry.host)) {
75448
- if (listed === null) {
75612
+ const before = hosts.length;
75613
+ const step = progress.start(`Updating ${entry.host}`);
75614
+ try {
75615
+ if (entry.host === "cursor" && cursorBackupError) {
75449
75616
  hosts.push({
75450
- host: entry.host,
75451
- status: "skipped",
75452
- reason: `running processes could not be listed; close ${entry.host} and rerun`,
75453
- fix: `workit upgrade --hosts=${entry.host}`
75617
+ host: "cursor",
75618
+ status: "failed",
75619
+ reason: cursorBackupError,
75620
+ fix: entry.fix
75454
75621
  });
75455
75622
  continue;
75456
75623
  }
75457
- const sessions = running.filter((proc) => proc.host === entry.host && proc.kind === "session");
75458
- if (sessions.length) {
75624
+ if (!LIVE_HOSTS.has(entry.host)) {
75625
+ if (listed === null) {
75626
+ hosts.push({
75627
+ host: entry.host,
75628
+ status: "skipped",
75629
+ reason: `running processes could not be listed; close ${entry.host} and rerun`,
75630
+ fix: `workit upgrade --hosts=${entry.host}`
75631
+ });
75632
+ continue;
75633
+ }
75634
+ const sessions = running.filter((proc) => proc.host === entry.host && proc.kind === "session");
75635
+ if (sessions.length) {
75636
+ hosts.push({
75637
+ host: entry.host,
75638
+ status: "skipped",
75639
+ reason: `close ${entry.host} (${sessions.map(describe2).join("; ")}), then rerun`,
75640
+ fix: `workit upgrade --hosts=${entry.host}`
75641
+ });
75642
+ continue;
75643
+ }
75644
+ }
75645
+ const daemons = running.filter((proc) => proc.host === "codex" && proc.kind === "daemon");
75646
+ const stopDaemon = entry.host === "codex" && daemons.length > 0;
75647
+ const stopped = stopDaemon ? await progress.run("Stopping the Codex app-server daemon", () => runAsync("codex", ["app-server", "daemon", "stop"]), (result) => result.status === 0 ? "done" : "failed") : null;
75648
+ if (stopped && stopped.status !== 0) {
75459
75649
  hosts.push({
75460
- host: entry.host,
75461
- status: "skipped",
75462
- reason: `close ${entry.host} (${sessions.map(describe2).join("; ")}), then rerun`,
75463
- fix: `workit upgrade --hosts=${entry.host}`
75650
+ host: "codex",
75651
+ status: "failed",
75652
+ reason: `could not stop the Codex app-server daemon (${daemons.map(describe2).join("; ")})`,
75653
+ fix: "codex app-server daemon stop && workit upgrade --hosts=codex"
75464
75654
  });
75465
75655
  continue;
75466
75656
  }
75467
- }
75468
- const daemons = running.filter((proc) => proc.host === "codex" && proc.kind === "daemon");
75469
- const stopDaemon = entry.host === "codex" && daemons.length > 0;
75470
- if (stopDaemon && run("codex", ["app-server", "daemon", "stop"]).status !== 0) {
75471
- hosts.push({
75472
- host: "codex",
75473
- status: "failed",
75474
- reason: `could not stop the Codex app-server daemon (${daemons.map(describe2).join("; ")})`,
75475
- fix: "codex app-server daemon stop && workit upgrade --hosts=codex"
75476
- });
75477
- continue;
75478
- }
75479
- let outcome;
75480
- try {
75481
- outcome = applyEntry(entry, { run, paths, configDir });
75482
- if (entry.host === "codex" && outcome.status === "updated") {
75483
- const agents = refreshCodexAgents(run, paths.codexHome);
75484
- if (!agents.ok)
75657
+ let outcome;
75658
+ try {
75659
+ outcome = await applyEntry(entry, { run, runAsync, paths, configDir });
75660
+ if (entry.host === "codex" && outcome.status === "updated") {
75661
+ const agents = await refreshCodexAgents(runAsync, paths.codexHome);
75662
+ if (!agents.ok)
75663
+ outcome = {
75664
+ ...outcome,
75665
+ status: "failed",
75666
+ reason: `${outcome.reason}; ${agents.note}`,
75667
+ fix: agents.fix
75668
+ };
75669
+ else if (agents.note)
75670
+ outcome = { ...outcome, reason: `${outcome.reason}; ${agents.note}` };
75671
+ }
75672
+ } catch (error) {
75673
+ outcome = {
75674
+ host: entry.host,
75675
+ status: "failed",
75676
+ reason: errorText(error),
75677
+ fix: entry.fix
75678
+ };
75679
+ }
75680
+ if (stopDaemon) {
75681
+ const started = (await progress.run("Restarting the Codex app-server daemon", () => runAsync("codex", ["app-server", "daemon", "start"]), (result) => result.status === 0 ? "done" : "failed")).status === 0;
75682
+ outcome = started ? {
75683
+ ...outcome,
75684
+ reason: `${outcome.reason}; app-server daemon stopped and started again`
75685
+ } : {
75686
+ ...outcome,
75687
+ status: "failed",
75688
+ reason: `${outcome.reason}; the app-server daemon did not start again`,
75689
+ fix: "codex app-server daemon start"
75690
+ };
75691
+ }
75692
+ if (entry.host === "opencode") {
75693
+ const servers = running.filter((proc) => proc.host === "opencode" && proc.kind === "daemon");
75694
+ if (servers.length && outcome.status === "updated")
75485
75695
  outcome = {
75486
75696
  ...outcome,
75487
- status: "failed",
75488
- reason: `${outcome.reason}; ${agents.note}`,
75489
- fix: agents.fix
75697
+ reason: `${outcome.reason}; restart opencode serve (${servers.map(describe2).join("; ")}) to load it`
75490
75698
  };
75491
- else if (agents.note)
75492
- outcome = { ...outcome, reason: `${outcome.reason}; ${agents.note}` };
75493
75699
  }
75494
- } catch (error) {
75495
- outcome = {
75496
- host: entry.host,
75497
- status: "failed",
75498
- reason: errorText(error),
75499
- fix: entry.fix
75500
- };
75501
- }
75502
- if (stopDaemon) {
75503
- const started = run("codex", ["app-server", "daemon", "start"]).status === 0;
75504
- outcome = started ? {
75505
- ...outcome,
75506
- reason: `${outcome.reason}; app-server daemon stopped and started again`
75507
- } : {
75508
- ...outcome,
75509
- status: "failed",
75510
- reason: `${outcome.reason}; the app-server daemon did not start again`,
75511
- fix: "codex app-server daemon start"
75512
- };
75513
- }
75514
- if (entry.host === "opencode") {
75515
- const servers = running.filter((proc) => proc.host === "opencode" && proc.kind === "daemon");
75516
- if (servers.length && outcome.status === "updated")
75517
- outcome = {
75518
- ...outcome,
75519
- reason: `${outcome.reason}; restart opencode serve (${servers.map(describe2).join("; ")}) to load it`
75520
- };
75700
+ hosts.push(outcome);
75701
+ } finally {
75702
+ const added = hosts.length > before ? hosts[hosts.length - 1] : undefined;
75703
+ step.end(statusOf(added), added && added.status !== "updated" ? added.reason : undefined);
75521
75704
  }
75522
- hosts.push(outcome);
75523
75705
  }
75524
75706
  if (hosts.some((entry) => entry.host === "codex" && entry.status === "current")) {
75525
75707
  let agents;
75526
75708
  try {
75527
- agents = refreshCodexAgents(run, paths.codexHome);
75709
+ agents = await refreshCodexAgents(runAsync, paths.codexHome);
75528
75710
  } catch (error) {
75529
75711
  agents = {
75530
75712
  ok: false,
@@ -75548,7 +75730,7 @@ function applyUpgrade(plan, deps = {}) {
75548
75730
  }
75549
75731
  if (plan.cli)
75550
75732
  try {
75551
- hosts.push(applyCli(plan.cli, run, backup));
75733
+ hosts.push(await progress.run("Updating the CLI", () => applyCli(plan.cli, runAsync, backup), statusOf));
75552
75734
  } catch (error) {
75553
75735
  hosts.push({
75554
75736
  host: "cli",
@@ -75569,8 +75751,8 @@ var codexPluginRoot = (codexHome) => {
75569
75751
  }
75570
75752
  const newest = versions.reduce((best, version) => best && versionAtLeast(best, version) ? best : version, null);
75571
75753
  return newest ? path46.join(dir, newest) : null;
75572
- }, opencodeVersion = (run) => {
75573
- const listing = run("opencode", ["plugin", "list"]);
75754
+ }, opencodeVersion = async (run) => {
75755
+ const listing = await run("opencode", ["plugin", "list"]);
75574
75756
  if (listing.status !== 0)
75575
75757
  return;
75576
75758
  for (const line of listing.stdout.split(/\r?\n/)) {
@@ -75583,8 +75765,8 @@ var codexPluginRoot = (codexHome) => {
75583
75765
  return version;
75584
75766
  }
75585
75767
  return;
75586
- }, applyEntry = (entry, context) => {
75587
- const { run, paths, configDir } = context;
75768
+ }, applyEntry = async (entry, context) => {
75769
+ const { run, runAsync, paths, configDir } = context;
75588
75770
  const { home, env } = paths;
75589
75771
  const fail = (reason) => ({
75590
75772
  host: entry.host,
@@ -75636,12 +75818,12 @@ var codexPluginRoot = (codexHome) => {
75636
75818
  reason: entry.repair ? `reinstalled missing plugin files (${entry.latest})` : `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75637
75819
  };
75638
75820
  }
75639
- const before = entry.host === "opencode" ? opencodeVersion(run) : undefined;
75821
+ const before = entry.host === "opencode" ? await opencodeVersion(runAsync) : undefined;
75640
75822
  for (const command of entry.commands)
75641
- if (run(command.command, command.args).status !== 0)
75823
+ if ((await runAsync(command.command, command.args)).status !== 0)
75642
75824
  return fail(`\`${command.command} ${command.args.join(" ")}\` failed`);
75643
75825
  if (entry.host === "opencode") {
75644
- const after = opencodeVersion(run);
75826
+ const after = await opencodeVersion(runAsync);
75645
75827
  if (before && after)
75646
75828
  return after === before ? {
75647
75829
  host: "opencode",
@@ -75670,7 +75852,7 @@ var codexPluginRoot = (codexHome) => {
75670
75852
  reason: `${entry.installedVersion ?? "?"} -> ${after ?? "?"}; new sessions load it`
75671
75853
  };
75672
75854
  }
75673
- const verify = entry.host === "codex" ? run("codex", ["plugin", "list", "--json"]) : run("pi", ["list"]);
75855
+ const verify = entry.host === "codex" ? await runAsync("codex", ["plugin", "list", "--json"]) : await runAsync("pi", ["list"]);
75674
75856
  if (verify.status !== 0)
75675
75857
  return fail("update verification failed");
75676
75858
  if (entry.host === "codex") {
@@ -75688,28 +75870,28 @@ var codexPluginRoot = (codexHome) => {
75688
75870
  status: "updated",
75689
75871
  reason: entry.repair ? `reinstalled missing plugin files (${entry.latest})` : `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75690
75872
  };
75691
- }, refreshCodexAgents = (run, codexHome) => {
75873
+ }, refreshCodexAgents = async (run, codexHome) => {
75692
75874
  const root = codexPluginRoot(codexHome);
75693
75875
  const launcher = root && path46.join(root, "dist", "launch-mcp.js");
75694
75876
  if (!launcher || !existsSync22(launcher))
75695
75877
  return { ok: true, note: "" };
75696
75878
  const fix = `node ${JSON.stringify(launcher)} --install-agents`;
75697
- return run("node", [launcher, "--install-agents"]).status === 0 ? { ok: true, note: "agents refreshed" } : { ok: false, note: "agent refresh failed", fix };
75698
- }, applyCli = (cli, run, backup) => {
75879
+ return (await run("node", [launcher, "--install-agents"])).status === 0 ? { ok: true, note: "agents refreshed" } : { ok: false, note: "agent refresh failed", fix };
75880
+ }, applyCli = async (cli, run, backup) => {
75699
75881
  const fail = (reason) => ({
75700
75882
  host: "cli",
75701
75883
  status: "failed",
75702
75884
  reason,
75703
75885
  fix: CLI_FIX
75704
75886
  });
75705
- const currentRoot = run("npm", ["root", "--global"]);
75887
+ const currentRoot = await run("npm", ["root", "--global"]);
75706
75888
  if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== cli.root)
75707
75889
  return fail("Global npm location changed; preview again");
75708
75890
  const cliBackup = path46.join(backup, "cli-package");
75709
75891
  cpSync5(path46.join(cli.root, "@brainervirus/workit-cli"), cliBackup, {
75710
75892
  recursive: true
75711
75893
  });
75712
- if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${cli.latest}`]).status !== 0)
75894
+ if ((await run("npm", ["install", "--global", `@brainervirus/workit-cli@${cli.latest}`])).status !== 0)
75713
75895
  return fail(`CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path46.join(cliBackup, "dist/index.js")}`);
75714
75896
  const installed = objectOrNull(path46.join(cli.root, "@brainervirus/workit-cli/package.json"));
75715
75897
  if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== cli.latest)
@@ -75725,6 +75907,7 @@ var init_apply = __esm(() => {
75725
75907
  init_setup_state();
75726
75908
  init_setup();
75727
75909
  init_safe_write();
75910
+ init_progress();
75728
75911
  init_processes();
75729
75912
  init_shared();
75730
75913
  });
@@ -76145,9 +76328,13 @@ async function runUpgradeCommand(argv, deps = {}) {
76145
76328
  const legacy = flags.has("--apply");
76146
76329
  const json = flags.has("--json") || legacy;
76147
76330
  const run = runner(deps);
76148
- const plan = previewUpgrade(raw, deps);
76149
- if (hostFlag === undefined || flags.has("--cli"))
76150
- planCli(plan, run);
76331
+ const progress = deps.progress ?? createProgress({ stream: process.stderr, json });
76332
+ const plan = progress.runSync("Planning the upgrade", () => {
76333
+ const planned = previewUpgrade(raw, deps);
76334
+ if (hostFlag === undefined || flags.has("--cli"))
76335
+ planCli(planned, run);
76336
+ return planned;
76337
+ });
76151
76338
  plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
76152
76339
  const { home, env } = upgradePaths(deps);
76153
76340
  const permissions = planHostPermissions({
@@ -76198,11 +76385,11 @@ Nothing changed.
76198
76385
  for (const warning of plan.warnings ?? [])
76199
76386
  out.write(`warning: ${warning}
76200
76387
  `);
76201
- const result = applyUpgrade(plan, deps);
76388
+ const result = await applyUpgrade(plan, { ...deps, progress });
76202
76389
  let permissionsOk = true;
76203
76390
  let written;
76204
76391
  if (permissions.changes.length && !permissions.refused && !result.error?.startsWith("configuration changed")) {
76205
- written = applyHostPermissions(permissions, { home, env });
76392
+ written = progress.runSync("Refreshing the permission block", () => applyHostPermissions(permissions, { home, env }), (entries) => entries.every((entry) => entry.status === "written") ? "done" : "failed");
76206
76393
  permissionsOk = written.every((entry) => entry.status === "written");
76207
76394
  for (const entry of written)
76208
76395
  result.hosts.push({
@@ -76214,7 +76401,7 @@ Nothing changed.
76214
76401
  }
76215
76402
  const failed = !result.ok || !permissionsOk;
76216
76403
  const cliVersion = result.hosts.some((h) => h.host === "cli" && h.status === "updated") ? plan.cli.latest : package_default.version;
76217
- const report = (deps.doctor ?? ((version) => runDoctor({ home, env, cliVersion: version, only: "upgrade" })))(cliVersion);
76404
+ const report = progress.runSync("Checking the result with workit doctor", () => (deps.doctor ?? ((version) => runDoctor({ home, env, cliVersion: version, only: "upgrade" })))(cliVersion));
76218
76405
  const left = report.checks.filter((check) => check.status === "warn" || check.status === "fail");
76219
76406
  if (json) {
76220
76407
  out.write(JSON.stringify({
@@ -76272,7 +76459,7 @@ async function runLaunchCommand(argv, deps = {}) {
76272
76459
  for (const skipped of plan.skipped)
76273
76460
  (deps.out ?? process.stderr).write(`${skipped.host}: ${skipped.reason}${skipped.fix ? ` (run: ${skipped.fix})` : ""}
76274
76461
  `);
76275
- const result = applyUpgrade(plan, deps);
76462
+ const result = await applyUpgrade(plan, deps);
76276
76463
  if (!result.ok) {
76277
76464
  (deps.out ?? process.stdout).write(`${result.error}
76278
76465
  `);
@@ -76301,6 +76488,7 @@ var init_upgrade = __esm(() => {
76301
76488
  init_host_install();
76302
76489
  init_doctor();
76303
76490
  init_host_permissions();
76491
+ init_progress();
76304
76492
  init_apply();
76305
76493
  init_plan();
76306
76494
  init_report();
@@ -76831,7 +77019,7 @@ var init_redact = __esm(() => {
76831
77019
  });
76832
77020
 
76833
77021
  // packages/workit-core/src/checks.ts
76834
- import { spawn, spawnSync as spawnSync19 } from "node:child_process";
77022
+ import { spawn as spawn2, spawnSync as spawnSync19 } from "node:child_process";
76835
77023
  import { createHash as createHash17 } from "node:crypto";
76836
77024
  import fs21 from "node:fs";
76837
77025
  import os18 from "node:os";
@@ -77060,7 +77248,7 @@ function runCheckCommand(argv, options) {
77060
77248
  shell: plan.shell
77061
77249
  };
77062
77250
  try {
77063
- child = spawn(plan.file, plan.args, spawnOptions);
77251
+ child = spawn2(plan.file, plan.args, spawnOptions);
77064
77252
  } catch (error) {
77065
77253
  finish(null, null, error.code ?? error.message);
77066
77254
  return;
@@ -79078,6 +79266,8 @@ async function prepareHeavy(io, settings, label, runCwd, timeoutMs) {
79078
79266
  wrapper: null,
79079
79267
  env: io.env
79080
79268
  };
79269
+ const progress = createProgress({ stream: process.stderr, json: io.json });
79270
+ let waiting = null;
79081
79271
  const acquired = await acquireLease({
79082
79272
  dir,
79083
79273
  slots: settings.slots,
@@ -79090,11 +79280,20 @@ async function prepareHeavy(io, settings, label, runCwd, timeoutMs) {
79090
79280
  onStatus: (status) => {
79091
79281
  const slots = `${settings.slots} slot${settings.slots === 1 ? "" : "s"}`;
79092
79282
  const holders = status.holders.map((holder) => describeHolder(holder)).join("; ");
79283
+ if (progress.mode === "spinner") {
79284
+ const text = `Waiting for the heavy-check lease: held by ${holders || "nobody; waiters ahead"}, queue position ${status.position}`;
79285
+ if (waiting)
79286
+ waiting.label(text);
79287
+ else
79288
+ waiting = progress.start(text);
79289
+ return;
79290
+ }
79093
79291
  io.stderr(status.first ? `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)
79094
79292
  ` : `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"}
79095
79293
  `);
79096
79294
  }
79097
79295
  });
79296
+ waiting?.end(acquired.ok ? "done" : "failed");
79098
79297
  if (!acquired.ok && acquired.reason === "timeout") {
79099
79298
  const holders = acquired.holders.map((holder) => describeHolder(holder)).join("; ");
79100
79299
  const waited = formatWait(acquired.waitedMs);
@@ -79376,6 +79575,7 @@ var init_check = __esm(() => {
79376
79575
  init_package();
79377
79576
  init_ledger();
79378
79577
  init_output();
79578
+ init_progress();
79379
79579
  init_host_session();
79380
79580
  init_resources();
79381
79581
  MAX_UNVERIFIED_RUN_MS = 6 * 3600000;
@@ -87930,6 +88130,11 @@ async function wait2(argv, io) {
87930
88130
  let verdict = { state: "waiting", reason: "not_polled" };
87931
88131
  let baseContexts = null;
87932
88132
  let expectedMissing = [];
88133
+ const waiting = createProgress({ stream: process.stderr, json: io.json }).start("Waiting for CI");
88134
+ const abort = (...args) => {
88135
+ waiting.end("failed");
88136
+ return forgeFail(io, ...args);
88137
+ };
87933
88138
  for (;; ) {
87934
88139
  const read = resolved.forge.prStatus(number.data);
87935
88140
  polls += 1;
@@ -87939,7 +88144,7 @@ async function wait2(argv, io) {
87939
88144
  status = read.data;
87940
88145
  const doc = buildStatusDoc(io.cwd, resolved, status, { logLines: 0, behind: false });
87941
88146
  if (!doc.ok)
87942
- return forgeFail(io, doc);
88147
+ return abort(doc);
87943
88148
  recordPrStatus(io.cwd, doc.data, actor);
87944
88149
  if (status.requiredContexts === null && baseContexts === null) {
87945
88150
  const base = resolved.forge.branchChecks(status.base);
@@ -87950,12 +88155,14 @@ async function wait2(argv, io) {
87950
88155
  verdict = waitVerdict(doc.data, { head, elapsedMs, expectedMissing });
87951
88156
  if (verdict.state !== "waiting")
87952
88157
  break;
88158
+ const pending = doc.data.checks.pending;
88159
+ waiting.label(`Waiting for CI: ${pending.length} check${pending.length === 1 ? "" : "s"} pending${pending.length ? ` (${pending.slice(0, 3).join(", ")}${pending.length > 3 ? ", …" : ""})` : ""}`);
87953
88160
  } else {
87954
88161
  errors += 1;
87955
88162
  if (read.code !== "unavailable" && read.code !== "failed")
87956
- return forgeFail(io, read);
88163
+ return abort(read);
87957
88164
  if (errors >= MAX_POLL_ERRORS)
87958
- return forgeFail(io, read, { polls });
88165
+ return abort(read, { polls });
87959
88166
  }
87960
88167
  const remaining = deadline - forgeDeps.now();
87961
88168
  if (remaining <= 0)
@@ -87963,6 +88170,7 @@ async function wait2(argv, io) {
87963
88170
  await forgeDeps.sleep(Math.min(pollDelay(intervalMs, polls - 1), remaining));
87964
88171
  }
87965
88172
  const elapsedMs = forgeDeps.now() - started;
88173
+ waiting.end(verdict.state === "ready" ? "done" : verdict.state === "waiting" ? "skipped" : "failed");
87966
88174
  if (!status)
87967
88175
  return emit(io, fail2("unavailable", "no successful poll before the timeout", { data: { polls } }));
87968
88176
  const left = deadline - forgeDeps.now();
@@ -88129,6 +88337,7 @@ var init_ci = __esm(() => {
88129
88337
  init_ledger();
88130
88338
  init_autonomy();
88131
88339
  init_output();
88340
+ init_progress();
88132
88341
  init_forge_common();
88133
88342
  });
88134
88343
 
@@ -107016,7 +107225,7 @@ var init_audit = __esm(() => {
107016
107225
  });
107017
107226
 
107018
107227
  // packages/workit-cli/src/test-audit/mutate.ts
107019
- import { spawn as spawn2 } from "node:child_process";
107228
+ import { spawn as spawn3 } from "node:child_process";
107020
107229
  import {
107021
107230
  existsSync as existsSync29,
107022
107231
  lstatSync as lstatSync9,
@@ -107255,7 +107464,7 @@ function copyWorkingTree(repo) {
107255
107464
  function runCommand(command, cwd, timeoutMs, env) {
107256
107465
  const started = performance.now();
107257
107466
  return new Promise((resolve) => {
107258
- const child = spawn2(command, {
107467
+ const child = spawn3(command, {
107259
107468
  cwd,
107260
107469
  env,
107261
107470
  shell: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-claude-code",
3
- "version": "8.40.8",
3
+ "version": "8.41.0",
4
4
  "private": false,
5
5
  "description": "Workit Claude Code plugin — session and per-turn task context, branch policy on git shell commands, workit 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-cli": "^8.40.8",
43
- "@brainervirus/workit-core": "^8.40.8"
42
+ "@brainervirus/workit-cli": "^8.41.0",
43
+ "@brainervirus/workit-core": "^8.41.0"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=24"