@brainervirus/workit-claude-code 8.1.0 → 8.1.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.
package/dist/workit.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@brainervirus/workit-cli",
68
- version: "8.1.0",
68
+ version: "8.1.1",
69
69
  private: false,
70
70
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
71
71
  keywords: [
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
103
103
  },
104
104
  dependencies: {
105
105
  "@babel/parser": "7.29.9",
106
- "@brainervirus/workit-core": "^8.1.0",
107
- "@brainervirus/workit-cursor": "^8.1.0",
108
- "@brainervirus/workit-opencode": "^8.1.0",
106
+ "@brainervirus/workit-core": "^8.1.1",
107
+ "@brainervirus/workit-cursor": "^8.1.1",
108
+ "@brainervirus/workit-opencode": "^8.1.1",
109
109
  "@inkjs/ui": "2.0.0",
110
110
  ink: "7.1.1",
111
111
  react: "19.2.8"
@@ -12831,12 +12831,13 @@ function pushForge(cwd, options = {}) {
12831
12831
  };
12832
12832
  const forge = deriveForge(url, options);
12833
12833
  if (!forge) {
12834
- const host = parseRemoteUrl(url)?.host || null;
12834
+ const parsed = parseRemoteUrl(url);
12835
+ const host = parsed?.host || null;
12835
12836
  return {
12836
12837
  ok: false,
12837
12838
  code: "unavailable",
12838
- error: host ? `cannot tell whether push host "${host}" is GitHub or GitLab` : `push URL ${redactRemote(url)} of remote "${remote}" is not a GitHub/GitLab URL`,
12839
- unblock: host ? `map the alias in ~/.ssh/config (Host ${host} / HostName github.com|gitlab.com) or set github.host / gitlab.host in ~/.config/workit/vcs.json` : `git remote set-url --push ${remote} <ssh-or-https-url>`
12839
+ error: host ? parsed?.protocol === "ssh" && !host.includes(".") ? `unsupported_forge: could not resolve ssh alias "${host}" to GitHub or GitLab; PR and CI verbs need one (workit git push still pushes with git)` : parsed?.protocol === "ssh" ? `unsupported_forge: ssh host "${host}" is not a GitHub/GitLab host or an alias workit can resolve; PR and CI verbs need one (workit git push still pushes with git)` : `unsupported_forge: cannot tell whether push host "${host}" is GitHub or GitLab; PR and CI verbs need one (workit git push still pushes with git)` : `push URL ${redactRemote(url)} of remote "${remote}" is not a GitHub/GitLab URL`,
12840
+ unblock: host ? `if ${host} is an ssh alias of GitHub/GitLab, add "Host ${host}" with "HostName github.com" (or gitlab.com) to ~/.ssh/config; if it is a self-hosted GitHub/GitLab, set github.host or gitlab.host in ~/.config/workit/vcs.json; other forges (Bitbucket, Gitea…) have no PR/CI verbs` : `git remote set-url --push ${remote} <ssh-or-https-url>`
12840
12841
  };
12841
12842
  }
12842
12843
  return { ok: true, remote, url: redactRemote(url), forge };
@@ -12847,8 +12848,8 @@ function forgeConflict(derived, configuredProvider) {
12847
12848
  return null;
12848
12849
  return {
12849
12850
  code: "blocked",
12850
- error: `forge_mismatch: push remote ${derived.host} is ${derived.kind} but the workspace vcs.provider is ${configured}`,
12851
- unblock: `set vcs.provider to "${derived.kind}" for this repo in ~/.config/workit/workspaces.json (workit-github-override), or push to a ${configured} remote`
12851
+ error: `forge_mismatch: push remote ${derived.host} is ${derived.kind} but the workspace vcs.provider is ${configured}, so its account and grants do not apply; PR and CI verbs stop here (workit git push still pushes with git)`,
12852
+ unblock: `add a workspace whose glob matches only this repo, with "vcs": {"provider": "${derived.kind}"} (and its account), to ~/.config/workit/workspaces.json; the most specific glob wins`
12852
12853
  };
12853
12854
  }
12854
12855
  var MAX_BUFFER, GIT_TIMEOUTS, git2 = (cwd, args, options = {}) => {
@@ -15235,6 +15236,7 @@ var init_commit_flavors = __esm(() => {
15235
15236
  var init_git = () => {};
15236
15237
 
15237
15238
  // packages/workit-core/src/core/workspaces.ts
15239
+ import { spawnSync as spawnSync8 } from "node:child_process";
15238
15240
  import { createHash as createHash8 } from "node:crypto";
15239
15241
  import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
15240
15242
  import path14 from "node:path";
@@ -15362,6 +15364,46 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
15362
15364
  };
15363
15365
  }
15364
15366
  return { ok: true };
15367
+ }, WorkspaceConfigError, mainCheckouts, mainCheckoutOf = (cwd) => {
15368
+ const cached = mainCheckouts.get(cwd);
15369
+ if (cached !== undefined)
15370
+ return cached;
15371
+ const env = { ...process.env };
15372
+ for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
15373
+ delete env[name];
15374
+ const git = (args) => {
15375
+ const run = spawnSync8("git", args, {
15376
+ cwd,
15377
+ env,
15378
+ encoding: "utf8",
15379
+ timeout: 5000,
15380
+ windowsHide: true
15381
+ });
15382
+ return run.status === 0 ? run.stdout : null;
15383
+ };
15384
+ let main = null;
15385
+ const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
15386
+ const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
15387
+ if (gitDir && common && path14.resolve(gitDir) !== path14.resolve(common)) {
15388
+ const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
15389
+ main = listed?.trim() || (path14.basename(common) === ".git" ? path14.dirname(common) : common);
15390
+ }
15391
+ mainCheckouts.set(cwd, main);
15392
+ return main;
15393
+ }, matchingEntries = (cwd, entries) => {
15394
+ const match = (where) => {
15395
+ const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
15396
+ return entries.filter((entry) => {
15397
+ const glob = entry.glob.replaceAll("\\", "/");
15398
+ const canonical = canonicalGlob(glob);
15399
+ return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
15400
+ });
15401
+ };
15402
+ const direct = match(cwd);
15403
+ if (direct.length)
15404
+ return direct;
15405
+ const main = mainCheckoutOf(cwd);
15406
+ return main ? match(main) : direct;
15365
15407
  }, realpathOf = (p) => {
15366
15408
  const real = realpathSync3.native ?? realpathSync3;
15367
15409
  try {
@@ -15383,41 +15425,27 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
15383
15425
  if (named.length === 1)
15384
15426
  return named[0];
15385
15427
  if (named.length > 1)
15386
- throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
15428
+ throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
15387
15429
  if (matches.length > 0) {
15388
- throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
15430
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
15389
15431
  }
15390
- throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
15432
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
15391
15433
  }
15392
15434
  if (matches.length < 2)
15393
15435
  return matches[0];
15394
15436
  const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
15395
15437
  const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
15396
15438
  if (mostSpecific.length > 1) {
15397
- throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
15439
+ throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
15398
15440
  }
15399
15441
  return mostSpecific[0];
15400
15442
  }, resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
15401
- const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
15402
- const matches = [];
15403
- for (const ws of entries) {
15404
- const glob = ws.glob.replaceAll("\\", "/");
15405
- const canonical = canonicalGlob(glob);
15406
- let matched = false;
15407
- for (const target of targets) {
15408
- if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
15409
- matched = true;
15410
- break;
15411
- }
15412
- }
15413
- if (matched)
15414
- matches.push(ws);
15415
- }
15443
+ const matches = matchingEntries(cwd, entries);
15416
15444
  return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
