@brainervirus/workit-cli 8.37.4 → 8.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +811 -358
  3. package/package.json +3 -3
package/dist/index.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.37.4",
9320
+ version: "8.38.1",
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.37.4",
9358
+ "@brainervirus/workit-cursor": "^8.38.1",
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.37.4"
9364
+ "@brainervirus/workit-core": "^8.38.1"
9365
9365
  },
9366
9366
  engines: {
9367
9367
  node: ">=24"
@@ -68218,7 +68218,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68218
68218
  if (!state)
68219
68219
  continue;
68220
68220
  problems.push(`Cursor registers the Workit plugin at ${entry}, but that directory is ${state}`);
68221
- fixes.push("Run `workit init` and select Cursor to install the plugin files");
68221
+ fixes.push("Run `workit upgrade --hosts=cursor` to reinstall the Cursor plugin files");
68222
68222
  }
68223
68223
  }
68224
68224
  for (const install of codexWorkitInstalls(res)) {
@@ -69643,9 +69643,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69643
69643
  return `${host}: ${seen.join(", ")}`;
69644
69644
  });
69645
69645
  return { id: "hook_runs", status: "pass", detail: parts.join("; ") };
69646
- }, RUN_CHECKS, INSTALLER_REQUIRED, runDoctor = (options = {}) => {
69646
+ }, RUN_CHECKS, UPGRADE_CHECKS, INSTALLER_REQUIRED, runDoctor = (options = {}) => {
69647
69647
  const res = resolve(options);
69648
- const raw = RUN_CHECKS.map((fn) => fn(res));
69648
+ const raw = (options.only === "upgrade" ? RUN_CHECKS.filter((fn) => UPGRADE_CHECKS.has(fn)) : RUN_CHECKS).map((fn) => fn(res));
69649
69649
  const checks = res.installer ? raw.map((c) => c.status === "fail" && !INSTALLER_REQUIRED.has(c.id) ? {
69650
69650
  ...c,
69651
69651
  status: "warn",
@@ -69743,6 +69743,20 @@ var init_doctor = __esm(() => {
69743
69743
  checkGrantRules("codex", "codex_grant_rules"),
69744
69744
  checkGrantRules("cursor", "cursor_grant_rules")
69745
69745
  ];
69746
+ UPGRADE_CHECKS = new Set([
69747
+ checkVersions,
69748
+ checkCodexPin,
69749
+ checkCodexAgents,
69750
+ checkPiExtension,
69751
+ checkOpencodeVersion,
69752
+ checkClaudePlugin,
69753
+ checkAssets,
69754
+ checkLauncher,
69755
+ checkPluginFiles,
69756
+ checkStalePin,
69757
+ checkStaleInstall,
69758
+ ...RUN_CHECKS.slice(-6)
69759
+ ]);
69746
69760
  INSTALLER_REQUIRED = new Set([
69747
69761
  "runtime",
69748
69762
  "assets",
@@ -74829,6 +74843,7 @@ import {
74829
74843
  mkdtempSync as mkdtempSync3,
74830
74844
  rmSync as rmSync8,
74831
74845
  readFileSync as readFileSync21,
74846
+ readdirSync as readdirSync12,
74832
74847
  mkdirSync as mkdirSync11,
74833
74848
  copyFileSync as copyFileSync6,
74834
74849
  chmodSync as chmodSync2,
@@ -74846,6 +74861,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
74846
74861
  const plan = {
74847
74862
  ok: true,
74848
74863
  blocked: [],
74864
+ failed: [],
74849
74865
  skipped: [],
74850
74866
  entries: [],
74851
74867
  configDigests: {},
@@ -74861,7 +74877,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
74861
74877
  paths.opencodeConfig + "c",
74862
74878
  path44.join(home, ".cursor/settings.json"),
74863
74879
  path44.join(home, ".cursor/mcp.json"),
74864
- path44.join(home, ".cursor/plugins/local/workit/package.json"),
74880
+ path44.join(paths.cursorRoot, "package.json"),
74865
74881
  paths.codexConfig,
74866
74882
  paths.piSettings
74867
74883
  ])
@@ -74874,93 +74890,150 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
74874
74890
  file: state.config.file,
74875
74891
  description: "Remove ignored Workit trustedPaths; native host permissions remain unchanged"
74876
74892
  });
74877
- const installedSources = sources(paths, run, hosts);
74878
- if (installedSources.some((source) => source.host === "opencode")) {
74879
- const scratch = mkdtempSync3(path44.join(os16.tmpdir(), "workit-upgrade-opencode-"));
74880
- let probe;
74881
- try {
74882
- probe = runner({
74883
- ...deps,
74884
- env: {
74885
- ...deps.env,
74886
- XDG_DATA_HOME: path44.join(scratch, "data"),
74887
- XDG_STATE_HOME: path44.join(scratch, "state"),
74888
- XDG_CACHE_HOME: path44.join(scratch, "cache")
74889
- }
74890
- })("opencode", ["--version"]);
74891
- } finally {
74892
- rmSync8(scratch, { recursive: true, force: true });
74893
- }
74894
- const installed = probe.status === 0 ? probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] : undefined;
74895
- if (installed && !versionAtLeast(installed, SUPPORT_MATRIX.opencode.minimum))
74896
- (plan.warnings ??= []).push(`opencode ${installed}: ${OPENCODE_V1_FIX}`);
74893
+ } catch (error) {
74894
+ plan.blocked.push(errorText(error));
74895
+ }
74896
+ const installedSources = sources(paths, run, hosts, plan.failed);
74897
+ let opencodeHelp;
74898
+ if (installedSources.some((source) => source.host === "opencode")) {
74899
+ const probe = opencodeProbe(deps, ["--version"]);
74900
+ const installed = probe.status === 0 ? probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] : undefined;
74901
+ if (installed && !versionAtLeast(installed, SUPPORT_MATRIX.opencode.minimum))
74902
+ (plan.warnings ??= []).push(`opencode ${installed}: ${OPENCODE_V1_FIX}`);
74903
+ }
74904
+ for (const host of ["opencode", "pi"]) {
74905
+ if (new Set(installedSources.filter((source) => source.host === host).map((source) => source.selector)).size > 1)
74906
+ plan.failed.push({
74907
+ host,
74908
+ status: "failed",
74909
+ reason: "conflicting Workit registrations; reconcile them in the native host before upgrading"
74910
+ });
74911
+ }
74912
+ const conflicted = new Set(plan.failed.map((entry) => entry.host));
74913
+ for (const source of installedSources.filter((candidate, index, list) => !conflicted.has(candidate.host) && list.findIndex((entry) => entry.host === candidate.host && entry.selector === candidate.selector) === index)) {
74914
+ if (source.kind === "pinned" || source.kind === "local") {
74915
+ plan.skipped.push({
74916
+ host: source.host,
74917
+ reason: `${source.kind} source preserved; change its source explicitly to upgrade`
74918
+ });
74919
+ continue;
74897
74920
  }
74898
- for (const host of ["opencode", "pi"]) {
74899
- if (new Set(installedSources.filter((source) => source.host === host).map((source) => source.selector)).size > 1)
74900
- plan.blocked.push(`${host}: conflicting Workit registrations; reconcile them in the native host before upgrading`);
74921
+ if (source.host === "claude-code") {
74922
+ const [market] = source.selector.split("@").slice(1);
74923
+ plan.entries.push({
74924
+ host: "claude-code",
74925
+ selector: source.selector,
74926
+ latest: "",
74927
+ installedVersion: source.installedVersion,
74928
+ commands: [
74929
+ {
74930
+ command: "claude",
74931
+ args: ["plugin", "marketplace", "update", market ?? "workit"]
74932
+ },
74933
+ { command: "claude", args: ["plugin", "update", source.selector] }
74934
+ ],
74935
+ fix: `claude plugin marketplace update ${market ?? "workit"} && claude plugin update ${source.selector}`
74936
+ });
74937
+ continue;
74901
74938
  }
74902
- for (const source of installedSources.filter((candidate, index, list) => list.findIndex((entry) => entry.host === candidate.host && entry.selector === candidate.selector) === index)) {
74903
- if (source.kind !== "floating") {
74904
- plan.skipped.push({
74905
- host: source.host,
74906
- reason: `${source.kind} source preserved; change its source explicitly to upgrade`
74907
- });
74908
- continue;
74909
- }
74910
- if (source.host === "opencode") {
74939
+ if (source.host === "opencode") {
74940
+ opencodeHelp ??= opencodeProbe(deps, ["plugin", "--help"]).stdout;
74941
+ if (!/^\s*(?:opencode\s+plugin\s+)?update\b/m.test(opencodeHelp)) {
74911
74942
  plan.skipped.push({
74912
- host: source.host,
74913
- reason: "OpenCode server plugin preserved: scoped native updates are unavailable in qualified 2.0.21; Workit never updates unrelated plugins"
74943
+ host: "opencode",
74944
+ reason: "this OpenCode has no `opencode plugin update`; upgrade OpenCode, then run the fix. Workit never updates unrelated plugins",
74945
+ fix: OPENCODE_UPDATE
74914
74946
  });
74915
74947
  continue;
74916
74948
  }
74917
- const latest = run("npm", ["view", `@brainervirus/workit-${source.host}`, "version"]);
74918
- if (latest.status !== 0 || !/^\d+\.\d+\.\d+$/.test(latest.stdout.trim())) {
74919
- plan.blocked.push(`${source.host}: registry version unavailable; existing installation preserved`);
74920
- continue;
74921
- }
74922
- if (source.installedVersion && versionAtLeast(source.installedVersion, latest.stdout.trim())) {
74923
- plan.skipped.push({
74924
- host: source.host,
74925
- reason: `already current (${source.installedVersion})`
74926
- });
74927
- continue;
74928
- }
74929
- const commands = source.host === "pi" ? [{ command: "pi", args: ["update", "--extension", source.selector] }] : source.host === "codex" ? [{ command: "codex", args: ["plugin", "add", "workit@workflow-toolkit", "--json"] }] : planHostInstall("cursor", {
74930
- home,
74931
- env: deps.env,
74932
- cwd: home,
74933
- packageVersion: latest.stdout.trim(),
74934
- targetDir: path44.join(home, ".cursor/plugins/local/workit")
74935
- });
74936
74949
  plan.entries.push({
74937
- host: source.host,
74950
+ host: "opencode",
74938
74951
  selector: source.selector,
74939
- latest: latest.stdout.trim(),
74940
- commands
74952
+ latest: "",
74953
+ commands: [{ command: "opencode", args: ["plugin", "update", OPENCODE_PACKAGE] }],
74954
+ fix: OPENCODE_UPDATE
74941
74955
  });
74956
+ continue;
74942
74957
  }
74943
- } catch (error) {
74944
- plan.blocked.push(error instanceof Error ? error.message : "installed source could not be read");
74958
+ const latest = registryVersion(run, `@brainervirus/workit-${source.host}`);
74959
+ if (!latest) {
74960
+ plan.failed.push({
74961
+ host: source.host,
74962
+ status: "failed",
74963
+ reason: "registry version unavailable; existing installation preserved",
74964
+ fix: fixFor(source.host, source.selector)
74965
+ });
74966
+ continue;
74967
+ }
74968
+ if (source.installedVersion && versionAtLeast(source.installedVersion, latest)) {
74969
+ plan.skipped.push({
74970
+ host: source.host,
74971
+ reason: `already current (${source.installedVersion})`
74972
+ });
74973
+ continue;
74974
+ }
74975
+ const commands = source.host === "pi" ? [{ command: "pi", args: ["update", "--extension", source.selector] }] : source.host === "codex" ? [
74976
+ {
74977
+ command: "codex",
74978
+ args: ["plugin", "marketplace", "upgrade", CODEX_MARKETPLACE]
74979
+ },
74980
+ {
74981
+ command: "codex",
74982
+ args: ["plugin", "add", `workit@${CODEX_MARKETPLACE}`, "--json"]
74983
+ }
74984
+ ] : planHostInstall("cursor", {
74985
+ home,
74986
+ env: deps.env,
74987
+ cwd: home,
74988
+ packageVersion: latest,
74989
+ targetDir: paths.cursorRoot
74990
+ });
74991
+ plan.entries.push({
74992
+ host: source.host,
74993
+ selector: source.selector,
74994
+ latest,
74995
+ commands,
74996
+ installedVersion: source.installedVersion,
74997
+ ...source.kind === "repair" ? { repair: true } : {},
74998
+ fix: fixFor(source.host, source.selector)
74999
+ });
74945
75000
  }
74946
- plan.ok = plan.blocked.length === 0;
75001
+ plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
74947
75002
  return plan;
74948
75003
  }
74949
75004
  function applyUpgrade(plan, deps = {}) {
74950
- if (!plan.ok)
74951
- return { ok: false, error: plan.blocked.join("; ") };
75005
+ const hosts = [...plan.failed];
75006
+ const done = (backup) => {
75007
+ const failures = hosts.filter((entry) => entry.status === "failed");
75008
+ return {
75009
+ ok: failures.length === 0,
75010
+ ...backup ? { backup } : {},
75011
+ ...failures.length ? {
75012
+ error: failures.map((entry) => `${entry.host}: ${entry.reason}`).join("; ")
75013
+ } : {},
75014
+ hosts
75015
+ };
75016
+ };
75017
+ for (const skipped of plan.skipped)
75018
+ hosts.push({
75019
+ host: skipped.host,
75020
+ status: skipped.reason.startsWith("already current") ? "current" : "skipped",
75021
+ reason: skipped.reason,
75022
+ ...skipped.fix ? { fix: skipped.fix } : {}
75023
+ });
75024
+ if (plan.blocked.length)
75025
+ return { ok: false, error: plan.blocked.join("; "), hosts };
74952
75026
  if (!plan.entries.length && !plan.migrations.length && !plan.cli)
74953
- return { ok: true };
75027
+ return done();
74954
75028
  for (const [file, expected] of Object.entries(plan.configDigests))
74955
75029
  if (digest2(file) !== expected)
74956
- return { ok: false, error: `configuration changed; preview again: ${file}` };
74957
- const { home, env, configDir } = upgradePaths(deps);
74958
- const active = new Set(deps.activeHosts?.() ?? runningHosts(deps));
74959
- if (plan.entries.some((entry) => active.has(entry.host)))
74960
- return {
74961
- ok: false,
74962
- error: "Stop the selected running hosts before upgrading their loaded plugins"
74963
- };
75030
+ return {
75031
+ ok: false,
75032
+ error: `configuration changed; preview again: ${file}`,
75033
+ hosts
75034
+ };
75035
+ const paths = upgradePaths(deps);
75036
+ const { home, configDir } = paths;
74964
75037
  const backup = path44.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
74965
75038
  mkdirSync11(backup, { recursive: true, mode: 448 });
74966
75039
  for (const file of Object.keys(plan.configDigests))
@@ -74975,151 +75048,163 @@ function applyUpgrade(plan, deps = {}) {
74975
75048
  return {
74976
75049
  ok: false,
74977
75050
  backup,
74978
- error: "Unknown configuration migration; upgrade the CLI before applying"
75051
+ error: "Unknown configuration migration; upgrade the CLI before applying",
75052
+ hosts
74979
75053
  };
74980
75054
  delete value.trustedPaths;
74981
75055
  writeFileAtomic(migration.file, JSON.stringify(value, null, 2) + `
74982
75056
  `, 384);
74983
75057
  }
74984
75058
  const run = runner(deps);
74985
- const cursorRoot = path44.join(home, ".cursor/plugins/local/workit");
74986
- if (plan.entries.some((entry) => entry.host === "cursor") && existsSync20(cursorRoot))
74987
- cpSync5(cursorRoot, path44.join(backup, "cursor-package"), { recursive: true });
75059
+ let cursorBackupError = null;
75060
+ if (plan.entries.some((entry) => entry.host === "cursor") && existsSync20(paths.cursorRoot))
75061
+ try {
75062
+ cpSync5(paths.cursorRoot, path44.join(backup, "cursor-package"), { recursive: true });
75063
+ } catch (error) {
75064
+ cursorBackupError = `could not back up the Cursor plugin: ${errorText(error)}`;
75065
+ }
75066
+ const listed = plan.entries.length ? listProcesses(deps) : [];
75067
+ const running = (listed ?? []).flatMap((entry) => classifyProcess(entry) ?? []);
74988
75068
  for (const entry of plan.entries) {
74989
- if (entry.host === "opencode")
74990
- return {
74991
- ok: false,
74992
- backup,
74993
- error: "Scoped OpenCode server-plugin updates are unsupported; registration preserved"
74994
- };
74995
- if (entry.host === "cursor") {
74996
- const preview = {
74997
- ok: true,
74998
- blocked: [],
74999
- preserved: [],
75000
- overrides: [],
75001
- platforms: ["cursor"],
75002
- state: readSetupState(configDir),
75003
- mutations: [
75004
- {
75005
- type: "install-host",
75006
- platform: "cursor",
75007
- path: path44.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
75008
- commands: entry.commands.map((command) => ({
75009
- ...command,
75010
- purpose: command.purpose ?? "Upgrade Cursor"
75011
- }))
75012
- },
75013
- {
75014
- type: "install-adapter",
75015
- platform: "cursor",
75016
- path: path44.join(home, ".cursor/plugins/local/workit")
75017
- }
75018
- ]
75019
- };
75020
- const result = applySetupPreview(preview, {
75021
- home,
75022
- env,
75023
- cwd: home,
75024
- preferInstalledAdapter: true,
75025
- runHostCommand: (command) => {
75026
- const outcome = run(command.command, command.args);
75027
- return { exitCode: outcome.status ?? 1, stdout: outcome.stdout, stderr: "" };
75028
- }
75069
+ if (entry.host === "cursor" && cursorBackupError) {
75070
+ hosts.push({ host: "cursor", status: "failed", reason: cursorBackupError, fix: entry.fix });
75071
+ continue;
75072
+ }
75073
+ if (!LIVE_HOSTS.has(entry.host)) {
75074
+ if (listed === null) {
75075
+ hosts.push({
75076
+ host: entry.host,
75077
+ status: "skipped",
75078
+ reason: `running processes could not be listed; close ${entry.host} and rerun`,
75079
+ fix: `workit upgrade --hosts=${entry.host}`
75080
+ });
75081
+ continue;
75082
+ }
75083
+ const sessions = running.filter((proc) => proc.host === entry.host && proc.kind === "session");
75084
+ if (sessions.length) {
75085
+ hosts.push({
75086
+ host: entry.host,
75087
+ status: "skipped",
75088
+ reason: `close ${entry.host} (${sessions.map(describe2).join("; ")}), then rerun`,
75089
+ fix: `workit upgrade --hosts=${entry.host}`
75090
+ });
75091
+ continue;
75092
+ }
75093
+ }
75094
+ const daemons = running.filter((proc) => proc.host === "codex" && proc.kind === "daemon");
75095
+ const stopDaemon = entry.host === "codex" && daemons.length > 0;
75096
+ if (stopDaemon && run("codex", ["app-server", "daemon", "stop"]).status !== 0) {
75097
+ hosts.push({
75098
+ host: "codex",
75099
+ status: "failed",
75100
+ reason: `could not stop the Codex app-server daemon (${daemons.map(describe2).join("; ")})`,
75101
+ fix: "codex app-server daemon stop && workit upgrade --hosts=codex"
75029
75102
  });
75030
- const pkg = object2(path44.join(home, ".cursor/plugins/local/workit/package.json"));
75031
- if (!result.ok || pkg?.version !== entry.latest)
75032
- return {
75033
- ok: false,
75034
- backup,
75035
- error: "Cursor update did not verify the requested package version; inspect the installer before retrying"
75036
- };
75037
75103
  continue;
75038
75104
  }
75039
- for (const command of entry.commands)
75040
- if (run(command.command, command.args).status !== 0)
75041
- return {
75042
- ok: false,
75043
- backup,
75044
- error: `${entry.host}: update failed; inspect the native host before retrying`
75105
+ let outcome;
75106
+ try {
75107
+ outcome = applyEntry(entry, { run, paths, configDir });
75108
+ if (entry.host === "codex" && outcome.status === "updated") {
75109
+ const agents = refreshCodexAgents(run, paths.codexHome);
75110
+ if (!agents.ok)
75111
+ outcome = {
75112
+ ...outcome,
75113
+ status: "failed",
75114
+ reason: `${outcome.reason}; ${agents.note}`,
75115
+ fix: agents.fix
75116
+ };
75117
+ else if (agents.note)
75118
+ outcome = { ...outcome, reason: `${outcome.reason}; ${agents.note}` };
75119
+ }
75120
+ } catch (error) {
75121
+ outcome = {
75122
+ host: entry.host,
75123
+ status: "failed",
75124
+ reason: errorText(error),
75125
+ fix: entry.fix
75126
+ };
75127
+ }
75128
+ if (stopDaemon) {
75129
+ const started = run("codex", ["app-server", "daemon", "start"]).status === 0;
75130
+ outcome = started ? {
75131
+ ...outcome,
75132
+ reason: `${outcome.reason}; app-server daemon stopped and started again`
75133
+ } : {
75134
+ ...outcome,
75135
+ status: "failed",
75136
+ reason: `${outcome.reason}; the app-server daemon did not start again`,
75137
+ fix: "codex app-server daemon start"
75138
+ };
75139
+ }
75140
+ if (entry.host === "opencode") {
75141
+ const servers = running.filter((proc) => proc.host === "opencode" && proc.kind === "daemon");
75142
+ if (servers.length && outcome.status === "updated")
75143
+ outcome = {
75144
+ ...outcome,
75145
+ reason: `${outcome.reason}; restart opencode serve (${servers.map(describe2).join("; ")}) to load it`
75045
75146
  };
75046
- const verify = entry.host === "codex" ? run("codex", ["plugin", "list", "--json"]) : run("pi", ["list"]);
75047
- if (verify.status !== 0)
75048
- return { ok: false, backup, error: `${entry.host}: update verification failed` };
75049
- if (entry.host === "codex") {
75050
- let installed = [];
75051
- try {
75052
- installed = JSON.parse(verify.stdout).installed ?? [];
75053
- } catch {}
75054
- if (!installed.some((plugin) => plugin.name === "workit" && plugin.marketplaceName === "workflow-toolkit" && plugin.version === entry.latest))
75055
- return { ok: false, backup, error: "Codex did not report the requested Workit version" };
75056
- }
75057
- if (entry.host === "pi" && piInstalledVersion(verify.stdout, entry.selector) !== entry.latest)
75058
- return { ok: false, backup, error: "Pi did not report the requested Workit package version" };
75059
- }
75060
- if (plan.cli) {
75061
- const currentRoot = run("npm", ["root", "--global"]);
75062
- if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== plan.cli.root)
75063
- return { ok: false, backup, error: "Global npm location changed; preview again" };
75064
- const cliBackup = path44.join(backup, "cli-package");
75065
- cpSync5(path44.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
75066
- if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${plan.cli.latest}`]).status !== 0)
75067
- return {
75147
+ }
75148
+ hosts.push(outcome);
75149
+ }
75150
+ if (hosts.some((entry) => entry.host === "codex" && entry.status === "current")) {
75151
+ let agents;
75152
+ try {
75153
+ agents = refreshCodexAgents(run, paths.codexHome);
75154
+ } catch (error) {
75155
+ agents = {
75068
75156
  ok: false,
75069
- backup,
75070
- error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path44.join(cliBackup, "dist/index.js")}`
75157
+ note: `agent refresh failed: ${errorText(error)}`,
75158
+ fix: "workit upgrade --hosts=codex"
75071
75159
  };
75072
- const installed = object2(path44.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
75073
- if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== plan.cli.latest)
75074
- return {
75075
- ok: false,
75076
- backup,
75077
- error: `CLI update did not verify the requested version; preserved package at ${cliBackup}`
75160
+ }
75161
+ const index = hosts.findIndex((entry) => entry.host === "codex" && entry.status === "current");
75162
+ if (!agents.ok)
75163
+ hosts[index] = {
75164
+ ...hosts[index],
75165
+ status: "failed",
75166
+ reason: agents.note,
75167
+ fix: agents.fix
75168
+ };
75169
+ else if (agents.note)
75170
+ hosts[index] = {
75171
+ ...hosts[index],
75172
+ reason: `${hosts[index].reason}; ${agents.note}`
75078
75173
  };
75079
75174
  }
75080
- return { ok: true, backup };
75175
+ if (plan.cli)
75176
+ try {
75177
+ hosts.push(applyCli(plan.cli, run, backup));
75178
+ } catch (error) {
75179
+ hosts.push({ host: "cli", status: "failed", reason: errorText(error), fix: CLI_FIX });
75180
+ }
75181
+ return done(backup);
75081
75182
  }
75082
75183
  async function runUpgradeCommand(argv, deps = {}) {
75083
75184
  const out = deps.out ?? process.stdout;
75084
75185
  const flags = new Set(argv);
75085
- const unknown = argv.filter((arg) => !["--apply", "--confirm", "--json", "--cli", "--preview"].includes(arg) && !arg.startsWith("--hosts="));
75186
+ const unknown = argv.filter((arg) => !["--apply", "--confirm", "--json", "--cli", "--preview", "--yes"].includes(arg) && !arg.startsWith("--hosts="));
75086
75187
  const hostFlag = argv.find((arg) => arg.startsWith("--hosts="))?.slice(8);
75087
75188
  const raw = hostFlag === "none" ? [] : hostFlag?.split(",") ?? HOSTS2;
75088
- if (unknown.length || raw.some((host) => !HOSTS2.includes(host)) || flags.has("--preview") && flags.has("--apply")) {
75089
- out.write(`Usage: workit upgrade [--hosts=opencode,cursor,codex,pi|none] [--cli] [--preview | --apply --confirm] [--json]
75189
+ if (unknown.length || raw.some((host) => !HOSTS2.includes(host)) || flags.has("--preview") && (flags.has("--apply") || flags.has("--yes"))) {
75190
+ out.write(USAGE3);
75191
+ return 2;
75192
+ }
75193
+ if (flags.has("--apply") && !flags.has("--confirm")) {
75194
+ out.write(`Apply requires --confirm after reviewing the upgrade preview.
75090
75195
  `);
75091
75196
  return 2;
75092
75197
  }
75198
+ const legacy = flags.has("--apply");
75199
+ const json = flags.has("--json") || legacy;
75200
+ const run = runner(deps);
75093
75201
  const plan = previewUpgrade(raw, deps);
75094
- if (flags.has("--cli")) {
75095
- const run = runner(deps);
75096
- const root = run("npm", ["root", "--global"]);
75097
- if (root.status !== 0 || !path44.isAbsolute(root.stdout.trim())) {
75098
- plan.blocked.push("Global npm location could not be verified");
75099
- } else {
75100
- const packageFile = path44.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
75101
- const pkg = object2(packageFile);
75102
- if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(path44.dirname(packageFile)).isSymbolicLink()) {
75103
- plan.skipped.push({
75104
- host: "cli",
75105
- reason: "No managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
75106
- });
75107
- } else {
75108
- const latest = run("npm", ["view", "@brainervirus/workit-cli", "version"]);
75109
- if (latest.status !== 0 || !/^\d+\.\d+\.\d+$/.test(latest.stdout.trim()))
75110
- plan.blocked.push("CLI registry version unavailable; existing installation preserved");
75111
- else if (!versionAtLeast(pkg.version, latest.stdout.trim())) {
75112
- plan.cli = { latest: latest.stdout.trim(), root: root.stdout.trim() };
75113
- plan.configDigests[packageFile] = digest2(packageFile);
75114
- } else
75115
- plan.skipped.push({ host: "cli", reason: `already current (${pkg.version})` });
75116
- }
75117
- }
75118
- plan.ok = plan.blocked.length === 0;
75119
- }
75202
+ if (hostFlag === undefined || flags.has("--cli"))
75203
+ planCli(plan, run);
75204
+ plan.ok = plan.blocked.length === 0 && plan.failed.length === 0;
75120
75205
  const { home, env } = upgradePaths(deps);
75121
75206
  const permissions = planHostPermissions({
75122
- hosts: hostFlag === undefined ? [...raw, "claude-code"] : raw,
75207
+ hosts: raw,
75123
75208
  refreshOnly: true,
75124
75209
  shell: false,
75125
75210
  home,
@@ -75133,43 +75218,88 @@ async function runUpgradeCommand(argv, deps = {}) {
75133
75218
  })),
75134
75219
  ...permissions.refused ? { refused: permissions.refused } : {}
75135
75220
  };
75136
- if (!flags.has("--apply")) {
75137
- out.write(JSON.stringify(permissions.changes.length ? { ...plan, permissions: permissionPreview } : plan, null, 2) + `
75221
+ const withPermissions = (value) => permissions.changes.length ? { ...value, permissions: permissionPreview } : value;
75222
+ const applyCommand = `workit upgrade ${[...hostFlag === undefined ? [] : [`--hosts=${hostFlag}`], ...flags.has("--cli") ? ["--cli"] : [], "--yes"].join(" ")}`;
75223
+ const nothingChanged = `preview only; nothing changed. Apply with: ${applyCommand}
75224
+ `;
75225
+ if (json && !legacy && !flags.has("--yes")) {
75226
+ out.write(JSON.stringify(withPermissions(plan), null, 2) + `
75138
75227
  `);
75139
- return plan.ok ? 0 : 1;
75228
+ return plan.blocked.length ? 1 : 0;
75140
75229
  }
75141
- if (!flags.has("--confirm")) {
75142
- out.write(`Apply requires --confirm after reviewing the upgrade preview.
75230
+ if (!json)
75231
+ out.write(summary(plan, permissions.changes));
75232
+ if (flags.has("--preview")) {
75233
+ out.write(nothingChanged);
75234
+ return plan.blocked.length ? 1 : 0;
75235
+ }
75236
+ if (plan.blocked.length) {
75237
+ out.write(`Upgrade blocked: ${plan.blocked.join("; ")}
75238
+ Nothing changed.
75143
75239
  `);
75144
- return 2;
75240
+ return 1;
75145
75241
  }
75146
- for (const warning of plan.warnings ?? [])
75147
- out.write(`warning: ${warning}
75242
+ const work = plan.entries.length + plan.migrations.length + (plan.cli ? 1 : 0) + permissions.changes.length;
75243
+ if (work > 0 && !flags.has("--yes") && !legacy) {
75244
+ const confirmed = await (deps.confirm ?? ((q) => askConfirm(q, out)))("Apply this upgrade? [y/N] ");
75245
+ if (!confirmed) {
75246
+ out.write(nothingChanged);
75247
+ return 0;
75248
+ }
75249
+ }
75250
+ if (json)
75251
+ for (const warning of plan.warnings ?? [])
75252
+ out.write(`warning: ${warning}
75148
75253
  `);
75149
75254
  const result = applyUpgrade(plan, deps);
75150
- if (!result.ok || permissions.changes.length === 0) {
75151
- out.write(JSON.stringify(result, null, 2) + `
75152
- `);
75153
- return result.ok ? 0 : 1;
75255
+ let permissionsOk = true;
75256
+ let written;
75257
+ if (permissions.changes.length && !permissions.refused && !result.error?.startsWith("configuration changed")) {
75258
+ written = applyHostPermissions(permissions, { home, env });
75259
+ permissionsOk = written.every((entry) => entry.status === "written");
75260
+ for (const entry of written)
75261
+ result.hosts.push({
75262
+ host: entry.host,
75263
+ status: entry.status === "written" ? "updated" : "failed",
75264
+ reason: `permission block ${entry.status === "written" ? "refreshed" : entry.status} in ${entry.file}`,
75265
+ ...entry.status === "written" ? {} : { fix: `workit setup --hosts=${entry.host}` }
75266
+ });
75154
75267
  }
75155
- if (permissions.refused) {
75156
- out.write(JSON.stringify({ ...result, permissions: permissionPreview }, null, 2) + `
75268
+ const failed = !result.ok || !permissionsOk;
75269
+ const cliVersion = result.hosts.some((h) => h.host === "cli" && h.status === "updated") ? plan.cli.latest : package_default.version;
75270
+ const report = (deps.doctor ?? ((version) => runDoctor({ home, env, cliVersion: version, only: "upgrade" })))(cliVersion);
75271
+ const left = report.checks.filter((check) => check.status === "warn" || check.status === "fail");
75272
+ if (json) {
75273
+ out.write(JSON.stringify({
75274
+ ...result,
75275
+ ok: !failed,
75276
+ ...written ? { permissions: { changes: permissionPreview.changes, written } } : permissions.refused ? { permissions: permissionPreview } : {},
75277
+ doctor: { clean: left.length === 0, left }
75278
+ }, null, 2) + `
75157
75279
  `);
75158
- return 0;
75280
+ return failed ? 1 : 0;
75159
75281
  }
75160
- const written = applyHostPermissions(permissions, { home, env });
75161
- const permissionsOk = written.every((entry) => entry.status === "written");
75162
- out.write(JSON.stringify({
75163
- ...result,
75164
- ok: permissionsOk,
75165
- permissions: { changes: permissionPreview.changes, written }
75166
- }, null, 2) + `
75282
+ if (result.error && result.hosts.every((h) => h.status !== "failed"))
75283
+ out.write(`Upgrade stopped: ${result.error}
75284
+ `);
75285
+ out.write(table(result.hosts));
75286
+ if (permissions.refused)
75287
+ out.write(`permissions: not refreshed: ${permissions.refused}
75288
+ `);
75289
+ if (result.backup)
75290
+ out.write(`Backup: ${result.backup}
75167
75291
  `);
75168
- return permissionsOk ? 0 : 1;
75292
+ out.write(left.length === 0 ? `Verified: workit doctor is clean for versions, assets, agents and permissions.
75293
+ ` : `Left after upgrade (workit doctor):
75294
+ ${left.map((check) => ` ${check.id}: ${check.detail}${check.fix ? `
75295
+ fix: ${check.fix}` : ""}`).join(`
75296
+ `)}
75297
+ `);
75298
+ return failed ? 1 : 0;
75169
75299
  }
75170
75300
  async function runLaunchCommand(argv, deps = {}) {
75171
75301
  const [host, ...args] = argv;
75172
- if (!HOSTS2.includes(host)) {
75302
+ if (!LAUNCH_HOSTS.includes(host)) {
75173
75303
  (deps.out ?? process.stdout).write(`Usage: workit launch <opencode|cursor|codex|pi> [--auto-upgrade] [-- host arguments]
75174
75304
  `);
75175
75305
  return 2;
@@ -75190,9 +75320,10 @@ async function runLaunchCommand(argv, deps = {}) {
75190
75320
  }
75191
75321
  if (auto) {
75192
75322
  const plan = previewUpgrade([host], deps);
75323
+ const reasons = [...plan.blocked, ...plan.failed.map((entry) => entry.reason)];
75193
75324
  if (plan.ok) {
75194
75325
  for (const skipped of plan.skipped)
75195
- (deps.out ?? process.stderr).write(`${skipped.host}: ${skipped.reason}
75326
+ (deps.out ?? process.stderr).write(`${skipped.host}: ${skipped.reason}${skipped.fix ? ` (run: ${skipped.fix})` : ""}
75196
75327
  `);
75197
75328
  const result = applyUpgrade(plan, deps);
75198
75329
  if (!result.ok) {
@@ -75200,11 +75331,11 @@ async function runLaunchCommand(argv, deps = {}) {
75200
75331
  `);
75201
75332
  return 1;
75202
75333
  }
75203
- } else if (plan.blocked.every((reason) => reason.includes("registry version unavailable"))) {
75204
- (deps.out ?? process.stderr).write(`Upgrade unavailable: ${plan.blocked.join("; ")}. Starting the unchanged host.
75334
+ } else if (plan.blocked.length === 0 && reasons.every((reason) => reason.includes("registry version unavailable"))) {
75335
+ (deps.out ?? process.stderr).write(`Upgrade unavailable: ${reasons.join("; ")}. Starting the unchanged host.
75205
75336
  `);
75206
75337
  } else {
75207
- (deps.out ?? process.stderr).write(`Upgrade blocked: ${plan.blocked.join("; ")}
75338
+ (deps.out ?? process.stderr).write(`Upgrade blocked: ${reasons.join("; ")}
75208
75339
  `);
75209
75340
  return 1;
75210
75341
  }
@@ -75216,13 +75347,14 @@ async function runLaunchCommand(argv, deps = {}) {
75216
75347
  env
75217
75348
  }).status ?? 1;
75218
75349
  }
75219
- var HOSTS2, upgradePaths = (deps) => {
75350
+ var LAUNCH_HOSTS, HOSTS2, LIVE_HOSTS, OPENCODE_PACKAGE = "@brainervirus/workit-opencode", OPENCODE_UPDATE, CODEX_MARKETPLACE = "workflow-toolkit", CLI_FIX = "npm install --global @brainervirus/workit-cli@latest", fixFor = (host, selector = "") => host === "pi" ? `pi update --extension ${selector || "npm:@brainervirus/workit-pi"}` : host === "codex" ? `codex plugin marketplace upgrade ${CODEX_MARKETPLACE} && codex plugin add workit@${CODEX_MARKETPLACE}` : host === "claude-code" ? "claude plugin marketplace update workit && claude plugin update workit@workit" : host === "opencode" ? OPENCODE_UPDATE : host === "cursor" ? "workit upgrade --hosts=cursor" : CLI_FIX, upgradePaths = (deps) => {
75220
75351
  const home = deps.home ?? deps.env?.HOME ?? os16.homedir();
75221
75352
  const env = {
75222
75353
  ...process.env,
75223
75354
  ...deps.home || deps.env?.HOME ? {
75224
75355
  HOME: home,
75225
75356
  CODEX_HOME: path44.join(home, ".codex"),
75357
+ CLAUDE_CONFIG_DIR: path44.join(home, ".claude"),
75226
75358
  PI_CODING_AGENT_DIR: path44.join(home, ".pi/agent"),
75227
75359
  XDG_CONFIG_HOME: path44.join(home, ".config"),
75228
75360
  WORKFLOW_TOOLKIT_CONFIG: undefined,
@@ -75232,13 +75364,16 @@ var HOSTS2, upgradePaths = (deps) => {
75232
75364
  HOME: home
75233
75365
  };
75234
75366
  const xdg = env.XDG_CONFIG_HOME || path44.join(home, ".config");
75367
+ const codexHome = env.CODEX_HOME ?? path44.join(home, ".codex");
75235
75368
  return {
75236
75369
  home,
75237
75370
  env,
75238
75371
  configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path44.join(xdg, "workit"),
75239
75372
  opencodeConfig: path44.join(xdg, "opencode/opencode.json"),
75240
75373
  piSettings: path44.join(env.PI_CODING_AGENT_DIR ?? path44.join(home, ".pi/agent"), "settings.json"),
75241
- codexConfig: path44.join(env.CODEX_HOME ?? path44.join(home, ".codex"), "config.toml")
75374
+ codexHome,
75375
+ codexConfig: path44.join(codexHome, "config.toml"),
75376
+ cursorRoot: path44.join(home, ".cursor/plugins/local/workit")
75242
75377
  };
75243
75378
  }, object2 = (file) => {
75244
75379
  if (!existsSync20(file))
@@ -75252,6 +75387,12 @@ var HOSTS2, upgradePaths = (deps) => {
75252
75387
  if (!data || typeof data !== "object" || Array.isArray(data))
75253
75388
  throw new Error(`${file} is not a configuration object`);
75254
75389
  return data;
75390
+ }, objectOrNull = (file) => {
75391
+ try {
75392
+ return object2(file);
75393
+ } catch {
75394
+ return null;
75395
+ }
75255
75396
  }, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync21(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
75256
75397
  const { home, env } = upgradePaths(deps);
75257
75398
  let step;
@@ -75266,48 +75407,85 @@ var HOSTS2, upgradePaths = (deps) => {
75266
75407
  host,
75267
75408
  selector,
75268
75409
  kind: selector === `@brainervirus/workit-${host}` || selector === `@brainervirus/workit-${host}@latest` || selector === `npm:@brainervirus/workit-${host}` || selector === `npm:@brainervirus/workit-${host}@latest` ? "floating" : new RegExp(`^(?:npm:)?@brainervirus/workit-${host}@[^/]+$`).test(selector) ? "pinned" : "local"
75269
- }), sources = (paths, run, hosts) => {
75270
- const { home, opencodeConfig, piSettings } = paths;
75410
+ }), errorText = (error) => error instanceof Error ? error.message : "installed source could not be read", sources = (paths, run, hosts, failed) => {
75411
+ const { home } = paths;
75271
75412
  const result = [];
75272
- if (hosts.includes("opencode") && existsSync20(opencodeConfig + "c"))
75273
- throw new Error("OpenCode JSONC config is present; inspect effective native configuration before updating");
75274
- const oc = hosts.includes("opencode") ? object2(opencodeConfig) : null;
75275
- if (oc && [oc.plugins, oc.plugin].some((value) => value !== undefined && !Array.isArray(value)))
75276
- throw new Error("OpenCode plugin registrations must be arrays");
75277
- const plugins = [...oc?.plugins ?? [], ...oc?.plugin ?? []];
75278
- for (const pin of plugins)
75279
- if (typeof pin === "string" && isWorkitPlugin(pin))
75280
- result.push(classify("opencode", pin));
75281
- const cursor = hosts.includes("cursor") ? object2(path44.join(home, ".cursor/plugins/local/workit/package.json")) : null;
75282
- if (cursor?.name === "@brainervirus/workit-cursor") {
75283
- const mcp = object2(path44.join(home, ".cursor/mcp.json"));
75284
- const args = mcp?.mcpServers?.workit?.args ?? [];
75285
- const selector = args.find((arg) => typeof arg === "string" && arg.startsWith("--package="));
75286
- result.push(classify("cursor", typeof selector === "string" ? selector.slice(10) : "local"));
75287
- }
75288
- const pi = hosts.includes("pi") ? object2(piSettings) : null;
75289
- if (pi?.packages !== undefined && !Array.isArray(pi.packages))
75290
- throw new Error("Pi package registrations must be an array");
75291
- for (const entry of pi?.packages ?? []) {
75292
- const source = typeof entry === "string" ? entry : entry?.source;
75293
- if (typeof source !== "string")
75294
- continue;
75295
- const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
75296
- const local = path44.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path44.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
75297
- if (npm || local)
75298
- result.push(classify("pi", source));
75299
- }
75300
- const listing = hosts.includes("codex") ? run("codex", ["plugin", "list", "--json"]) : { status: 1, stdout: "" };
75301
- if (hosts.includes("codex") && listing.status !== 0) {
75302
- let available = false;
75413
+ const guard = (host, read) => {
75414
+ if (!hosts.includes(host))
75415
+ return;
75303
75416
  try {
75304
- hostCommand("codex", [], { home, env: paths.env });
75305
- available = true;
75306
- } catch {}
75307
- if (available || existsSync20(paths.codexConfig))
75308
- throw new Error("Codex plugin inventory failed; inspect the native host before upgrading");
75309
- }
75310
- if (listing.status === 0) {
75417
+ read();
75418
+ } catch (error) {
75419
+ failed.push({
75420
+ host,
75421
+ status: "failed",
75422
+ reason: errorText(error),
75423
+ fix: fixFor(host)
75424
+ });
75425
+ }
75426
+ };
75427
+ guard("opencode", () => {
75428
+ if (existsSync20(paths.opencodeConfig + "c"))
75429
+ throw new Error("OpenCode JSONC config is present; inspect effective native configuration before updating");
75430
+ const oc = object2(paths.opencodeConfig);
75431
+ if (oc && [oc.plugins, oc.plugin].some((value) => value !== undefined && !Array.isArray(value)))
75432
+ throw new Error("OpenCode plugin registrations must be arrays");
75433
+ for (const pin of [...oc?.plugins ?? [], ...oc?.plugin ?? []])
75434
+ if (typeof pin === "string" && isWorkitPlugin(pin))
75435
+ result.push(classify("opencode", pin));
75436
+ });
75437
+ guard("cursor", () => {
75438
+ const cursor = objectOrNull(path44.join(paths.cursorRoot, "package.json"));
75439
+ const mcp = objectOrNull(path44.join(home, ".cursor/mcp.json"));
75440
+ const args = mcp?.mcpServers?.workit?.args ?? [];
75441
+ const arg = args.find((value) => typeof value === "string" && value.startsWith("--package="));
75442
+ const selector = typeof arg === "string" ? arg.slice(10) : "local";
75443
+ if (cursor?.name === "@brainervirus/workit-cursor") {
75444
+ result.push({
75445
+ ...classify("cursor", selector),
75446
+ installedVersion: cursor.version
75447
+ });
75448
+ return;
75449
+ }
75450
+ const settings = objectOrNull(path44.join(home, ".cursor/settings.json"));
75451
+ const dirs = Array.isArray(settings?.plugin_dirs) ? settings.plugin_dirs : [];
75452
+ const registered = mcp?.mcpServers?.workit !== undefined || dirs.some((dir) => String(dir).replaceAll("\\", "/").replace(/\/+$/, "").endsWith("local/workit"));
75453
+ if (registered)
75454
+ result.push({ host: "cursor", selector, kind: "repair" });
75455
+ });
75456
+ guard("pi", () => {
75457
+ const pi = object2(paths.piSettings);
75458
+ if (pi?.packages !== undefined && !Array.isArray(pi.packages))
75459
+ throw new Error("Pi package registrations must be an array");
75460
+ for (const entry of pi?.packages ?? []) {
75461
+ const source = typeof entry === "string" ? entry : entry?.source;
75462
+ if (typeof source !== "string")
75463
+ continue;
75464
+ const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
75465
+ const local = path44.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path44.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
75466
+ if (!npm && !local)
75467
+ continue;
75468
+ const found = classify("pi", source);
75469
+ if (found.kind === "floating") {
75470
+ const inventory = run("pi", ["list"]);
75471
+ if (inventory.status === 0)
75472
+ found.installedVersion = piInstalledVersion(inventory.stdout, source);
75473
+ }
75474
+ result.push(found);
75475
+ }
75476
+ });
75477
+ guard("codex", () => {
75478
+ const listing = run("codex", ["plugin", "list", "--json"]);
75479
+ if (listing.status !== 0) {
75480
+ let available = false;
75481
+ try {
75482
+ hostCommand("codex", [], { home, env: paths.env });
75483
+ available = true;
75484
+ } catch {}
75485
+ if (available || existsSync20(paths.codexConfig))
75486
+ throw new Error("Codex plugin inventory failed; inspect the native host before upgrading");
75487
+ return;
75488
+ }
75311
75489
  let installed;
75312
75490
  try {
75313
75491
  installed = JSON.parse(listing.stdout).installed ?? [];
@@ -75320,30 +75498,30 @@ var HOSTS2, upgradePaths = (deps) => {
75320
75498
  if (plugin.name !== "workit")
75321
75499
  continue;
75322
75500
  const source = plugin.source;
75323
- const npm = plugin.marketplaceName === "workflow-toolkit" && source?.source === "npm" && source?.package === "@brainervirus/workit-codex";
75501
+ const npm = plugin.marketplaceName === CODEX_MARKETPLACE && source?.source === "npm" && source?.package === "@brainervirus/workit-codex";
75324
75502
  result.push({
75325
75503
  ...classify("codex", npm ? `${source.package}${source.version ? `@${source.version}` : ""}` : "local"),
75326
75504
  installedVersion: plugin.version
75327
75505
  });
75328
75506
  }
75329
- }
75330
- for (const source of result) {
75331
- if (source.host === "cursor")
75332
- source.installedVersion = cursor?.version;
75333
- if (source.host === "pi" && source.kind === "floating") {
75334
- const inventory = run("pi", ["list"]);
75335
- if (inventory.status === 0)
75336
- source.installedVersion = piInstalledVersion(inventory.stdout, source.selector);
75337
- }
75338
- }
75507
+ });
75508
+ guard("claude-code", () => {
75509
+ for (const install of claudeWorkitInstalls(home, paths.env, null))
75510
+ result.push({
75511
+ host: "claude-code",
75512
+ selector: install.id,
75513
+ kind: "floating",
75514
+ installedVersion: install.version ?? undefined
75515
+ });
75516
+ });
75339
75517
  return result;
75340
75518
  }, piInstalledVersion = (listing, selector) => {
75341
75519
  const lines = listing.split(/\r?\n/).map((line) => line.trim());
75342
75520
  const index = lines.indexOf(selector);
75343
75521
  if (index < 0 || !lines[index + 1] || !path44.isAbsolute(lines[index + 1]))
75344
75522
  return;
75345
- const pkg = object2(path44.join(lines[index + 1], "package.json"));
75346
- return pkg?.name === "@brainervirus/workit-pi" ? pkg.version : undefined;
75523
+ const found = objectOrNull(path44.join(lines[index + 1], "package.json"));
75524
+ return found?.name === "@brainervirus/workit-pi" ? found.version : undefined;
75347
75525
  }, versionAtLeast = (installed, latest) => {
75348
75526
  if (!/^\d+\.\d+\.\d+$/.test(installed))
75349
75527
  return false;
@@ -75354,14 +75532,273 @@ var HOSTS2, upgradePaths = (deps) => {
75354
75532
  return current[index] > target[index];
75355
75533
  }
75356
75534
  return true;
75357
- }, runningHosts = (deps) => {
75535
+ }, opencodeProbe = (deps, args) => {
75536
+ const scratch = mkdtempSync3(path44.join(os16.tmpdir(), "workit-upgrade-opencode-"));
75537
+ try {
75538
+ return runner({
75539
+ ...deps,
75540
+ env: {
75541
+ ...deps.env,
75542
+ XDG_DATA_HOME: path44.join(scratch, "data"),
75543
+ XDG_STATE_HOME: path44.join(scratch, "state"),
75544
+ XDG_CACHE_HOME: path44.join(scratch, "cache")
75545
+ }
75546
+ })("opencode", args);
75547
+ } finally {
75548
+ rmSync8(scratch, { recursive: true, force: true });
75549
+ }
75550
+ }, registryVersion = (run, name) => {
75551
+ const latest = run("npm", ["view", name, "version"]);
75552
+ const version = latest.stdout.trim();
75553
+ return latest.status === 0 && /^\d+\.\d+\.\d+$/.test(version) ? version : undefined;
75554
+ }, PROCESS_HOSTS, classifyProcess = (proc) => {
75555
+ if (proc.state.startsWith("Z") || proc.args.includes("<defunct>"))
75556
+ return null;
75557
+ const argv0 = proc.args.trim().split(/\s+/)[0] ?? "";
75558
+ const name = path44.basename(argv0.replaceAll("\\", "/")).replace(/^\[|\]$/g, "").replace(/\.exe$/i, "").toLowerCase();
75559
+ const host = PROCESS_HOSTS[name];
75560
+ if (!host)
75561
+ return null;
75562
+ if (host === "codex" && /\bapp-server\b/.test(proc.args))
75563
+ return /\bdaemon\s+pid-update-loop\b/.test(proc.args) ? null : { ...proc, host, kind: "daemon" };
75564
+ if (host === "opencode" && /\bserve\b/.test(proc.args))
75565
+ return { ...proc, host, kind: "daemon" };
75566
+ return { ...proc, host, kind: "session" };
75567
+ }, listProcesses = (deps) => {
75568
+ if (deps.processes)
75569
+ return deps.processes();
75570
+ if (deps.activeHosts)
75571
+ return deps.activeHosts().map((host) => ({ pid: 0, state: "S", args: host }));
75358
75572
  const run = runner(deps);
75359
- const list = process.platform === "win32" ? run("tasklist", ["/fo", "csv", "/nh"]) : run("ps", ["-eo", "comm="]);
75573
+ if (process.platform === "win32") {
75574
+ const list = run("tasklist", ["/fo", "csv", "/nh"]);
75575
+ if (list.status !== 0)
75576
+ return null;
75577
+ return list.stdout.split(/\r?\n/).flatMap((line) => {
75578
+ const [image, pid] = line.split('","').map((cell) => cell.replaceAll('"', ""));
75579
+ return image && /^\d+$/.test(pid ?? "") ? [{ pid: Number(pid), state: "R", args: image }] : [];
75580
+ });
75581
+ }
75582
+ const list = run("ps", ["-eo", "pid=,stat=,args="]);
75360
75583
  if (list.status !== 0)
75361
- return HOSTS2;
75362
- return HOSTS2.filter((host) => new RegExp(`(?:^|[\\/",\\s])${host}(?:\\.exe)?(?:[",\\s]|$)`, "im").test(list.stdout));
75363
- };
75584
+ return null;
75585
+ return list.stdout.split(/\r?\n/).flatMap((line) => {
75586
+ const match = /^\s*(\d+)\s+(\S+)\s+(.+)$/.exec(line);
75587
+ return match ? [{ pid: Number(match[1]), state: match[2], args: match[3] }] : [];
75588
+ });
75589
+ }, describe2 = (proc) => `pid ${proc.pid}: ${proc.args.length > 120 ? `${proc.args.slice(0, 117)}...` : proc.args}`, codexPluginRoot = (codexHome) => {
75590
+ const dir = path44.join(codexHome, "plugins", "cache", CODEX_MARKETPLACE, "workit");
75591
+ let versions = [];
75592
+ try {
75593
+ versions = readdirSync12(dir).filter((version) => /^\d+\.\d+\.\d+$/.test(version));
75594
+ } catch {
75595
+ return null;
75596
+ }
75597
+ const newest = versions.reduce((best, version) => best && versionAtLeast(best, version) ? best : version, null);
75598
+ return newest ? path44.join(dir, newest) : null;
75599
+ }, applyEntry = (entry, context) => {
75600
+ const { run, paths, configDir } = context;
75601
+ const { home, env } = paths;
75602
+ const fail = (reason) => ({
75603
+ host: entry.host,
75604
+ status: "failed",
75605
+ reason,
75606
+ fix: entry.fix
75607
+ });
75608
+ if (entry.host === "cursor") {
75609
+ const preview = {
75610
+ ok: true,
75611
+ blocked: [],
75612
+ preserved: [],
75613
+ overrides: [],
75614
+ platforms: ["cursor"],
75615
+ state: readSetupState(configDir),
75616
+ mutations: [
75617
+ {
75618
+ type: "install-host",
75619
+ platform: "cursor",
75620
+ path: path44.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
75621
+ commands: entry.commands.map((command) => ({
75622
+ ...command,
75623
+ purpose: command.purpose ?? "Upgrade Cursor"
75624
+ }))
75625
+ },
75626
+ { type: "install-adapter", platform: "cursor", path: paths.cursorRoot }
75627
+ ]
75628
+ };
75629
+ const result = applySetupPreview(preview, {
75630
+ home,
75631
+ env,
75632
+ cwd: home,
75633
+ preferInstalledAdapter: true,
75634
+ runHostCommand: (command) => {
75635
+ const outcome = run(command.command, command.args);
75636
+ return {
75637
+ exitCode: outcome.status ?? 1,
75638
+ stdout: outcome.stdout,
75639
+ stderr: ""
75640
+ };
75641
+ }
75642
+ });
75643
+ const installed = objectOrNull(path44.join(paths.cursorRoot, "package.json"));
75644
+ if (!result.ok || installed?.version !== entry.latest)
75645
+ return fail("Cursor install did not verify the requested package version; inspect the installer before retrying");
75646
+ return {
75647
+ host: "cursor",
75648
+ status: "updated",
75649
+ reason: entry.repair ? `reinstalled missing plugin files (${entry.latest})` : `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75650
+ };
75651
+ }
75652
+ for (const command of entry.commands)
75653
+ if (run(command.command, command.args).status !== 0)
75654
+ return fail(`\`${command.command} ${command.args.join(" ")}\` failed`);
75655
+ if (entry.host === "opencode")
75656
+ return {
75657
+ host: "opencode",
75658
+ status: "updated",
75659
+ reason: "scoped update ran; OpenCode V2 loads it live"
75660
+ };
75661
+ if (entry.host === "claude-code") {
75662
+ const after = claudeWorkitInstalls(home, env, null).find((install) => install.id === entry.selector)?.version;
75663
+ return after && after === entry.installedVersion ? {
75664
+ host: "claude-code",
75665
+ status: "current",
75666
+ reason: `already current (${after})`
75667
+ } : {
75668
+ host: "claude-code",
75669
+ status: "updated",
75670
+ reason: `${entry.installedVersion ?? "?"} -> ${after ?? "?"}; new sessions load it`
75671
+ };
75672
+ }
75673
+ const verify = entry.host === "codex" ? run("codex", ["plugin", "list", "--json"]) : run("pi", ["list"]);
75674
+ if (verify.status !== 0)
75675
+ return fail("update verification failed");
75676
+ if (entry.host === "codex") {
75677
+ let installed = [];
75678
+ try {
75679
+ installed = JSON.parse(verify.stdout).installed ?? [];
75680
+ } catch {}
75681
+ if (!installed.some((plugin) => plugin.name === "workit" && plugin.marketplaceName === CODEX_MARKETPLACE && plugin.version === entry.latest))
75682
+ return fail("Codex did not report the requested Workit version");
75683
+ }
75684
+ if (entry.host === "pi" && piInstalledVersion(verify.stdout, entry.selector) !== entry.latest)
75685
+ return fail("Pi did not report the requested Workit package version");
75686
+ return {
75687
+ host: entry.host,
75688
+ status: "updated",
75689
+ reason: `${entry.installedVersion ?? "?"} -> ${entry.latest}`
75690
+ };
75691
+ }, refreshCodexAgents = (run, codexHome) => {
75692
+ const root = codexPluginRoot(codexHome);
75693
+ const launcher = root && path44.join(root, "dist", "launch-mcp.js");
75694
+ if (!launcher || !existsSync20(launcher))
75695
+ return { ok: true, note: "" };
75696
+ const fix = `node ${JSON.stringify(launcher)} --install-agents`;
75697
+ return run("node", [launcher, "--install-agents"]).status === 0 ? { ok: true, note: "agents refreshed" } : { ok: false, note: "agent refresh failed", fix };
75698
+ }, applyCli = (cli, run, backup) => {
75699
+ const fail = (reason) => ({
75700
+ host: "cli",
75701
+ status: "failed",
75702
+ reason,
75703
+ fix: CLI_FIX
75704
+ });
75705
+ const currentRoot = run("npm", ["root", "--global"]);
75706
+ if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== cli.root)
75707
+ return fail("Global npm location changed; preview again");
75708
+ const cliBackup = path44.join(backup, "cli-package");
75709
+ cpSync5(path44.join(cli.root, "@brainervirus/workit-cli"), cliBackup, {
75710
+ recursive: true
75711
+ });
75712
+ if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${cli.latest}`]).status !== 0)
75713
+ return fail(`CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path44.join(cliBackup, "dist/index.js")}`);
75714
+ const installed = objectOrNull(path44.join(cli.root, "@brainervirus/workit-cli/package.json"));
75715
+ if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== cli.latest)
75716
+ return fail(`CLI update did not verify the requested version; preserved package at ${cliBackup}`);
75717
+ return {
75718
+ host: "cli",
75719
+ status: "updated",
75720
+ reason: `${cli.installed} -> ${cli.latest}`
75721
+ };
75722
+ }, planCli = (plan, run) => {
75723
+ const root = run("npm", ["root", "--global"]);
75724
+ if (root.status !== 0 || !path44.isAbsolute(root.stdout.trim())) {
75725
+ plan.skipped.push({
75726
+ host: "cli",
75727
+ reason: "global npm location could not be verified",
75728
+ fix: CLI_FIX
75729
+ });
75730
+ return;
75731
+ }
75732
+ const packageFile = path44.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
75733
+ const installed = objectOrNull(packageFile);
75734
+ if (installed?.name !== "@brainervirus/workit-cli" || lstatSync7(path44.dirname(packageFile)).isSymbolicLink()) {
75735
+ plan.skipped.push({
75736
+ host: "cli",
75737
+ reason: "no managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
75738
+ });
75739
+ return;
75740
+ }
75741
+ const latest = registryVersion(run, "@brainervirus/workit-cli");
75742
+ if (!latest)
75743
+ plan.failed.push({
75744
+ host: "cli",
75745
+ status: "failed",
75746
+ reason: "CLI registry version unavailable; existing installation preserved",
75747
+ fix: CLI_FIX
75748
+ });
75749
+ else if (!versionAtLeast(installed.version, latest)) {
75750
+ plan.cli = {
75751
+ latest,
75752
+ root: root.stdout.trim(),
75753
+ installed: installed.version
75754
+ };
75755
+ plan.configDigests[packageFile] = digest2(packageFile);
75756
+ } else
75757
+ plan.skipped.push({
75758
+ host: "cli",
75759
+ reason: `already current (${installed.version})`
75760
+ });
75761
+ }, pad2 = (text) => text.padEnd(12), summary = (plan, permissions) => {
75762
+ const lines = ["Workit upgrade plan:"];
75763
+ if (plan.cli)
75764
+ lines.push(` ${pad2("cli")}update ${plan.cli.installed} -> ${plan.cli.latest}`);
75765
+ for (const entry of plan.entries) {
75766
+ const what = entry.repair ? `reinstall missing plugin files (${entry.latest})` : entry.latest ? `update ${entry.installedVersion ?? "?"} -> ${entry.latest}` : `update (${entry.commands.map((c) => `${c.command} ${c.args.join(" ")}`).join(" && ")})`;
75767
+ lines.push(` ${pad2(entry.host)}${what}`);
75768
+ }
75769
+ for (const skipped of plan.skipped)
75770
+ lines.push(` ${pad2(skipped.host)}${skipped.reason.startsWith("already current") ? "" : "skip: "}${skipped.reason}${skipped.fix ? ` (run: ${skipped.fix})` : ""}`);
75771
+ for (const failed of plan.failed)
75772
+ lines.push(` ${pad2(failed.host)}cannot upgrade: ${failed.reason}${failed.fix ? ` (run: ${failed.fix})` : ""}`);
75773
+ for (const migration of plan.migrations)
75774
+ lines.push(` ${pad2("config")}${migration.description}`);
75775
+ for (const change of permissions)
75776
+ lines.push(` ${pad2("permissions")}refresh the ${change.host} block in ${change.file}`);
75777
+ for (const blocked of plan.blocked)
75778
+ lines.push(` blocked: ${blocked}`);
75779
+ for (const warning of plan.warnings ?? [])
75780
+ lines.push(` warning: ${warning}`);
75781
+ return lines.join(`
75782
+ `) + `
75783
+ `;
75784
+ }, LABEL, table = (hosts) => [
75785
+ "Result:",
75786
+ ...hosts.map((entry) => ` ${pad2(entry.host)}${LABEL[entry.status].padEnd(16)}${entry.reason}${entry.fix && entry.status !== "updated" && entry.status !== "current" ? `
75787
+ ${pad2("")}${"".padEnd(16)}fix: ${entry.fix}` : ""}`)
75788
+ ].join(`
75789
+ `) + `
75790
+ `, askConfirm = async (question, out) => {
75791
+ if (!process.stdin.isTTY) {
75792
+ out.write(`Not a terminal: rerun with --yes to apply.
75793
+ `);
75794
+ return false;
75795
+ }
75796
+ await Promise.resolve().then(() => init_prompt());
75797
+ return /^y(?:es)?$/i.test((await askLine(question)).trim());
75798
+ }, USAGE3 = `Usage: workit upgrade [--hosts=opencode,cursor,codex,pi,claude-code|none] [--cli] [--preview | --yes | --apply --confirm] [--json]
75799
+ `;
75364
75800
  var init_upgrade = __esm(() => {
75801
+ init_package();
75365
75802
  init_registration();
75366
75803
  init_setup_state();
75367
75804
  init_host_install();
@@ -75370,7 +75807,23 @@ var init_upgrade = __esm(() => {
75370
75807
  init_support_matrix();
75371
75808
  init_safe_write();
75372
75809
  init_host_permissions();
75373
- HOSTS2 = ["opencode", "cursor", "codex", "pi"];
75810
+ LAUNCH_HOSTS = ["opencode", "cursor", "codex", "pi"];
75811
+ HOSTS2 = [...LAUNCH_HOSTS, "claude-code"];
75812
+ LIVE_HOSTS = new Set(["opencode", "claude-code"]);
75813
+ OPENCODE_UPDATE = `opencode plugin update ${OPENCODE_PACKAGE}`;
75814
+ PROCESS_HOSTS = {
75815
+ claude: "claude-code",
75816
+ codex: "codex",
75817
+ cursor: "cursor",
75818
+ opencode: "opencode",
75819
+ pi: "pi"
75820
+ };
75821
+ LABEL = {
75822
+ updated: "updated",
75823
+ current: "already current",
75824
+ skipped: "skipped",
75825
+ failed: "FAILED"
75826
+ };
75374
75827
  });
75375
75828
 
75376
75829
  // packages/workit-cli/src/verbs/upgrade.ts
@@ -76337,7 +76790,7 @@ function show(argv, io) {
76337
76790
  return emit(io, fail2("not_found", `no workspace named "${named[0]}" in ${listed.path}`));
76338
76791
  return emit(io, ok2({ path: listed.path, defaults: DEFAULT_GRANTS, workspaces: rows }), () => rows.flatMap((entry) => [
76339
76792
  `${entry.name} (${entry.glob})`,
76340
- ...describe2(entry.grants, entry.configured, entry, entry.verification),
76793
+ ...describe3(entry.grants, entry.configured, entry, entry.verification),
76341
76794
  ...endpointMismatch({ ...entry, workspace: entry.name }) ? [`note: ${endpointMismatch({ ...entry, workspace: entry.name })}`] : []
76342
76795
  ]));
76343
76796
  }
@@ -76358,7 +76811,7 @@ function show(argv, io) {
76358
76811
  ...data.note ? [`note: ${data.note}`] : [],
76359
76812
  ...tierModelIssues.map((issue) => `note: ${issue}`),
76360
76813
  data.workspace ? `workspace ${data.workspace} (${data.source === "default" ? "D4 defaults" : `from ${data.source}`})` : "no workspace matches this checkout: D4 defaults apply",
76361
- ...describe2(data.grants, data.configured, data, data.verification),
76814
+ ...describe3(data.grants, data.configured, data, data.verification),
76362
76815
  ...endpointMismatch(data) ? [`note: ${endpointMismatch(data)}`] : [],
76363
76816
  ...describeReleaseTracks(tracks).map((line) => ` ${line}`),
76364
76817
  "raising a grant needs the user: workit grant set <workspace> <kind>=<value>"
@@ -76433,7 +76886,7 @@ async function change(verb, argv, io, deps) {
76433
76886
  return emit(io, fail2(written.code, written.error));
76434
76887
  return emit(io, ok2(written), (data) => [
76435
76888
  `${data.workspace}: grants updated in ${data.path}${data.backup ? ` (previous copy: ${data.backup})` : ""}`,
76436
- ...describe2(data.grants, data.configured, data, data.verification)
76889
+ ...describe3(data.grants, data.configured, data, data.verification)
76437
76890
  ]);
76438
76891
  }
76439
76892
  async function runGrant(argv, io, deps = defaultDeps) {
@@ -76447,13 +76900,13 @@ async function runGrant(argv, io, deps = defaultDeps) {
76447
76900
  return change(sub, rest, io, deps);
76448
76901
  return usage2(io, `unknown subcommand "${sub}"`);
76449
76902
  }
76450
- var USAGE3 = "workit grant show [<workspace>] [--all] | grant set <workspace> <kind>=<true|false|verified>… [defaultEndpoint=commit|pr|green|merged] [verification=proportional|self|independent] | grant unset <workspace> <kind>… (kinds: push, pr, merge, release, rerun, defaultEndpoint, verification)", AGENT_ENV, agentMarker = (env) => AGENT_ENV.find((name) => env[name]) ?? Object.keys(env).find((name) => name.startsWith("CODEX_") && env[name]), defaultDeps, parseValue = (raw) => raw === "true" || raw === "yes" || raw === "on" ? true : raw === "false" || raw === "no" || raw === "off" ? false : raw === "verified" ? "verified" : null, STOPS_AT, endpointLabel = (view) => view.effectiveEndpoint === view.defaultEndpoint ? view.defaultEndpoint : `${view.defaultEndpoint} (effective: ${view.effectiveEndpoint}${view.endpointReason ? `, ${view.endpointReason}` : ""})`, VERIFICATION_STRICTNESS, VERIFICATION_TEXT, describe2 = (grants, configured, endpoint, verification) => [
76903
+ var USAGE4 = "workit grant show [<workspace>] [--all] | grant set <workspace> <kind>=<true|false|verified>… [defaultEndpoint=commit|pr|green|merged] [verification=proportional|self|independent] | grant unset <workspace> <kind>… (kinds: push, pr, merge, release, rerun, defaultEndpoint, verification)", AGENT_ENV, agentMarker = (env) => AGENT_ENV.find((name) => env[name]) ?? Object.keys(env).find((name) => name.startsWith("CODEX_") && env[name]), defaultDeps, parseValue = (raw) => raw === "true" || raw === "yes" || raw === "on" ? true : raw === "false" || raw === "no" || raw === "off" ? false : raw === "verified" ? "verified" : null, STOPS_AT, endpointLabel = (view) => view.effectiveEndpoint === view.defaultEndpoint ? view.defaultEndpoint : `${view.defaultEndpoint} (effective: ${view.effectiveEndpoint}${view.endpointReason ? `, ${view.endpointReason}` : ""})`, VERIFICATION_STRICTNESS, VERIFICATION_TEXT, describe3 = (grants, configured, endpoint, verification) => [
76451
76904
  ...GRANT_KINDS.map((kind) => ` ${kind.padEnd(8)}${String(grants[kind]).padEnd(10)}${configured.includes(kind) ? "configured" : "default"}${kind === "release" ? " (workit git push --release: git-flow main and develop, landed release tags)" : ""}`),
76452
76905
  ` default endpoint: ${endpointLabel(endpoint)} (an unnamed request stops at ${STOPS_AT[endpoint.effectiveEndpoint]})`,
76453
76906
  ...endpoint.endpointIssue ? [` note: ${endpoint.endpointIssue}`] : [],
76454
76907
  ` verification: ${verification} (${VERIFICATION_TEXT[verification]})`
76455
76908
  ], usage2 = (io, message) => emit(io, fail2("invalid_input", `${message}
76456
- usage: ${USAGE3}`)), run10 = (argv, io) => runGrant(argv, io);
76909
+ usage: ${USAGE4}`)), run10 = (argv, io) => runGrant(argv, io);
76457
76910
  var init_grant = __esm(() => {
76458
76911
  init_autonomy();
76459
76912
  init_release_tracks();
@@ -76668,7 +77121,7 @@ async function runWorkspace(argv, io) {
76668
77121
  return set2(rest, io);
76669
77122
  return usage3(io, `unknown subcommand "${sub}"`);
76670
77123
  }
76671
- var USAGE4 = "workit workspace list | workspace add <name> --glob <g> [--from <parent>] | workspace set <name> <key>=<value>…", INHERITED_KEYS, GRANT_KEYS, SCOPE_KEYS, SETTABLE_KEYS, SEGMENT, UNSAFE_SEGMENTS, ceilingOf = (entry) => ({
77124
+ var USAGE5 = "workit workspace list | workspace add <name> --glob <g> [--from <parent>] | workspace set <name> <key>=<value>…", INHERITED_KEYS, GRANT_KEYS, SCOPE_KEYS, SETTABLE_KEYS, SEGMENT, UNSAFE_SEGMENTS, ceilingOf = (entry) => ({
76672
77125
  grants: { ...DEFAULT_GRANTS, ...configuredGrants(entry).grants },
76673
77126
  endpoint: isDefaultEndpoint(entry?.defaultEndpoint) ? entry.defaultEndpoint : DEFAULT_ENDPOINT,
76674
77127
  verification: verificationOf(entry)
@@ -76693,7 +77146,7 @@ var USAGE4 = "workit workspace list | workspace add <name> --glob <g> [--from <p
76693
77146
  ...looser(shadowed.verification, DEFAULT_VERIFICATION) ? { verification: shadowed.verification } : {}
76694
77147
  };
76695
77148
  }, usage3 = (io, message) => emit(io, fail2("invalid_input", `${message}
76696
- usage: ${USAGE4}`)), load = (io) => {
77149
+ usage: ${USAGE5}`)), load = (io) => {
76697
77150
  const current = readWorkspacesResult();
76698
77151
  if (current.status === "malformed" || current.status === "invalid")
76699
77152
  return emit(io, fail2("invalid_input", current.error ?? `${current.path} is invalid`));
@@ -77939,7 +78392,7 @@ async function prepareHeavy(io, settings, label, runCwd, timeoutMs) {
77939
78392
  async function run12(argv, io) {
77940
78393
  const options = parse6(argv);
77941
78394
  if (options instanceof Error)
77942
- return emit(io, fail2("invalid_input", options.message, { unblock: USAGE5 }));
78395
+ return emit(io, fail2("invalid_input", options.message, { unblock: USAGE6 }));
77943
78396
  const config = loadCheckConfig(io.cwd);
77944
78397
  if (config.note)
77945
78398
  io.stderr(`note: ${config.note}
@@ -77950,7 +78403,7 @@ async function run12(argv, io) {
77950
78403
  let name = options.name;
77951
78404
  if (options.command === null) {
77952
78405
  if (options.positionals.length !== 1 || options.name || options.shell)
77953
- return emit(io, fail2("invalid_input", "pass a check name, or a command after --", { unblock: USAGE5 }));
78406
+ return emit(io, fail2("invalid_input", "pass a check name, or a command after --", { unblock: USAGE6 }));
77954
78407
  name = options.positionals[0];
77955
78408
  if (config.error)
77956
78409
  return emit(io, fail2("invalid_input", `check config is invalid: ${config.error}`));
@@ -77965,11 +78418,11 @@ async function run12(argv, io) {
77965
78418
  } else {
77966
78419
  if (options.positionals.length)
77967
78420
  return emit(io, fail2("invalid_input", `unexpected argument ${options.positionals[0]} before --`, {
77968
- unblock: USAGE5
78421
+ unblock: USAGE6
77969
78422
  }));
77970
78423
  command = options.command;
77971
78424
  if (!command.length)
77972
- return emit(io, fail2("invalid_input", "no command after --", { unblock: USAGE5 }));
78425
+ return emit(io, fail2("invalid_input", "no command after --", { unblock: USAGE6 }));
77973
78426
  if (options.shell && command.length !== 1)
77974
78427
  return emit(io, fail2("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
77975
78428
  }
@@ -78150,7 +78603,7 @@ async function run12(argv, io) {
78150
78603
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
78151
78604
  return 0;
78152
78605
  }
78153
- var USAGE5 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path50.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, ANSI_COLOR, WARNING_LINE, NO_WARNINGS, HOST_TOOL_WAIT_CAP_MS = 1e5;
78606
+ var USAGE6 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path50.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, ANSI_COLOR, WARNING_LINE, NO_WARNINGS, HOST_TOOL_WAIT_CAP_MS = 1e5;
78154
78607
  var init_check = __esm(() => {
78155
78608
  init_check_config();
78156
78609
  init_checks3();
@@ -78328,9 +78781,9 @@ async function run13(argv, io) {
78328
78781
  return runHeavy(rest, io);
78329
78782
  if (sub === "heavy")
78330
78783
  return heavy(rest, io);
78331
- return emit(io, fail2("invalid_input", `unknown lock subcommand ${sub ?? "(none)"}`, { unblock: USAGE6 }));
78784
+ return emit(io, fail2("invalid_input", `unknown lock subcommand ${sub ?? "(none)"}`, { unblock: USAGE7 }));
78332
78785
  }
78333
- var USAGE6 = "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) => {
78786
+ 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) => {
78334
78787
  const split = args.indexOf("--");
78335
78788
  return (split < 0 ? args : args.slice(split + 1)).join(" ").trim();
78336
78789
  };
@@ -78347,13 +78800,13 @@ var exports_models = {};
78347
78800
  __export(exports_models, {
78348
78801
  run: () => run14
78349
78802
  });
78350
- import { existsSync as existsSync22, readdirSync as readdirSync12, readFileSync as readFileSync22, statSync as statSync9 } from "node:fs";
78803
+ import { existsSync as existsSync22, readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync9 } from "node:fs";
78351
78804
  import os21 from "node:os";
78352
78805
  import path52 from "node:path";
78353
78806
  async function run14(argv, io) {
78354
78807
  const [sub, ...args] = argv.filter((arg) => arg !== "--json");
78355
78808
  if (sub !== "show")
78356
- return emit(io, fail2("invalid_input", `usage: ${USAGE7}`, { unblock: "workit help models" }));
78809
+ return emit(io, fail2("invalid_input", `usage: ${USAGE8}`, { unblock: "workit help models" }));
78357
78810
  const role = flag3(args, "--role");
78358
78811
  const tier = flag3(args, "--tier");
78359
78812
  const hostFlag = flag3(args, "--host");
@@ -78400,7 +78853,7 @@ async function run14(argv, io) {
78400
78853
  ...(data.issues ?? []).map((issue) => `note: ${issue}`)
78401
78854
  ]);
78402
78855
  }
78403
- var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>] [--report [--days <n>]] [--json]", flag3 = (argv, name) => {
78856
+ var USAGE8 = "workit models show [--role <role>] [--tier <tier>] [--host <host>] [--report [--days <n>]] [--json]", flag3 = (argv, name) => {
78404
78857
  const at = argv.findIndex((arg) => arg === name || arg.startsWith(`${name}=`));
78405
78858
  if (at < 0)
78406
78859
  return null;
@@ -78430,7 +78883,7 @@ var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
78430
78883
  let agents = 0;
78431
78884
  const dirs = (dir) => {
78432
78885
  try {
78433
- return readdirSync12(dir).map((name) => path52.join(dir, name));
78886
+ return readdirSync13(dir).map((name) => path52.join(dir, name));
78434
78887
  } catch {
78435
78888
  return [];
78436
78889
  }
@@ -85969,9 +86422,9 @@ async function run15(argv, io) {
85969
86422
  return threads(rest, io);
85970
86423
  if (sub === "reply")
85971
86424
  return reply(rest, io);
85972
- return usage4(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE8);
86425
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE9);
85973
86426
  }
85974
- var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--issue <#n|ID|url>]… [--refs] [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE8 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, NEED_TEXT, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : landed.lane === "none" && !landed.verdictId ? `merged at ${(landed.head ?? "?").slice(0, 12)} with no verdict (lane none)` : landed.review === "self-reviewed" ? `merged self-reviewed (lane self) with the author's verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, acceptedHow = (verdict) => verdict.cover ? `stack verdict ${verdict.cover.verdictId} on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)}` : verdict.carry ? carryLabel(verdict.carry) : verdict.basis, METHODS2;
86427
+ var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--issue <#n|ID|url>]… [--refs] [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE9 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, NEED_TEXT, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : landed.lane === "none" && !landed.verdictId ? `merged at ${(landed.head ?? "?").slice(0, 12)} with no verdict (lane none)` : landed.review === "self-reviewed" ? `merged self-reviewed (lane self) with the author's verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, acceptedHow = (verdict) => verdict.cover ? `stack verdict ${verdict.cover.verdictId} on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)}` : verdict.carry ? carryLabel(verdict.carry) : verdict.basis, METHODS2;
85975
86428
  var init_pr = __esm(() => {
85976
86429
  init_autonomy();
85977
86430
  init_issue_link();
@@ -86538,9 +86991,9 @@ async function run17(argv, io) {
86538
86991
  return push(rest, io);
86539
86992
  if (sub === "trailers")
86540
86993
  return trailers(rest, io);
86541
- return usage4(io, sub && !sub.startsWith("-") ? `unknown git subcommand "${sub}"` : "missing git subcommand", USAGE9);
86994
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown git subcommand "${sub}"` : "missing git subcommand", USAGE10);
86542
86995
  }
86543
- var BRANCH_USAGE = "workit git branch <name> | --kind feature|bugfix|release|hotfix --slug <s> [--base <b>] [--track <t>] [--carry] [--json]", COMMIT_USAGE = "workit git commit -m|--message <msg>… | -F|--file <file|-> [--trailer <key: value>]… [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>] [--json]", PUSH_USAGE = "workit git push [--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]] [--release <main|develop|v-tag>…] [--json]", USAGE9 = "workit git branch|commit|push|trailers ... (workit help git)", short2 = (sha) => sha ? sha.slice(0, 12) : "(none)", COMMIT_SWITCHES, READ_ONLY;
86996
+ var BRANCH_USAGE = "workit git branch <name> | --kind feature|bugfix|release|hotfix --slug <s> [--base <b>] [--track <t>] [--carry] [--json]", COMMIT_USAGE = "workit git commit -m|--message <msg>… | -F|--file <file|-> [--trailer <key: value>]… [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>] [--json]", PUSH_USAGE = "workit git push [--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]] [--release <main|develop|v-tag>…] [--json]", USAGE10 = "workit git branch|commit|push|trailers ... (workit help git)", short2 = (sha) => sha ? sha.slice(0, 12) : "(none)", COMMIT_SWITCHES, READ_ONLY;
86544
86997
  var init_git2 = __esm(() => {
86545
86998
  init_autonomy();
86546
86999
  init_branch();
@@ -87453,9 +87906,9 @@ async function run18(argv, io) {
87453
87906
  return finish2(rest, io);
87454
87907
  if (sub === "publish")
87455
87908
  return publish(rest, io);
87456
- return usage4(io, sub && !sub.startsWith("-") ? `unknown release subcommand "${sub}"` : "missing release subcommand", USAGE10);
87909
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown release subcommand "${sub}"` : "missing release subcommand", USAGE11);
87457
87910
  }
87458
- var START_USAGE = "workit release start <version> [--hotfix] [--track <t>] [--json]", FINISH_USAGE = "workit release finish [<version>] [--track <t>] [--dry-run] [--json]", PUBLISH_USAGE = "workit release publish [<version>] [--json]", USAGE10 = "workit release start|finish|publish ... (workit help release)", short4 = (sha) => sha ? sha.slice(0, 12) : "(none)", blocked = (io, grant) => emit(io, fail2("blocked", grant.error, { unblock: grant.unblock, data: { reason: grant.reason } })), isLocalRemote = (cwd, remote) => {
87911
+ var START_USAGE = "workit release start <version> [--hotfix] [--track <t>] [--json]", FINISH_USAGE = "workit release finish [<version>] [--track <t>] [--dry-run] [--json]", PUBLISH_USAGE = "workit release publish [<version>] [--json]", USAGE11 = "workit release start|finish|publish ... (workit help release)", short4 = (sha) => sha ? sha.slice(0, 12) : "(none)", blocked = (io, grant) => emit(io, fail2("blocked", grant.error, { unblock: grant.unblock, data: { reason: grant.reason } })), isLocalRemote = (cwd, remote) => {
87459
87912
  const url = remote ? pushUrl(cwd, remote) : null;
87460
87913
  return !url || parseRemoteUrl(url)?.protocol === "file";
87461
87914
  }, stepsOf = (plan) => [
@@ -87499,31 +87952,31 @@ async function run19(argv, io) {
87499
87952
  package: "value"
87500
87953
  });
87501
87954
  if (typeof flags === "string")
87502
- return usage4(io, flags, USAGE11);
87955
+ return usage4(io, flags, USAGE12);
87503
87956
  if (flags.positionals.length > 1)
87504
- return usage4(io, `unexpected argument ${flags.positionals[1]}`, USAGE11);
87957
+ return usage4(io, `unexpected argument ${flags.positionals[1]}`, USAGE12);
87505
87958
  const named = flags.positionals[0];
87506
87959
  if (named !== undefined && !ENDPOINTS[named])
87507
- return usage4(io, `unknown endpoint "${named}"`, USAGE11);
87960
+ return usage4(io, `unknown endpoint "${named}"`, USAGE12);
87508
87961
  if (flags.values.expect !== undefined && !ENDPOINTS[flags.values.expect])
87509
- return usage4(io, "--expect must be pushed, pr, merged or released", USAGE11);
87962
+ return usage4(io, "--expect must be pushed, pr, merged or released", USAGE12);
87510
87963
  if (named && flags.values.expect && ENDPOINTS[named] !== ENDPOINTS[flags.values.expect])
87511
- return usage4(io, `${named} and --expect ${flags.values.expect} disagree`, USAGE11);
87964
+ return usage4(io, `${named} and --expect ${flags.values.expect} disagree`, USAGE12);
87512
87965
  const expect = ENDPOINTS[named ?? ""] ?? ENDPOINTS[flags.values.expect ?? ""] ?? (flags.values.tag || flags.values.package ? "released" : flags.values.pr ? "pr" : "pushed");
87513
87966
  const pr = positiveInt(flags.values.pr, "--pr");
87514
87967
  if (typeof pr === "string")
87515
- return usage4(io, pr, USAGE11);
87968
+ return usage4(io, pr, USAGE12);
87516
87969
  if (pr && flags.values.branch)
87517
- return usage4(io, "pass --pr or --branch, not both", USAGE11);
87970
+ return usage4(io, "pass --pr or --branch, not both", USAGE12);
87518
87971
  const sha = flags.values.sha ?? null;
87519
87972
  if (sha !== null && !/^[0-9a-f]{7,64}$/u.test(sha))
87520
- return usage4(io, "--sha must be a commit sha (7-64 hex chars)", USAGE11);
87973
+ return usage4(io, "--sha must be a commit sha (7-64 hex chars)", USAGE12);
87521
87974
  const actor = actorFromEnv(io.env);
87522
87975
  const branch = flags.values.branch ?? null;
87523
87976
  let result;
87524
87977
  if (expect === "pushed") {
87525
87978
  if (pr)
87526
- return usage4(io, "push is checked by --branch, not --pr", USAGE11);
87979
+ return usage4(io, "push is checked by --branch, not --pr", USAGE12);
87527
87980
  result = verifyPushed(io.cwd, { branch, sha, actor });
87528
87981
  } else if (expect === "released") {
87529
87982
  result = verifyReleased(io.cwd, { tag: flags.values.tag ?? null, pkg: flags.values.package ?? null, sha, actor }, forgeDeps.npm);
@@ -87548,7 +88001,7 @@ async function run19(argv, io) {
87548
88001
  unblock: expect === "pushed" ? "workit git push" : expect === "pr" ? "workit git push && workit pr create --fill" : expect === "merged" ? "workit pr status # then workit pr merge when READY and granted" : "check the release workflow; the tag/package is not published"
87549
88002
  }));
87550
88003
  }
87551
- var USAGE11 = "workit verify-delivery [push|pr|merge|release] [--expect pushed|pr|merged|released] [--branch <b>] [--pr <n>] [--sha <sha>] [--tag <t>] [--package <name[@version]>] [--json]", ENDPOINTS, render2 = (report) => [
88004
+ var USAGE12 = "workit verify-delivery [push|pr|merge|release] [--expect pushed|pr|merged|released] [--branch <b>] [--pr <n>] [--sha <sha>] [--tag <t>] [--package <name[@version]>] [--json]", ENDPOINTS, render2 = (report) => [
87552
88005
  `${report.expect}: ${report.delivered ? "delivered" : "NOT delivered"}${report.branch ? ` (${report.branch}${report.sha ? ` @ ${report.sha.slice(0, 12)}` : ""})` : ""}${report.pr ? ` #${report.pr}` : ""}`,
87553
88006
  ...report.observations.map((entry) => ` ${entry.ok ? "ok" : "x "} ${entry.kind}: expected ${entry.expected ?? "-"}, observed ${entry.observed ?? "(none)"}${entry.note ? ` — ${entry.note}` : ""}`)
87554
88007
  ];
@@ -87785,9 +88238,9 @@ async function run20(argv, io) {
87785
88238
  return sync(rest, io);
87786
88239
  if (sub === "land")
87787
88240
  return land(rest, io);
87788
- return usage4(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE12);
88241
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE13);
87789
88242
  }
87790
- var PLAN_USAGE = "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>] [--json]", STATUS_USAGE2 = "workit stack status [--name <n>] [--json]", SYNC_USAGE = "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]… [--json]", LAND_USAGE = "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--timeout 20m] [--interval 30s] [--json]", USAGE12 = "workit stack plan|status|sync|land ... (workit help stack)", short5 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail2(result.code, result.error, {
88243
+ var PLAN_USAGE = "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>] [--json]", STATUS_USAGE2 = "workit stack status [--name <n>] [--json]", SYNC_USAGE = "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]… [--json]", LAND_USAGE = "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--timeout 20m] [--interval 30s] [--json]", USAGE13 = "workit stack plan|status|sync|land ... (workit help stack)", short5 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail2(result.code, result.error, {
87791
88244
  unblock: result.unblock,
87792
88245
  data: result.data ?? {}
87793
88246
  })), label3 = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
@@ -88866,9 +89319,9 @@ async function run21(argv, io) {
88866
89319
  return status4(rest, io);
88867
89320
  if (sub === "worktree")
88868
89321
  return worktree(rest, io);
88869
- return usage4(io, sub && !sub.startsWith("-") ? `unknown fanout subcommand "${sub}"` : "missing fanout subcommand", USAGE13);
89322
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown fanout subcommand "${sub}"` : "missing fanout subcommand", USAGE14);
88870
89323
  }
88871
- var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] [--json]", BRIEF_USAGE = "workit fanout brief <slice> [--name <n>] [--mode new|resume] [--attempt <n>] [--json]", CHECK_USAGE = "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] [--json]", STATUS_USAGE3 = "workit fanout status [--name <n>] [--stuck-after 30m] [--offline] [--json]", WORKTREE_USAGE = "workit fanout worktree create <slice> [--name <n>] | worktree release <slice> [--name <n>] [--force] [--json]", USAGE13 = "workit fanout plan|brief|check|status|worktree ... (workit help fanout)", failed = (io, result) => emit(io, fail2(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), renderCheck = (data) => [
89324
+ var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] [--json]", BRIEF_USAGE = "workit fanout brief <slice> [--name <n>] [--mode new|resume] [--attempt <n>] [--json]", CHECK_USAGE = "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] [--json]", STATUS_USAGE3 = "workit fanout status [--name <n>] [--stuck-after 30m] [--offline] [--json]", WORKTREE_USAGE = "workit fanout worktree create <slice> [--name <n>] | worktree release <slice> [--name <n>] [--force] [--json]", USAGE14 = "workit fanout plan|brief|check|status|worktree ... (workit help fanout)", failed = (io, result) => emit(io, fail2(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), renderCheck = (data) => [
88872
89325
  `fanout ${data.name} against ${data.trunkRef}: ${data.ready ? "ready to land" : "blocked"}`,
88873
89326
  ...data.slices.map((slice) => slice.status === "landed" ? ` -- ${slice.id} (${slice.branch}) landed (${slice.landed?.how}${slice.landed?.pr ? ` #${slice.landed.pr}` : ""})` : ` ${slice.status === "ready" ? "ok " : "x "}${slice.id} (${slice.branch}) ${slice.changed} file${slice.changed === 1 ? "" : "s"} · verdict ${slice.verdict.accepted ? "accepted" : slice.verdict.basis}${slice.reasons.length ? `
88874
89327
  ${slice.reasons.join(`
@@ -89166,7 +89619,7 @@ async function standing(io, values, args) {
89166
89619
  return usage5(io, `unexpected argument: ${rest[1]}`);
89167
89620
  return fromResult(io, clearStanding(context, { fanout, lead, leads, target: rest[0] ?? null }), (row) => row.target ? `cleared standing order ${row.target} for fanout ${fanout}` : `cleared every standing order for fanout ${fanout}`);
89168
89621
  }
89169
- var USAGE14 = "workit ledger decision|ruling|verdict|standing|list|check ... (workit help ledger for the grammar)", STANDING_USAGE = 'workit ledger standing add "<order>" | add --stop ["<note>"] | add --clear | list | clear [<id>] [--fanout <name>] [--json]', OPTIONS, SESSION_SAFE2, ROLE_SAFE, actorFor = (io, values) => {
89622
+ var USAGE15 = "workit ledger decision|ruling|verdict|standing|list|check ... (workit help ledger for the grammar)", STANDING_USAGE = 'workit ledger standing add "<order>" | add --stop ["<note>"] | add --clear | list | clear [<id>] [--fanout <name>] [--json]', OPTIONS, SESSION_SAFE2, ROLE_SAFE, actorFor = (io, values) => {
89170
89623
  const actor = actorFromEnv(io.env);
89171
89624
  if (values.session !== undefined && values.as !== undefined)
89172
89625
  return new Error("pass --session or --as, not both");
@@ -89193,7 +89646,7 @@ var USAGE14 = "workit ledger decision|ruling|verdict|standing|list|check ... (wo
89193
89646
  return;
89194
89647
  const parsed = Number(value);
89195
89648
  return Number.isInteger(parsed) && parsed > 0 ? parsed : new Error(`${flag} must be a positive integer`);
89196
- }, failed2 = (io, result) => emit(io, fail2(result.code, result.error, result.unblock ? { unblock: result.unblock } : {})), fromResult = (io, result, human) => result.ok ? emit(io, ok2(result.value), human) : failed2(io, result), usage5 = (io, error) => emit(io, fail2("invalid_input", error, { unblock: USAGE14 })), passedTestOn = (cwd, verdict) => {
89649
+ }, failed2 = (io, result) => emit(io, fail2(result.code, result.error, result.unblock ? { unblock: result.unblock } : {})), fromResult = (io, result, human) => result.ok ? emit(io, ok2(result.value), human) : failed2(io, result), usage5 = (io, error) => emit(io, fail2("invalid_input", error, { unblock: USAGE15 })), passedTestOn = (cwd, verdict) => {
89197
89650
  const ledger = readLedger(cwd);
89198
89651
  return ledger.ok && ledger.value.rows.some((row) => row.type === "check" && row.observer === "workit_cli" && row.name === "test" && row.result === "passed" && row.dirty !== true && row.branch === verdict.branch && row.head === verdict.head);
89199
89652
  }, verdictLines = (check) => {
@@ -105126,7 +105579,7 @@ var init_rules = __esm(() => {
105126
105579
 
105127
105580
  // packages/workit-cli/src/test-audit/audit.ts
105128
105581
  import { spawnSync as spawnSync29 } from "node:child_process";
105129
- import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync27, statSync as statSync10 } from "node:fs";
105582
+ import { existsSync as existsSync24, readdirSync as readdirSync14, readFileSync as readFileSync27, statSync as statSync10 } from "node:fs";
105130
105583
  import path68 from "node:path";
105131
105584
  function git7(cwd, args) {
105132
105585
  const run = spawnSync29("git", ["-c", "core.quotepath=off", ...args], {
@@ -105137,7 +105590,7 @@ function git7(cwd, args) {
105137
105590
  return { ok: run.status === 0, stdout: run.stdout ?? "" };
105138
105591
  }
105139
105592
  function walkTests(dir, out) {
105140
- for (const entry of readdirSync13(dir, { withFileTypes: true })) {
105593
+ for (const entry of readdirSync14(dir, { withFileTypes: true })) {
105141
105594
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
105142
105595
  continue;
105143
105596
  const full = path68.join(dir, entry.name);
@@ -105273,7 +105726,7 @@ import {
105273
105726
  lstatSync as lstatSync8,
105274
105727
  mkdirSync as mkdirSync12,
105275
105728
  mkdtempSync as mkdtempSync4,
105276
- readdirSync as readdirSync14,
105729
+ readdirSync as readdirSync15,
105277
105730
  readFileSync as readFileSync28,
105278
105731
  readlinkSync as readlinkSync3,
105279
105732
  realpathSync as realpathSync10,
@@ -105370,7 +105823,7 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
105370
105823
  let removed = 0;
105371
105824
  let names = [];
105372
105825
  try {
105373
- names = readdirSync14(tmp);
105826
+ names = readdirSync15(tmp);
105374
105827
  } catch {
105375
105828
  return 0;
105376
105829
  }
@@ -105399,7 +105852,7 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
105399
105852
  }
105400
105853
  function linkNodeModules(origin, target, repo, copy) {
105401
105854
  mkdirSync12(target, { recursive: true });
105402
- for (const entry of readdirSync14(origin, { withFileTypes: true })) {
105855
+ for (const entry of readdirSync15(origin, { withFileTypes: true })) {
105403
105856
  const from = path69.join(origin, entry.name);
105404
105857
  const to = path69.join(target, entry.name);
105405
105858
  if (entry.name.startsWith("@") && entry.isDirectory()) {
@@ -105842,7 +106295,7 @@ async function run23(argv, io) {
105842
106295
  const options = parse8(argv);
105843
106296
  if (typeof options === "string")
105844
106297
  return emit(io, fail2("invalid_input", `${options}
105845
- usage: ${USAGE15}`));
106298
+ usage: ${USAGE16}`));
105846
106299
  const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
105847
106300
  const repo = top.ok ? path70.resolve(top.stdout.trim()) : null;
105848
106301
  const display = (file) => toPosix(path70.relative(io.cwd, file) || file);
@@ -105928,7 +106381,7 @@ usage: ${USAGE15}`));
105928
106381
  }
105929
106382
  return emit(io, ok2(data), human);
105930
106383
  }
105931
- var USAGE15 = "workit test-audit [paths…] [--diff [base]] [--rule <ids>] [--min-severity <level>] [--fail-on <level>] [--mutate [--test-cmd <cmd>] [--max-mutants <n>] [--budget <s>] [--timeout <s>]] [--json]", LEVELS, VALUE_FLAGS2, quote = (arg) => /^[\w./@:=+-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, "'\\''")}'`;
106384
+ var USAGE16 = "workit test-audit [paths…] [--diff [base]] [--rule <ids>] [--min-severity <level>] [--fail-on <level>] [--mutate [--test-cmd <cmd>] [--max-mutants <n>] [--budget <s>] [--timeout <s>]] [--json]", LEVELS, VALUE_FLAGS2, quote = (arg) => /^[\w./@:=+-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, "'\\''")}'`;
105932
106385
  var init_test_audit = __esm(() => {
105933
106386
  init_audit();
105934
106387
  init_mutate();
@@ -105954,7 +106407,7 @@ __export(exports_knowledge, {
105954
106407
  async function run24(argv, io) {
105955
106408
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
105956
106409
  if (sub !== "lint" || rest.length > 0)
105957
- return emit(io, fail2("invalid_input", `usage: ${USAGE16}`));
106410
+ return emit(io, fail2("invalid_input", `usage: ${USAGE17}`));
105958
106411
  const report = lintKnowledge(checkRoot(io.cwd));
105959
106412
  if (report.unreadable.length > 0)
105960
106413
  return emit(io, fail2("unavailable", `knowledge lint: cannot read ${report.unreadable.join(", ")}`, {
@@ -105971,7 +106424,7 @@ async function run24(argv, io) {
105971
106424
  `);
105972
106425
  return report.findings.length === 0 ? 0 : 1;
105973
106426
  }
105974
- var USAGE16 = "workit knowledge lint [--json]";
106427
+ var USAGE17 = "workit knowledge lint [--json]";
105975
106428
  var init_knowledge2 = __esm(() => {
105976
106429
  init_check_config();
105977
106430
  init_knowledge();
@@ -105988,7 +106441,7 @@ import path71 from "node:path";
105988
106441
  async function run25(argv, io) {
105989
106442
  const [sub, ...rest] = argv;
105990
106443
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
105991
- return usage4(io, `unknown subcommand "${sub ?? ""}"`, USAGE17);
106444
+ return usage4(io, `unknown subcommand "${sub ?? ""}"`, USAGE18);
105992
106445
  const flags = parseFlags(rest, {
105993
106446
  markdown: "value",
105994
106447
  file: "value",
@@ -105997,18 +106450,18 @@ async function run25(argv, io) {
105997
106450
  date: "value"
105998
106451
  });
105999
106452
  if (typeof flags === "string")
106000
- return usage4(io, flags, USAGE17);
106453
+ return usage4(io, flags, USAGE18);
106001
106454
  const [issueId, extra] = flags.positionals;
106002
106455
  if (!issueId || !ISSUE_RE2.test(issueId))
106003
- return usage4(io, "an issue id like ABC-123 is required", USAGE17);
106456
+ return usage4(io, "an issue id like ABC-123 is required", USAGE18);
106004
106457
  if (extra !== undefined)
106005
- return usage4(io, `unexpected argument ${extra}`, USAGE17);
106458
+ return usage4(io, `unexpected argument ${extra}`, USAGE18);
106006
106459
  const minutes = positiveInt(flags.values.minutes, "--minutes");
106007
106460
  if (typeof minutes === "string")
106008
- return usage4(io, minutes, USAGE17);
106461
+ return usage4(io, minutes, USAGE18);
106009
106462
  const date = youTrackWorkDateMs(flags.values.date ?? "auto");
106010
106463
  if ("error" in date)
106011
- return usage4(io, date.error, USAGE17);
106464
+ return usage4(io, date.error, USAGE18);
106012
106465
  const dateMs = date.data.dateMs;
106013
106466
  const markedDay = flags.values.date !== undefined && flags.values.date !== "auto" ? { dateMs } : {};
106014
106467
  const root = io.cwd;
@@ -106026,24 +106479,24 @@ async function run25(argv, io) {
106026
106479
  };
106027
106480
  if (sub === "note") {
106028
106481
  if (Boolean(flags.values.markdown) === Boolean(flags.values.file))
106029
- return usage4(io, "pass exactly one of --markdown or --file", USAGE17);
106482
+ return usage4(io, "pass exactly one of --markdown or --file", USAGE18);
106030
106483
  let markdown = flags.values.markdown ?? "";
106031
106484
  if (flags.values.file)
106032
106485
  try {
106033
106486
  markdown = readFileSync29(path71.resolve(io.cwd, flags.values.file), "utf8");
106034
106487
  } catch (error) {
106035
- return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE17);
106488
+ return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE18);
106036
106489
  }
106037
106490
  if (!markdown.trim())
106038
- return usage4(io, "the note is empty", USAGE17);
106491
+ return usage4(io, "the note is empty", USAGE18);
106039
106492
  const missing = step("comment", "comments", { operation: "note", issueId, markdown }, markdown, async (text) => commentResult(await postComment(issueId, text, root))) ?? (minutes ? step("time", "workItems", { operation: "note.time", issueId, markdown, minutes, ...markedDay }, "workit update", (text) => logTimeUpdate({ issueId, minutes, text, dateMs, workspace_root: root })) : null);
106040
106493
  if (missing)
106041
106494
  return emit(io, fail2("unavailable", missing));
106042
106495
  } else {
106043
106496
  if (!minutes)
106044
- return usage4(io, `${sub} needs --minutes <n>`, USAGE17);
106497
+ return usage4(io, `${sub} needs --minutes <n>`, USAGE18);
106045
106498
  if (sub === "meeting" && !flags.values.text)
106046
- return usage4(io, "meeting needs --text <what the meeting was>", USAGE17);
106499
+ return usage4(io, "meeting needs --text <what the meeting was>", USAGE18);
106047
106500
  const text = flags.values.text ?? "";
106048
106501
  const missing = step(sub, "workItems", { operation: sub, issueId, minutes, text, ...markedDay }, text, (marked) => logTimeUpdate({ issueId, minutes, text: marked, dateMs, workspace_root: root }));
106049
106502
  if (missing)
@@ -106072,7 +106525,7 @@ async function run25(argv, io) {
106072
106525
  ...data.steps.flatMap((entry) => entry.note ? [` note (${entry.step}): ${entry.note}`] : [])
106073
106526
  ]);
106074
106527
  }
106075
- var USAGE17 = "workit youtrack note <ISSUE> (--markdown <t> | --file <p>) [--minutes <n>] [--date auto|YYYY-MM-DD] | youtrack time <ISSUE> --minutes <n> [--text <t>] [--date …] | youtrack meeting <ISSUE> --minutes <n> --text <t> [--date …]", commentResult = (value) => {
106528
+ var USAGE18 = "workit youtrack note <ISSUE> (--markdown <t> | --file <p>) [--minutes <n>] [--date auto|YYYY-MM-DD] | youtrack time <ISSUE> --minutes <n> [--text <t>] [--date …] | youtrack meeting <ISSUE> --minutes <n> --text <t> [--date …]", commentResult = (value) => {
106076
106529
  try {
106077
106530
  return { ok: true, data: unwrap(value) };
106078
106531
  } catch (error) {
@@ -106103,7 +106556,7 @@ import path72 from "node:path";
106103
106556
  async function run26(argv, io) {
106104
106557
  const [sub, ...rest] = argv;
106105
106558
  if (sub !== "apply")
106106
- return usage4(io, `unknown subcommand "${sub ?? ""}"`, USAGE18);
106559
+ return usage4(io, `unknown subcommand "${sub ?? ""}"`, USAGE19);
106107
106560
  const flags = parseFlags(rest, {
106108
106561
  entries: "value",
106109
106562
  path: "value",
@@ -106111,18 +106564,18 @@ async function run26(argv, io) {
106111
106564
  preview: "boolean"
106112
106565
  });
106113
106566
  if (typeof flags === "string")
106114
- return usage4(io, flags, USAGE18);
106567
+ return usage4(io, flags, USAGE19);
106115
106568
  if (flags.positionals.length)
106116
- return usage4(io, `unexpected argument ${flags.positionals[0]}`, USAGE18);
106569
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, USAGE19);
106117
106570
  const normalizeOnly = flags.booleans.has("normalize-only");
106118
106571
  if (!normalizeOnly && !flags.values.entries)
106119
- return usage4(io, "pass --entries <JSON> or --normalize-only", USAGE18);
106572
+ return usage4(io, "pass --entries <JSON> or --normalize-only", USAGE19);
106120
106573
  let entries;
106121
106574
  if (flags.values.entries)
106122
106575
  try {
106123
106576
  entries = readEntries(flags.values.entries, io.cwd);
106124
106577
  } catch (error) {
106125
- return usage4(io, `--entries is not valid JSON: ${String(error)}`, USAGE18);
106578
+ return usage4(io, `--entries is not valid JSON: ${String(error)}`, USAGE19);
106126
106579
  }
106127
106580
  const input = {
106128
106581
  entries,
@@ -106144,7 +106597,7 @@ async function run26(argv, io) {
106144
106597
  `changelog updated: ${String(applied.path ?? "CHANGELOG.md")}`
106145
106598
  ]);
106146
106599
  }
106147
- var USAGE18 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
106600
+ var USAGE19 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
106148
106601
  const text = raw === "-" ? readFileSync30(0, "utf8") : raw.startsWith("@") ? readFileSync30(path72.resolve(cwd, raw.slice(1)), "utf8") : raw;
106149
106602
  return JSON.parse(text);
106150
106603
  };
@@ -106239,12 +106692,12 @@ async function resumeBrief(argv, io) {
106239
106692
  strict: true
106240
106693
  }));
106241
106694
  } catch (error) {
106242
- return emit(io, fail2("invalid_input", error.message, { unblock: USAGE19 }));
106695
+ return emit(io, fail2("invalid_input", error.message, { unblock: USAGE20 }));
106243
106696
  }
106244
106697
  const last = values.last === undefined ? 10 : Number(values.last);
106245
106698
  if (!Number.isInteger(last) || last < 1)
106246
106699
  return emit(io, fail2("invalid_input", "--last must be a positive integer", {
106247
- unblock: USAGE19
106700
+ unblock: USAGE20
106248
106701
  }));
106249
106702
  const built = buildHandoff(io.cwd, {
106250
106703
  last,
@@ -106269,7 +106722,7 @@ async function run27(argv, io) {
106269
106722
  await Promise.resolve().then(() => init_task());
106270
106723
  return runTaskCommand(["handoff", ...argv]);
106271
106724
  }
106272
- var USAGE19 = "workit handoff [--note <text>] [--next <text>] [--record] [--last <n>] [--json]", MAX_BRIEF_BYTES = 4096, short7 = (sha) => sha ? sha.slice(0, 12) : "no commit", bytes = (lines) => Buffer.byteLength(`${lines.join(`
106725
+ var USAGE20 = "workit handoff [--note <text>] [--next <text>] [--record] [--last <n>] [--json]", MAX_BRIEF_BYTES = 4096, short7 = (sha) => sha ? sha.slice(0, 12) : "no commit", bytes = (lines) => Buffer.byteLength(`${lines.join(`
106273
106726
  `)}
106274
106727
  `), TAIL_LINE_BYTES = 480, capLine = (line) => Buffer.byteLength(line) <= TAIL_LINE_BYTES ? line : `${Buffer.from(line).subarray(0, TAIL_LINE_BYTES).toString().replace(/\uFFFD$/u, "")}… (cut)`;
106275
106728
  var init_handoff = __esm(() => {
@@ -106414,8 +106867,8 @@ var VERBS = [
106414
106867
  {
106415
106868
  name: "upgrade",
106416
106869
  group: "setup",
106417
- usage: "workit upgrade [--hosts=a,b|none] [--cli] [--preview | --apply --confirm]",
106418
- summary: "Preview upgrades (--apply --confirm to apply)",
106870
+ usage: "workit upgrade [--hosts=a,b|none] [--cli] [--preview | --yes | --apply --confirm] [--json]",
106871
+ summary: "Upgrade the CLI and every host after one confirmation, then verify with doctor",
106419
106872
  load: () => Promise.resolve().then(() => (init_upgrade2(), exports_upgrade))
106420
106873
  },
106421
106874
  {
@@ -107224,7 +107677,7 @@ async function main(argv, overrides = {}, options = {}) {
107224
107677
  }));
107225
107678
  return help(io, subcommandHelp(entry, found));
107226
107679
  }
107227
- return emit(io, ok2({ version: package_default.version, verbs: listed().map(describe3) }), () => helpText());
107680
+ return emit(io, ok2({ version: package_default.version, verbs: listed().map(describe4) }), () => helpText());
107228
107681
  }
107229
107682
  const entry = findVerb(command);
107230
107683
  if (!entry)
@@ -107359,7 +107812,7 @@ function endOfOptions(io, entry, args) {
107359
107812
  }
107360
107813
  var words2 = (args) => args.filter((arg) => !arg.startsWith("-"));
107361
107814
  var help = (io, doc) => emit(io, ok2(doc), (data) => data.text);
107362
- var describe3 = (entry) => ({
107815
+ var describe4 = (entry) => ({
107363
107816
  name: entry.name,
107364
107817
  group: entry.group,
107365
107818
  usage: entry.usage,