15417
15445
  }, resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
15418
15446
  const result = readWorkspacesResult(dir);
15419
15447
  if (result.status === "malformed" || result.status === "invalid")
15420
- throw new Error(result.error);
15448
+ throw new WorkspaceConfigError(result.error);
15421
15449
  return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
15422
15450
  }, runtimeWorkspacePolicySchema = (kind) => {
15423
15451
  const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
@@ -15443,31 +15471,26 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
15443
15471
  } catch (error) {
15444
15472
  if (error.code === "ENOENT")
15445
15473
  return null;
15446
- throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
15474
+ throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
15447
15475
  }
15448
15476
  let parsed;
15449
15477
  try {
15450
15478
  parsed = JSON.parse(raw);
15451
15479
  } catch {
15452
- throw new Error(`${file} is not valid JSON`);
15480
+ throw new WorkspaceConfigError(`${file} is not valid JSON`);
15453
15481
  }
15454
15482
  const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
15455
15483
  if (!validated.success) {
15456
15484
  const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
15457
- throw new Error(`${file} has invalid workspace matching data: ${detail}`);
15485
+ throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
15458
15486
  }
15459
15487
  const entries = validated.data.workspaces;
15460
15488
  for (const entry of entries ?? []) {
15461
15489
  const glob = validateWorkspaceGlob(entry.glob);
15462
15490
  if (!glob.ok)
15463
- throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
15491
+ throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
15464
15492
  }
15465
- const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
15466
- const matches = (entries ?? []).filter((entry) => {
15467
- const glob = entry.glob.replaceAll("\\", "/");
15468
- const canonical = canonicalGlob(glob);
15469
- return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
15470
- });
15493
+ const matches = matchingEntries(cwd, entries ?? []);
15471
15494
  const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
15472
15495
  const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
15473
15496
  return selected ? { file, selected } : null;
@@ -15482,7 +15505,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
15482
15505
  const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
15483
15506
  if (!policy.success) {
15484
15507
  const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
15485
- throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
15508
+ throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
15486
15509
  }
15487
15510
  return policy.data;
15488
15511
  }, resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
@@ -15492,10 +15515,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
15492
15515
  const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
15493
15516
  const profileName = requestedProfile?.trim() || candidate.defaultProfile;
15494
15517
  if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
15495
- throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
15518
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
15496
15519
  const profile = profileName ? candidate.profiles?.[profileName] : undefined;
15497
15520
  if (profileName && !profile)
15498
- throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
15521
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
15499
15522
  const profilePolicy = profile?.[key];
15500
15523
  const workspacePolicy = candidate[key];
15501
15524
  return {
@@ -15666,6 +15689,10 @@ var init_workspaces = __esm(() => {
15666
15689
  });
15667
15690
  UNSUPPORTED_GLOB = /[?[\]{}]/;
15668
15691
  UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
15692
+ WorkspaceConfigError = class WorkspaceConfigError extends Error {
15693
+ name = "WorkspaceConfigError";
15694
+ };
15695
+ mainCheckouts = new Map;
15669
15696
  runtimeWorkspaceIndexSchema = object({
15670
15697
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
15671
15698
  }).passthrough();
@@ -15673,7 +15700,7 @@ var init_workspaces = __esm(() => {
15673
15700
 
15674
15701
  // packages/workit-core/src/core/release-tracks.ts
15675
15702
  import { createHash as createHash9 } from "node:crypto";
15676
- import { spawnSync as spawnSync8 } from "node:child_process";
15703
+ import { spawnSync as spawnSync9 } from "node:child_process";
15677
15704
  import { readFileSync as readFileSync5 } from "node:fs";
15678
15705
  function readReleaseTracks(raw) {
15679
15706
  const out = { tracks: [], issues: [], error: null, protectedBranches: [] };
@@ -16075,7 +16102,7 @@ var ReleaseTrackError, KNOWN_FIELDS, BRANCH_FIELDS, PLACEHOLDER, HAS_PLACEHOLDER
16075
16102
  const remote = remotes.find((name) => bare.startsWith(`${name}/`));
16076
16103
  return remote ? bare.slice(remote.length + 1) : bare;
16077
16104
  }, MAX_RECORDED_HOPS = 10, git4 = (cwd, args) => {
16078
- const run = spawnSync8("git", args, {
16105
+ const run = spawnSync9("git", args, {
16079
16106
  cwd,
16080
16107
  encoding: "utf8",
16081
16108
  timeout: 20000,
@@ -16136,7 +16163,7 @@ var init_release_tracks = __esm(() => {
16136
16163
  // packages/workit-core/src/core/vcs-config.ts
16137
16164
  import fs12 from "node:fs";
16138
16165
  import path15 from "node:path";
16139
- import { spawnSync as spawnSync9 } from "node:child_process";
16166
+ import { spawnSync as spawnSync10 } from "node:child_process";
16140
16167
  function vcsConfig(mode, cwd, options = {}) {
16141
16168
  const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
16142
16169
  const wsVcs = ws?.vcs ?? {};
@@ -16219,7 +16246,7 @@ function vcsConfig(mode, cwd, options = {}) {
16219
16246
  return out;
16220
16247
  }
16221
16248
  var workspacesPath = () => path15.join(configDir(), "workspaces.json"), vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), remoteProvider = (cwd) => {
16222
- const r = spawnSync9("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
16249
+ const r = spawnSync10("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
16223
16250
  if (r.status !== 0)
16224
16251
  return null;
16225
16252
  const url = (r.stdout ?? "").trim();
@@ -18535,7 +18562,7 @@ var init_pr_body = __esm(() => {
18535
18562
 
18536
18563
  // packages/workit-core/src/core/pr-create.ts
18537
18564
  import path19 from "node:path";
18538
- import { spawnSync as spawnSync10 } from "node:child_process";
18565
+ import { spawnSync as spawnSync11 } from "node:child_process";
18539
18566
  import { pathToFileURL } from "node:url";
18540
18567
  var safePushUrl = (remote) => {
18541
18568
  const raw = remote.trim();
@@ -18563,7 +18590,7 @@ var safePushUrl = (remote) => {
18563
18590
  const target = safePushUrl(remote);
18564
18591
  if (!target)
18565
18592
  return false;
18566
- const result = spawnSync10("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
18593
+ const result = spawnSync11("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
18567
18594
  if (result.status !== 0 && result.status !== 1)
18568
18595
  return false;
18569
18596
  const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
@@ -62851,7 +62878,7 @@ var init_support_matrix = __esm(() => {
62851
62878
  });
62852
62879
 
62853
62880
  // packages/workit-cli/src/admin/host-install.ts
62854
- import { spawnSync as spawnSync11 } from "node:child_process";
62881
+ import { spawnSync as spawnSync12 } from "node:child_process";
62855
62882
  import {
62856
62883
  accessSync,
62857
62884
  constants as constants2,
@@ -63122,7 +63149,7 @@ function runHostCommand(step, context = {}) {
63122
63149
  stderr: `Cannot safely execute batch shim ${step.command}; resolve its Node.js CLI entrypoint and retry`
63123
63150
  };
63124
63151
  }
63125
- const result = spawnSync11(step.command, step.args, {
63152
+ const result = spawnSync12(step.command, step.args, {
63126
63153
  cwd: step.cwd,
63127
63154
  env: (() => {
63128
63155
  const home = context.home ?? context.env?.HOME ?? os6.homedir();
@@ -63327,7 +63354,7 @@ var init_skill_manifests = __esm(() => {
63327
63354
  });
63328
63355
 
63329
63356
  // packages/workit-cli/src/admin/doctor.ts
63330
- import { spawnSync as spawnSync12 } from "node:child_process";
63357
+ import { spawnSync as spawnSync13 } from "node:child_process";
63331
63358
  import {
63332
63359
  existsSync as existsSync13,
63333
63360
  lstatSync as lstatSync4,
@@ -63411,7 +63438,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63411
63438
  }
63412
63439
  return false;
63413
63440
  }, versionOf = (bin, env, timeout) => {
63414
- const r = spawnSync12(bin, ["--version"], { encoding: "utf8", env, timeout });
63441
+ const r = spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout });
63415
63442
  if (r.error)
63416
63443
  return null;
63417
63444
  return (r.stdout ?? "").trim();
@@ -63660,7 +63687,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63660
63687
  `)) {
63661
63688
  return false;
63662
63689
  }
63663
- return spawnSync12(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
63690
+ return spawnSync13(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
63664
63691
  } catch {
63665
63692
  return false;
63666
63693
  }
@@ -63874,7 +63901,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63874
63901
  if (!cmd)
63875
63902
  return null;
63876
63903
  try {
63877
- const r = spawnSync12(cmd, ["view", pkgName, "version"], {
63904
+ const r = spawnSync13(cmd, ["view", pkgName, "version"], {
63878
63905
  encoding: "utf8",
63879
63906
  timeout: 20000,
63880
63907
  env: res.env
@@ -64313,7 +64340,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64313
64340
  }, defaultIdentityProbes = (env) => ({
64314
64341
  origin: (cwd) => {
64315
64342
  try {
64316
- const out = spawnSync12("git", ["remote", "get-url", "origin"], {
64343
+ const out = spawnSync13("git", ["remote", "get-url", "origin"], {
64317
64344
  cwd,
64318
64345
  encoding: "utf8",
64319
64346
  env
@@ -64325,7 +64352,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64325
64352
  },
64326
64353
  ghLogin: (e) => {
64327
64354
  try {
64328
- const out = spawnSync12("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
64355
+ const out = spawnSync13("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
64329
64356
  const login = (out.stdout ?? "").trim();
64330
64357
  return out.status === 0 && login ? login : null;
64331
64358
  } catch {
@@ -64334,7 +64361,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64334
64361
  },
64335
64362
  glabLogin: (e) => {
64336
64363
  try {
64337
- const out = spawnSync12("glab", ["api", "user", "--jq", ".username"], {
64364
+ const out = spawnSync13("glab", ["api", "user", "--jq", ".username"], {
64338
64365
  encoding: "utf8",
64339
64366
  env: e
64340
64367
  });
@@ -64346,7 +64373,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64346
64373
  },
64347
64374
  sshLogin: (host) => {
64348
64375
  try {
64349
- const out = spawnSync12("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
64376
+ const out = spawnSync13("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
64350
64377
  encoding: "utf8",
64351
64378
  env
64352
64379
  });
@@ -69504,7 +69531,7 @@ var init_init = __esm(() => {
69504
69531
  });
69505
69532
 
69506
69533
  // packages/workit-cli/src/upgrade.ts
69507
- import { spawnSync as spawnSync13 } from "node:child_process";
69534
+ import { spawnSync as spawnSync14 } from "node:child_process";
69508
69535
  import {
69509
69536
  existsSync as existsSync17,
69510
69537
  mkdtempSync as mkdtempSync3,
@@ -69860,7 +69887,7 @@ async function runLaunchCommand(argv, deps = {}) {
69860
69887
  }
69861
69888
  if (deps.run)
69862
69889
  return deps.run(host, hostArgs).status ?? 1;
69863
- return spawnSync13(launch.command, launch.args, {
69890
+ return spawnSync14(launch.command, launch.args, {
69864
69891
  stdio: "inherit",
69865
69892
  env
69866
69893
  }).status ?? 1;
@@ -70042,7 +70069,7 @@ var init_launch = __esm(() => {
70042
70069
  });
70043
70070
 
70044
70071
  // packages/workit-core/src/knowledge.ts
70045
- import { spawnSync as spawnSync14 } from "node:child_process";
70072
+ import { spawnSync as spawnSync15 } from "node:child_process";
70046
70073
  import fs17 from "node:fs";
70047
70074
  import path36 from "node:path";
70048
70075
  function lintKnowledge(root) {
@@ -70163,7 +70190,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
70163
70190
  return "unreadable";
70164
70191
  }
70165
70192
  }, trackedTopLevel = (root) => {
70166
- const run = spawnSync14("git", ["ls-files", "-z", "--cached"], {
70193
+ const run = spawnSync15("git", ["ls-files", "-z", "--cached"], {
70167
70194
  cwd: root,
70168
70195
  encoding: "utf8",
70169
70196
  maxBuffer: 256 * 1024 * 1024,
@@ -70542,7 +70569,7 @@ var init_redact = __esm(() => {
70542
70569
  });
70543
70570
 
70544
70571
  // packages/workit-core/src/checks.ts
70545
- import { spawn, spawnSync as spawnSync15 } from "node:child_process";
70572
+ import { spawn, spawnSync as spawnSync16 } from "node:child_process";
70546
70573
  import { createHash as createHash14 } from "node:crypto";
70547
70574
  import fs18 from "node:fs";
70548
70575
  import os13 from "node:os";
@@ -70721,7 +70748,7 @@ function runCheckCommand(argv, options) {
70721
70748
  if (!child?.pid)
70722
70749
  return;
70723
70750
  if (windows)
70724
- spawnSync15("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
70751
+ spawnSync16("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
70725
70752
  stdio: "ignore",
70726
70753
  windowsHide: true
70727
70754
  });
@@ -71443,7 +71470,7 @@ var init_check = __esm(() => {
71443
71470
  var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
71444
71471
 
71445
71472
  // packages/workit-core/src/git/ops.ts
71446
- import { spawnSync as spawnSync16 } from "node:child_process";
71473
+ import { spawnSync as spawnSync17 } from "node:child_process";
71447
71474
  import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
71448
71475
  import path39 from "node:path";
71449
71476
  function operationInProgress(cwd) {
@@ -71931,7 +71958,8 @@ ${gitOutput(run)}`);
71931
71958
  url: plan.url,
71932
71959
  previous: before.sha,
71933
71960
  pushed,
71934
- forced
71961
+ forced,
71962
+ ...input.identity ? { identity: input.identity } : {}
71935
71963
  });
71936
71964
  return success2({
71937
71965
  remote: plan.remote,
@@ -71975,7 +72003,7 @@ ${run.stdout}`;
71975
72003
  } : { ok: false, lease: false, error: gitError(run) };
71976
72004
  }
71977
72005
  var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
71978
- const result = spawnSync16("git", args, {
72006
+ const result = spawnSync17("git", args, {
71979
72007
  cwd,
71980
72008
  encoding: "utf8",
71981
72009
  env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
@@ -72355,16 +72383,16 @@ function waitVerdict(doc, options) {
72355
72383
  const checks = doc.checks.state;
72356
72384
  if (checks === "failing")
72357
72385
  return { state: "failed", reason: "checks_failing" };
72358
- if (checks === "pending")
72359
- return { state: "waiting", reason: "checks_pending" };
72360
- if (checks === "passing")
72361
- return { state: "ready", reason: "checks_passing" };
72362
- if (doc.conflicts)
72386
+ if (doc.conflicts && checks !== "passing")
72363
72387
  return {
72364
72388
  state: "blocked",
72365
72389
  reason: "conflicts",
72366
72390
  unblock: `rebase onto ${doc.base} and push; CI does not run on a conflicting PR`
72367
72391
  };
72392
+ if (checks === "pending")
72393
+ return { state: "waiting", reason: "checks_pending" };
72394
+ if (checks === "passing")
72395
+ return { state: "ready", reason: "checks_passing" };
72368
72396
  const ciRequired = doc.checks.missingRequired === null || doc.mergeState === "ci_must_pass" || doc.mergeState === "ci_still_running";
72369
72397
  if (ciRequired)
72370
72398
  return { state: "waiting", reason: "no_checks_yet" };
@@ -72472,7 +72500,7 @@ var init_report = __esm(() => {
72472
72500
  });
72473
72501
 
72474
72502
  // packages/workit-core/src/forge/pr-ops.ts
72475
- import { spawnSync as spawnSync17 } from "node:child_process";
72503
+ import { spawnSync as spawnSync18 } from "node:child_process";
72476
72504
  function fillFromCommits(cwd, resolved, base) {
72477
72505
  const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
72478
72506
  if (!base.startsWith("-"))
@@ -72480,7 +72508,7 @@ function fillFromCommits(cwd, resolved, base) {
72480
72508
  const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
72481
72509
  if (!from)
72482
72510
  return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
72483
- const log = spawnSync17("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
72511
+ const log = spawnSync18("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
72484
72512
  cwd,
72485
72513
  encoding: "utf8",
72486
72514
  timeout: GIT_TIMEOUTS.local,
@@ -72785,7 +72813,7 @@ var init_pr_ops = __esm(() => {
72785
72813
  });
72786
72814
 
72787
72815
  // packages/workit-core/src/forge/exec.ts
72788
- import { spawnSync as spawnSync18 } from "node:child_process";
72816
+ import { spawnSync as spawnSync19 } from "node:child_process";
72789
72817
  import fs20 from "node:fs";
72790
72818
  import path41 from "node:path";
72791
72819
  function findExecutable(bin, env) {
@@ -72820,7 +72848,7 @@ function systemRunner(kind, apiHost, env = process.env) {
72820
72848
  if (!exe)
72821
72849
  return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
72822
72850
  const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
72823
- const result = spawnSync18(exe, [...args], {
72851
+ const result = spawnSync19(exe, [...args], {
72824
72852
  encoding: "utf8",
72825
72853
  env: { ...childEnv, ...token },
72826
72854
  maxBuffer: MAX_BUFFER2,
@@ -72839,17 +72867,45 @@ function systemRunner(kind, apiHost, env = process.env) {
72839
72867
  };
72840
72868
  };
72841
72869
  }
72870
+ function forgeReason(run) {
72871
+ const parts = [];
72872
+ try {
72873
+ const body = JSON.parse(run.stdout);
72874
+ const add = (value) => {
72875
+ if (typeof value === "string" && value.trim())
72876
+ parts.push(value.trim());
72877
+ else if (Array.isArray(value))
72878
+ value.forEach(add);
72879
+ else if (value && typeof value === "object") {
72880
+ const record = value;
72881
+ if (typeof record.message === "string")
72882
+ add(record.message);
72883
+ else if (!("code" in record))
72884
+ Object.values(record).forEach(add);
72885
+ }
72886
+ };
72887
+ add(body.message);
72888
+ add(body.error);
72889
+ add(body.errors);
72890
+ } catch {}
72891
+ const stderr = (run.stderr.split(/\r?\n/u).find((value) => value.trim()) ?? "").trim();
72892
+ const unique = [...new Set(parts)];
72893
+ const text = unique.length ? `${unique.join("; ")}${stderr ? ` (${stderr.replace(/^gh: |^glab: /u, "")})` : ""}` : stderr || (run.stdout.split(/\r?\n/u).find((value) => value.trim()) ?? "");
72894
+ return redactText(text).slice(0, 300);
72895
+ }
72842
72896
  function cliFailure(bin, apiHost, run, what, timeoutMs) {
72843
72897
  if (run.missing)
72844
72898
  return failure3("unavailable", `${bin} is not installed`, installHint(bin));
72845
72899
  if (run.timedOut)
72846
- return failure3("unavailable", `${bin} api ${what} timed out after ${timeoutMs} ms`);
72900
+ return failure3("unavailable", run.timeoutMs === 0 ? `${bin} api ${what} not attempted: the command's time budget ran out` : `${bin} api ${what} timed out after ${run.timeoutMs ?? timeoutMs} ms`, `check the network to ${apiHost}, then retry`);
72847
72901
  const stderr = redactText(run.stderr).trim();
72848
72902
  if (/auth login|not logged in|no token|authentication required|bad credentials|\b401\b|unauthorized/iu.test(stderr))
72849
72903
  return failure3("unavailable", `${bin} is not authenticated for ${apiHost}`, loginHint(bin, apiHost));
72850
72904
  if (/\b404\b|not found/iu.test(stderr))
72851
72905
  return failure3("not_found", `${what} was not found`);
72852
72906
  const first = stderr.split(/\r?\n/u).find((value) => value.trim()) ?? `exit ${run.status}`;
72907
+ if (isNetworkError(stderr))
72908
+ return failure3("unavailable", `${bin} api ${what} failed (network): ${first.slice(0, 200)}`, `check the network to ${apiHost}, then retry`);
72853
72909
  return failure3("failed", `${bin} api ${what} failed: ${first.slice(0, 200)}`);
72854
72910
  }
72855
72911
  function apiJson(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.api, options = {}) {
@@ -72880,12 +72936,15 @@ function apiWrite(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.a
72880
72936
  if (run.status !== 0) {
72881
72937
  const text = redactText(`${run.stderr}
72882
72938
  ${run.stdout}`).trim();
72883
- const reason = (text.split(/\r?\n/u).find((value) => value.trim()) ?? "").slice(0, 200);
72939
+ const reason = forgeReason(run);
72884
72940
  if (!run.missing && !run.timedOut && /\b409\b|head branch was modified|sha does not match|sha.*mismatch/iu.test(text))
72885
72941
  return failure3("blocked", `head_moved: ${what} refused because the head changed (${reason})`, "workit pr status # re-check the new head, then verify it again");
72886
72942
  if (!run.missing && !run.timedOut && /\b(405|406|422)\b/u.test(text))
72887
72943
  return failure3("blocked", `${what} refused by the forge: ${reason}`);
72888
- return cliFailure(bin, apiHost, run, what, timeoutMs);
72944
+ const failed = cliFailure(bin, apiHost, run, what, timeoutMs);
72945
+ if (!failed.ok && failed.code === "unavailable" && !run.missing && !/ is not authenticated for /u.test(failed.error))
72946
+ return failure3("unavailable", failed.error, "workit pr status # the write may have gone through; check it first, then retry");
72947
+ return failed;
72889
72948
  }
72890
72949
  if (!run.stdout.trim())
72891
72950
  return { ok: true, data: {} };
@@ -72895,11 +72954,12 @@ ${run.stdout}`).trim();
72895
72954
  return failure3("failed", `${bin} api ${what} returned invalid JSON`);
72896
72955
  }
72897
72956
  }
72898
- var FORGE_TIMEOUTS, MAX_BUFFER2, installHint = (bin) => bin === "gh" ? "install the GitHub CLI (https://cli.github.com) and run: gh auth login" : "install glab (https://gitlab.com/gitlab-org/cli) and run: glab auth login", loginHint = (bin, apiHost) => `${bin} auth login --hostname ${apiHost}`;
72957
+ var FORGE_TIMEOUTS, MAX_BUFFER2, installHint = (bin) => bin === "gh" ? "install the GitHub CLI (https://cli.github.com) and run: gh auth login" : "install glab (https://gitlab.com/gitlab-org/cli) and run: glab auth login", loginHint = (bin, apiHost) => `${bin} auth login --hostname ${apiHost}`, NETWORK, isNetworkError = (text) => NETWORK.test(text);
72899
72958
  var init_exec = __esm(() => {
72900
72959
  init_redact();
72901
72960
  FORGE_TIMEOUTS = { api: 20000, log: 30000 };
72902
72961
  MAX_BUFFER2 = 64 * 1024 * 1024;
72962
+ NETWORK = /error connecting|could not resolve|no such host|connection (refused|reset|timed out)|network is unreachable|i\/o timeout|tls handshake|unexpected eof|\bHTTP 5\d\d\b|bad gateway|service unavailable|gateway time-?out/iu;
72903
72963
  });
72904
72964
 
72905
72965
  // packages/workit-core/src/forge/github.ts
@@ -73491,6 +73551,82 @@ var init_gitlab = __esm(() => {
73491
73551
 
73492
73552
  // packages/workit-core/src/forge/resolve.ts
73493
73553
  import fs21 from "node:fs";
73554
+ function memo4(scope, key, compute, now = Date.now) {
73555
+ let entries = memoScopes.get(scope.owner);
73556
+ if (!entries)
73557
+ memoScopes.set(scope.owner, entries = new Map);
73558
+ const full = `${scope.key} ${key}`;
73559
+ const hit = entries.get(full);
73560
+ if (hit && now() - hit.at < MEMO_TTL_MS)
73561
+ return hit.value;
73562
+ const { keep, value } = compute();
73563
+ if (keep)
73564
+ entries.set(full, { at: now(), value });
73565
+ return value;
73566
+ }
73567
+ function clampedRunner(base, limits, now, token) {
73568
+ return (bin, args, callOptions) => {
73569
+ let timeoutMs = callOptions.timeoutMs;
73570
+ if (limits.deadline !== null) {
73571
+ const remaining = limits.deadline - now();
73572
+ if (remaining <= 0)
73573
+ return {
73574
+ status: null,
73575
+ stdout: "",
73576
+ stderr: "",
73577
+ timedOut: true,
73578
+ missing: false,
73579
+ timeoutMs: 0
73580
+ };
73581
+ timeoutMs = Math.min(timeoutMs, remaining);
73582
+ }
73583
+ const run = base(bin, args, { ...callOptions, timeoutMs, ...token ? { token } : {} });
73584
+ return run.timedOut && run.timeoutMs === undefined ? { ...run, timeoutMs } : run;
73585
+ };
73586
+ }
73587
+ function resolveCredential(workspace, derived, run, scope) {
73588
+ const { kind, apiHost } = derived;
73589
+ const expectedAccount = workspace?.vcs?.account ?? null;
73590
+ const tokenFile = workspace?.vcs?.tokenFile;
73591
+ if (typeof tokenFile === "string" && tokenFile.trim()) {
73592
+ const workspaceHost = workspace?.vcs?.host;
73593
+ const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
73594
+ if (derived.via === "host_name" && !explicit)
73595
+ return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
73596
+ let token = "";
73597
+ try {
73598
+ token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
73599
+ } catch {
73600
+ return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
73601
+ }
73602
+ if (!token)
73603
+ return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is empty`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
73604
+ return success2({ token, credential: "workspace_token_file" });
73605
+ }
73606
+ if (!expectedAccount || kind !== "github")
73607
+ return success2({ token: undefined, credential: "cli_login" });
73608
+ return memo4(scope, `token ${apiHost} ${expectedAccount}`, () => {
73609
+ const issued = run("gh", ["auth", "token", "--hostname", apiHost, "--user", expectedAccount], {
73610
+ timeoutMs: FORGE_TIMEOUTS.api
73611
+ });
73612
+ const value = authTokenResult(issued, apiHost, expectedAccount, workspace?.name ?? "?");
73613
+ return { keep: value.ok, value };
73614
+ });
73615
+ }
73616
+ function authTokenResult(issued, apiHost, account, workspace) {
73617
+ if (issued.missing)
73618
+ return failure3("unavailable", "gh is not installed", "install the GitHub CLI (https://cli.github.com) and run: gh auth login");
73619
+ if (issued.timedOut)
73620
+ return failure3("unavailable", `gh auth token ${issued.timeoutMs === 0 ? "not attempted: the time budget ran out" : `timed out after ${issued.timeoutMs ?? FORGE_TIMEOUTS.api} ms`} (looking up the ${account} login on ${apiHost})`, "gh auth status # check gh and the network, then retry");
73621
+ const token = issued.status === 0 ? issued.stdout.trim() : "";
73622
+ if (token)
73623
+ return success2({ token, credential: "gh_account_token" });
73624
+ const stderr = redactText(issued.stderr).trim();
73625
+ const first = stderr.split(/\r?\n/u).find((line) => line.trim()) ?? "";
73626
+ if (issued.status !== 0 && first && !/no oauth token|not logged in|no account/iu.test(stderr))
73627
+ return failure3("unavailable", `gh auth token failed${isNetworkError(stderr) ? " (network)" : ""}: ${first.slice(0, 200)}`, "gh auth status # check gh and the network, then retry");
73628
+ return failure3("blocked", `identity_unavailable: gh has no login for ${account} on ${apiHost} (workspace ${workspace})`, `gh auth login --hostname ${apiHost} # sign in as ${account}; workit passes that account's token per call and never switches the active account`);
73629
+ }
73494
73630
  function resolveForge(cwd, options = {}) {
73495
73631
  let workspace;
73496
73632
  try {
@@ -73508,48 +73644,16 @@ function resolveForge(cwd, options = {}) {
73508
73644
  const derived = pushed.forge;
73509
73645
  const { kind, apiHost } = derived;
73510
73646
  const base = options.runner ?? systemRunner(kind, apiHost, options.env ?? process.env);
73647
+ const scope = memoScope(options.runner, options.env ?? process.env);
73511
73648
  const now = options.now ?? Date.now;
73512
73649
  const expectedAccount = workspace?.vcs?.account ?? null;
73513
- let token;
73514
- let credential = "cli_login";
73515
- const tokenFile = workspace?.vcs?.tokenFile;
73516
- if (typeof tokenFile === "string" && tokenFile.trim()) {
73517
- const workspaceHost = workspace?.vcs?.host;
73518
- const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
73519
- if (derived.via === "host_name" && !explicit)
73520
- return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
73521
- try {
73522
- token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
73523
- } catch {
73524
- return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
73525
- }
73526
- if (!token)
73527
- return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is empty`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
73528
- credential = "workspace_token_file";
73529
- } else if (expectedAccount && kind === "github") {
73530
- const issued = base("gh", ["auth", "token", "--hostname", apiHost, "--user", expectedAccount], {
73531
- timeoutMs: FORGE_TIMEOUTS.api
73532
- });
73533
- if (issued.missing)
73534
- return failure3("unavailable", "gh is not installed", "install the GitHub CLI (https://cli.github.com) and run: gh auth login");
73535
- token = issued.status === 0 ? issued.stdout.trim() : "";
73536
- if (!token)
73537
- return failure3("blocked", `identity_unavailable: gh has no login for ${expectedAccount} on ${apiHost} (workspace ${workspace?.name ?? "?"})`, `gh auth login --hostname ${apiHost} # sign in as ${expectedAccount}; workit passes that account's token per call and never switches the active account`);
73538
- credential = "gh_account_token";
73539
- }
73540
- const limits = { deadline: null };
73541
- const runner = (bin, args, callOptions) => {
73542
- let timeoutMs = callOptions.timeoutMs;
73543
- if (limits.deadline !== null) {
73544
- const remaining = limits.deadline - now();
73545
- if (remaining <= 0)
73546
- return { status: null, stdout: "", stderr: "", timedOut: true, missing: false };
73547
- timeoutMs = Math.min(timeoutMs, remaining);
73548
- }
73549
- return base(bin, args, { ...callOptions, timeoutMs, ...token ? { token } : {} });
73550
- };
73650
+ const limits = { deadline: options.deadline ?? null };
73651
+ const credential = resolveCredential(workspace, derived, clampedRunner(base, limits, now), scope);
73652
+ if (!credential.ok)
73653
+ return credential;
73654
+ const runner = clampedRunner(base, limits, now, credential.data.token);
73551
73655
  const headRepo = derived.repo;
73552
- const probe = createForge(derived, headRepo, runner);
73656
+ const probe = memoizedIdentity(createForge(derived, headRepo, runner), scope, credential.data);
73553
73657
  const info = probe.repoInfo(headRepo);
73554
73658
  if (!info.ok && info.code !== "not_found")
73555
73659
  return info;
@@ -73564,13 +73668,13 @@ function resolveForge(cwd, options = {}) {
73564
73668
  const baseRepo = upstream && upstream !== headRepo ? upstream : info.ok && info.data.parent ? info.data.parent : headRepo;
73565
73669
  const baseRemote = baseRepo === headRepo ? pushed.remote : remotes.find((name) => repoOfRemote(name) === baseRepo) ?? pushed.remote;
73566
73670
  return success2({
73567
- forge: baseRepo === headRepo ? probe : createForge(derived, baseRepo, runner),
73671
+ forge: baseRepo === headRepo ? probe : memoizedIdentity(createForge(derived, baseRepo, runner), scope, credential.data),
73568
73672
  remote: pushed.remote,
73569
73673
  url: pushed.url,
73570
73674
  derived,
73571
73675
  workspace: workspace?.name ?? null,
73572
73676
  expectedAccount,
73573
- credential,
73677
+ credential: credential.data.credential,
73574
73678
  headRepo,
73575
73679
  headProjectId,
73576
73680
  fork: baseRepo !== headRepo,
@@ -73583,14 +73687,74 @@ function checkIdentity(resolved) {
73583
73687
  const identity = forge.identity(expectedAccount);
73584
73688
  if (!identity.ok)
73585
73689
  return identity;
73586
- if (identity.data.matches === false) {
73587
- const bin = forge.kind === "github" ? "gh" : "glab";
73588
- const where = `workspace ${resolved.workspace ?? "?"}`;
73589
- return failure3("blocked", `identity_mismatch: the ${resolved.credential === "workspace_token_file" ? "vcs.tokenFile token" : `${bin} login`} is ${identity.data.login} but ${where} expects ${expectedAccount}`, resolved.credential === "workspace_token_file" ? `put a token for ${expectedAccount} in the ${where} vcs.tokenFile` : `${bin} auth login --hostname ${forge.apiHost} # sign in as ${expectedAccount}`);
73590
- }
73690
+ if (identity.data.matches === false)
73691
+ return mismatch(resolved, identity.data.login ?? "?", resolved.workspace);
73591
73692
  return identity;
73592
73693
  }
73593
- var configuredHosts = () => {
73694
+ function checkPushIdentity(cwd, options = {}) {
73695
+ let workspace;
73696
+ try {
73697
+ workspace = resolveRuntimeWorkspaceVcs(cwd);
73698
+ } catch (error) {
73699
+ return failure3("blocked", error instanceof Error ? error.message : String(error), "fix ~/.config/workit/workspaces.json (workit init → Workspaces)");
73700
+ }
73701
+ const account = workspace?.vcs?.account ?? null;
73702
+ const skipped = (forge, reason, unblock) => success2({
73703
+ status: "skipped",
73704
+ forge,
73705
+ account,
73706
+ login: null,
73707
+ reason,
73708
+ ...unblock ? { unblock } : {}
73709
+ });
73710
+ const pushed = pushForge(cwd, {
73711
+ branch: options.branch,
73712
+ hosts: configuredHosts(),
73713
+ sshConfig: options.sshConfig
73714
+ });
73715
+ if (!pushed.ok) {
73716
+ const host = /^unsupported_forge: .*?"([^"]+)"/u.exec(pushed.error)?.[1];
73717
+ const what = /could not resolve ssh alias/u.test(pushed.error) ? `could not resolve ssh alias ${host} to GitHub or GitLab` : pushed.error.startsWith("unsupported_forge: ssh host") ? `ssh host ${host} is not a GitHub/GitLab host or an alias workit can resolve` : `${host} is not a GitHub or GitLab host workit knows`;
73718
+ return skipped(null, host ? `unsupported_forge: ${what}; account checks skipped, pushed with git only (no PR or CI verbs)` : pushed.error, pushed.unblock);
73719
+ }
73720
+ const derived = pushed.forge;
73721
+ const conflict = forgeConflict(derived, workspace?.vcs?.provider);
73722
+ if (conflict)
73723
+ return skipped(derived.kind, conflict.error, conflict.unblock);
73724
+ if (!account)
73725
+ return success2({ status: "no_account", forge: derived.kind, account, login: null });
73726
+ const base = options.runner ?? systemRunner(derived.kind, derived.apiHost, options.env ?? process.env);
73727
+ const scope = memoScope(options.runner, options.env ?? process.env);
73728
+ const now = options.now ?? Date.now;
73729
+ const limits = { deadline: now() + (options.budgetMs ?? PUSH_IDENTITY_BUDGET_MS) };
73730
+ const credential = resolveCredential(workspace, derived, clampedRunner(base, limits, now), scope);
73731
+ if (!credential.ok)
73732
+ return unreachable(credential) ? skipped(derived.kind, credential.error, credential.unblock) : credential;
73733
+ const forge = memoizedIdentity(createForge(derived, derived.repo, clampedRunner(base, limits, now, credential.data.token)), scope, credential.data);
73734
+ const identity = forge.identity(account);
73735
+ if (!identity.ok)
73736
+ return unreachable(identity) ? skipped(derived.kind, identity.error, identity.unblock) : identity;
73737
+ if (identity.data.matches === false)
73738
+ return mismatch({ forge, credential: credential.data.credential, expectedAccount: account }, identity.data.login ?? "?", workspace?.name ?? null);
73739
+ if (identity.data.matches === null)
73740
+ return skipped(derived.kind, identity.data.note ?? "the credential cannot read /user");
73741
+ return success2({ status: "verified", forge: derived.kind, account, login: identity.data.login });
73742
+ }
73743
+ var MEMO_TTL_MS, SYSTEM_SCOPE, memoScopes, memoScope = (runner, env) => ({
73744
+ owner: runner ?? SYSTEM_SCOPE,
73745
+ key: [env.GH_CONFIG_DIR, env.GLAB_CONFIG_DIR, env.XDG_CONFIG_HOME, env.HOME].map((value) => value ?? "").join("|")
73746
+ }), memoizedIdentity = (forge, scope, credential) => {
73747
+ if (credential.credential === "cli_login")
73748
+ return forge;
73749
+ const identity = (expected) => forge.identity(expected);
73750
+ return {
73751
+ ...forge,
73752
+ identity: (expected) => memo4(scope, `identity ${forge.kind} ${forge.apiHost} ${credential.credential} ${credential.token ?? ""} ${expected ?? ""}`, () => {
73753
+ const value = identity(expected);
73754
+ return { keep: value.ok, value };
73755
+ })
73756
+ };
73757
+ }, configuredHosts = () => {
73594
73758
  const vcs = readVcsConfig();
73595
73759
  if (vcs.status !== "valid")
73596
73760
  return {};
@@ -73599,7 +73763,13 @@ var configuredHosts = () => {
73599
73763
  return typeof value === "string" && value.trim() ? [value.trim()] : [];
73600
73764
  };
73601
73765
  return { github: host("github"), gitlab: host("gitlab") };
73602
- }, createForge = (derived, repo, runner) => derived.kind === "github" ? createGitHubForge({ apiHost: derived.apiHost, repo, runner }) : createGitLabForge({ apiHost: derived.apiHost, repo, runner });
73766
+ }, createForge = (derived, repo, runner) => derived.kind === "github" ? createGitHubForge({ apiHost: derived.apiHost, repo, runner }) : createGitLabForge({ apiHost: derived.apiHost, repo, runner }), mismatch = (resolved, login, workspace) => {
73767
+ const { forge, expectedAccount } = resolved;
73768
+ const bin = forge.kind === "github" ? "gh" : "glab";
73769
+ const where = `workspace ${workspace ?? "?"}`;
73770
+ const fileFix = `put a token for ${expectedAccount} in the ${where} vcs.tokenFile`;
73771
+ return failure3("blocked", `identity_mismatch: the ${resolved.credential === "workspace_token_file" ? "vcs.tokenFile token" : `${bin} login`} is ${login} but ${where} expects ${expectedAccount}`, resolved.credential === "workspace_token_file" ? fileFix : forge.kind === "github" ? `gh auth login --hostname ${forge.apiHost} # sign in as ${expectedAccount}` : `${fileFix} (glab keeps one login per host, so workit never switches it)`);
73772
+ }, unreachable = (result) => result.code === "unavailable" && !/ is not authenticated for /u.test(result.error), PUSH_IDENTITY_BUDGET_MS = 5000;
73603
73773
  var init_resolve = __esm(() => {
73604
73774
  init_vcs_config();
73605
73775
  init_workspaces();
@@ -73607,6 +73777,10 @@ var init_resolve = __esm(() => {
73607
73777
  init_exec();
73608
73778
  init_github();
73609
73779
  init_gitlab();
73780
+ init_redact();
73781
+ MEMO_TTL_MS = 5 * 60000;
73782
+ SYSTEM_SCOPE = {};
73783
+ memoScopes = new WeakMap;
73610
73784
  });
73611
73785
 
73612
73786
  // packages/workit-core/src/duration.ts
@@ -73699,16 +73873,19 @@ function releaseTrunk(io, track, branch) {
73699
73873
  tracked: Boolean(resolved.releaseTrack)
73700
73874
  };
73701
73875
  }
73702
- function connect(io, branch) {
73876
+ function connect(io, branch, options = {}) {
73877
+ const verbDeadline = options.deadline ?? null;
73703
73878
  const resolved = resolveForge(io.cwd, {
73704
73879
  branch,
73705
73880
  env: io.env,
73706
73881
  runner: forgeDeps.runner,
73707
- now: forgeDeps.now
73882
+ now: forgeDeps.now,
73883
+ deadline: verbDeadline ?? forgeDeps.now() + CONNECT_BUDGET_MS
73708
73884
  });
73709
73885
  if (!resolved.ok)
73710
73886
  return resolved;
73711
73887
  const identity = checkIdentity(resolved.data);
73888
+ resolved.data.limits.deadline = verbDeadline;
73712
73889
  if (!identity.ok)
73713
73890
  return identity;
73714
73891
  return {
@@ -73763,7 +73940,12 @@ function renderStatus(doc) {
73763
73940
  lines.push(`next: ${doc.next}`);
73764
73941
  return lines;
73765
73942
  }
73766
- var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
73943
+ var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), CONNECT_BUDGET_MS = 15000, pushIdentity = (io, branch) => checkPushIdentity(io.cwd, {
73944
+ branch,
73945
+ env: io.env,
73946
+ runner: forgeDeps.runner,
73947
+ now: forgeDeps.now
73948
+ }), short = (sha) => sha ? sha.slice(0, 7) : "?";
73767
73949
  var init_forge_common = __esm(() => {
73768
73950
  init_resolve();
73769
73951
  init_vcs_config();
@@ -74013,16 +74195,17 @@ async function wait(argv, io) {
74013
74195
  const head = flags.values.head ?? null;
74014
74196
  if (head !== null && !/^[0-9a-f]{7,64}$/u.test(head))
74015
74197
  return usage3(io, "--head must be a commit sha (7-64 hex chars)", WAIT_USAGE);
74016
- const connected = connect(io, flags.values.branch);
74198
+ const started = forgeDeps.now();
74199
+ const deadline = started + timeoutMs;
74200
+ const connected = connect(io, flags.values.branch, {
74201
+ deadline: started + Math.max(timeoutMs, ONE_POLL_MS)
74202
+ });
74017
74203
  if (!connected.ok)
74018
74204
  return forgeFail(io, connected);
74019
74205
  const resolved = connected.data;
74020
74206
  const number = selectPr(io.cwd, resolved, { pr, branch: flags.values.branch ?? null });
74021
74207
  if (!number.ok)
74022
74208
  return forgeFail(io, number);
74023
- const started = forgeDeps.now();
74024
- const deadline = started + timeoutMs;
74025
- resolved.limits.deadline = started + Math.max(timeoutMs, ONE_POLL_MS);
74026
74209
  let polls = 0;
74027
74210
  let errors = 0;
74028
74211
  let status = null;
@@ -74359,12 +74542,11 @@ async function push(argv, io) {
74359
74542
  const plan = pushPreflight(io.cwd);
74360
74543
  if (!plan.ok)
74361
74544
  return forgeFail(io, plan);
74362
- if (!plan.data.local) {
74363
- const connected = connect(io, plan.data.branch);
74364
- if (!connected.ok)
74365
- return forgeFail(io, connected);
74366
- }
74367
- const grant = requireGrant(io.cwd, "push", { forge: !plan.data.local });
74545
+ const identity = plan.data.local ? null : pushIdentity(io, plan.data.branch);
74546
+ if (identity && !identity.ok)
74547
+ return forgeFail(io, identity);
74548
+ const checked = identity?.data ?? null;
74549
+ const grant = requireGrant(io.cwd, "push", { forge: checked?.forge != null });
74368
74550
  if (!grant.allowed)
74369
74551
  return emit(io, fail("blocked", grant.error, {
74370
74552
  unblock: grant.unblock,
@@ -74375,14 +74557,22 @@ async function push(argv, io) {
74375
74557
  overwriteUnintegrated: flags.booleans.has("overwrite-unintegrated"),
74376
74558
  expect,
74377
74559
  setUpstream: flags.booleans.has("set-upstream"),
74378
- actor: actorFromEnv(io.env)
74560
+ actor: actorFromEnv(io.env),
74561
+ ...checked ? { identity: { ...checked } } : {}
74379
74562
  });
74380
74563
  if (!result.ok)
74381
74564
  return forgeFail(io, result, {
74382
74565
  branch: plan.data.branch,
74383
74566
  sha: plan.data.sha
74384
74567
  });
74385
- return emit(io, ok(result.data), (data) => [
74568
+ const warnings = checked?.status === "skipped" ? [
74569
+ `identity check skipped: ${checked.reason}${checked.unblock ? ` (to restore it: ${checked.unblock})` : ""}`
74570
+ ] : [];
74571
+ for (const warning of warnings)
74572
+ io.stderr(`warning: ${warning}
74573
+ `);
74574
+ const envelope = ok({ ...result.data, identity: checked });
74575
+ return emit(io, warnings.length ? { ...envelope, warnings } : envelope, (data) => [
74386
74576
  data.pushed ? `pushed ${data.branch} ${short2(data.previous)} -> ${short2(data.sha)} to ${data.remote}${data.forced ? " (force-with-lease)" : ""}; remote tip verified` : `${data.remote}/${data.branch} is already at ${short2(data.sha)}; remote tip verified`,
74387
74577
  ...data.upstream ? [`upstream: ${data.remote}/${data.branch}`] : []
74388
74578
  ]);
@@ -74415,7 +74605,7 @@ var init_git2 = __esm(() => {
74415
74605
  });
74416
74606
 
74417
74607
  // packages/workit-core/src/forge/verify.ts
74418
- import { spawnSync as spawnSync19 } from "node:child_process";
74608
+ import { spawnSync as spawnSync20 } from "node:child_process";
74419
74609
  function finish(cwd, report, actor) {
74420
74610
  const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
74421
74611
  const out = { ...report, delivered };
@@ -74628,7 +74818,7 @@ var NPM_NAME, SEMVER, SAFE_ARG, systemNpm = (args) => {
74628
74818
  };
74629
74819
  const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
74630
74820
  const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
74631
- const result = process.platform === "win32" ? spawnSync19(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync19("npm", [...args], options);
74821
+ const result = process.platform === "win32" ? spawnSync20(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync20("npm", [...args], options);
74632
74822
  return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
74633
74823
  }, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
74634
74824
  try {
@@ -74735,7 +74925,7 @@ var init_verify_delivery = __esm(() => {
74735
74925
  });
74736
74926
 
74737
74927
  // packages/workit-core/src/stack.ts
74738
- import { spawnSync as spawnSync20 } from "node:child_process";
74928
+ import { spawnSync as spawnSync21 } from "node:child_process";
74739
74929
  import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
74740
74930
  import fs22 from "node:fs";
74741
74931
  import path44 from "node:path";
@@ -75248,7 +75438,7 @@ function commitKeys(cwd, range) {
75248
75438
  const patches = new Map;
75249
75439
  const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
75250
75440
  if (diff.ok && diff.stdout.trim()) {
75251
- const ids = spawnSync20("git", ["patch-id", "--stable"], {
75441
+ const ids = spawnSync21("git", ["patch-id", "--stable"], {
75252
75442
  cwd,
75253
75443
  input: diff.stdout,
75254
75444
  encoding: "utf8",
@@ -75872,7 +76062,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
75872
76062
  ...unblock ? { unblock } : {},
75873
76063
  ...data ? { data } : {}
75874
76064
  }), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
75875
- const run = spawnSync20("git", args, {
76065
+ const run = spawnSync21("git", args, {
75876
76066
  cwd,
75877
76067
  encoding: "utf8",
75878
76068
  timeout: timeoutMs,
@@ -76167,7 +76357,7 @@ var init_stack2 = __esm(() => {
76167
76357
  });
76168
76358
 
76169
76359
  // packages/workit-core/src/fanout.ts
76170
- import { spawnSync as spawnSync21 } from "node:child_process";
76360
+ import { spawnSync as spawnSync22 } from "node:child_process";
76171
76361
  import { createHash as createHash16 } from "node:crypto";
76172
76362
  import fs23 from "node:fs";
76173
76363
  import path45 from "node:path";
@@ -76940,7 +77130,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
76940
77130
  text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
76941
77131
  };
76942
77132
  }, gitRun4 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
76943
- const run = spawnSync21("git", args, {
77133
+ const run = spawnSync22("git", args, {
76944
77134
  cwd,
76945
77135
  encoding: "utf8",
76946
77136
  timeout: timeoutMs,
@@ -77460,7 +77650,7 @@ var init_fanout_brief = __esm(() => {
77460
77650
  });
77461
77651
 
77462
77652
  // packages/workit-core/src/fanout-landed.ts
77463
- import { spawnSync as spawnSync22 } from "node:child_process";
77653
+ import { spawnSync as spawnSync23 } from "node:child_process";
77464
77654
  function forgeBreaker(resolved) {
77465
77655
  if (!resolved)
77466
77656
  return { forge: null, down: () => null };
@@ -77551,7 +77741,7 @@ function trunkPatchIds(ctx, fork) {
77551
77741
  "--"
77552
77742
  ]);
77553
77743
  if (log.ok && log.stdout.trim()) {
77554
- const run = spawnSync22("git", ["patch-id", "--stable"], {
77744
+ const run = spawnSync23("git", ["patch-id", "--stable"], {
77555
77745
  cwd: ctx.cwd,
77556
77746
  input: log.stdout,
77557
77747
  encoding: "utf8",
@@ -94494,11 +94684,11 @@ var init_rules = __esm(() => {
94494
94684
  });
94495
94685
 
94496
94686
  // packages/workit-cli/src/test-audit/audit.ts
94497
- import { spawnSync as spawnSync23 } from "node:child_process";
94687
+ import { spawnSync as spawnSync24 } from "node:child_process";
94498
94688
  import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync21, statSync as statSync7 } from "node:fs";
94499
94689
  import path49 from "node:path";
94500
94690
  function git6(cwd, args) {
94501
- const run = spawnSync23("git", ["-c", "core.quotepath=off", ...args], {
94691
+ const run = spawnSync24("git", ["-c", "core.quotepath=off", ...args], {
94502
94692
  cwd,
94503
94693
  encoding: "utf8",
94504
94694
  maxBuffer: 64 * 1024 * 1024
@@ -96391,9 +96581,21 @@ async function main(argv, overrides = {}, options = {}) {
96391
96581
  globalThis[Symbol.for("workit.migrationReporter")] = (report) => io.stderr(`workit: migrated ${report.tasks} task${report.tasks === 1 ? "" : "s"} from ${report.from} to ${report.to} (backup: ${report.backup})
96392
96582
  `);
96393
96583
  const verb = await entry.load();
96584
+ const runVerb = (target) => guarded(target, () => verb.run(ended, target));
96394
96585
  if (!io.json)
96395
- return verb.run(ended, io);
96396
- return runJsonPure(io, command, (pure) => verb.run(ended, pure));
96586
+ return runVerb(io);
96587
+ return runJsonPure(io, command, runVerb);
96588
+ }
96589
+ async function guarded(io, run) {
96590
+ try {
96591
+ return await run();
96592
+ } catch (error) {
96593
+ if (!(error instanceof Error) || error.name !== "WorkspaceConfigError")
96594
+ throw error;
96595
+ return emit(io, fail("blocked", error.message, {
96596
+ unblock: "fix ~/.config/workit/workspaces.json (workit init → Workspaces)"
96597
+ }));
96598
+ }
96397
96599
  }
96398
96600
  var CODE_FOR_EXIT = {
96399
96601
  1: "failed",