@brainervirus/workit-claude-code 8.29.2 → 8.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workit.js CHANGED
@@ -6428,11 +6428,12 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
6428
6428
  };
6429
6429
  }
6430
6430
  }
6431
+ const document2 = parsed;
6431
6432
  return {
6432
6433
  status: "valid",
6433
6434
  path: file,
6434
- entries: validated.data.workspaces ?? [],
6435
- document: parsed
6435
+ entries: document2.workspaces ?? [],
6436
+ document: document2
6436
6437
  };
6437
6438
  }, readWorkspacesResult = (dir = configDir()) => {
6438
6439
  const file = path2.join(dir, "workspaces.json");
@@ -6570,7 +6571,40 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
6570
6571
  return glob;
6571
6572
  const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
6572
6573
  return real + (prefix.endsWith("/") ? "/" : "") + rest;
6573
- }, globSpecificity = (glob) => glob.replaceAll("\\", "/").split("/").filter((segment) => segment.length > 0 && !/[*?[\]{]/.test(segment)).length, selectWorkspaceMatch = (matches, cwd, workspaceName) => {
6574
+ }, globSpecificity = (glob) => glob.replaceAll("\\", "/").split("/").filter((segment) => segment.length > 0 && !/[*?[\]{]/.test(segment)).length, globRoot = (glob) => {
6575
+ const normalized = glob.replaceAll("\\", "/");
6576
+ const wild = /[*?[\]{]/.exec(normalized);
6577
+ const prefix = wild ? normalized.slice(0, wild.index) : normalized;
6578
+ return prefix.replace(/\/+$/u, "") || "/";
6579
+ }, literalPrefixes = (glob) => {
6580
+ const normalized = glob.replaceAll("\\", "/");
6581
+ const prefixOf = (value) => {
6582
+ const wild = /[*?[\]{]/.exec(value);
6583
+ return wild ? value.slice(0, wild.index) : value;
6584
+ };
6585
+ return [
6586
+ ...new Set([prefixOf(normalized), prefixOf(canonicalGlob(normalized).replaceAll("\\", "/"))])
6587
+ ];
6588
+ }, globsMayOverlap = (a, b) => literalPrefixes(a).some((pa) => literalPrefixes(b).some((pb) => pa.startsWith(pb) || pb.startsWith(pa))), overlappingEntries = (glob, entries) => entries.filter((entry) => globsMayOverlap(entry.glob, glob)), workspaceOverlaps = (entries) => {
6589
+ const out = [];
6590
+ for (const [i, a] of entries.entries())
6591
+ for (const b of entries.slice(i + 1)) {
6592
+ const aInB = matchWorkspace(b.glob, globRoot(a.glob));
6593
+ const bInA = matchWorkspace(a.glob, globRoot(b.glob));
6594
+ if (!aInB && !bInA)
6595
+ continue;
6596
+ const [inner, outer] = aInB && !bInA ? [a, b] : bInA && !aInB ? [b, a] : globSpecificity(a.glob) >= globSpecificity(b.glob) ? [a, b] : [b, a];
6597
+ const si = globSpecificity(inner.glob);
6598
+ const so = globSpecificity(outer.glob);
6599
+ out.push({
6600
+ inner: inner.name,
6601
+ outer: outer.name,
6602
+ at: globRoot(inner.glob),
6603
+ winner: si === so ? null : si > so ? inner.name : outer.name
6604
+ });
6605
+ }
6606
+ return out;
6607
+ }, workspaceMatches = (cwd, entries) => matchingEntries(cwd, entries), selectWorkspaceMatch = (matches, cwd, workspaceName) => {
6574
6608
  if (workspaceName !== undefined) {
6575
6609
  const named = matches.filter((entry) => entry.name === workspaceName);
6576
6610
  if (named.length === 1)
@@ -9251,7 +9285,7 @@ var package_default;
9251
9285
  var init_package = __esm(() => {
9252
9286
  package_default = {
9253
9287
  name: "@brainervirus/workit-cli",
9254
- version: "8.29.2",
9288
+ version: "8.30.0",
9255
9289
  private: false,
9256
9290
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
9257
9291
  keywords: [
@@ -9289,13 +9323,13 @@ var init_package = __esm(() => {
9289
9323
  },
9290
9324
  dependencies: {
9291
9325
  "@babel/parser": "7.29.9",
9292
- "@brainervirus/workit-cursor": "^8.29.2",
9326
+ "@brainervirus/workit-cursor": "^8.30.0",
9293
9327
  "@inkjs/ui": "2.0.0",
9294
9328
  ink: "7.1.1",
9295
9329
  react: "19.2.8"
9296
9330
  },
9297
9331
  devDependencies: {
9298
- "@brainervirus/workit-core": "^8.29.2"
9332
+ "@brainervirus/workit-core": "^8.30.0"
9299
9333
  },
9300
9334
  engines: {
9301
9335
  node: ">=24"
@@ -9660,6 +9694,27 @@ function effectiveEndpoint(endpoint, context) {
9660
9694
  return { endpoint: "green", reason: missing("merge") };
9661
9695
  return { endpoint };
9662
9696
  }
9697
+ function endpointMismatch(view) {
9698
+ const { defaultEndpoint: endpoint } = view;
9699
+ const ws = view.workspace ?? "<workspace>";
9700
+ const explicit = (kind) => view.configured.includes(kind) && view.grants[kind] !== false;
9701
+ if (explicit("merge") && endpoint !== "merged")
9702
+ return `merge granted, but endpoint ${endpoint} stops at ${STOPS[endpoint]}; to land on green: workit grant set ${ws} defaultEndpoint=merged (your terminal)`;
9703
+ if (explicit("pr") && endpoint === "commit")
9704
+ return `pr granted, but endpoint commit stops at the local commit; to open PRs: workit grant set ${ws} defaultEndpoint=pr (your terminal)`;
9705
+ return null;
9706
+ }
9707
+ function entryMismatch(entry) {
9708
+ if (!entry)
9709
+ return null;
9710
+ const { grants } = configuredGrants(entry);
9711
+ return endpointMismatch({
9712
+ workspace: entry.name ?? null,
9713
+ grants: { ...DEFAULT_GRANTS, ...grants },
9714
+ configured: GRANT_KINDS.filter((kind) => grants[kind] !== undefined),
9715
+ defaultEndpoint: endpointOf(entry).endpoint
9716
+ });
9717
+ }
9663
9718
  function endpointView(entry, context) {
9664
9719
  const { raw, endpoint, issue } = endpointOf(entry);
9665
9720
  const effective = effectiveEndpoint(endpoint, context);
@@ -9876,7 +9931,7 @@ var accountHome = () => {
9876
9931
  }, grantsHome, grantsDir = () => path11.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
9877
9932
  const configured = path11.resolve(resolveConfigDir());
9878
9933
  return configured === path11.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path11.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
9879
- }, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), raisesEndpoint = (current, next) => DEFAULT_ENDPOINTS.indexOf(next) > DEFAULT_ENDPOINTS.indexOf(current), VERIFICATION_MODES, DEFAULT_VERIFICATION = "proportional", verificationOf = (entry) => {
9934
+ }, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), raisesEndpoint = (current, next) => DEFAULT_ENDPOINTS.indexOf(next) > DEFAULT_ENDPOINTS.indexOf(current), STOPS, VERIFICATION_MODES, DEFAULT_VERIFICATION = "proportional", verificationOf = (entry) => {
9880
9935
  const value = entry?.verification;
9881
9936
  return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
9882
9937
  }, isGrantKind = (value) => typeof value === "string" && GRANT_KINDS.includes(value), isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
@@ -9909,6 +9964,12 @@ var init_autonomy = __esm(() => {
9909
9964
  rerun: true
9910
9965
  });
9911
9966
  DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
9967
+ STOPS = {
9968
+ commit: "the local commit",
9969
+ pr: "the PR",
9970
+ green: "a green PR",
9971
+ merged: "the merge"
9972
+ };
9912
9973
  VERIFICATION_MODES = [
9913
9974
  "proportional",
9914
9975
  "self",
@@ -15376,9 +15437,18 @@ function liftStop(context, input) {
15376
15437
  }
15377
15438
  return { ok: true, value: cleared };
15378
15439
  }
15440
+ function trackedDirty(cwd) {
15441
+ const status = gitRun2(cwd, ["status", "--porcelain", "--untracked-files=no"]);
15442
+ return status.ok ? status.stdout.trim().length > 0 : null;
15443
+ }
15444
+ function untrackedFiles2(cwd) {
15445
+ const listed = gitRun2(cwd, ["ls-files", "--others", "--exclude-standard"]);
15446
+ return listed.ok ? listed.stdout.split(`
15447
+ `).filter(Boolean) : [];
15448
+ }
15379
15449
  function dirtyElsewhere(cwd, branch, head) {
15380
15450
  if (headSha(cwd) === head)
15381
- return worktreeTree(cwd)?.dirty !== false;
15451
+ return trackedDirty(cwd) !== false;
15382
15452
  const list = git4(cwd, ["worktree", "list", "--porcelain"]);
15383
15453
  let worktree = null;
15384
15454
  for (const line of (list ?? "").split(`
@@ -15386,7 +15456,7 @@ function dirtyElsewhere(cwd, branch, head) {
15386
15456
  if (line.startsWith("worktree "))
15387
15457
  worktree = line.slice("worktree ".length);
15388
15458
  else if (line === `branch refs/heads/${branch}` && worktree) {
15389
- const status = gitRun2(worktree, ["status", "--porcelain", "--untracked-files=normal"]);
15459
+ const status = gitRun2(worktree, ["status", "--porcelain", "--untracked-files=no"]);
15390
15460
  return status.ok && status.stdout.trim().length > 0;
15391
15461
  }
15392
15462
  }
@@ -15413,8 +15483,9 @@ function recordVerdict(context, input) {
15413
15483
  const key = codeKey(context.cwd, { branch, base: context.base });
15414
15484
  if (!key.head)
15415
15485
  return err("not_found", `branch "${branch}" has no commit to judge`);
15416
- if (key.dirty === true || key.dirty === null && dirtyElsewhere(context.cwd, branch, key.head))
15417
- return err("blocked", `dirty_worktree: ${branch} has uncommitted changes, so its head is not what was judged`, "commit or stash the changes, re-check, then record the verdict");
15486
+ const dirty = key.dirty === null ? dirtyElsewhere(context.cwd, branch, key.head) : trackedDirty(context.cwd) !== false;
15487
+ if (dirty)
15488
+ return err("blocked", `dirty_worktree: ${branch} has uncommitted changes to tracked files, so its head is not what was judged`, "commit or stash the changes, re-check, then record the verdict");
15418
15489
  const session = context.actor.session;
15419
15490
  const selfReason = input.self === true ? "flag" : session && input.derivedFrom !== null ? null : "no_session";
15420
15491
  const self2 = selfReason !== null;
@@ -15440,9 +15511,14 @@ function recordVerdict(context, input) {
15440
15511
  selfReason,
15441
15512
  sessionSource: context.sessionSource ?? "env",
15442
15513
  ...context.sessionBound === true ? { sessionBound: true } : {},
15443
- evidenceRefs: input.evidenceRefs ?? []
15514
+ evidenceRefs: input.evidenceRefs ?? [],
15515
+ ...untrackedField(context.cwd)
15444
15516
  }, { now: context.now });
15445
15517
  }
15518
+ function untrackedField(cwd) {
15519
+ const files = untrackedFiles2(cwd);
15520
+ return files.length ? { untracked: files.slice(0, MAX_UNTRACKED_LISTED), untrackedCount: files.length } : {};
15521
+ }
15446
15522
  function recordHandoff(context, input) {
15447
15523
  return appendRow(context.cwd, { ...common(context, noteKey(context)), type: "handoff", note: input.note, next: input.next }, { now: context.now });
15448
15524
  }
@@ -15783,7 +15859,7 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
15783
15859
  }, required2 = (value, flag) => {
15784
15860
  const text = value?.trim();
15785
15861
  return text ? { ok: true, value: text } : err("invalid_input", `${flag} is required`);
15786
- }, fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session), isDelegateSession = (session) => session !== null && (/-w-[a-z0-9._-]+(?:\+try\d)?$/u.test(session) || /-v\d+$/u.test(session) || session.includes(":")), stopOrderText = (note) => `STOP: start nothing new, stop every process you started (dev servers, browsers, watchers), commit only finished work, and report to the lead now${note ? ` (${note})` : ""}`, OBSERVED_ONLY, MAX_DIRTY_PATHS = 10, dirtyState = (cwd) => {
15862
+ }, fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session), isDelegateSession = (session) => session !== null && (/-w-[a-z0-9._-]+(?:\+try\d)?$/u.test(session) || /-v\d+$/u.test(session) || session.includes(":")), stopOrderText = (note) => `STOP: start nothing new, stop every process you started (dev servers, browsers, watchers), commit only finished work, and report to the lead now${note ? ` (${note})` : ""}`, MAX_UNTRACKED_LISTED = 20, OBSERVED_ONLY, MAX_DIRTY_PATHS = 10, dirtyState = (cwd) => {
15787
15863
  const run = gitRun2(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=normal"], GIT_TIMEOUTS.worktree);
15788
15864
  if (!run.ok)
15789
15865
  return { dirty: false, files: 0, paths: [] };
@@ -68429,6 +68505,24 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68429
68505
  return [];
68430
68506
  }
68431
68507
  }, checkWorkspaceMismatch = (res) => {
68508
+ const check = checkWorkspaceMatch(res);
68509
+ const result = readWorkspacesResult(res.configDir);
68510
+ if (result.status !== "valid")
68511
+ return check;
68512
+ const overlaps = workspaceOverlaps(result.entries);
68513
+ if (overlaps.length === 0)
68514
+ return check;
68515
+ const lines = overlaps.map((overlap) => overlap.winner ? `overlap: ${overlap.inner} inside ${overlap.outer}, ${overlap.winner} wins at ${overlap.at}` : `overlap: ${overlap.inner} and ${overlap.outer} tie at ${overlap.at} (ambiguous)`);
68516
+ const ties = overlaps.filter((overlap) => !overlap.winner);
68517
+ return {
68518
+ ...check,
68519
+ detail: [check.detail, ...lines].join("; "),
68520
+ ...ties.length && check.status === "pass" ? {
68521
+ status: "warn",
68522
+ fix: `Make one glob more specific than the other in ${result.path} (workit workspace list shows the effective entry)`
68523
+ } : {}
68524
+ };
68525
+ }, checkWorkspaceMatch = (res) => {
68432
68526
  const result = readWorkspacesResult(res.configDir);
68433
68527
  const file = result.path;
68434
68528
  if (result.status === "missing" || result.status === "valid" && result.entries.length === 0)
@@ -68902,6 +68996,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
68902
68996
  status: "pass",
68903
68997
  detail: "no hook passed its deadline in the last day"
68904
68998
  };
68999
+ }, checkGrantEndpoint = (res) => {
69000
+ let line = null;
69001
+ try {
69002
+ line = entryMismatch(resolveWorkspaceFrom(res.cwd, res.configDir));
69003
+ } catch {}
69004
+ return line ? {
69005
+ id: "grant_endpoint",
69006
+ status: "warn",
69007
+ detail: line.split("; ")[0],
69008
+ fix: line.split("; ")[1]
69009
+ } : { id: "grant_endpoint", status: "pass", detail: "grants and default endpoint agree" };
68905
69010
  }, checkChecksConfig = (res) => {
68906
69011
  const root = checkRoot(res.cwd);
68907
69012
  const current = existsSync16(path38.join(root, CHECKS_FILE));
@@ -69195,6 +69300,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
69195
69300
  return report;
69196
69301
  };
69197
69302
  var init_doctor = __esm(() => {
69303
+ init_autonomy();
69198
69304
  init_support_matrix();
69199
69305
  init_store_lock();
69200
69306
  init_runtime_identity();
@@ -69250,6 +69356,7 @@ var init_doctor = __esm(() => {
69250
69356
  checkLogWritable,
69251
69357
  checkHeavyScope,
69252
69358
  checkHookDeadline,
69359
+ checkGrantEndpoint,
69253
69360
  checkChecksConfig,
69254
69361
  checkHostPermissions("claude-code", "claude_permissions"),
69255
69362
  checkHostPermissions("codex", "codex_permissions"),
@@ -75853,7 +75960,8 @@ function show(argv, io) {
75853
75960
  return emit(io, fail2("not_found", `no workspace named "${named[0]}" in ${listed.path}`));
75854
75961
  return emit(io, ok2({ path: listed.path, defaults: DEFAULT_GRANTS, workspaces: rows }), () => rows.flatMap((entry) => [
75855
75962
  `${entry.name} (${entry.glob})`,
75856
- ...describe2(entry.grants, entry.configured, entry, entry.verification)
75963
+ ...describe2(entry.grants, entry.configured, entry, entry.verification),
75964
+ ...endpointMismatch({ ...entry, workspace: entry.name }) ? [`note: ${endpointMismatch({ ...entry, workspace: entry.name })}`] : []
75857
75965
  ]));
75858
75966
  }
75859
75967
  let autonomy;
@@ -75874,6 +75982,7 @@ function show(argv, io) {
75874
75982
  ...tierModelIssues.map((issue) => `note: ${issue}`),
75875
75983
  data.workspace ? `workspace ${data.workspace} (${data.source === "default" ? "D4 defaults" : `from ${data.source}`})` : "no workspace matches this checkout: D4 defaults apply",
75876
75984
  ...describe2(data.grants, data.configured, data, data.verification),
75985
+ ...endpointMismatch(data) ? [`note: ${endpointMismatch(data)}`] : [],
75877
75986
  ...describeReleaseTracks(tracks).map((line) => ` ${line}`),
75878
75987
  "raising a grant needs the user: workit grant set <workspace> <kind>=<value>"
75879
75988
  ]);
@@ -75933,7 +76042,9 @@ async function change(verb, argv, io, deps) {
75933
76042
  const command = `workit grant ${verb} ${workspace} ${specs.join(" ")}`;
75934
76043
  const agent = agentMarker(io.env);
75935
76044
  if (!deps.interactive() || agent)
75936
- return emit(io, fail2("blocked", `grant_raise_refused: raising ${raised.map(({ kind }) => kind).join(", ")} needs the user at an interactive terminal${agent ? ` (this shell belongs to an agent: ${agent} is set)` : ""}; grants are never raised headless`, { unblock: `ask the user to run, in their own terminal: ${command}` }));
76045
+ return emit(io, fail2("blocked", `grant_raise_refused: raising ${raised.map(({ kind }) => kind).join(", ")} needs the user at an interactive terminal${agent ? ` (this shell belongs to an agent: ${agent} is set)` : ""}; grants are never raised headless`, {
76046
+ unblock: `ask the user to run, in a terminal outside the agent host (not Claude Code's \`!\` prefix or an agent shell): ${command}`
76047
+ }));
75937
76048
  const answer = await askOrCancel(io, () => deps.ask(`Raise ${raised.map(({ kind, value }) => `${kind}=${value}`).join(", ")} for workspace "${workspace}"? An agent there may then do this without asking. Type the workspace name to confirm: `));
75938
76049
  if (answer === null)
75939
76050
  return cancelled(io);
@@ -76003,6 +76114,288 @@ var init_grant = __esm(() => {
76003
76114
  };
76004
76115
  });
76005
76116
 
76117
+ // packages/workit-cli/src/verbs/workspace.ts
76118
+ var exports_workspace = {};
76119
+ __export(exports_workspace, {
76120
+ INHERITED_KEYS: () => INHERITED_KEYS,
76121
+ run: () => run11,
76122
+ runWorkspace: () => runWorkspace
76123
+ });
76124
+ import { copyFileSync as copyFileSync7, existsSync as existsSync21 } from "node:fs";
76125
+ function list2(argv, io) {
76126
+ if (argv.length)
76127
+ return usage3(io, `unexpected argument ${argv[0]}`);
76128
+ const current = load(io);
76129
+ if (typeof current === "number")
76130
+ return current;
76131
+ const entries = entriesOf(current);
76132
+ const matches = workspaceMatches(io.cwd, entries);
76133
+ let effective = null;
76134
+ let reason;
76135
+ try {
76136
+ effective = resolveWorkspaceFromEntries(io.cwd, entries)?.name ?? null;
76137
+ reason = matches.length === 0 ? "no glob matches this directory: the defaults apply" : matches.length === 1 ? "the only glob that matches" : `the most specific of ${matches.length} matching globs (${matches.map((entry) => `${entry.name}: ${globSpecificity(entry.glob)} literal segments`).join(", ")})`;
76138
+ } catch (error) {
76139
+ reason = error instanceof WorkspaceConfigError ? error.message : String(error);
76140
+ }
76141
+ const rows = entries.map((entry) => ({
76142
+ name: entry.name,
76143
+ glob: entry.glob,
76144
+ keys: Object.keys(entry).filter((key) => key !== "name" && key !== "glob")
76145
+ }));
76146
+ return emit(io, ok2({
76147
+ path: current.path,
76148
+ workspaces: rows,
76149
+ cwd: io.cwd,
76150
+ effective,
76151
+ matches: matches.map((entry) => entry.name),
76152
+ reason
76153
+ }), (data) => [
76154
+ ...data.workspaces.length ? [] : [`no workspaces in ${data.path}`],
76155
+ ...data.workspaces.map((row) => `${row.name === data.effective ? "*" : " "} ${row.name} (${row.glob})${row.keys.length ? `: ${row.keys.join(", ")}` : ""}`),
76156
+ `${data.cwd}: ${data.effective ? `workspace ${data.effective}` : "no workspace"} (${data.reason})`
76157
+ ]);
76158
+ }
76159
+ function add2(argv, io) {
76160
+ const positional = argv.filter((arg, index) => !arg.startsWith("--") && !["--glob", "--from"].includes(argv[index - 1] ?? ""));
76161
+ const [name, extra] = positional;
76162
+ if (!name)
76163
+ return usage3(io, "add needs <name>");
76164
+ if (extra)
76165
+ return usage3(io, `unexpected argument ${extra}`);
76166
+ const glob = flagValue(argv, "--glob");
76167
+ if (!glob)
76168
+ return usage3(io, "add needs --glob <glob>");
76169
+ const checked = validateWorkspaceGlob(glob);
76170
+ if (!checked.ok)
76171
+ return emit(io, fail2("invalid_input", checked.error));
76172
+ const from = flagValue(argv, "--from");
76173
+ if (from === null)
76174
+ return usage3(io, "--from needs <parent>");
76175
+ const current = load(io);
76176
+ if (typeof current === "number")
76177
+ return current;
76178
+ const entries = entriesOf(current);
76179
+ if (entries.some((entry) => entry.name === name))
76180
+ return emit(io, fail2("invalid_input", `a workspace named "${name}" already exists in ${current.path}`, {
76181
+ unblock: `workit workspace set ${name} <key>=<value>`
76182
+ }));
76183
+ const parent = from === undefined ? undefined : entries.find((entry) => entry.name === from);
76184
+ if (from !== undefined && !parent)
76185
+ return emit(io, fail2("not_found", `no workspace named "${from}" in ${current.path}`));
76186
+ const inherited = parent ? Object.fromEntries(INHERITED_KEYS.filter((key) => parent[key] !== undefined).map((key) => [
76187
+ key,
76188
+ structuredClone(parent[key])
76189
+ ])) : {};
76190
+ const shadowed = overlappingEntries(glob, entries);
76191
+ const entry = {
76192
+ name,
76193
+ glob,
76194
+ ...inherited,
76195
+ ...clampToShadowed(shadowedCeiling(entries))
76196
+ };
76197
+ const result = save(current, [...entries, entry]);
76198
+ if (!result.ok)
76199
+ return emit(io, fail2("invalid_input", result.error));
76200
+ const ceiling = ceilingOf(entry);
76201
+ return emit(io, ok2({
76202
+ path: result.path,
76203
+ backup: result.backup,
76204
+ workspace: entry,
76205
+ inherited: Object.keys(inherited),
76206
+ shadows: shadowed.map((item) => item.name),
76207
+ grants: ceiling.grants,
76208
+ defaultEndpoint: ceiling.endpoint,
76209
+ verification: ceiling.verification
76210
+ }), (data) => [
76211
+ `${name} (${glob}) added to ${written(data.path, data.backup)}`,
76212
+ ...data.inherited.length ? [` from ${from}: ${data.inherited.join(", ")}`] : [],
76213
+ ...data.shadows.length ? [` overlaps: ${data.shadows.join(", ")}`] : [],
76214
+ ` grants start at the strictest level of every workspace and the defaults:`,
76215
+ ` grants: ${GRANT_KINDS.map((kind) => `${kind}=${String(data.grants[kind])}`).join(" ")}; default endpoint ${data.defaultEndpoint}; verification ${data.verification}`,
76216
+ ` raising a grant needs the user, in their own terminal: workit grant set ${name} <kind>=<value>`
76217
+ ]);
76218
+ }
76219
+ function set2(argv, io) {
76220
+ const [name, ...specs] = argv;
76221
+ if (!name || specs.length === 0)
76222
+ return usage3(io, "set needs <name> and <key>=<value>");
76223
+ const grantSpecs = [];
76224
+ const changes = [];
76225
+ for (const spec of specs) {
76226
+ const eq = spec.indexOf("=");
76227
+ if (eq <= 0)
76228
+ return usage3(io, `${spec}: expected <key>=<value>`);
76229
+ const key = spec.slice(0, eq);
76230
+ const keyPath = key.split(".");
76231
+ if (keyPath.some((part) => !SEGMENT.test(part) || UNSAFE_SEGMENTS.has(part)))
76232
+ return usage3(io, `${spec}: each key segment is letters, digits, _ or -`);
76233
+ if (GRANT_KEYS.has(keyPath[0])) {
76234
+ grantSpecs.push(spec);
76235
+ continue;
76236
+ }
76237
+ if (SCOPE_KEYS.has(keyPath[0]))
76238
+ return emit(io, fail2("blocked", `${keyPath[0]} decides where the entry's grants apply; workspace set does not change it`, {
76239
+ unblock: `ask the user to edit ${keyPath[0]} of "${name}" in their own editor, or add a new entry: workit workspace add <name> --glob <g> --from ${name}`
76240
+ }));
76241
+ if (!SETTABLE_KEYS.has(keyPath[0]))
76242
+ return usage3(io, `${spec}: unknown key ${keyPath[0]} (settable: ${[...SETTABLE_KEYS].join(", ")})`);
76243
+ changes.push({ path: keyPath, value: parseSetValue(spec.slice(eq + 1)) });
76244
+ }
76245
+ if (grantSpecs.length)
76246
+ return emit(io, fail2("blocked", `workspace set does not change grants, defaultEndpoint or verification (${grantSpecs.join(", ")})`, {
76247
+ unblock: `ask the user to run, in their own terminal outside the agent host: workit grant set ${name} ${grantSpecs.join(" ")}`
76248
+ }));
76249
+ const current = load(io);
76250
+ if (typeof current === "number")
76251
+ return current;
76252
+ const entries = structuredClone(entriesOf(current));
76253
+ const entry = entries.find((item) => item.name === name);
76254
+ if (!entry)
76255
+ return emit(io, fail2("not_found", `no workspace named "${name}" in ${current.path}`));
76256
+ const credential = changes.find((change) => change.path[0] === "vcs");
76257
+ if (credential && (configuredGrants(entry).source !== "default" || entry.defaultEndpoint !== undefined))
76258
+ return emit(io, fail2("blocked", `${name} has grants of its own; its vcs decides the account they act through, so workspace set does not change it`, {
76259
+ unblock: `ask the user to change ${credential.path.join(".")} of "${name}" in their own editor, or to unset the grants first (workit grant unset ${name} …)`
76260
+ }));
76261
+ for (const change of changes) {
76262
+ let target = entry;
76263
+ for (const part of change.path.slice(0, -1)) {
76264
+ const next = target[part];
76265
+ if (next === undefined || next === null)
76266
+ target[part] = {};
76267
+ else if (typeof next !== "object" || Array.isArray(next))
76268
+ return usage3(io, `${change.path.join(".")}: ${part} is not an object`);
76269
+ target = target[part];
76270
+ }
76271
+ const last = change.path[change.path.length - 1];
76272
+ if (change.value === null)
76273
+ delete target[last];
76274
+ else
76275
+ target[last] = change.value;
76276
+ }
76277
+ const result = save(current, entries);
76278
+ if (!result.ok)
76279
+ return emit(io, fail2("invalid_input", result.error));
76280
+ return emit(io, ok2({ path: result.path, backup: result.backup, workspace: entry }), (data) => [
76281
+ `${name}: ${changes.map((change) => change.path.join(".")).join(", ")} updated in ${written(data.path, data.backup)}`
76282
+ ]);
76283
+ }
76284
+ async function runWorkspace(argv, io) {
76285
+ const [sub, ...rest] = argv.filter((arg) => arg !== "--json" && arg !== "--");
76286
+ if (sub === undefined || sub === "list")
76287
+ return list2(rest, io);
76288
+ if (sub === "add")
76289
+ return add2(rest, io);
76290
+ if (sub === "set")
76291
+ return set2(rest, io);
76292
+ return usage3(io, `unknown subcommand "${sub}"`);
76293
+ }
76294
+ 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) => ({
76295
+ grants: { ...DEFAULT_GRANTS, ...configuredGrants(entry).grants },
76296
+ endpoint: isDefaultEndpoint(entry?.defaultEndpoint) ? entry.defaultEndpoint : DEFAULT_ENDPOINT,
76297
+ verification: verificationOf(entry)
76298
+ }), STRICTNESS, looser = (current, next) => STRICTNESS.indexOf(next) > STRICTNESS.indexOf(current), shadowedCeiling = (shadowed) => {
76299
+ if (shadowed.length === 0)
76300
+ return null;
76301
+ const ceilings = shadowed.map(ceilingOf);
76302
+ const grants = { ...DEFAULT_GRANTS };
76303
+ for (const kind of GRANT_KINDS)
76304
+ grants[kind] = ceilings.map((ceiling) => ceiling.grants[kind]).reduce((low, value) => raises(kind, value, low) ? value : low);
76305
+ return {
76306
+ grants,
76307
+ endpoint: ceilings.map((ceiling) => ceiling.endpoint).reduce((low, value) => raisesEndpoint(value, low) ? value : low),
76308
+ verification: ceilings.map((ceiling) => ceiling.verification).reduce((strict, value) => looser(value, strict) ? value : strict)
76309
+ };
76310
+ }, clampToShadowed = (shadowed) => {
76311
+ if (!shadowed)
76312
+ return {};
76313
+ const autonomy = Object.fromEntries(GRANT_KINDS.filter((kind) => raises(kind, shadowed.grants[kind], DEFAULT_GRANTS[kind])).map((kind) => [kind, shadowed.grants[kind]]));
76314
+ return {
76315
+ ...Object.keys(autonomy).length ? { autonomy } : {},
76316
+ ...looser(shadowed.verification, DEFAULT_VERIFICATION) ? { verification: shadowed.verification } : {}
76317
+ };
76318
+ }, usage3 = (io, message) => emit(io, fail2("invalid_input", `${message}
76319
+ usage: ${USAGE4}`)), load = (io) => {
76320
+ const current = readWorkspacesResult();
76321
+ if (current.status === "malformed" || current.status === "invalid")
76322
+ return emit(io, fail2("invalid_input", current.error ?? `${current.path} is invalid`));
76323
+ return current;
76324
+ }, entriesOf = (current) => current.entries, save = (current, entries) => {
76325
+ const document2 = { ...current.document, workspaces: entries };
76326
+ const validated = validateWorkspacesDocument(document2, current.path);
76327
+ if (validated.status !== "valid")
76328
+ return { ok: false, error: validated.error ?? "invalid workspaces.json" };
76329
+ const latest = readWorkspacesResult();
76330
+ if (latest.revision !== current.revision)
76331
+ return { ok: false, error: `${current.path} changed; retry` };
76332
+ let backup = null;
76333
+ try {
76334
+ if (existsSync21(current.path)) {
76335
+ backup = `${current.path}.bak`;
76336
+ copyFileSync7(current.path, backup);
76337
+ }
76338
+ writeFileAtomic(current.path, `${JSON.stringify(document2, null, 2)}
76339
+ `);
76340
+ } catch (error) {
76341
+ return {
76342
+ ok: false,
76343
+ error: `failed to write ${current.path}: ${error instanceof Error ? error.message : String(error)}`
76344
+ };
76345
+ }
76346
+ return { ok: true, path: current.path, backup };
76347
+ }, written = (path, backup) => `${path}${backup ? ` (previous copy: ${backup})` : ""}`, flagValue = (argv, flag) => {
76348
+ const index = argv.findIndex((arg) => arg === flag || arg.startsWith(`${flag}=`));
76349
+ if (index < 0)
76350
+ return;
76351
+ const arg = argv[index];
76352
+ if (arg.includes("="))
76353
+ return arg.slice(flag.length + 1);
76354
+ return argv[index + 1] ?? null;
76355
+ }, parseSetValue = (raw) => {
76356
+ try {
76357
+ return JSON.parse(raw);
76358
+ } catch {
76359
+ return raw;
76360
+ }
76361
+ }, run11 = (argv, io) => runWorkspace(argv, io);
76362
+ var init_workspace = __esm(() => {
76363
+ init_autonomy();
76364
+ init_safe_write();
76365
+ init_workspaces();
76366
+ init_output();
76367
+ INHERITED_KEYS = [
76368
+ "vcs",
76369
+ "issues",
76370
+ "branchPolicy",
76371
+ "commitPolicy",
76372
+ "trailers",
76373
+ "youtrack",
76374
+ "models"
76375
+ ];
76376
+ GRANT_KEYS = new Set(["autonomy", "autoApprove", "defaultEndpoint", "verification"]);
76377
+ SCOPE_KEYS = new Set(["name", "glob"]);
76378
+ SETTABLE_KEYS = new Set([
76379
+ "vcs",
76380
+ "issues",
76381
+ "branchPolicy",
76382
+ "commitPolicy",
76383
+ "trailers",
76384
+ "liveSessionGuard",
76385
+ "youtrack",
76386
+ "models",
76387
+ "fanout",
76388
+ "delegation",
76389
+ "defaultProfile",
76390
+ "profiles",
76391
+ "releaseTracks",
76392
+ "resources"
76393
+ ]);
76394
+ SEGMENT = /^[A-Za-z0-9_-]+$/u;
76395
+ UNSAFE_SEGMENTS = new Set(["__proto__", "constructor", "prototype"]);
76396
+ STRICTNESS = ["independent", "self", "proportional"];
76397
+ });
76398
+
76006
76399
  // packages/workit-core/src/core/heavy-rules.ts
76007
76400
  import fs22 from "node:fs";
76008
76401
  import path46 from "node:path";
@@ -76876,7 +77269,7 @@ __export(exports_check, {
76876
77269
  OUTPUT_LINES: () => OUTPUT_LINES,
76877
77270
  boundedOutput: () => boundedOutput,
76878
77271
  hostToolWaitCapMs: () => hostToolWaitCapMs,
76879
- run: () => run11,
77272
+ run: () => run12,
76880
77273
  workspaceResources: () => workspaceResources
76881
77274
  });
76882
77275
  import os20 from "node:os";
@@ -77160,10 +77553,10 @@ async function prepareHeavy(io, settings, label, runCwd, timeoutMs) {
77160
77553
  env
77161
77554
  };
77162
77555
  }
77163
- async function run11(argv, io) {
77556
+ async function run12(argv, io) {
77164
77557
  const options = parse6(argv);
77165
77558
  if (options instanceof Error)
77166
- return emit(io, fail2("invalid_input", options.message, { unblock: USAGE4 }));
77559
+ return emit(io, fail2("invalid_input", options.message, { unblock: USAGE5 }));
77167
77560
  const config = loadCheckConfig(io.cwd);
77168
77561
  if (config.note)
77169
77562
  io.stderr(`note: ${config.note}
@@ -77174,7 +77567,7 @@ async function run11(argv, io) {
77174
77567
  let name = options.name;
77175
77568
  if (options.command === null) {
77176
77569
  if (options.positionals.length !== 1 || options.name || options.shell)
77177
- return emit(io, fail2("invalid_input", "pass a check name, or a command after --", { unblock: USAGE4 }));
77570
+ return emit(io, fail2("invalid_input", "pass a check name, or a command after --", { unblock: USAGE5 }));
77178
77571
  name = options.positionals[0];
77179
77572
  if (config.error)
77180
77573
  return emit(io, fail2("invalid_input", `check config is invalid: ${config.error}`));
@@ -77189,11 +77582,11 @@ async function run11(argv, io) {
77189
77582
  } else {
77190
77583
  if (options.positionals.length)
77191
77584
  return emit(io, fail2("invalid_input", `unexpected argument ${options.positionals[0]} before --`, {
77192
- unblock: USAGE4
77585
+ unblock: USAGE5
77193
77586
  }));
77194
77587
  command = options.command;
77195
77588
  if (!command.length)
77196
- return emit(io, fail2("invalid_input", "no command after --", { unblock: USAGE4 }));
77589
+ return emit(io, fail2("invalid_input", "no command after --", { unblock: USAGE5 }));
77197
77590
  if (options.shell && command.length !== 1)
77198
77591
  return emit(io, fail2("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
77199
77592
  }
@@ -77372,7 +77765,7 @@ async function run11(argv, io) {
77372
77765
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
77373
77766
  return 0;
77374
77767
  }
77375
- var USAGE4 = '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(path49.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, HOST_TOOL_WAIT_CAP_MS = 1e5;
77768
+ 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(path49.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, HOST_TOOL_WAIT_CAP_MS = 1e5;
77376
77769
  var init_check = __esm(() => {
77377
77770
  init_check_config();
77378
77771
  init_checks3();
@@ -77395,7 +77788,7 @@ var init_check = __esm(() => {
77395
77788
  // packages/workit-cli/src/verbs/lock.ts
77396
77789
  var exports_lock = {};
77397
77790
  __export(exports_lock, {
77398
- run: () => run12
77791
+ run: () => run13
77399
77792
  });
77400
77793
  import fs25 from "node:fs";
77401
77794
  import path50 from "node:path";
@@ -77466,7 +77859,7 @@ async function runHeavy(args, io) {
77466
77859
  unblock: "workit lock run [--shell] [--wait <s>] [--timeout <s>] -- <cmd…>"
77467
77860
  }));
77468
77861
  const check = await Promise.resolve().then(() => (init_check(), {}));
77469
- return run11([`--heavy`, ...head, ...args.slice(split)], io);
77862
+ return run12([`--heavy`, ...head, ...args.slice(split)], io);
77470
77863
  }
77471
77864
  function readPatterns(file) {
77472
77865
  try {
@@ -77537,7 +77930,7 @@ async function heavy(args, io) {
77537
77930
  unblock: "workit lock heavy list|add <pattern>|remove <pattern>|test -- <cmd…>"
77538
77931
  }));
77539
77932
  }
77540
- async function run12(argv, io) {
77933
+ async function run13(argv, io) {
77541
77934
  const [sub, ...rest] = argv;
77542
77935
  if (sub === "status")
77543
77936
  return status(io);
@@ -77547,9 +77940,9 @@ async function run12(argv, io) {
77547
77940
  return runHeavy(rest, io);
77548
77941
  if (sub === "heavy")
77549
77942
  return heavy(rest, io);
77550
- return emit(io, fail2("invalid_input", `unknown lock subcommand ${sub ?? "(none)"}`, { unblock: USAGE5 }));
77943
+ return emit(io, fail2("invalid_input", `unknown lock subcommand ${sub ?? "(none)"}`, { unblock: USAGE6 }));
77551
77944
  }
77552
- var USAGE5 = "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) => {
77945
+ 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) => {
77553
77946
  const split = args.indexOf("--");
77554
77947
  return (split < 0 ? args : args.slice(split + 1)).join(" ").trim();
77555
77948
  };
@@ -77564,15 +77957,15 @@ var init_lock = __esm(() => {
77564
77957
  // packages/workit-cli/src/verbs/models.ts
77565
77958
  var exports_models = {};
77566
77959
  __export(exports_models, {
77567
- run: () => run13
77960
+ run: () => run14
77568
77961
  });
77569
- import { existsSync as existsSync21, readdirSync as readdirSync10, readFileSync as readFileSync21, statSync as statSync9 } from "node:fs";
77962
+ import { existsSync as existsSync22, readdirSync as readdirSync10, readFileSync as readFileSync21, statSync as statSync9 } from "node:fs";
77570
77963
  import os21 from "node:os";
77571
77964
  import path51 from "node:path";
77572
- async function run13(argv, io) {
77965
+ async function run14(argv, io) {
77573
77966
  const [sub, ...args] = argv.filter((arg) => arg !== "--json");
77574
77967
  if (sub !== "show")
77575
- return emit(io, fail2("invalid_input", `usage: ${USAGE6}`, { unblock: "workit help models" }));
77968
+ return emit(io, fail2("invalid_input", `usage: ${USAGE7}`, { unblock: "workit help models" }));
77576
77969
  const role = flag3(args, "--role");
77577
77970
  const tier = flag3(args, "--tier");
77578
77971
  const hostFlag = flag3(args, "--host");
@@ -77619,7 +78012,7 @@ async function run13(argv, io) {
77619
78012
  ...(data.issues ?? []).map((issue) => `note: ${issue}`)
77620
78013
  ]);
77621
78014
  }
77622
- var USAGE6 = "workit models show [--role <role>] [--tier <tier>] [--host <host>] [--report [--days <n>]] [--json]", flag3 = (argv, name) => {
78015
+ var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>] [--report [--days <n>]] [--json]", flag3 = (argv, name) => {
77623
78016
  const at = argv.findIndex((arg) => arg === name || arg.startsWith(`${name}=`));
77624
78017
  if (at < 0)
77625
78018
  return null;
@@ -77662,7 +78055,7 @@ var USAGE6 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
77662
78055
  continue;
77663
78056
  const agentType = JSON.parse(readFileSync21(meta, "utf8")).agentType;
77664
78057
  const transcript = meta.replace(/\.meta\.json$/, ".jsonl");
77665
- if (typeof agentType !== "string" || !existsSync21(transcript))
78058
+ if (typeof agentType !== "string" || !existsSync22(transcript))
77666
78059
  continue;
77667
78060
  const ran = modelsOf(transcript);
77668
78061
  if (!ran.size)
@@ -77801,7 +78194,7 @@ var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock)
77801
78194
 
77802
78195
  // packages/workit-core/src/git/ops.ts
77803
78196
  import { spawnSync as spawnSync20 } from "node:child_process";
77804
- import { existsSync as existsSync22, readFileSync as readFileSync22 } from "node:fs";
78197
+ import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
77805
78198
  import path52 from "node:path";
77806
78199
  function operationInProgress(cwd) {
77807
78200
  const markers = [
@@ -77819,11 +78212,11 @@ function operationInProgress(cwd) {
77819
78212
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
77820
78213
  for (const [index, [, operation]] of markers.entries()) {
77821
78214
  const marker = found[index];
77822
- if (!marker || !existsSync22(marker))
78215
+ if (!marker || !existsSync23(marker))
77823
78216
  continue;
77824
- if (index === 1 && existsSync22(path52.join(marker, "applying")))
78217
+ if (index === 1 && existsSync23(path52.join(marker, "applying")))
77825
78218
  return { operation: "am", commitAllowed: false, branch: null };
77826
- if (index === 0 && existsSync22(path52.join(marker, "amend")) && !conflicted()) {
78219
+ if (index === 0 && existsSync23(path52.join(marker, "amend")) && !conflicted()) {
77827
78220
  let headName = "";
77828
78221
  try {
77829
78222
  headName = readFileSync22(path52.join(marker, "head-name"), "utf8").trim();
@@ -78154,7 +78547,7 @@ function gitCommit(cwd, input) {
78154
78547
  if (paths.some((value) => value.startsWith("-") || !value.trim()))
78155
78548
  return failure3("invalid_input", "paths must not be empty or start with -");
78156
78549
  const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
78157
- const addable = paths.filter((value) => existsSync22(path52.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
78550
+ const addable = paths.filter((value) => existsSync23(path52.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
78158
78551
  if (addable.length) {
78159
78552
  const added = git5(cwd, ["add", "-A", "--", ...addable]);
78160
78553
  if (!added.ok)
@@ -78182,9 +78575,13 @@ function gitCommit(cwd, input) {
78182
78575
  timeoutMs: COMMIT_TIMEOUT_MS,
78183
78576
  env: { ...input.env ?? process.env, WORKIT_TRAILERS: trailersOn ? "on" : "off" }
78184
78577
  });
78185
- if (!committed.ok)
78578
+ if (!committed.ok) {
78579
+ const output = gitOutput(committed);
78580
+ const refusal = hookRefusal(output);
78186
78581
  return failure3("failed", `git commit failed (a hook or git refused it):
78187
- ${gitOutput(committed)}`);
78582
+ ${output}${refusal ? `
78583
+ ${refusal}` : ""}`);
78584
+ }
78188
78585
  const sha = headSha(cwd);
78189
78586
  if (!sha)
78190
78587
  return failure3("failed", "the commit did not resolve");
@@ -78641,6 +79038,14 @@ var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}
78641
79038
  const lines = redactText(`${run.stderr}
78642
79039
  ${run.stdout ?? ""}`).split(/\r?\n/u).map((value) => value.trim()).filter((value) => value && !value.startsWith("hint:"));
78643
79040
  return lines.slice(-max).join(" / ").slice(0, 400) || "git failed";
79041
+ }, hookRefusal = (output) => {
79042
+ const match = /✖\s+(.*?)\s*\[([\w-]+)\]/u.exec(output);
79043
+ if (!match)
79044
+ return null;
79045
+ const [, text, rule] = match;
79046
+ const limit = /(?:longer|shorter|exceed\w*|more|less|than)\D*?(\d+)/iu.exec(text)?.[1];
79047
+ const got = /(?:current length is|got|is)\s+(\d+)/iu.exec(text)?.[1];
79048
+ return `commit refused by commit-msg hook: ${rule}${limit ? ` ${limit}` : ""}${got ? ` (got ${got})` : ""}${limit || got ? "" : ` (${text})`}`;
78644
79049
  }, OUTPUT_LINES2 = 200, OUTPUT_LINE_CHARS = 1000, gitOutput = (run) => {
78645
79050
  const lines = redactText(`${run.stderr}
78646
79051
  ${run.stdout ?? ""}`).split(/\r?\n/u).map((value) => value.trimEnd()).filter((value) => value.trim() && !value.trimStart().startsWith("hint:")).map((value) => value.length > OUTPUT_LINE_CHARS ? `${value.slice(0, OUTPUT_LINE_CHARS)}…` : value);
@@ -79157,6 +79562,8 @@ function waitVerdict(doc, options) {
79157
79562
  };
79158
79563
  if (checks === "pending")
79159
79564
  return { state: "waiting", reason: "checks_pending" };
79565
+ if (doc.checks.pending.length > 0)
79566
+ return { state: "waiting", reason: "checks_pending" };
79160
79567
  if (checks === "passing")
79161
79568
  return { state: "ready", reason: "checks_passing" };
79162
79569
  const ciRequired = doc.checks.missingRequired === null || doc.mergeState === "ci_must_pass" || doc.mergeState === "ci_still_running";
@@ -81365,7 +81772,35 @@ function ancestors(slices) {
81365
81772
  }
81366
81773
  return out;
81367
81774
  }
81368
- function analyzeOverlap(plan, candidates) {
81775
+ function regeneratedFiles(root, tracked) {
81776
+ const found = new Map;
81777
+ let scripts;
81778
+ try {
81779
+ const manifest = JSON.parse(fs29.readFileSync(path56.join(root, "package.json"), "utf8"));
81780
+ scripts = manifest.scripts ?? {};
81781
+ } catch {
81782
+ return found;
81783
+ }
81784
+ const runner = fs29.existsSync(path56.join(root, "bun.lock")) ? "bun run" : fs29.existsSync(path56.join(root, "pnpm-lock.yaml")) ? "pnpm run" : fs29.existsSync(path56.join(root, "yarn.lock")) ? "yarn run" : "npm run";
81785
+ const outputs = tracked.filter((file) => !CODE.test(file));
81786
+ for (const [name, command] of Object.entries(scripts)) {
81787
+ if (!name.startsWith("write:") || typeof command !== "string")
81788
+ continue;
81789
+ for (const file of command.match(/[\w./-]+\.[cm]?[jt]s\b/gu) ?? []) {
81790
+ let source;
81791
+ try {
81792
+ source = fs29.readFileSync(path56.join(root, file), "utf8");
81793
+ } catch {
81794
+ continue;
81795
+ }
81796
+ for (const output of outputs)
81797
+ if (!found.has(output) && source.includes(output))
81798
+ found.set(output, `${runner} ${name}`);
81799
+ }
81800
+ }
81801
+ return found;
81802
+ }
81803
+ function analyzeOverlap(plan, candidates, regenerated = new Map) {
81369
81804
  const shared = [...DEFAULT_SHARED, ...plan.shared];
81370
81805
  const before = ancestors(plan.slices);
81371
81806
  const related = (a, b) => Boolean(before.get(a)?.has(b) || before.get(b)?.has(a));
@@ -81386,6 +81821,11 @@ function analyzeOverlap(plan, candidates) {
81386
81821
  overlaps.push({ path: file, slices: ids, resolution: "owner", owner: owners[0].id });
81387
81822
  continue;
81388
81823
  }
81824
+ const writer = spellings.map((spelling) => regenerated.get(spelling)).find(Boolean);
81825
+ if (writer) {
81826
+ overlaps.push({ path: file, slices: ids, resolution: "regenerated", writer });
81827
+ continue;
81828
+ }
81389
81829
  if (owners.length === 0 && matching.every((a, i) => matching.slice(i + 1).every((b) => related(a.id, b.id)))) {
81390
81830
  overlaps.push({ path: file, slices: ids, resolution: "serialized" });
81391
81831
  continue;
@@ -81668,7 +82108,7 @@ function planFanout(cwd, input, now = new Date) {
81668
82108
  const ref = trunkRef(cwd, plan.trunk);
81669
82109
  const tracked = ref ? nulList2(gitRun4(cwd, ["ls-tree", "-r", "-z", "--name-only", ref]).stdout) : [];
81670
82110
  const samples = plan.slices.flatMap((slice) => [...slice.scope, ...slice.owns].flatMap(scopeSamples));
81671
- const { overlaps, conflicts } = analyzeOverlap(plan, [...tracked, ...samples]);
82111
+ const { overlaps, conflicts } = analyzeOverlap(plan, [...tracked, ...samples], regeneratedFiles(mainCheckout(cwd), tracked));
81672
82112
  if (conflicts.length)
81673
82113
  return fanoutFail("blocked", `${conflicts.length} file-scope overlap${conflicts.length === 1 ? "" : "s"} between slices: ${conflicts[0].slices.join(" and ")} on ${conflicts[0].paths[0]}${conflicts[0].paths.length > 1 ? ` (+${conflicts[0].paths.length - 1})` : ""}`, conflicts[0].suggestion.text, { name: plan.name, conflicts, overlaps });
81674
82114
  const trunkCase = caseIndex(tracked);
@@ -81738,6 +82178,9 @@ function planFanout(cwd, input, now = new Date) {
81738
82178
  if (wave.length > IN_FLIGHT_CAP)
81739
82179
  notes.push(`wave ${index + 1} has ${wave.length} slices; keep 4-${IN_FLIGHT_CAP} in flight and queue the rest`);
81740
82180
  });
82181
+ for (const overlap of overlaps)
82182
+ if (overlap.writer)
82183
+ notes.push(`${overlap.path} is regenerated at fan-in: slices leave it uncommitted, and the lead runs \`${overlap.writer}\` after each rebase`);
81741
82184
  for (const slice of plan.slices)
81742
82185
  if (slice.dependsOn.length > 1 && slice.base === plan.trunk)
81743
82186
  notes.push(`slice ${slice.id} depends on ${slice.dependsOn.join(", ")}: start it from ${plan.trunk} after they land`);
@@ -81779,7 +82222,7 @@ var FANOUT_VERSION = 1, TIERS, REASONS, FAN_IN_MODES, fanoutFail = (code, error,
81779
82222
  if (Array.isArray(value) && value.every((item) => typeof item === "string"))
81780
82223
  return value;
81781
82224
  return null;
81782
- }, ID, quoteList = (items) => items.map((item) => `"${item}"`).join(", "), ownerSuggestion = (slice, paths) => ({
82225
+ }, ID, CODE, quoteList = (items) => items.map((item) => `"${item}"`).join(", "), ownerSuggestion = (slice, paths) => ({
81783
82226
  kind: "owner",
81784
82227
  slice,
81785
82228
  paths,
@@ -81829,6 +82272,7 @@ var init_fanout = __esm(() => {
81829
82272
  compiled = new Map;
81830
82273
  PLACEHOLDER3 = /^(?:<[^>]*>|tbd|todo|fixme|\?+|\.\.\.|…)$/iu;
81831
82274
  ID = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
82275
+ CODE = /\.(?:[cm]?[jt]sx?)$/u;
81832
82276
  });
81833
82277
 
81834
82278
  // packages/workit-core/src/forge/exec.ts
@@ -83628,6 +84072,17 @@ function unverifiedFlag(flags) {
83628
84072
  return "--unverified needs --reason <why the user asked for it>";
83629
84073
  return reason.length > MAX_REASON ? `--reason is capped at ${MAX_REASON} characters` : { reason };
83630
84074
  }
84075
+ function selector(flags) {
84076
+ const pr = positiveInt(flags.values.pr, "--pr");
84077
+ if (typeof pr === "string")
84078
+ return pr;
84079
+ const branch = flags.values.branch ?? flags.values.head ?? null;
84080
+ if (flags.values.branch !== undefined && flags.values.head !== undefined)
84081
+ return "pass --branch or --head, not both";
84082
+ if (pr && branch)
84083
+ return "pass --pr or --branch, not both";
84084
+ return { pr, branch };
84085
+ }
83631
84086
  function positiveInt(value, flag) {
83632
84087
  if (value === undefined)
83633
84088
  return null;
@@ -83714,7 +84169,7 @@ function renderStatus(doc) {
83714
84169
  lines.push(`next: ${doc.next}`);
83715
84170
  return lines;
83716
84171
  }
83717
- var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail2("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail2(result.code, result.error, { data, unblock: result.unblock })), CONNECT_BUDGET_MS = 15000, pushIdentity = (io, branch) => checkPushIdentity(io.cwd, {
84172
+ var forgeDeps, MAX_REASON = 500, usage4 = (io, message, line) => emit(io, fail2("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail2(result.code, result.error, { data, unblock: result.unblock })), CONNECT_BUDGET_MS = 15000, pushIdentity = (io, branch) => checkPushIdentity(io.cwd, {
83718
84173
  branch,
83719
84174
  env: io.env,
83720
84175
  runner: forgeDeps.runner,
@@ -83737,7 +84192,7 @@ var init_forge_common = __esm(() => {
83737
84192
  // packages/workit-cli/src/verbs/pr.ts
83738
84193
  var exports_pr = {};
83739
84194
  __export(exports_pr, {
83740
- run: () => run14
84195
+ run: () => run15
83741
84196
  });
83742
84197
  import { readFileSync as readFileSync24 } from "node:fs";
83743
84198
  import path58 from "node:path";
@@ -83753,25 +84208,24 @@ function readBodyFile(io, file) {
83753
84208
  async function status2(argv, io) {
83754
84209
  const flags = parseFlags(argv, { pr: "value", branch: "value", "log-lines": "value" });
83755
84210
  if (typeof flags === "string")
83756
- return usage3(io, flags, STATUS_USAGE);
84211
+ return usage4(io, flags, STATUS_USAGE);
83757
84212
  if (flags.positionals.length)
83758
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, STATUS_USAGE);
83759
- const pr = positiveInt(flags.values.pr, "--pr");
83760
- if (typeof pr === "string")
83761
- return usage3(io, pr, STATUS_USAGE);
83762
- if (pr && flags.values.branch)
83763
- return usage3(io, "pass --pr or --branch, not both", STATUS_USAGE);
84213
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, STATUS_USAGE);
84214
+ const picked = selector(flags);
84215
+ if (typeof picked === "string")
84216
+ return usage4(io, picked, STATUS_USAGE);
84217
+ const { pr, branch: pickedBranch } = picked;
83764
84218
  const logLines = flags.values["log-lines"] === "0" ? 0 : positiveInt(flags.values["log-lines"], "--log-lines");
83765
84219
  if (typeof logLines === "string")
83766
- return usage3(io, logLines, STATUS_USAGE);
84220
+ return usage4(io, logLines, STATUS_USAGE);
83767
84221
  if (logLines !== null && logLines > 500)
83768
- return usage3(io, "--log-lines is capped at 500", STATUS_USAGE);
83769
- const connected = connect(io, flags.values.branch);
84222
+ return usage4(io, "--log-lines is capped at 500", STATUS_USAGE);
84223
+ const connected = connect(io, pickedBranch);
83770
84224
  if (!connected.ok)
83771
84225
  return forgeFail(io, connected);
83772
84226
  const report = prStatusReport(io.cwd, connected.data, {
83773
84227
  pr,
83774
- branch: flags.values.branch ?? null,
84228
+ branch: pickedBranch,
83775
84229
  logLines: logLines ?? 60,
83776
84230
  identity: connected.data.identity,
83777
84231
  actor: actorFromEnv(io.env)
@@ -83863,23 +84317,23 @@ async function create4(argv, io) {
83863
84317
  refs: "boolean"
83864
84318
  });
83865
84319
  if (typeof flags === "string")
83866
- return usage3(io, flags, CREATE_USAGE);
84320
+ return usage4(io, flags, CREATE_USAGE);
83867
84321
  if (flags.positionals.length)
83868
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, CREATE_USAGE);
84322
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, CREATE_USAGE);
83869
84323
  const fill = flags.booleans.has("fill");
83870
84324
  if (!fill && !flags.values.title)
83871
- return usage3(io, "pass --title <t> or --fill", CREATE_USAGE);
84325
+ return usage4(io, "pass --title <t> or --fill", CREATE_USAGE);
83872
84326
  if (flags.values.body !== undefined && flags.values["body-file"] !== undefined)
83873
- return usage3(io, "pass --body or --body-file, not both", CREATE_USAGE);
84327
+ return usage4(io, "pass --body or --body-file, not both", CREATE_USAGE);
83874
84328
  let body = flags.values.body ?? null;
83875
84329
  if (flags.values["body-file"] !== undefined) {
83876
84330
  const read = readBodyFile(io, flags.values["body-file"]);
83877
84331
  if (typeof read === "string")
83878
- return usage3(io, read, CREATE_USAGE);
84332
+ return usage4(io, read, CREATE_USAGE);
83879
84333
  body = read.body;
83880
84334
  }
83881
84335
  if (flags.values.base !== undefined && flags.values.track !== undefined)
83882
- return usage3(io, "pass --base or --track, not both (--track picks the base for you)", CREATE_USAGE);
84336
+ return usage4(io, "pass --base or --track, not both (--track picks the base for you)", CREATE_USAGE);
83883
84337
  let base = flags.values.base ?? null;
83884
84338
  const branch = currentBranch(io.cwd);
83885
84339
  const kind = branchKindOf(branch);
@@ -83910,7 +84364,7 @@ async function create4(argv, io) {
83910
84364
  base = route.target;
83911
84365
  }
83912
84366
  if (!base)
83913
- return usage3(io, "no default target branch is configured; pass --base <b>", CREATE_USAGE);
84367
+ return usage4(io, "no default target branch is configured; pass --base <b>", CREATE_USAGE);
83914
84368
  const connected = connect(io, branch);
83915
84369
  if (!connected.ok)
83916
84370
  return forgeFail(io, connected);
@@ -83930,7 +84384,7 @@ async function create4(argv, io) {
83930
84384
  forge: { kind: forge.kind, repo: forge.repo, host: forge.apiHost }
83931
84385
  });
83932
84386
  if (!planned.ok)
83933
- return usage3(io, planned.error, CREATE_USAGE);
84387
+ return usage4(io, planned.error, CREATE_USAGE);
83934
84388
  const links = planned.data.links;
83935
84389
  const actor = actorFromEnv(io.env);
83936
84390
  const result = await createPullRequest(io.cwd, connected.data, {
@@ -83973,9 +84427,17 @@ async function create4(argv, io) {
83973
84427
  ...issueLines(links, opened, linked, backlinks, data.number),
83974
84428
  ...planned.data.warnings.map((warning) => `note: ${warning}`),
83975
84429
  ..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : [],
83976
- `next: ${data.next}`
84430
+ `next: ${data.next}${endpointNote(io.cwd)}`
83977
84431
  ]);
83978
84432
  }
84433
+ function endpointNote(cwd) {
84434
+ try {
84435
+ const line = endpointMismatch(resolveAutonomy(cwd));
84436
+ return line ? `; ${line}` : "";
84437
+ } catch {
84438
+ return "";
84439
+ }
84440
+ }
83979
84441
  function issueLines(links, opened, linked, backlinks, number) {
83980
84442
  if (links.length === 0)
83981
84443
  return [];
@@ -83994,6 +84456,9 @@ async function merge3(argv, io) {
83994
84456
  const flags = parseFlags(argv, {
83995
84457
  pr: "value",
83996
84458
  method: "value",
84459
+ squash: "boolean",
84460
+ merge: "boolean",
84461
+ rebase: "boolean",
83997
84462
  "delete-branch": "boolean",
83998
84463
  unverified: "boolean",
83999
84464
  reason: "value",
@@ -84002,33 +84467,37 @@ async function merge3(argv, io) {
84002
84467
  "body-file": "value"
84003
84468
  });
84004
84469
  if (typeof flags === "string")
84005
- return usage3(io, flags, MERGE_USAGE);
84470
+ return usage4(io, flags, MERGE_USAGE);
84006
84471
  const unverified = unverifiedFlag(flags);
84007
84472
  if (typeof unverified === "string")
84008
- return usage3(io, unverified, MERGE_USAGE);
84473
+ return usage4(io, unverified, MERGE_USAGE);
84009
84474
  if (flags.positionals.length)
84010
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, MERGE_USAGE);
84011
- const pr = positiveInt(flags.values.pr, "--pr");
84012
- if (typeof pr === "string")
84013
- return usage3(io, pr, MERGE_USAGE);
84014
- const method = flags.values.method ?? "squash";
84475
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, MERGE_USAGE);
84476
+ const picked = selector(flags);
84477
+ if (typeof picked === "string")
84478
+ return usage4(io, picked, MERGE_USAGE);
84479
+ const { pr } = picked;
84480
+ const aliases = ["squash", "merge", "rebase"].filter((name) => flags.booleans.has(name));
84481
+ if (aliases.length > 1 || aliases.length === 1 && flags.values.method !== undefined)
84482
+ return usage4(io, "pass one of --squash, --merge, --rebase or --method", MERGE_USAGE);
84483
+ const method = aliases[0] ?? flags.values.method ?? "squash";
84015
84484
  if (!METHODS2.has(method))
84016
- return usage3(io, "--method must be squash, merge or rebase", MERGE_USAGE);
84485
+ return usage4(io, "--method must be squash, merge or rebase", MERGE_USAGE);
84017
84486
  const { subject } = flags.values;
84018
84487
  if (flags.values.body !== undefined && flags.values["body-file"] !== undefined)
84019
- return usage3(io, "pass --body or --body-file, not both", MERGE_USAGE);
84488
+ return usage4(io, "pass --body or --body-file, not both", MERGE_USAGE);
84020
84489
  let body = flags.values.body;
84021
84490
  if (flags.values["body-file"] !== undefined) {
84022
84491
  const read = readBodyFile(io, flags.values["body-file"]);
84023
84492
  if (typeof read === "string")
84024
- return usage3(io, read, MERGE_USAGE);
84493
+ return usage4(io, read, MERGE_USAGE);
84025
84494
  body = read.body;
84026
84495
  }
84027
84496
  if ((subject !== undefined || body !== undefined) && method !== "squash")
84028
- return usage3(io, "--subject and --body set a squash message; use --method squash", MERGE_USAGE);
84497
+ return usage4(io, "--subject and --body set a squash message; use --method squash", MERGE_USAGE);
84029
84498
  if (subject !== undefined && (!subject.trim() || subject.includes(`
84030
84499
  `)))
84031
- return usage3(io, "--subject must be one non-empty line", MERGE_USAGE);
84500
+ return usage4(io, "--subject must be one non-empty line", MERGE_USAGE);
84032
84501
  const connected = connect(io);
84033
84502
  if (!connected.ok)
84034
84503
  return forgeFail(io, connected);
@@ -84060,12 +84529,13 @@ async function merge3(argv, io) {
84060
84529
  async function ready(argv, io) {
84061
84530
  const flags = parseFlags(argv, { pr: "value", undo: "boolean" });
84062
84531
  if (typeof flags === "string")
84063
- return usage3(io, flags, READY_USAGE);
84532
+ return usage4(io, flags, READY_USAGE);
84064
84533
  if (flags.positionals.length)
84065
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, READY_USAGE);
84066
- const pr = positiveInt(flags.values.pr, "--pr");
84067
- if (typeof pr === "string")
84068
- return usage3(io, pr, READY_USAGE);
84534
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, READY_USAGE);
84535
+ const picked = selector(flags);
84536
+ if (typeof picked === "string")
84537
+ return usage4(io, picked, READY_USAGE);
84538
+ const { pr } = picked;
84069
84539
  const connected = connect(io);
84070
84540
  if (!connected.ok)
84071
84541
  return forgeFail(io, connected);
@@ -84091,17 +84561,18 @@ async function edit(argv, io) {
84091
84561
  "add-reviewer": "list"
84092
84562
  });
84093
84563
  if (typeof flags === "string")
84094
- return usage3(io, flags, EDIT_USAGE);
84564
+ return usage4(io, flags, EDIT_USAGE);
84095
84565
  if (flags.positionals.length)
84096
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, EDIT_USAGE);
84097
- const pr = positiveInt(flags.values.pr, "--pr");
84098
- if (typeof pr === "string")
84099
- return usage3(io, pr, EDIT_USAGE);
84566
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, EDIT_USAGE);
84567
+ const picked = selector(flags);
84568
+ if (typeof picked === "string")
84569
+ return usage4(io, picked, EDIT_USAGE);
84570
+ const { pr } = picked;
84100
84571
  let body;
84101
84572
  if (flags.values["body-file"] !== undefined) {
84102
84573
  const read = readBodyFile(io, flags.values["body-file"]);
84103
84574
  if (typeof read === "string")
84104
- return usage3(io, read, EDIT_USAGE);
84575
+ return usage4(io, read, EDIT_USAGE);
84105
84576
  body = read.body;
84106
84577
  }
84107
84578
  const connected = connect(io);
@@ -84119,7 +84590,7 @@ async function edit(argv, io) {
84119
84590
  }
84120
84591
  });
84121
84592
  if (!result.ok)
84122
- return result.code === "invalid_input" ? usage3(io, result.error, EDIT_USAGE) : forgeFail(io, result);
84593
+ return result.code === "invalid_input" ? usage4(io, result.error, EDIT_USAGE) : forgeFail(io, result);
84123
84594
  const kind = connected.data.forge.kind;
84124
84595
  return emit(io, ok2(result.data), (data) => [
84125
84596
  `edited ${prLabel(kind, data.number)} (${data.changed.join(", ")}): ${data.title} -> ${data.base} ${data.url}`
@@ -84128,12 +84599,13 @@ async function edit(argv, io) {
84128
84599
  async function threads(argv, io) {
84129
84600
  const flags = parseFlags(argv, { pr: "value" });
84130
84601
  if (typeof flags === "string")
84131
- return usage3(io, flags, THREADS_USAGE);
84602
+ return usage4(io, flags, THREADS_USAGE);
84132
84603
  if (flags.positionals.length)
84133
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, THREADS_USAGE);
84134
- const pr = positiveInt(flags.values.pr, "--pr");
84135
- if (typeof pr === "string")
84136
- return usage3(io, pr, THREADS_USAGE);
84604
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, THREADS_USAGE);
84605
+ const picked = selector(flags);
84606
+ if (typeof picked === "string")
84607
+ return usage4(io, picked, THREADS_USAGE);
84608
+ const { pr } = picked;
84137
84609
  const connected = connect(io);
84138
84610
  if (!connected.ok)
84139
84611
  return forgeFail(io, connected);
@@ -84154,20 +84626,21 @@ async function reply(argv, io) {
84154
84626
  resolve: "boolean"
84155
84627
  });
84156
84628
  if (typeof flags === "string")
84157
- return usage3(io, flags, REPLY_USAGE);
84629
+ return usage4(io, flags, REPLY_USAGE);
84158
84630
  if (flags.positionals.length)
84159
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, REPLY_USAGE);
84160
- const pr = positiveInt(flags.values.pr, "--pr");
84161
- if (typeof pr === "string")
84162
- return usage3(io, pr, REPLY_USAGE);
84631
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, REPLY_USAGE);
84632
+ const picked = selector(flags);
84633
+ if (typeof picked === "string")
84634
+ return usage4(io, picked, REPLY_USAGE);
84635
+ const { pr } = picked;
84163
84636
  const thread = flags.values.thread;
84164
84637
  if (!thread)
84165
- return usage3(io, "--thread <id> is required (workit pr threads)", REPLY_USAGE);
84638
+ return usage4(io, "--thread <id> is required (workit pr threads)", REPLY_USAGE);
84166
84639
  let body = null;
84167
84640
  if (flags.values["body-file"] !== undefined) {
84168
84641
  const read = readBodyFile(io, flags.values["body-file"]);
84169
84642
  if (typeof read === "string")
84170
- return usage3(io, read, REPLY_USAGE);
84643
+ return usage4(io, read, REPLY_USAGE);
84171
84644
  body = read.body;
84172
84645
  }
84173
84646
  const connected = connect(io);
@@ -84180,13 +84653,13 @@ async function reply(argv, io) {
84180
84653
  resolve: flags.booleans.has("resolve")
84181
84654
  });
84182
84655
  if (!result.ok)
84183
- return result.code === "invalid_input" ? usage3(io, result.error, REPLY_USAGE) : forgeFail(io, result);
84656
+ return result.code === "invalid_input" ? usage4(io, result.error, REPLY_USAGE) : forgeFail(io, result);
84184
84657
  const kind = connected.data.forge.kind;
84185
84658
  return emit(io, ok2(result.data), (data) => [
84186
84659
  `${[data.replied ? "replied to" : null, data.resolved ? "resolved" : null].filter(Boolean).join(" and ")} thread ${data.thread} on ${prLabel(kind, data.number)}${data.replyUrl ? ` ${data.replyUrl}` : ""}`
84187
84660
  ]);
84188
84661
  }
84189
- async function run14(argv, io) {
84662
+ async function run15(argv, io) {
84190
84663
  const [sub, ...rest] = argv;
84191
84664
  if (sub === "status")
84192
84665
  return status2(rest, io);
@@ -84202,10 +84675,11 @@ async function run14(argv, io) {
84202
84675
  return threads(rest, io);
84203
84676
  if (sub === "reply")
84204
84677
  return reply(rest, io);
84205
- return usage3(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE7);
84678
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE8);
84206
84679
  }
84207
- 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>] [--method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --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]", USAGE7 = "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;
84680
+ 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] [--unverified --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;
84208
84681
  var init_pr = __esm(() => {
84682
+ init_autonomy();
84209
84683
  init_issue_link();
84210
84684
  init_pr_edit();
84211
84685
  init_pr_ops();
@@ -84230,7 +84704,7 @@ var init_pr = __esm(() => {
84230
84704
  // packages/workit-cli/src/verbs/ci.ts
84231
84705
  var exports_ci = {};
84232
84706
  __export(exports_ci, {
84233
- run: () => run15
84707
+ run: () => run16
84234
84708
  });
84235
84709
  async function wait2(argv, io) {
84236
84710
  const flags = parseFlags(argv, {
@@ -84241,23 +84715,23 @@ async function wait2(argv, io) {
84241
84715
  interval: "value"
84242
84716
  });
84243
84717
  if (typeof flags === "string")
84244
- return usage3(io, flags, WAIT_USAGE);
84718
+ return usage4(io, flags, WAIT_USAGE);
84245
84719
  if (flags.positionals.length)
84246
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, WAIT_USAGE);
84720
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, WAIT_USAGE);
84247
84721
  const pr = positiveInt(flags.values.pr, "--pr");
84248
84722
  if (typeof pr === "string")
84249
- return usage3(io, pr, WAIT_USAGE);
84723
+ return usage4(io, pr, WAIT_USAGE);
84250
84724
  const timeoutMs = parseDuration2(flags.values.timeout ?? "20m");
84251
84725
  const intervalMs = parseDuration2(flags.values.interval ?? "30s");
84252
84726
  if (timeoutMs === null || timeoutMs > 6 * 3600000)
84253
- return usage3(io, "--timeout must be a duration up to 6h (20m, 90s)", WAIT_USAGE);
84727
+ return usage4(io, "--timeout must be a duration up to 6h (20m, 90s)", WAIT_USAGE);
84254
84728
  if (intervalMs === null || intervalMs < 1000 || intervalMs > 600000)
84255
- return usage3(io, "--interval must be between 1s and 10m", WAIT_USAGE);
84729
+ return usage4(io, "--interval must be between 1s and 10m", WAIT_USAGE);
84256
84730
  if (pr && flags.values.branch)
84257
- return usage3(io, "pass --pr or --branch, not both", WAIT_USAGE);
84731
+ return usage4(io, "pass --pr or --branch, not both", WAIT_USAGE);
84258
84732
  const head = flags.values.head ?? null;
84259
84733
  if (head !== null && !/^[0-9a-f]{7,64}$/u.test(head))
84260
- return usage3(io, "--head must be a commit sha (7-64 hex chars)", WAIT_USAGE);
84734
+ return usage4(io, "--head must be a commit sha (7-64 hex chars)", WAIT_USAGE);
84261
84735
  const started = forgeDeps.now();
84262
84736
  const deadline = started + timeoutMs;
84263
84737
  const connected = connect(io, flags.values.branch, {
@@ -84314,7 +84788,8 @@ async function wait2(argv, io) {
84314
84788
  return forgeFail(io, built);
84315
84789
  const doc = built.data;
84316
84790
  const data = {
84317
- state: verdict.state,
84791
+ state: verdict.state === "waiting" ? "in_progress" : verdict.state,
84792
+ ...verdict.state === "failed" && doc.checks.pending.length > 0 ? { failFast: true, stillRunning: doc.checks.pending } : {},
84318
84793
  reason: verdict.reason,
84319
84794
  polls,
84320
84795
  elapsedMs,
@@ -84352,20 +84827,20 @@ async function rerun(argv, io) {
84352
84827
  force: "boolean"
84353
84828
  });
84354
84829
  if (typeof flags === "string")
84355
- return usage3(io, flags, RERUN_USAGE);
84830
+ return usage4(io, flags, RERUN_USAGE);
84356
84831
  if (flags.positionals.length)
84357
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, RERUN_USAGE);
84832
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, RERUN_USAGE);
84358
84833
  const pr = positiveInt(flags.values.pr, "--pr");
84359
84834
  if (typeof pr === "string")
84360
- return usage3(io, pr, RERUN_USAGE);
84835
+ return usage4(io, pr, RERUN_USAGE);
84361
84836
  const names = flags.lists.check ?? [];
84362
84837
  if (pr && flags.values.branch)
84363
- return usage3(io, "pass --pr or --branch, not both", RERUN_USAGE);
84838
+ return usage4(io, "pass --pr or --branch, not both", RERUN_USAGE);
84364
84839
  if (names.length && flags.booleans.has("failed"))
84365
- return usage3(io, "pass --check or --failed, not both", RERUN_USAGE);
84840
+ return usage4(io, "pass --check or --failed, not both", RERUN_USAGE);
84366
84841
  const reason = flags.values.reason;
84367
84842
  if (reason !== "flake" && reason !== "infra")
84368
- return usage3(io, "--reason flake|infra is required: rerun only what you judged a flake or an infrastructure failure", RERUN_USAGE);
84843
+ return usage4(io, "--reason flake|infra is required: rerun only what you judged a flake or an infrastructure failure", RERUN_USAGE);
84369
84844
  const connected = connect(io, flags.values.branch);
84370
84845
  if (!connected.ok)
84371
84846
  return forgeFail(io, connected);
@@ -84402,16 +84877,16 @@ async function rerun(argv, io) {
84402
84877
  async function logs(argv, io) {
84403
84878
  const flags = parseFlags(argv, { pr: "value", branch: "value", failed: "boolean" });
84404
84879
  if (typeof flags === "string")
84405
- return usage3(io, flags, LOGS_USAGE);
84880
+ return usage4(io, flags, LOGS_USAGE);
84406
84881
  if (flags.positionals.length)
84407
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, LOGS_USAGE);
84882
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, LOGS_USAGE);
84408
84883
  if (!flags.booleans.has("failed"))
84409
- return usage3(io, "pass --failed: only the failing steps' tails are printed", LOGS_USAGE);
84884
+ return usage4(io, "pass --failed: only the failing steps' tails are printed", LOGS_USAGE);
84410
84885
  const pr = positiveInt(flags.values.pr, "--pr");
84411
84886
  if (typeof pr === "string")
84412
- return usage3(io, pr, LOGS_USAGE);
84887
+ return usage4(io, pr, LOGS_USAGE);
84413
84888
  if (pr && flags.values.branch)
84414
- return usage3(io, "pass --pr or --branch, not both", LOGS_USAGE);
84889
+ return usage4(io, "pass --pr or --branch, not both", LOGS_USAGE);
84415
84890
  const connected = connect(io, flags.values.branch);
84416
84891
  if (!connected.ok)
84417
84892
  return forgeFail(io, connected);
@@ -84446,7 +84921,7 @@ async function logs(argv, io) {
84446
84921
  ])
84447
84922
  ] : [`PR #${data.pr} head ${data.head.slice(0, 7)}: no failing checks`]);
84448
84923
  }
84449
- async function run15(argv, io) {
84924
+ async function run16(argv, io) {
84450
84925
  const [sub, ...rest] = argv;
84451
84926
  if (sub === "wait")
84452
84927
  return wait2(rest, io);
@@ -84454,7 +84929,7 @@ async function run15(argv, io) {
84454
84929
  return rerun(rest, io);
84455
84930
  if (sub === "logs")
84456
84931
  return logs(rest, io);
84457
- return usage3(io, sub && !sub.startsWith("-") ? `unknown ci subcommand "${sub}"` : "missing ci subcommand", `${WAIT_USAGE}
84932
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown ci subcommand "${sub}"` : "missing ci subcommand", `${WAIT_USAGE}
84458
84933
  ${RERUN_USAGE}
84459
84934
  ${LOGS_USAGE}`);
84460
84935
  }
@@ -84497,7 +84972,7 @@ var init_implicit_task = __esm(() => {
84497
84972
  // packages/workit-cli/src/verbs/git.ts
84498
84973
  var exports_git = {};
84499
84974
  __export(exports_git, {
84500
- run: () => run16
84975
+ run: () => run17
84501
84976
  });
84502
84977
  import { readFileSync as readFileSync25 } from "node:fs";
84503
84978
  import path59 from "node:path";
@@ -84521,18 +84996,18 @@ async function branch(argv, io) {
84521
84996
  carry: "boolean"
84522
84997
  });
84523
84998
  if (typeof flags === "string")
84524
- return usage3(io, flags, BRANCH_USAGE);
84999
+ return usage4(io, flags, BRANCH_USAGE);
84525
85000
  const { kind, slug } = flags.values;
84526
85001
  if (flags.positionals.length > 1)
84527
- return usage3(io, `unexpected argument ${flags.positionals[1]}`, BRANCH_USAGE);
85002
+ return usage4(io, `unexpected argument ${flags.positionals[1]}`, BRANCH_USAGE);
84528
85003
  if (kind === undefined !== (slug === undefined))
84529
- return usage3(io, "--kind and --slug go together", BRANCH_USAGE);
85004
+ return usage4(io, "--kind and --slug go together", BRANCH_USAGE);
84530
85005
  if (kind !== undefined && !BRANCH_KINDS.includes(kind))
84531
- return usage3(io, `--kind must be ${BRANCH_KINDS.join(", ")}; ${kindHint(io.cwd, kind)}`, BRANCH_USAGE);
85006
+ return usage4(io, `--kind must be ${BRANCH_KINDS.join(", ")}; ${kindHint(io.cwd, kind)}`, BRANCH_USAGE);
84532
85007
  if (kind !== undefined && flags.positionals.length)
84533
- return usage3(io, "pass a name or --kind/--slug, not both", BRANCH_USAGE);
85008
+ return usage4(io, "pass a name or --kind/--slug, not both", BRANCH_USAGE);
84534
85009
  if (kind === undefined && !flags.positionals[0])
84535
- return usage3(io, "missing branch name", BRANCH_USAGE);
85010
+ return usage4(io, "missing branch name", BRANCH_USAGE);
84536
85011
  const result = gitBranch(io.cwd, {
84537
85012
  ...kind !== undefined ? { kind, slug } : { name: flags.positionals[0] },
84538
85013
  base: flags.values.base ?? null,
@@ -84614,21 +85089,21 @@ function commitMessage(parsed, io) {
84614
85089
  async function commit(argv, io) {
84615
85090
  const parsed = parseCommit(argv);
84616
85091
  if (typeof parsed === "string")
84617
- return usage3(io, parsed, COMMIT_USAGE);
85092
+ return usage4(io, parsed, COMMIT_USAGE);
84618
85093
  if (parsed.all && parsed.paths.length)
84619
- return usage3(io, "pass --all or paths, not both", COMMIT_USAGE);
85094
+ return usage4(io, "pass --all or paths, not both", COMMIT_USAGE);
84620
85095
  if (parsed.noEdit && !parsed.amend)
84621
- return usage3(io, "--no-edit only applies with --amend", COMMIT_USAGE);
85096
+ return usage4(io, "--no-edit only applies with --amend", COMMIT_USAGE);
84622
85097
  const given = parsed.messages.length + parsed.files.length > 0;
84623
85098
  if (parsed.noEdit && given)
84624
- return usage3(io, "--no-edit keeps the message; drop -m/-F or --no-edit", COMMIT_USAGE);
85099
+ return usage4(io, "--no-edit keeps the message; drop -m/-F or --no-edit", COMMIT_USAGE);
84625
85100
  if (!given && !parsed.noEdit)
84626
- return usage3(io, parsed.amend ? "--amend needs -m/-F or --no-edit" : "missing -m <msg> or -F <file>", COMMIT_USAGE);
85101
+ return usage4(io, parsed.amend ? "--amend needs -m/-F or --no-edit" : "missing -m <msg> or -F <file>", COMMIT_USAGE);
84627
85102
  const message = given ? commitMessage(parsed, io) : null;
84628
85103
  if (message !== null && typeof message !== "string")
84629
- return usage3(io, message.error, COMMIT_USAGE);
85104
+ return usage4(io, message.error, COMMIT_USAGE);
84630
85105
  if (message !== null && !message.trim())
84631
- return usage3(io, "the commit message is empty", COMMIT_USAGE);
85106
+ return usage4(io, "the commit message is empty", COMMIT_USAGE);
84632
85107
  const result = gitCommit(io.cwd, {
84633
85108
  message,
84634
85109
  all: parsed.all,
@@ -84653,7 +85128,7 @@ async function commit(argv, io) {
84653
85128
  }
84654
85129
  async function push(argv, io) {
84655
85130
  if (argv.includes("--force") || argv.includes("-f"))
84656
- return usage3(io, "workit never force-pushes blindly; use --force-with-lease (the lease is the tip workit last recorded)", PUSH_USAGE);
85131
+ return usage4(io, "workit never force-pushes blindly; use --force-with-lease (the lease is the tip workit last recorded)", PUSH_USAGE);
84657
85132
  const normalized = argv.map((arg) => arg === "-u" ? "--set-upstream" : arg);
84658
85133
  const flags = parseFlags(normalized, {
84659
85134
  "set-upstream": "boolean",
@@ -84663,20 +85138,20 @@ async function push(argv, io) {
84663
85138
  release: "list"
84664
85139
  });
84665
85140
  if (typeof flags === "string")
84666
- return usage3(io, flags, PUSH_USAGE);
85141
+ return usage4(io, flags, PUSH_USAGE);
84667
85142
  const release = flags.lists.release;
84668
85143
  if (release) {
84669
85144
  if (flags.booleans.size || flags.values.expect !== undefined || flags.positionals.length)
84670
- return usage3(io, "--release takes only ref names (repeat it: --release main --release v1.2.0)", PUSH_USAGE);
85145
+ return usage4(io, "--release takes only ref names (repeat it: --release main --release v1.2.0)", PUSH_USAGE);
84671
85146
  return releasePush(release, io);
84672
85147
  }
84673
85148
  if (flags.positionals.length)
84674
- return usage3(io, `unexpected argument ${flags.positionals[0]} (workit pushes the current branch)`, PUSH_USAGE);
85149
+ return usage4(io, `unexpected argument ${flags.positionals[0]} (workit pushes the current branch)`, PUSH_USAGE);
84675
85150
  const expect = flags.values.expect ?? null;
84676
85151
  if ((expect !== null || flags.booleans.has("overwrite-unintegrated")) && !flags.booleans.has("force-with-lease"))
84677
- return usage3(io, "--expect and --overwrite-unintegrated only apply with --force-with-lease", PUSH_USAGE);
85152
+ return usage4(io, "--expect and --overwrite-unintegrated only apply with --force-with-lease", PUSH_USAGE);
84678
85153
  if (expect !== null && !/^[0-9a-f]{7,64}$/u.test(expect))
84679
- return usage3(io, "--expect must be a commit sha (at least 7 hex digits)", PUSH_USAGE);
85154
+ return usage4(io, "--expect must be a commit sha (at least 7 hex digits)", PUSH_USAGE);
84680
85155
  const plan = pushPreflight(io.cwd);
84681
85156
  if (!plan.ok)
84682
85157
  return forgeFail(io, plan);
@@ -84711,7 +85186,7 @@ async function push(argv, io) {
84711
85186
  `);
84712
85187
  const envelope = ok2({ ...result.data, identity: checked });
84713
85188
  return emit(io, warnings.length ? { ...envelope, warnings } : envelope, (data) => [
84714
- 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`,
85189
+ data.pushed ? `pushed ${data.branch} ${short2(data.previous)} -> ${short2(data.sha)} to ${data.remote}${data.forced ? " (force-with-lease)" : ""}; remote tip verified` : `nothing to push: ${data.remote}/${data.branch} is already at ${short2(data.sha)}`,
84715
85190
  ...data.upstream ? [`upstream: ${data.remote}/${data.branch}`] : []
84716
85191
  ]);
84717
85192
  }
@@ -84744,19 +85219,21 @@ async function releasePush(names, io) {
84744
85219
  if (!result.ok)
84745
85220
  return forgeFail(io, result);
84746
85221
  return emit(io, ok2({ ...result.data, identity: checked }), (data) => [
84747
- ...data.refs.map((ref) => ref.pushed ? `pushed ${ref.name} ${short2(ref.previous)} -> ${short2(ref.sha)} to ${data.remote}; remote tip verified` : `${data.remote} ${ref.name} is already at ${short2(ref.sha)}; remote tip verified`),
85222
+ ...data.refs.map((ref) => ref.pushed ? `pushed ${ref.name} ${short2(ref.previous)} -> ${short2(ref.sha)} to ${data.remote}; remote tip verified` : `nothing to push: ${data.remote} ${ref.name} is already at ${short2(ref.sha)}`),
84748
85223
  ..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : []
84749
85224
  ]);
84750
85225
  }
84751
85226
  function trailers(argv, io) {
84752
85227
  const extra = argv.find((arg) => arg !== "--json");
84753
85228
  if (extra !== undefined)
84754
- return usage3(io, `unexpected argument ${extra}`, "workit git trailers");
85229
+ return usage4(io, `unexpected argument ${extra}`, "workit git trailers");
84755
85230
  const state = workspaceTrailers(io.cwd);
84756
85231
  return emit(io, ok2(state), (data) => data.enabled ? "on" : "off");
84757
85232
  }
84758
- async function run16(argv, io) {
85233
+ async function run17(argv, io) {
84759
85234
  const [sub, ...rest] = argv;
85235
+ if (sub !== undefined && READ_ONLY.has(sub))
85236
+ return usage4(io, `workit git has no ${sub}`, `read-only git is fine raw: \`git -C ${io.cwd} ${sub}\``);
84760
85237
  if (sub === "branch")
84761
85238
  return branch(rest, io);
84762
85239
  if (sub === "commit")
@@ -84765,9 +85242,9 @@ async function run16(argv, io) {
84765
85242
  return push(rest, io);
84766
85243
  if (sub === "trailers")
84767
85244
  return trailers(rest, io);
84768
- return usage3(io, sub && !sub.startsWith("-") ? `unknown git subcommand "${sub}"` : "missing git subcommand", USAGE8);
85245
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown git subcommand "${sub}"` : "missing git subcommand", USAGE9);
84769
85246
  }
84770
- 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]", USAGE8 = "workit git branch|commit|push|trailers ... (workit help git)", short2 = (sha) => sha ? sha.slice(0, 12) : "(none)", COMMIT_SWITCHES;
85247
+ 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;
84771
85248
  var init_git2 = __esm(() => {
84772
85249
  init_autonomy();
84773
85250
  init_branch();
@@ -84784,6 +85261,7 @@ var init_git2 = __esm(() => {
84784
85261
  "--no-edit": "noEdit",
84785
85262
  "--allow-empty": "allowEmpty"
84786
85263
  };
85264
+ READ_ONLY = new Set(["status", "diff", "log", "fetch", "show", "blame"]);
84787
85265
  });
84788
85266
 
84789
85267
  // packages/workit-core/src/forge/verify.ts
@@ -85448,7 +85926,7 @@ var init_release = __esm(() => {
85448
85926
  // packages/workit-cli/src/verbs/release.ts
85449
85927
  var exports_release = {};
85450
85928
  __export(exports_release, {
85451
- run: () => run17
85929
+ run: () => run18
85452
85930
  });
85453
85931
  async function subVerb(load, argv, io) {
85454
85932
  let out = "";
@@ -85469,9 +85947,9 @@ async function subVerb(load, argv, io) {
85469
85947
  async function start(argv, io) {
85470
85948
  const flags = parseFlags(argv, { hotfix: "boolean", track: "value" });
85471
85949
  if (typeof flags === "string")
85472
- return usage3(io, flags, START_USAGE);
85950
+ return usage4(io, flags, START_USAGE);
85473
85951
  if (flags.positionals.length !== 1)
85474
- return usage3(io, flags.positionals.length ? "pass one version" : "missing <version>", START_USAGE);
85952
+ return usage4(io, flags.positionals.length ? "pass one version" : "missing <version>", START_USAGE);
85475
85953
  const result = releaseStart(io.cwd, {
85476
85954
  version: flags.positionals[0],
85477
85955
  hotfix: flags.booleans.has("hotfix"),
@@ -85524,9 +86002,9 @@ async function releasePr(io, plan, dryRun) {
85524
86002
  async function finish2(argv, io) {
85525
86003
  const flags = parseFlags(argv, { track: "value", "dry-run": "boolean" });
85526
86004
  if (typeof flags === "string")
85527
- return usage3(io, flags, FINISH_USAGE);
86005
+ return usage4(io, flags, FINISH_USAGE);
85528
86006
  if (flags.positionals.length > 1)
85529
- return usage3(io, `unexpected argument ${flags.positionals[1]}`, FINISH_USAGE);
86007
+ return usage4(io, `unexpected argument ${flags.positionals[1]}`, FINISH_USAGE);
85530
86008
  const dryRun = flags.booleans.has("dry-run");
85531
86009
  const planned = planReleaseFinish(io.cwd, {
85532
86010
  version: flags.positionals[0] ?? null,
@@ -85636,9 +86114,9 @@ async function finish2(argv, io) {
85636
86114
  async function publish(argv, io) {
85637
86115
  const flags = parseFlags(argv, {});
85638
86116
  if (typeof flags === "string")
85639
- return usage3(io, flags, PUBLISH_USAGE);
86117
+ return usage4(io, flags, PUBLISH_USAGE);
85640
86118
  if (flags.positionals.length > 1)
85641
- return usage3(io, `unexpected argument ${flags.positionals[1]}`, PUBLISH_USAGE);
86119
+ return usage4(io, `unexpected argument ${flags.positionals[1]}`, PUBLISH_USAGE);
85642
86120
  const located = releaseToPublish(io.cwd, flags.positionals[0] ?? null);
85643
86121
  if (!located.ok)
85644
86122
  return forgeFail(io, located);
@@ -85671,7 +86149,7 @@ async function publish(argv, io) {
85671
86149
  `published ${data.branch} (${short4(data.sha)}) to ${data.remote}; remote tip verified`
85672
86150
  ]);
85673
86151
  }
85674
- async function run17(argv, io) {
86152
+ async function run18(argv, io) {
85675
86153
  const [sub, ...rest] = argv;
85676
86154
  if (sub === "start")
85677
86155
  return start(rest, io);
@@ -85679,9 +86157,9 @@ async function run17(argv, io) {
85679
86157
  return finish2(rest, io);
85680
86158
  if (sub === "publish")
85681
86159
  return publish(rest, io);
85682
- return usage3(io, sub && !sub.startsWith("-") ? `unknown release subcommand "${sub}"` : "missing release subcommand", USAGE9);
86160
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown release subcommand "${sub}"` : "missing release subcommand", USAGE10);
85683
86161
  }
85684
- 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]", USAGE9 = "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) => {
86162
+ 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) => {
85685
86163
  const url = remote ? pushUrl(cwd, remote) : null;
85686
86164
  return !url || parseRemoteUrl(url)?.protocol === "file";
85687
86165
  }, stepsOf = (plan) => [
@@ -85713,9 +86191,9 @@ var init_release2 = __esm(() => {
85713
86191
  // packages/workit-cli/src/verbs/verify-delivery.ts
85714
86192
  var exports_verify_delivery = {};
85715
86193
  __export(exports_verify_delivery, {
85716
- run: () => run18
86194
+ run: () => run19
85717
86195
  });
85718
- async function run18(argv, io) {
86196
+ async function run19(argv, io) {
85719
86197
  const flags = parseFlags(argv, {
85720
86198
  expect: "value",
85721
86199
  branch: "value",
@@ -85725,31 +86203,31 @@ async function run18(argv, io) {
85725
86203
  package: "value"
85726
86204
  });
85727
86205
  if (typeof flags === "string")
85728
- return usage3(io, flags, USAGE10);
86206
+ return usage4(io, flags, USAGE11);
85729
86207
  if (flags.positionals.length > 1)
85730
- return usage3(io, `unexpected argument ${flags.positionals[1]}`, USAGE10);
86208
+ return usage4(io, `unexpected argument ${flags.positionals[1]}`, USAGE11);
85731
86209
  const named = flags.positionals[0];
85732
86210
  if (named !== undefined && !ENDPOINTS[named])
85733
- return usage3(io, `unknown endpoint "${named}"`, USAGE10);
86211
+ return usage4(io, `unknown endpoint "${named}"`, USAGE11);
85734
86212
  if (flags.values.expect !== undefined && !ENDPOINTS[flags.values.expect])
85735
- return usage3(io, "--expect must be pushed, pr, merged or released", USAGE10);
86213
+ return usage4(io, "--expect must be pushed, pr, merged or released", USAGE11);
85736
86214
  if (named && flags.values.expect && ENDPOINTS[named] !== ENDPOINTS[flags.values.expect])
85737
- return usage3(io, `${named} and --expect ${flags.values.expect} disagree`, USAGE10);
86215
+ return usage4(io, `${named} and --expect ${flags.values.expect} disagree`, USAGE11);
85738
86216
  const expect = ENDPOINTS[named ?? ""] ?? ENDPOINTS[flags.values.expect ?? ""] ?? (flags.values.tag || flags.values.package ? "released" : flags.values.pr ? "pr" : "pushed");
85739
86217
  const pr = positiveInt(flags.values.pr, "--pr");
85740
86218
  if (typeof pr === "string")
85741
- return usage3(io, pr, USAGE10);
86219
+ return usage4(io, pr, USAGE11);
85742
86220
  if (pr && flags.values.branch)
85743
- return usage3(io, "pass --pr or --branch, not both", USAGE10);
86221
+ return usage4(io, "pass --pr or --branch, not both", USAGE11);
85744
86222
  const sha = flags.values.sha ?? null;
85745
86223
  if (sha !== null && !/^[0-9a-f]{7,64}$/u.test(sha))
85746
- return usage3(io, "--sha must be a commit sha (7-64 hex chars)", USAGE10);
86224
+ return usage4(io, "--sha must be a commit sha (7-64 hex chars)", USAGE11);
85747
86225
  const actor = actorFromEnv(io.env);
85748
86226
  const branch = flags.values.branch ?? null;
85749
86227
  let result;
85750
86228
  if (expect === "pushed") {
85751
86229
  if (pr)
85752
- return usage3(io, "push is checked by --branch, not --pr", USAGE10);
86230
+ return usage4(io, "push is checked by --branch, not --pr", USAGE11);
85753
86231
  result = verifyPushed(io.cwd, { branch, sha, actor });
85754
86232
  } else if (expect === "released") {
85755
86233
  result = verifyReleased(io.cwd, { tag: flags.values.tag ?? null, pkg: flags.values.package ?? null, sha, actor }, forgeDeps.npm);
@@ -85774,7 +86252,7 @@ async function run18(argv, io) {
85774
86252
  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"
85775
86253
  }));
85776
86254
  }
85777
- var USAGE10 = "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) => [
86255
+ 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) => [
85778
86256
  `${report.expect}: ${report.delivered ? "delivered" : "NOT delivered"}${report.branch ? ` (${report.branch}${report.sha ? ` @ ${report.sha.slice(0, 12)}` : ""})` : ""}${report.pr ? ` #${report.pr}` : ""}`,
85779
86257
  ...report.observations.map((entry) => ` ${entry.ok ? "ok" : "x "} ${entry.kind}: expected ${entry.expected ?? "-"}, observed ${entry.observed ?? "(none)"}${entry.note ? ` — ${entry.note}` : ""}`)
85780
86258
  ];
@@ -85797,12 +86275,12 @@ var init_verify_delivery = __esm(() => {
85797
86275
  // packages/workit-cli/src/verbs/stack.ts
85798
86276
  var exports_stack = {};
85799
86277
  __export(exports_stack, {
85800
- run: () => run19
86278
+ run: () => run20
85801
86279
  });
85802
86280
  async function plan(argv, io) {
85803
86281
  const flags = parseFlags(argv, { name: "value", trunk: "value", track: "value" });
85804
86282
  if (typeof flags === "string")
85805
- return usage3(io, flags, PLAN_USAGE);
86283
+ return usage4(io, flags, PLAN_USAGE);
85806
86284
  const name = flags.values.name ?? null;
85807
86285
  const existingByName = name ? readStack(io.cwd, name) : null;
85808
86286
  if (existingByName && !existingByName.ok)
@@ -85815,7 +86293,7 @@ async function plan(argv, io) {
85815
86293
  }
85816
86294
  const track = flags.values.track ?? null;
85817
86295
  if (track !== null && flags.values.trunk !== undefined)
85818
- return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE);
86296
+ return usage4(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE);
85819
86297
  let trunk = flags.values.trunk ?? existing?.trunk ?? null;
85820
86298
  if (trunk === null || track !== null) {
85821
86299
  const derived = releaseTrunk(io, track, flags.positionals[0] ?? null);
@@ -85852,9 +86330,9 @@ async function plan(argv, io) {
85852
86330
  async function status3(argv, io) {
85853
86331
  const flags = parseFlags(argv, { name: "value" });
85854
86332
  if (typeof flags === "string")
85855
- return usage3(io, flags, STATUS_USAGE2);
86333
+ return usage4(io, flags, STATUS_USAGE2);
85856
86334
  if (flags.positionals.length)
85857
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, STATUS_USAGE2);
86335
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, STATUS_USAGE2);
85858
86336
  const selected = selectStack(io.cwd, flags.values.name ?? null);
85859
86337
  if (!selected.ok)
85860
86338
  return stackFailed(io, selected);
@@ -85878,16 +86356,16 @@ async function sync(argv, io) {
85878
86356
  force: "list"
85879
86357
  });
85880
86358
  if (typeof flags === "string")
85881
- return usage3(io, flags, SYNC_USAGE);
86359
+ return usage4(io, flags, SYNC_USAGE);
85882
86360
  if (flags.positionals.length)
85883
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, SYNC_USAGE);
86361
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, SYNC_USAGE);
85884
86362
  const selected = selectStack(io.cwd, flags.values.name ?? null);
85885
86363
  if (!selected.ok)
85886
86364
  return stackFailed(io, selected);
85887
86365
  const force = new Set(flags.lists.force ?? []);
85888
86366
  const unknown = [...force].filter((branch) => !selected.data.branches.some((entry) => entry.branch === branch));
85889
86367
  if (unknown.length)
85890
- return usage3(io, `--force ${unknown[0]}: not a branch of stack ${selected.data.name}`, SYNC_USAGE);
86368
+ return usage4(io, `--force ${unknown[0]}: not a branch of stack ${selected.data.name}`, SYNC_USAGE);
85891
86369
  const local = flags.booleans.has("local");
85892
86370
  let ctx = null;
85893
86371
  if (!local) {
@@ -85928,24 +86406,24 @@ async function land(argv, io) {
85928
86406
  reason: "value"
85929
86407
  });
85930
86408
  if (typeof flags === "string")
85931
- return usage3(io, flags, LAND_USAGE);
86409
+ return usage4(io, flags, LAND_USAGE);
85932
86410
  const unverified = unverifiedFlag(flags);
85933
86411
  if (typeof unverified === "string")
85934
- return usage3(io, unverified, LAND_USAGE);
86412
+ return usage4(io, unverified, LAND_USAGE);
85935
86413
  if (flags.positionals.length)
85936
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, LAND_USAGE);
86414
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, LAND_USAGE);
85937
86415
  const max = positiveInt(flags.values.max, "--max");
85938
86416
  if (typeof max === "string")
85939
- return usage3(io, max, LAND_USAGE);
86417
+ return usage4(io, max, LAND_USAGE);
85940
86418
  const method = flags.values.method ?? "squash";
85941
86419
  if (!METHODS3.has(method))
85942
- return usage3(io, "--method must be squash, merge or rebase", LAND_USAGE);
86420
+ return usage4(io, "--method must be squash, merge or rebase", LAND_USAGE);
85943
86421
  const timeoutMs = parseDuration2(flags.values.timeout ?? "20m");
85944
86422
  const intervalMs = parseDuration2(flags.values.interval ?? "30s");
85945
86423
  if (timeoutMs === null || timeoutMs > 6 * 3600000)
85946
- return usage3(io, "--timeout must be a duration up to 6h (20m, 90s)", LAND_USAGE);
86424
+ return usage4(io, "--timeout must be a duration up to 6h (20m, 90s)", LAND_USAGE);
85947
86425
  if (intervalMs === null || intervalMs < 1000 || intervalMs > 600000)
85948
- return usage3(io, "--interval must be between 1s and 10m", LAND_USAGE);
86426
+ return usage4(io, "--interval must be between 1s and 10m", LAND_USAGE);
85949
86427
  const selected = selectStack(io.cwd, flags.values.name ?? null);
85950
86428
  if (!selected.ok)
85951
86429
  return stackFailed(io, selected);
@@ -85979,7 +86457,7 @@ async function land(argv, io) {
85979
86457
  unblock: ${data.stoppedAt.unblock}` : data.complete ? "stack complete" : ""
85980
86458
  ].filter(Boolean));
85981
86459
  }
85982
- async function run19(argv, io) {
86460
+ async function run20(argv, io) {
85983
86461
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
85984
86462
  if (sub === "plan")
85985
86463
  return plan(rest, io);
@@ -85989,9 +86467,9 @@ async function run19(argv, io) {
85989
86467
  return sync(rest, io);
85990
86468
  if (sub === "land")
85991
86469
  return land(rest, io);
85992
- return usage3(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE11);
86470
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE12);
85993
86471
  }
85994
- 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>] [--timeout 20m] [--interval 30s] [--json]", USAGE11 = "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, { unblock: result.unblock, data: result.data ?? {} })), label3 = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
86472
+ 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>] [--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, { unblock: result.unblock, data: result.data ?? {} })), label3 = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
85995
86473
  `${data.dryRun ? "would sync" : "synced"} stack ${data.name}${data.merged.length ? `; merged: ${data.merged.map((item) => item.branch).join(", ")}` : ""}`,
85996
86474
  ...data.steps.map((step) => [
85997
86475
  ` ${step.branch} on ${step.parent}:`,
@@ -86425,7 +86903,8 @@ function createSliceWorktree(cwd, plan, input) {
86425
86903
  planCreatedAt: plan.createdAt,
86426
86904
  sliceHash: slice.hash,
86427
86905
  ...admin ? { adminId: admin.id, adminIno: admin.ino, adminToken: admin.token } : {},
86428
- ...baseSha ? { baseSha } : {}
86906
+ ...baseSha ? { baseSha } : {},
86907
+ ...deps.install ? { install: deps.install } : {}
86429
86908
  });
86430
86909
  return {
86431
86910
  ok: true,
@@ -86608,6 +87087,8 @@ var init_fanout_worktree = __esm(() => {
86608
87087
 
86609
87088
  // packages/workit-core/src/fanout-brief.ts
86610
87089
  import { createHash as createHash19 } from "node:crypto";
87090
+ import fs34 from "node:fs";
87091
+ import path63 from "node:path";
86611
87092
  function workerSession(lead, slice, attempt) {
86612
87093
  const tail = attempt === null ? "" : `+try${attempt}`;
86613
87094
  const full = `${lead}-w-${slice}${tail}`;
@@ -86631,10 +87112,12 @@ function renderBrief(cwd, plan, input) {
86631
87112
  if (taken)
86632
87113
  return fanoutFail("invalid_input", `attempt branch ${branch} is slice ${taken.id}'s branch`, "pick another --attempt number, or rename one of the branches in the plan");
86633
87114
  const head = branchRef(cwd, branch);
86634
- const mode = input.mode ?? (head ? "resume" : "new");
87115
+ const planned = `${slicePath(cwd, slice)}${input.attempt === null ? "" : `+try${input.attempt}`}`;
87116
+ const made = ledger.value.rows.findLast((row) => row.type === "fanout.worktree.created" && row.fanout === plan.name && row.slice === slice.id && typeof row.path === "string" && samePath(row.path, planned));
87117
+ const fresh = Boolean(head) && made?.baseSha === head;
87118
+ const mode = input.mode ?? (head && !fresh ? "resume" : "new");
86635
87119
  if (mode === "resume" && !head)
86636
87120
  return fanoutFail("not_found", `MODE resume needs the branch ${branch}, which does not exist here`, `workit fanout brief ${slice.id} --mode new`);
86637
- const planned = `${slicePath(cwd, slice)}${input.attempt === null ? "" : `+try${input.attempt}`}`;
86638
87121
  const exists = listWorktrees(cwd).some((entry) => samePath(entry.path, planned));
86639
87122
  excludeScratch(cwd);
86640
87123
  const scratch = exists ? `${planned}/${SCRATCH_DIR}` : SCRATCH_DIR;
@@ -86687,6 +87170,7 @@ function renderBrief(cwd, plan, input) {
86687
87170
  "STANDING:",
86688
87171
  ...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
86689
87172
  exists ? `WORKTREE: ${planned} (made by \`workit fanout worktree create ${slice.id}\`; yours alone)` : `WORKTREE: ${planned} (not made yet: the lead runs \`workit fanout worktree create ${slice.id}\` before spawning, then renders this brief again)`,
87173
+ ...exists && typeof made?.install === "string" && !fs34.existsSync(path63.join(planned, "node_modules")) ? [`INSTALL: \`${made.install}\` first: the main checkout's node_modules was not linked here`] : [],
86690
87174
  ...identityLines(identity, minted),
86691
87175
  ...rules(mode, branch, slice.base, exists ? planned : null)
86692
87176
  ];
@@ -86755,7 +87239,8 @@ var init_fanout_brief = __esm(() => {
86755
87239
  REPORT = [
86756
87240
  "REPORT: branch, head SHA, files changed, each VERIFY command with its exit code,",
86757
87241
  " each ACCEPTANCE line met / not met, rulings you made (`workit ledger ruling`),",
86758
- " anything out of scope as a follow-up, not a diff."
87242
+ " anything out of scope as a follow-up, not a diff. Report heads when pushed; CI is waited on once, at the end.",
87243
+ " A PR body goes through `--body-file -` (stdin), never a shared scratch path."
86759
87244
  ];
86760
87245
  });
86761
87246
 
@@ -87423,10 +87908,10 @@ var init_fanout_status = __esm(() => {
87423
87908
  // packages/workit-cli/src/verbs/fanout.ts
87424
87909
  var exports_fanout = {};
87425
87910
  __export(exports_fanout, {
87426
- run: () => run20
87911
+ run: () => run21
87427
87912
  });
87428
- import fs34 from "node:fs";
87429
- import path63 from "node:path";
87913
+ import fs35 from "node:fs";
87914
+ import path64 from "node:path";
87430
87915
  function forgeFor(io, offline) {
87431
87916
  if (offline)
87432
87917
  return { forge: null, error: "--offline" };
@@ -87469,7 +87954,7 @@ function resolveTrunk(io, raw, flag, track) {
87469
87954
  };
87470
87955
  if (named !== null && derived.trunk !== named)
87471
87956
  return {
87472
- code: usage3(io, `the plan names trunk ${named}; --track ${track} targets ${derived.trunk ?? "(none)"}`, PLAN_USAGE2)
87957
+ code: usage4(io, `the plan names trunk ${named}; --track ${track} targets ${derived.trunk ?? "(none)"}`, PLAN_USAGE2)
87473
87958
  };
87474
87959
  return { trunk: derived.tracked || track !== null ? derived.trunk : null };
87475
87960
  }
@@ -87481,18 +87966,18 @@ function plan2(argv, io) {
87481
87966
  "take-lead": "boolean"
87482
87967
  });
87483
87968
  if (typeof flags === "string")
87484
- return usage3(io, flags, PLAN_USAGE2);
87969
+ return usage4(io, flags, PLAN_USAGE2);
87485
87970
  const track = flags.values.track ?? null;
87486
87971
  if (track !== null && flags.values.trunk !== undefined)
87487
- return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
87972
+ return usage4(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
87488
87973
  if (flags.positionals.length !== 1)
87489
- return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
87490
- const file = path63.resolve(io.cwd, flags.positionals[0]);
87974
+ return usage4(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
87975
+ const file = path64.resolve(io.cwd, flags.positionals[0]);
87491
87976
  let raw;
87492
87977
  try {
87493
- raw = JSON.parse(fs34.readFileSync(file, "utf8"));
87978
+ raw = JSON.parse(fs35.readFileSync(file, "utf8"));
87494
87979
  } catch (error) {
87495
- return usage3(io, `cannot read ${file} as JSON: ${error.message}`, PLAN_USAGE2);
87980
+ return usage4(io, `cannot read ${file} as JSON: ${error.message}`, PLAN_USAGE2);
87496
87981
  }
87497
87982
  const trunk = resolveTrunk(io, raw, flags.values.trunk ?? null, track);
87498
87983
  if ("code" in trunk)
@@ -87513,7 +87998,7 @@ function plan2(argv, io) {
87513
87998
  `${data.created ? "planned" : "re-planned"} fanout ${data.name} on ${data.trunk}${data.fanIn === "integration" ? " (integration branch: one PR)" : ""}: ${data.slices.length} slice${data.slices.length === 1 ? "" : "s"}`,
87514
87999
  ...data.slices.map((slice) => ` ${slice.id} [${slice.tier}] ${slice.branch} <- ${slice.base}${slice.dependsOn.length ? ` after ${slice.dependsOn.join(", ")}` : ""}
87515
88000
  worktree ${slice.worktree}`),
87516
- ...data.overlaps.map((overlap) => ` shared ${overlap.path}: ${overlap.resolution === "owner" ? `owned by ${overlap.owner}` : `serialized (${overlap.slices.join(" -> ")})`}`),
88001
+ ...data.overlaps.map((overlap) => ` shared ${overlap.path}: ${overlap.resolution === "owner" ? `owned by ${overlap.owner}` : overlap.resolution === "regenerated" ? `regenerated at fan-in (${overlap.writer})` : `serialized (${overlap.slices.join(" -> ")})`}`),
87517
88002
  `waves: ${data.waves.map((wave) => wave.join(", ")).join(" | ")}`,
87518
88003
  `landing order: ${data.landingOrder.join(", ")}`,
87519
88004
  ...data.notes.map((note) => `note: ${note}`),
@@ -87523,16 +88008,16 @@ function plan2(argv, io) {
87523
88008
  function brief(argv, io) {
87524
88009
  const flags = parseFlags(argv, { name: "value", mode: "value", attempt: "value" });
87525
88010
  if (typeof flags === "string")
87526
- return usage3(io, flags, BRIEF_USAGE);
88011
+ return usage4(io, flags, BRIEF_USAGE);
87527
88012
  if (flags.positionals.length !== 1)
87528
- return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <slice>", BRIEF_USAGE);
88013
+ return usage4(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <slice>", BRIEF_USAGE);
87529
88014
  const mode = flags.values.mode ?? null;
87530
88015
  if (mode !== null && mode !== "new" && mode !== "resume")
87531
- return usage3(io, "--mode takes new or resume", BRIEF_USAGE);
88016
+ return usage4(io, "--mode takes new or resume", BRIEF_USAGE);
87532
88017
  const rawAttempt = flags.values.attempt;
87533
88018
  const attempt = rawAttempt === undefined ? null : Number(rawAttempt);
87534
88019
  if (attempt !== null && !(Number.isInteger(attempt) && attempt >= 1 && attempt <= 9))
87535
- return usage3(io, "--attempt takes a number from 1 to 9", BRIEF_USAGE);
88020
+ return usage4(io, "--attempt takes a number from 1 to 9", BRIEF_USAGE);
87536
88021
  const selected = selectFanout(io.cwd, flags.values.name ?? null, currentBranch(io.cwd));
87537
88022
  if (!selected.ok)
87538
88023
  return failed(io, selected);
@@ -87549,7 +88034,7 @@ function brief(argv, io) {
87549
88034
  function check2(argv, io) {
87550
88035
  const flags = parseFlags(argv, { name: "value", base: "value", offline: "boolean" });
87551
88036
  if (typeof flags === "string")
87552
- return usage3(io, flags, CHECK_USAGE);
88037
+ return usage4(io, flags, CHECK_USAGE);
87553
88038
  const selected = selectFanout(io.cwd, flags.values.name ?? null, currentBranch(io.cwd));
87554
88039
  if (!selected.ok)
87555
88040
  return failed(io, selected);
@@ -87575,13 +88060,13 @@ function check2(argv, io) {
87575
88060
  function status4(argv, io) {
87576
88061
  const flags = parseFlags(argv, { name: "value", "stuck-after": "value", offline: "boolean" });
87577
88062
  if (typeof flags === "string")
87578
- return usage3(io, flags, STATUS_USAGE3);
88063
+ return usage4(io, flags, STATUS_USAGE3);
87579
88064
  if (flags.positionals.length)
87580
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, STATUS_USAGE3);
88065
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, STATUS_USAGE3);
87581
88066
  const raw = flags.values["stuck-after"];
87582
88067
  const stuckAfterMs = raw === undefined ? null : parseDuration2(raw);
87583
88068
  if (stuckAfterMs === 0 || raw !== undefined && stuckAfterMs === null)
87584
- return usage3(io, "--stuck-after takes a duration such as 30m, 45m or 2h", STATUS_USAGE3);
88069
+ return usage4(io, "--stuck-after takes a duration such as 30m, 45m or 2h", STATUS_USAGE3);
87585
88070
  const selected = selectFanout(io.cwd, flags.values.name ?? null, currentBranch(io.cwd));
87586
88071
  if (!selected.ok)
87587
88072
  return failed(io, selected);
@@ -87599,13 +88084,13 @@ function worktree(argv, io) {
87599
88084
  const [action, ...rest] = argv;
87600
88085
  const flags = parseFlags(rest, { name: "value", force: "boolean" });
87601
88086
  if (typeof flags === "string")
87602
- return usage3(io, flags, WORKTREE_USAGE);
88087
+ return usage4(io, flags, WORKTREE_USAGE);
87603
88088
  if (action !== "create" && action !== "release")
87604
- return usage3(io, action ? `unknown worktree action "${action}"` : "missing create or release", WORKTREE_USAGE);
88089
+ return usage4(io, action ? `unknown worktree action "${action}"` : "missing create or release", WORKTREE_USAGE);
87605
88090
  if (flags.positionals.length !== 1)
87606
- return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <slice>", WORKTREE_USAGE);
88091
+ return usage4(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <slice>", WORKTREE_USAGE);
87607
88092
  if (action === "create" && flags.booleans.has("force"))
87608
- return usage3(io, "--force applies to release only", WORKTREE_USAGE);
88093
+ return usage4(io, "--force applies to release only", WORKTREE_USAGE);
87609
88094
  const selected = selectFanout(io.cwd, flags.values.name ?? null, currentBranch(io.cwd));
87610
88095
  if (!selected.ok)
87611
88096
  return failed(io, selected);
@@ -87643,7 +88128,7 @@ function worktree(argv, io) {
87643
88128
  ...data.notes.map((note) => `note: ${note}`)
87644
88129
  ]);
87645
88130
  }
87646
- async function run20(argv, io) {
88131
+ async function run21(argv, io) {
87647
88132
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
87648
88133
  if (sub === "plan")
87649
88134
  return plan2(rest, io);
@@ -87655,9 +88140,9 @@ async function run20(argv, io) {
87655
88140
  return status4(rest, io);
87656
88141
  if (sub === "worktree")
87657
88142
  return worktree(rest, io);
87658
- return usage3(io, sub && !sub.startsWith("-") ? `unknown fanout subcommand "${sub}"` : "missing fanout subcommand", USAGE12);
88143
+ return usage4(io, sub && !sub.startsWith("-") ? `unknown fanout subcommand "${sub}"` : "missing fanout subcommand", USAGE13);
87659
88144
  }
87660
- 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]", USAGE12 = "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) => [
88145
+ 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) => [
87661
88146
  `fanout ${data.name} against ${data.trunkRef}: ${data.ready ? "ready to land" : "blocked"}`,
87662
88147
  ...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 ? `
87663
88148
  ${slice.reasons.join(`
@@ -87709,7 +88194,7 @@ var init_fanout2 = __esm(() => {
87709
88194
  // packages/workit-cli/src/verbs/ledger.ts
87710
88195
  var exports_ledger = {};
87711
88196
  __export(exports_ledger, {
87712
- run: () => run21
88197
+ run: () => run22
87713
88198
  });
87714
88199
  import { randomBytes as randomBytes6 } from "node:crypto";
87715
88200
  import { parseArgs } from "node:util";
@@ -87733,30 +88218,30 @@ function targetBranch(io, rows, branch, pr) {
87733
88218
  }
87734
88219
  return { ok: true, value: branch ?? null };
87735
88220
  }
87736
- async function run21(argv, io) {
88221
+ async function run22(argv, io) {
87737
88222
  let parsed;
87738
88223
  try {
87739
88224
  parsed = parseArgs({ args: argv, options: OPTIONS, allowPositionals: true, strict: true });
87740
88225
  } catch (error) {
87741
- return usage4(io, error.message);
88226
+ return usage5(io, error.message);
87742
88227
  }
87743
88228
  const { values } = parsed;
87744
88229
  let positionals = parsed.positionals;
87745
88230
  if (positionals[0] === "add") {
87746
88231
  positionals = positionals.slice(1);
87747
88232
  if (!["decision", "ruling", "verdict"].includes(positionals[0] ?? ""))
87748
- return usage4(io, "ledger add takes decision|ruling|verdict");
88233
+ return usage5(io, "ledger add takes decision|ruling|verdict");
87749
88234
  }
87750
88235
  const [sub, ...rest] = positionals;
87751
88236
  const pr = positiveInt2(values.pr, "--pr");
87752
88237
  if (pr instanceof Error)
87753
- return usage4(io, pr.message);
88238
+ return usage5(io, pr.message);
87754
88239
  const last = positiveInt2(values.last, "--last");
87755
88240
  if (last instanceof Error)
87756
- return usage4(io, last.message);
88241
+ return usage5(io, last.message);
87757
88242
  const text = rest.join(" ");
87758
88243
  if (sub === undefined)
87759
- return usage4(io, "missing subcommand");
88244
+ return usage5(io, "missing subcommand");
87760
88245
  if (![
87761
88246
  "decision",
87762
88247
  "ruling",
@@ -87767,7 +88252,7 @@ async function run21(argv, io) {
87767
88252
  "check",
87768
88253
  "verify-integrity"
87769
88254
  ].includes(sub))
87770
- return usage4(io, `unknown ledger subcommand "${sub}"`);
88255
+ return usage5(io, `unknown ledger subcommand "${sub}"`);
87771
88256
  if (sub === "standing")
87772
88257
  return standing(io, values, rest);
87773
88258
  const ledger = readLedger(io.cwd, {
@@ -87778,7 +88263,7 @@ async function run21(argv, io) {
87778
88263
  const { rows } = ledger.value;
87779
88264
  if (sub === "verify-integrity") {
87780
88265
  if (rest.length)
87781
- return usage4(io, `unexpected argument: ${rest[0]}`);
88266
+ return usage5(io, `unexpected argument: ${rest[0]}`);
87782
88267
  const integrity = ledger.value.integrity ?? { chained: 0, legacy: 0, unverified: [] };
87783
88268
  return emit(io, ok2({
87784
88269
  path: ledger.value.path,
@@ -87790,7 +88275,7 @@ async function run21(argv, io) {
87790
88275
  }
87791
88276
  if (sub === "list" || sub === "show") {
87792
88277
  if (rest.length)
87793
- return usage4(io, `unexpected argument: ${rest[0]}`);
88278
+ return usage5(io, `unexpected argument: ${rest[0]}`);
87794
88279
  const listed = filterRows(rows, {
87795
88280
  branch: values.branch,
87796
88281
  pr,
@@ -87805,7 +88290,7 @@ async function run21(argv, io) {
87805
88290
  const isRead = sub === "check" || sub === "verdict" && values.how === undefined && !VERDICT_RESULTS.includes(rest[0] ?? "");
87806
88291
  if (isRead) {
87807
88292
  if (rest.length > (sub === "verdict" ? 1 : 0))
87808
- return usage4(io, `unexpected argument: ${rest.at(-1)}`);
88293
+ return usage5(io, `unexpected argument: ${rest.at(-1)}`);
87809
88294
  const target = targetBranch(io, rows, rest[0] ?? values.branch, pr);
87810
88295
  if (!target.ok)
87811
88296
  return failed2(io, target);
@@ -87829,7 +88314,7 @@ async function run21(argv, io) {
87829
88314
  }
87830
88315
  const acting = actorFor(io, values);
87831
88316
  if (acting instanceof Error)
87832
- return usage4(io, acting.message);
88317
+ return usage5(io, acting.message);
87833
88318
  const { actor } = acting;
87834
88319
  const context = {
87835
88320
  cwd: io.cwd,
@@ -87861,7 +88346,7 @@ async function run21(argv, io) {
87861
88346
  refs: values.ref
87862
88347
  })), (row) => `recorded ruling ${row.id}`);
87863
88348
  if (rest.length !== 1)
87864
- return usage4(io, `ledger verdict takes one result: ${VERDICT_RESULTS.join("|")}`);
88349
+ return usage5(io, `ledger verdict takes one result: ${VERDICT_RESULTS.join("|")}`);
87865
88350
  const claimed = values.self === true ? null : claimedSession(acting);
87866
88351
  const token = values.token ?? io.env.WORKIT_SESSION_TOKEN ?? null;
87867
88352
  const refusal = claimed === null ? null : sessionClaimRefusal(io.env, claimed, io.cwd, { token, flag: acting.source === "flag" });
@@ -87907,9 +88392,9 @@ async function standing(io, values, args) {
87907
88392
  if (action !== "add" && action !== "list" && action !== "clear")
87908
88393
  return emit(io, fail2("invalid_input", action ? `unknown standing action "${action}"` : "missing add, list or clear", { unblock: STANDING_USAGE }));
87909
88394
  if ((values.stop || values.clear) && action !== "add")
87910
- return usage4(io, `--stop and --clear go with standing add, not standing ${action}`);
88395
+ return usage5(io, `--stop and --clear go with standing add, not standing ${action}`);
87911
88396
  if (values.stop && values.clear)
87912
- return usage4(io, "pass --stop or --clear, not both");
88397
+ return usage5(io, "pass --stop or --clear, not both");
87913
88398
  const target = standingFanout(io, values);
87914
88399
  if (target instanceof Error)
87915
88400
  return emit(io, fail2("invalid_input", target.message, { unblock: STANDING_USAGE }));
@@ -87918,7 +88403,7 @@ async function standing(io, values, args) {
87918
88403
  const leads = target.plan ? planLeads(target.plan) : null;
87919
88404
  if (action === "list") {
87920
88405
  if (rest.length)
87921
- return usage4(io, `unexpected argument: ${rest[0]}`);
88406
+ return usage5(io, `unexpected argument: ${rest[0]}`);
87922
88407
  const ledger = readLedger(io.cwd);
87923
88408
  if (!ledger.ok)
87924
88409
  return failed2(io, ledger);
@@ -87934,7 +88419,7 @@ async function standing(io, values, args) {
87934
88419
  }
87935
88420
  const acting = actorFor(io, values);
87936
88421
  if (acting instanceof Error)
87937
- return usage4(io, acting.message);
88422
+ return usage5(io, acting.message);
87938
88423
  const context = {
87939
88424
  cwd: io.cwd,
87940
88425
  actor: acting.actor,
@@ -87946,16 +88431,16 @@ async function standing(io, values, args) {
87946
88431
  }));
87947
88432
  if (action === "add" && values.clear) {
87948
88433
  if (rest.length)
87949
- return usage4(io, `unexpected argument: ${rest[0]}`);
88434
+ return usage5(io, `unexpected argument: ${rest[0]}`);
87950
88435
  return fromResult(io, liftStop(context, { fanout, lead, leads }), (rows) => `lifted the stop line for fanout ${fanout} (cleared ${rows.map((row) => row.target).join(", ")}); running workers are told on their next tool call`);
87951
88436
  }
87952
88437
  if (action === "add")
87953
88438
  return fromResult(io, recordStanding(context, { fanout, lead, what: rest.join(" "), stop: values.stop === true }), (row) => row.stop ? `recorded stop line ${row.id} for fanout ${fanout}; running workers stop and report on their next tool call` : `recorded standing order ${row.id} for fanout ${fanout}; running workers get it on their next tool call`);
87954
88439
  if (rest.length > 1)
87955
- return usage4(io, `unexpected argument: ${rest[1]}`);
88440
+ return usage5(io, `unexpected argument: ${rest[1]}`);
87956
88441
  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}`);
87957
88442
  }
87958
- var USAGE13 = "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) => {
88443
+ 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) => {
87959
88444
  const actor = actorFromEnv(io.env);
87960
88445
  if (values.session !== undefined && values.as !== undefined)
87961
88446
  return new Error("pass --session or --as, not both");
@@ -87982,7 +88467,7 @@ var USAGE13 = "workit ledger decision|ruling|verdict|standing|list|check ... (wo
87982
88467
  return;
87983
88468
  const parsed = Number(value);
87984
88469
  return Number.isInteger(parsed) && parsed > 0 ? parsed : new Error(`${flag} must be a positive integer`);
87985
- }, 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), usage4 = (io, error) => emit(io, fail2("invalid_input", error, { unblock: USAGE13 })), passedTestOn = (cwd, verdict) => {
88470
+ }, 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) => {
87986
88471
  const ledger = readLedger(cwd);
87987
88472
  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);
87988
88473
  }, verdictLines = (check) => {
@@ -103028,7 +103513,7 @@ var init_ast = __esm(() => {
103028
103513
 
103029
103514
  // packages/workit-cli/src/test-audit/rules.ts
103030
103515
  import { builtinModules } from "node:module";
103031
- import path64 from "node:path";
103516
+ import path65 from "node:path";
103032
103517
  function testCallee(call) {
103033
103518
  let callee = call.callee;
103034
103519
  if (callee.type === "CallExpression")
@@ -103521,12 +104006,12 @@ function checkConstantsOnly(ctx, emit) {
103521
104006
  }
103522
104007
  function checkOverMocking(ctx, emit) {
103523
104008
  const { program } = ctx.parsed;
103524
- const unit = path64.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
104009
+ const unit = path65.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
103525
104010
  const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
103526
104011
  const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
103527
104012
  for (const call of mocked) {
103528
104013
  const spec = call.arguments[0].value;
103529
- const base = path64.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
104014
+ const base = path65.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
103530
104015
  if (base === unit)
103531
104016
  emit({
103532
104017
  rule: "over-mocking",
@@ -103915,8 +104400,8 @@ var init_rules = __esm(() => {
103915
104400
 
103916
104401
  // packages/workit-cli/src/test-audit/audit.ts
103917
104402
  import { spawnSync as spawnSync29 } from "node:child_process";
103918
- import { existsSync as existsSync23, readdirSync as readdirSync11, readFileSync as readFileSync26, statSync as statSync10 } from "node:fs";
103919
- import path65 from "node:path";
104403
+ import { existsSync as existsSync24, readdirSync as readdirSync11, readFileSync as readFileSync26, statSync as statSync10 } from "node:fs";
104404
+ import path66 from "node:path";
103920
104405
  function git7(cwd, args) {
103921
104406
  const run = spawnSync29("git", ["-c", "core.quotepath=off", ...args], {
103922
104407
  cwd,
@@ -103929,7 +104414,7 @@ function walkTests(dir, out) {
103929
104414
  for (const entry of readdirSync11(dir, { withFileTypes: true })) {
103930
104415
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
103931
104416
  continue;
103932
- const full = path65.join(dir, entry.name);
104417
+ const full = path66.join(dir, entry.name);
103933
104418
  if (entry.isDirectory()) {
103934
104419
  if (!SKIP_DIRS.has(entry.name))
103935
104420
  walkTests(full, out);
@@ -103941,8 +104426,8 @@ function expandPaths(cwd, inputs) {
103941
104426
  const files = [];
103942
104427
  const missing = [];
103943
104428
  for (const input of inputs.length > 0 ? inputs : ["."]) {
103944
- const full = path65.resolve(cwd, input);
103945
- if (!existsSync23(full))
104429
+ const full = path66.resolve(cwd, input);
104430
+ if (!existsSync24(full))
103946
104431
  missing.push(input);
103947
104432
  else if (statSync10(full).isDirectory())
103948
104433
  walkTests(full, files);
@@ -103978,11 +104463,11 @@ function changedLines(cwd, base) {
103978
104463
  for (let offset = 0;offset < count; offset += 1)
103979
104464
  lines.add(start + offset);
103980
104465
  }
103981
- changed.set(path65.join(root, file), lines);
104466
+ changed.set(path66.join(root, file), lines);
103982
104467
  }
103983
104468
  const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
103984
104469
  for (const file of untracked.stdout.split("\x00").filter(Boolean))
103985
- changed.set(path65.join(root, file), "all");
104470
+ changed.set(path66.join(root, file), "all");
103986
104471
  return changed;
103987
104472
  }
103988
104473
  function auditFiles(files, display) {
@@ -104044,7 +104529,7 @@ function auditFiles(files, display) {
104044
104529
  durationMs: Math.round(performance.now() - started)
104045
104530
  };
104046
104531
  }
104047
- var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path65.sep).join("/"), SEVERITY_ORDER, ruleIds;
104532
+ var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path66.sep).join("/"), SEVERITY_ORDER, ruleIds;
104048
104533
  var init_audit = __esm(() => {
104049
104534
  init_ast();
104050
104535
  init_rules();
@@ -104058,7 +104543,7 @@ var init_audit = __esm(() => {
104058
104543
  // packages/workit-cli/src/test-audit/mutate.ts
104059
104544
  import { spawn as spawn2 } from "node:child_process";
104060
104545
  import {
104061
- existsSync as existsSync24,
104546
+ existsSync as existsSync25,
104062
104547
  lstatSync as lstatSync8,
104063
104548
  mkdirSync as mkdirSync11,
104064
104549
  mkdtempSync as mkdtempSync4,
@@ -104071,10 +104556,10 @@ import {
104071
104556
  statSync as statSync11,
104072
104557
  symlinkSync,
104073
104558
  writeFileSync as writeFileSync11,
104074
- copyFileSync as copyFileSync7
104559
+ copyFileSync as copyFileSync8
104075
104560
  } from "node:fs";
104076
104561
  import os22 from "node:os";
104077
- import path66 from "node:path";
104562
+ import path67 from "node:path";
104078
104563
  function mutantsFor(source, file, lines) {
104079
104564
  const parsed = parseSource(source, file);
104080
104565
  const out = [];
@@ -104147,7 +104632,7 @@ function relatedTests(source, testFiles) {
104147
104632
  for (const match of text.matchAll(SPECIFIER)) {
104148
104633
  const spec = match[1];
104149
104634
  if (spec.startsWith(".")) {
104150
- if (stripExt(path66.posix.join(path66.posix.dirname(file), spec)) === target)
104635
+ if (stripExt(path67.posix.join(path67.posix.dirname(file), spec)) === target)
104151
104636
  return true;
104152
104637
  } else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
104153
104638
  return true;
@@ -104167,12 +104652,12 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
104167
104652
  for (const name of names) {
104168
104653
  if (!name.startsWith(TEMP_PREFIX))
104169
104654
  continue;
104170
- const dir = path66.join(tmp, name);
104655
+ const dir = path67.join(tmp, name);
104171
104656
  try {
104172
104657
  const stat = lstatSync8(dir);
104173
104658
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
104174
104659
  continue;
104175
- const pid = Number(readFileSync27(path66.join(dir, PID_FILE), "utf8"));
104660
+ const pid = Number(readFileSync27(path67.join(dir, PID_FILE), "utf8"));
104176
104661
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
104177
104662
  continue;
104178
104663
  } catch (error) {
@@ -104189,8 +104674,8 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
104189
104674
  function linkNodeModules(origin, target, repo, copy) {
104190
104675
  mkdirSync11(target, { recursive: true });
104191
104676
  for (const entry of readdirSync12(origin, { withFileTypes: true })) {
104192
- const from = path66.join(origin, entry.name);
104193
- const to = path66.join(target, entry.name);
104677
+ const from = path67.join(origin, entry.name);
104678
+ const to = path67.join(target, entry.name);
104194
104679
  if (entry.name.startsWith("@") && entry.isDirectory()) {
104195
104680
  linkNodeModules(from, to, repo, copy);
104196
104681
  continue;
@@ -104206,9 +104691,9 @@ function linkNodeModules(origin, target, repo, copy) {
104206
104691
  } catch {
104207
104692
  continue;
104208
104693
  }
104209
- const relative = path66.relative(repo, real);
104210
- if (!relative.startsWith("..") && !path66.isAbsolute(relative) && !relative.split(path66.sep).includes("node_modules")) {
104211
- symlinkSync(path66.join(copy, relative), to, "junction");
104694
+ const relative = path67.relative(repo, real);
104695
+ if (!relative.startsWith("..") && !path67.isAbsolute(relative) && !relative.split(path67.sep).includes("node_modules")) {
104696
+ symlinkSync(path67.join(copy, relative), to, "junction");
104212
104697
  continue;
104213
104698
  }
104214
104699
  }
@@ -104224,19 +104709,19 @@ function copySymlink(repo, tree, from, to) {
104224
104709
  }
104225
104710
  const realRepo = realpathSync10(repo);
104226
104711
  if (real && inside4(realRepo, real)) {
104227
- symlinkSync(path66.relative(path66.dirname(to), path66.join(tree, path66.relative(realRepo, real))), to);
104712
+ symlinkSync(path67.relative(path67.dirname(to), path67.join(tree, path67.relative(realRepo, real))), to);
104228
104713
  return;
104229
104714
  }
104230
104715
  if (real && statSync11(real).isFile())
104231
- copyFileSync7(real, to);
104716
+ copyFileSync8(real, to);
104232
104717
  else
104233
104718
  symlinkSync(readlinkSync3(from), to);
104234
104719
  }
104235
104720
  function writeInside(copy, file, content) {
104236
- const target = path66.join(copy, file);
104721
+ const target = path67.join(copy, file);
104237
104722
  try {
104238
104723
  const stat = lstatSync8(target);
104239
- if (!stat.isFile() || !inside4(realpathSync10(copy), realpathSync10(path66.dirname(target))))
104724
+ if (!stat.isFile() || !inside4(realpathSync10(copy), realpathSync10(path67.dirname(target))))
104240
104725
  return false;
104241
104726
  } catch {
104242
104727
  return false;
@@ -104247,9 +104732,9 @@ function writeInside(copy, file, content) {
104247
104732
  return true;
104248
104733
  }
104249
104734
  function copyWorkingTree(repo) {
104250
- const dir = mkdtempSync4(path66.join(os22.tmpdir(), TEMP_PREFIX));
104251
- writeFileSync11(path66.join(dir, PID_FILE), String(process.pid));
104252
- const tree = path66.join(dir, "tree");
104735
+ const dir = mkdtempSync4(path67.join(os22.tmpdir(), TEMP_PREFIX));
104736
+ writeFileSync11(path67.join(dir, PID_FILE), String(process.pid));
104737
+ const tree = path67.join(dir, "tree");
104253
104738
  const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
104254
104739
  if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
104255
104740
  const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
@@ -104266,29 +104751,29 @@ function copyWorkingTree(repo) {
104266
104751
  for (const relative of listed) {
104267
104752
  if (!relative)
104268
104753
  continue;
104269
- const from = path66.join(repo, relative);
104754
+ const from = path67.join(repo, relative);
104270
104755
  let stat;
104271
104756
  try {
104272
104757
  stat = lstatSync8(from);
104273
104758
  } catch {
104274
104759
  continue;
104275
104760
  }
104276
- const to = path66.join(tree, relative);
104277
- mkdirSync11(path66.dirname(to), { recursive: true });
104761
+ const to = path67.join(tree, relative);
104762
+ mkdirSync11(path67.dirname(to), { recursive: true });
104278
104763
  if (stat.isSymbolicLink())
104279
104764
  copySymlink(repo, tree, from, to);
104280
104765
  else if (stat.isFile())
104281
- copyFileSync7(from, to);
104282
- let parent = path66.dirname(relative);
104766
+ copyFileSync8(from, to);
104767
+ let parent = path67.dirname(relative);
104283
104768
  while (parent !== "." && !dirs.has(parent)) {
104284
104769
  dirs.add(parent);
104285
- parent = path66.dirname(parent);
104770
+ parent = path67.dirname(parent);
104286
104771
  }
104287
104772
  }
104288
104773
  for (const parent of dirs) {
104289
- const modules = path66.join(repo, parent, "node_modules");
104290
- if (existsSync24(modules) && !existsSync24(path66.join(tree, parent, "node_modules")))
104291
- linkNodeModules(modules, path66.join(tree, parent, "node_modules"), repo, tree);
104774
+ const modules = path67.join(repo, parent, "node_modules");
104775
+ if (existsSync25(modules) && !existsSync25(path67.join(tree, parent, "node_modules")))
104776
+ linkNodeModules(modules, path67.join(tree, parent, "node_modules"), repo, tree);
104292
104777
  }
104293
104778
  return { dir, tree };
104294
104779
  }
@@ -104327,17 +104812,17 @@ async function runMutation(options) {
104327
104812
  const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
104328
104813
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
104329
104814
  try {
104330
- return { file, text: readFileSync27(path66.join(repo, file), "utf8") };
104815
+ return { file, text: readFileSync27(path67.join(repo, file), "utf8") };
104331
104816
  } catch {
104332
104817
  return { file, text: "" };
104333
104818
  }
104334
104819
  });
104335
104820
  const candidates = [];
104336
104821
  for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
104337
- const relative = toPosix(path66.relative(repo, absolute));
104822
+ const relative = toPosix(path67.relative(repo, absolute));
104338
104823
  if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
104339
104824
  continue;
104340
- if (NOT_SOURCE.test(relative) || !existsSync24(absolute))
104825
+ if (NOT_SOURCE.test(relative) || !existsSync25(absolute))
104341
104826
  continue;
104342
104827
  try {
104343
104828
  candidates.push(...mutantsFor(readFileSync27(absolute, "utf8"), relative, lines));
@@ -104348,9 +104833,9 @@ async function runMutation(options) {
104348
104833
  for (const mutant of mutants)
104349
104834
  if (!related.has(mutant.file))
104350
104835
  related.set(mutant.file, relatedTests(mutant.file, testFiles));
104351
- const cwdRelative = path66.relative(repo, options.cwd);
104836
+ const cwdRelative = path67.relative(repo, options.cwd);
104352
104837
  const asArg = (file) => {
104353
- const relative = toPosix(path66.relative(cwdRelative, file));
104838
+ const relative = toPosix(path67.relative(cwdRelative, file));
104354
104839
  return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
104355
104840
  };
104356
104841
  const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
@@ -104386,7 +104871,7 @@ async function runMutation(options) {
104386
104871
  return finish3(options, 0, results, counts, started);
104387
104872
  removeStaleCopies();
104388
104873
  const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
104389
- const copyCwd = path66.join(copy, cwdRelative);
104874
+ const copyCwd = path67.join(copy, cwdRelative);
104390
104875
  let current = null;
104391
104876
  const restore = () => {
104392
104877
  if (current)
@@ -104421,7 +104906,7 @@ async function runMutation(options) {
104421
104906
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
104422
104907
  continue;
104423
104908
  }
104424
- const original = readFileSync27(path66.join(copy, mutant.file), "utf8");
104909
+ const original = readFileSync27(path67.join(copy, mutant.file), "utf8");
104425
104910
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
104426
104911
  current = { file: mutant.file, original };
104427
104912
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -104478,8 +104963,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
104478
104963
  return error.code === "EPERM";
104479
104964
  }
104480
104965
  }, inside4 = (root, file) => {
104481
- const relative = path66.relative(root, file);
104482
- return relative === "" || !relative.startsWith("..") && !path66.isAbsolute(relative);
104966
+ const relative = path67.relative(root, file);
104967
+ return relative === "" || !relative.startsWith("..") && !path67.isAbsolute(relative);
104483
104968
  }, active, killTree = (child) => {
104484
104969
  try {
104485
104970
  if (child.pid && process.platform !== "win32")
@@ -104525,10 +105010,10 @@ var init_mutate = __esm(() => {
104525
105010
  // packages/workit-cli/src/verbs/test-audit.ts
104526
105011
  var exports_test_audit = {};
104527
105012
  __export(exports_test_audit, {
104528
- run: () => run22
105013
+ run: () => run23
104529
105014
  });
104530
- import { existsSync as existsSync25 } from "node:fs";
104531
- import path67 from "node:path";
105015
+ import { existsSync as existsSync26 } from "node:fs";
105016
+ import path68 from "node:path";
104532
105017
  function parse8(argv) {
104533
105018
  const options = {
104534
105019
  paths: [],
@@ -104627,14 +105112,14 @@ function human(data) {
104627
105112
  }
104628
105113
  return lines;
104629
105114
  }
104630
- async function run22(argv, io) {
105115
+ async function run23(argv, io) {
104631
105116
  const options = parse8(argv);
104632
105117
  if (typeof options === "string")
104633
105118
  return emit(io, fail2("invalid_input", `${options}
104634
- usage: ${USAGE14}`));
105119
+ usage: ${USAGE15}`));
104635
105120
  const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
104636
- const repo = top.ok ? path67.resolve(top.stdout.trim()) : null;
104637
- const display = (file) => toPosix(path67.relative(io.cwd, file) || file);
105121
+ const repo = top.ok ? path68.resolve(top.stdout.trim()) : null;
105122
+ const display = (file) => toPosix(path68.relative(io.cwd, file) || file);
104638
105123
  let base = null;
104639
105124
  let changed = null;
104640
105125
  if (options.diff || options.mutate) {
@@ -104651,7 +105136,7 @@ usage: ${USAGE14}`));
104651
105136
  }
104652
105137
  let files;
104653
105138
  if (options.diff && changed)
104654
- files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync25(file)).toSorted();
105139
+ files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync26(file)).toSorted();
104655
105140
  else {
104656
105141
  const expanded = expandPaths(io.cwd, options.paths);
104657
105142
  if (expanded.missing.length > 0)
@@ -104717,7 +105202,7 @@ usage: ${USAGE14}`));
104717
105202
  }
104718
105203
  return emit(io, ok2(data), human);
104719
105204
  }
104720
- var USAGE14 = "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, "'\\''")}'`;
105205
+ 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, "'\\''")}'`;
104721
105206
  var init_test_audit = __esm(() => {
104722
105207
  init_audit();
104723
105208
  init_mutate();
@@ -104738,12 +105223,12 @@ var init_test_audit = __esm(() => {
104738
105223
  // packages/workit-cli/src/verbs/knowledge.ts
104739
105224
  var exports_knowledge = {};
104740
105225
  __export(exports_knowledge, {
104741
- run: () => run23
105226
+ run: () => run24
104742
105227
  });
104743
- async function run23(argv, io) {
105228
+ async function run24(argv, io) {
104744
105229
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
104745
105230
  if (sub !== "lint" || rest.length > 0)
104746
- return emit(io, fail2("invalid_input", `usage: ${USAGE15}`));
105231
+ return emit(io, fail2("invalid_input", `usage: ${USAGE16}`));
104747
105232
  const report = lintKnowledge(checkRoot(io.cwd));
104748
105233
  if (report.unreadable.length > 0)
104749
105234
  return emit(io, fail2("unavailable", `knowledge lint: cannot read ${report.unreadable.join(", ")}`, {
@@ -104760,7 +105245,7 @@ async function run23(argv, io) {
104760
105245
  `);
104761
105246
  return report.findings.length === 0 ? 0 : 1;
104762
105247
  }
104763
- var USAGE15 = "workit knowledge lint [--json]";
105248
+ var USAGE16 = "workit knowledge lint [--json]";
104764
105249
  var init_knowledge2 = __esm(() => {
104765
105250
  init_check_config();
104766
105251
  init_knowledge();
@@ -104770,14 +105255,14 @@ var init_knowledge2 = __esm(() => {
104770
105255
  // packages/workit-cli/src/verbs/youtrack.ts
104771
105256
  var exports_youtrack = {};
104772
105257
  __export(exports_youtrack, {
104773
- run: () => run24
105258
+ run: () => run25
104774
105259
  });
104775
105260
  import { readFileSync as readFileSync28 } from "node:fs";
104776
- import path68 from "node:path";
104777
- async function run24(argv, io) {
105261
+ import path69 from "node:path";
105262
+ async function run25(argv, io) {
104778
105263
  const [sub, ...rest] = argv;
104779
105264
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
104780
- return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE16);
105265
+ return usage4(io, `unknown subcommand "${sub ?? ""}"`, USAGE17);
104781
105266
  const flags = parseFlags(rest, {
104782
105267
  markdown: "value",
104783
105268
  file: "value",
@@ -104786,18 +105271,18 @@ async function run24(argv, io) {
104786
105271
  date: "value"
104787
105272
  });
104788
105273
  if (typeof flags === "string")
104789
- return usage3(io, flags, USAGE16);
105274
+ return usage4(io, flags, USAGE17);
104790
105275
  const [issueId, extra] = flags.positionals;
104791
105276
  if (!issueId || !ISSUE_RE2.test(issueId))
104792
- return usage3(io, "an issue id like ABC-123 is required", USAGE16);
105277
+ return usage4(io, "an issue id like ABC-123 is required", USAGE17);
104793
105278
  if (extra !== undefined)
104794
- return usage3(io, `unexpected argument ${extra}`, USAGE16);
105279
+ return usage4(io, `unexpected argument ${extra}`, USAGE17);
104795
105280
  const minutes = positiveInt(flags.values.minutes, "--minutes");
104796
105281
  if (typeof minutes === "string")
104797
- return usage3(io, minutes, USAGE16);
105282
+ return usage4(io, minutes, USAGE17);
104798
105283
  const date = youTrackWorkDateMs(flags.values.date ?? "auto");
104799
105284
  if ("error" in date)
104800
- return usage3(io, date.error, USAGE16);
105285
+ return usage4(io, date.error, USAGE17);
104801
105286
  const dateMs = date.data.dateMs;
104802
105287
  const markedDay = flags.values.date !== undefined && flags.values.date !== "auto" ? { dateMs } : {};
104803
105288
  const root = io.cwd;
@@ -104815,24 +105300,24 @@ async function run24(argv, io) {
104815
105300
  };
104816
105301
  if (sub === "note") {
104817
105302
  if (Boolean(flags.values.markdown) === Boolean(flags.values.file))
104818
- return usage3(io, "pass exactly one of --markdown or --file", USAGE16);
105303
+ return usage4(io, "pass exactly one of --markdown or --file", USAGE17);
104819
105304
  let markdown = flags.values.markdown ?? "";
104820
105305
  if (flags.values.file)
104821
105306
  try {
104822
- markdown = readFileSync28(path68.resolve(io.cwd, flags.values.file), "utf8");
105307
+ markdown = readFileSync28(path69.resolve(io.cwd, flags.values.file), "utf8");
104823
105308
  } catch (error) {
104824
- return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE16);
105309
+ return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE17);
104825
105310
  }
104826
105311
  if (!markdown.trim())
104827
- return usage3(io, "the note is empty", USAGE16);
105312
+ return usage4(io, "the note is empty", USAGE17);
104828
105313
  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);
104829
105314
  if (missing)
104830
105315
  return emit(io, fail2("unavailable", missing));
104831
105316
  } else {
104832
105317
  if (!minutes)
104833
- return usage3(io, `${sub} needs --minutes <n>`, USAGE16);
105318
+ return usage4(io, `${sub} needs --minutes <n>`, USAGE17);
104834
105319
  if (sub === "meeting" && !flags.values.text)
104835
- return usage3(io, "meeting needs --text <what the meeting was>", USAGE16);
105320
+ return usage4(io, "meeting needs --text <what the meeting was>", USAGE17);
104836
105321
  const text = flags.values.text ?? "";
104837
105322
  const missing = step(sub, "workItems", { operation: sub, issueId, minutes, text, ...markedDay }, text, (marked) => logTimeUpdate({ issueId, minutes, text: marked, dateMs, workspace_root: root }));
104838
105323
  if (missing)
@@ -104861,7 +105346,7 @@ async function run24(argv, io) {
104861
105346
  ...data.steps.flatMap((entry) => entry.note ? [` note (${entry.step}): ${entry.note}`] : [])
104862
105347
  ]);
104863
105348
  }
104864
- var USAGE16 = "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) => {
105349
+ 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) => {
104865
105350
  try {
104866
105351
  return { ok: true, data: unwrap(value) };
104867
105352
  } catch (error) {
@@ -104885,14 +105370,14 @@ var init_youtrack2 = __esm(() => {
104885
105370
  // packages/workit-cli/src/verbs/changelog.ts
104886
105371
  var exports_changelog = {};
104887
105372
  __export(exports_changelog, {
104888
- run: () => run25
105373
+ run: () => run26
104889
105374
  });
104890
105375
  import { readFileSync as readFileSync29 } from "node:fs";
104891
- import path69 from "node:path";
104892
- async function run25(argv, io) {
105376
+ import path70 from "node:path";
105377
+ async function run26(argv, io) {
104893
105378
  const [sub, ...rest] = argv;
104894
105379
  if (sub !== "apply")
104895
- return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE17);
105380
+ return usage4(io, `unknown subcommand "${sub ?? ""}"`, USAGE18);
104896
105381
  const flags = parseFlags(rest, {
104897
105382
  entries: "value",
104898
105383
  path: "value",
@@ -104900,18 +105385,18 @@ async function run25(argv, io) {
104900
105385
  preview: "boolean"
104901
105386
  });
104902
105387
  if (typeof flags === "string")
104903
- return usage3(io, flags, USAGE17);
105388
+ return usage4(io, flags, USAGE18);
104904
105389
  if (flags.positionals.length)
104905
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, USAGE17);
105390
+ return usage4(io, `unexpected argument ${flags.positionals[0]}`, USAGE18);
104906
105391
  const normalizeOnly = flags.booleans.has("normalize-only");
104907
105392
  if (!normalizeOnly && !flags.values.entries)
104908
- return usage3(io, "pass --entries <JSON> or --normalize-only", USAGE17);
105393
+ return usage4(io, "pass --entries <JSON> or --normalize-only", USAGE18);
104909
105394
  let entries;
104910
105395
  if (flags.values.entries)
104911
105396
  try {
104912
105397
  entries = readEntries(flags.values.entries, io.cwd);
104913
105398
  } catch (error) {
104914
- return usage3(io, `--entries is not valid JSON: ${String(error)}`, USAGE17);
105399
+ return usage4(io, `--entries is not valid JSON: ${String(error)}`, USAGE18);
104915
105400
  }
104916
105401
  const input = {
104917
105402
  entries,
@@ -104933,8 +105418,8 @@ async function run25(argv, io) {
104933
105418
  `changelog updated: ${String(applied.path ?? "CHANGELOG.md")}`
104934
105419
  ]);
104935
105420
  }
104936
- var USAGE17 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
104937
- const text = raw === "-" ? readFileSync29(0, "utf8") : raw.startsWith("@") ? readFileSync29(path69.resolve(cwd, raw.slice(1)), "utf8") : raw;
105421
+ var USAGE18 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
105422
+ const text = raw === "-" ? readFileSync29(0, "utf8") : raw.startsWith("@") ? readFileSync29(path70.resolve(cwd, raw.slice(1)), "utf8") : raw;
104938
105423
  return JSON.parse(text);
104939
105424
  };
104940
105425
  var init_changelog2 = __esm(() => {
@@ -104947,10 +105432,10 @@ var init_changelog2 = __esm(() => {
104947
105432
  var exports_handoff = {};
104948
105433
  __export(exports_handoff, {
104949
105434
  MAX_BRIEF_BYTES: () => MAX_BRIEF_BYTES,
104950
- run: () => run26
105435
+ run: () => run27
104951
105436
  });
104952
- import fs35 from "node:fs";
104953
- import path70 from "node:path";
105437
+ import fs36 from "node:fs";
105438
+ import path71 from "node:path";
104954
105439
  import { parseArgs as parseArgs2 } from "node:util";
104955
105440
  function render3(brief) {
104956
105441
  const lines = [
@@ -104990,10 +105475,10 @@ function bounded(brief, cwd) {
104990
105475
  let file = null;
104991
105476
  if (store.ok)
104992
105477
  try {
104993
- const dir = path70.join(store.value.root, "handoff");
104994
- fs35.mkdirSync(dir, { recursive: true });
104995
- file = path70.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
104996
- fs35.writeFileSync(file, `${[...body, ...tail].join(`
105478
+ const dir = path71.join(store.value.root, "handoff");
105479
+ fs36.mkdirSync(dir, { recursive: true });
105480
+ file = path71.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
105481
+ fs36.writeFileSync(file, `${[...body, ...tail].join(`
104997
105482
  `)}
104998
105483
  `, { mode: 384 });
104999
105484
  } catch {
@@ -105025,11 +105510,11 @@ async function resumeBrief(argv, io) {
105025
105510
  strict: true
105026
105511
  }));
105027
105512
  } catch (error) {
105028
- return emit(io, fail2("invalid_input", error.message, { unblock: USAGE18 }));
105513
+ return emit(io, fail2("invalid_input", error.message, { unblock: USAGE19 }));
105029
105514
  }
105030
105515
  const last = values.last === undefined ? 10 : Number(values.last);
105031
105516
  if (!Number.isInteger(last) || last < 1)
105032
- return emit(io, fail2("invalid_input", "--last must be a positive integer", { unblock: USAGE18 }));
105517
+ return emit(io, fail2("invalid_input", "--last must be a positive integer", { unblock: USAGE19 }));
105033
105518
  const built = buildHandoff(io.cwd, {
105034
105519
  last,
105035
105520
  note: values.note ?? null,
@@ -105047,13 +105532,13 @@ async function resumeBrief(argv, io) {
105047
105532
  }
105048
105533
  return emit(io, ok2({ ...data, recorded }), (brief) => bounded(brief, io.cwd));
105049
105534
  }
105050
- async function run26(argv, io) {
105535
+ async function run27(argv, io) {
105051
105536
  if (!argv.some((arg) => arg === "--task" || arg.startsWith("--task=")))
105052
105537
  return resumeBrief(argv, io);
105053
105538
  await Promise.resolve().then(() => init_task());
105054
105539
  return runTaskCommand(["handoff", ...argv]);
105055
105540
  }
105056
- var USAGE18 = "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(`
105541
+ 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(`
105057
105542
  `)}
105058
105543
  `);
105059
105544
  var init_handoff = __esm(() => {
@@ -105064,7 +105549,7 @@ var init_handoff = __esm(() => {
105064
105549
  // packages/workit-cli/src/main.ts
105065
105550
  init_host_session();
105066
105551
  init_package();
105067
- import path71 from "node:path";
105552
+ import path72 from "node:path";
105068
105553
 
105069
105554
  // packages/workit-cli/src/verbs/registry.ts
105070
105555
  var TASK_FAMILY_NAMES = [
@@ -105249,6 +105734,38 @@ var VERBS = [
105249
105734
  examples: ["workit grant show", "workit grant set my-repo push=true pr=true"],
105250
105735
  load: () => Promise.resolve().then(() => (init_grant(), exports_grant))
105251
105736
  }, { optional: true }),
105737
+ withSubcommands({
105738
+ name: "workspace",
105739
+ group: "setup",
105740
+ summary: "Edit workspace entries without the wizard; never raises a grant",
105741
+ subcommands: [
105742
+ {
105743
+ name: "list",
105744
+ usage: "workit workspace list",
105745
+ summary: "Show every entry and the one that applies to --cwd, and why"
105746
+ },
105747
+ {
105748
+ name: "add",
105749
+ usage: "workit workspace add <name> --glob <glob> [--from <parent>]",
105750
+ summary: "Add an entry; --from copies vcs, issues, branchPolicy, commitPolicy, trailers, youtrack and models, never grants"
105751
+ },
105752
+ {
105753
+ name: "set",
105754
+ usage: "workit workspace set <name> <key>=<value>…",
105755
+ summary: "Change non-grant keys (dotted paths, JSON values, null removes); grants stay with workit grant set"
105756
+ }
105757
+ ],
105758
+ examples: [
105759
+ "workit workspace list",
105760
+ "workit workspace add kit --glob '/home/me/work/libs/kit/**' --from work",
105761
+ "workit workspace set kit branchPolicy.integration=merge"
105762
+ ],
105763
+ notes: [
105764
+ "A new entry starts with no grants of its own. It starts at the strictest level of every entry and the defaults, so its folders never get more than they had. Remove an entry in your own editor. Raising a grant, defaultEndpoint or verification needs the user: workit grant set <name> <kind>=<value> in their own terminal.",
105765
+ "Every write keeps the previous file as workspaces.json.bak. `workit doctor` lists overlapping globs and which entry wins."
105766
+ ],
105767
+ load: () => Promise.resolve().then(() => (init_workspace(), exports_workspace))
105768
+ }, { optional: true }),
105252
105769
  ...TASK_FAMILY_NAMES.map(family2),
105253
105770
  {
105254
105771
  name: "check",
@@ -105360,7 +105877,7 @@ var VERBS = [
105360
105877
  },
105361
105878
  {
105362
105879
  name: "merge",
105363
- usage: "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --reason <why>]",
105880
+ usage: "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --reason <why>]",
105364
105881
  summary: "Merge when READY, verified by a non-author and granted; --subject/--body set the squash message"
105365
105882
  }
105366
105883
  ],
@@ -105794,7 +106311,9 @@ function closest(word, candidates) {
105794
106311
  }
105795
106312
  var COMMAND_HINTS = {
105796
106313
  status: "workit task status (this branch's task) or workit pr status (its PR); workit handoff prints the whole picture",
105797
- context: "workit handoff prints the branch, HEAD, dirty state, verdict and next command"
106314
+ context: "workit handoff prints the branch, HEAD, dirty state, verdict and next command",
106315
+ config: "workit grant show prints the workspace settings; workit doctor checks the install",
106316
+ issue: "workit youtrack <action> handles tracker issues; gh api repos/<owner>/<repo>/issues/<n> reads a GitHub one"
105798
106317
  };
105799
106318
  function commandUnblock(command) {
105800
106319
  const near = closest(command, VERBS.filter((entry) => !entry.planned).map((entry) => entry.name));
@@ -105936,7 +106455,7 @@ var defaultIo = (json) => ({
105936
106455
  });
105937
106456
  async function main(argv, overrides = {}, options = {}) {
105938
106457
  const parsed = parseGlobals(argv);
105939
- const cwd = path71.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
106458
+ const cwd = path72.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
105940
106459
  const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
105941
106460
  if (parsed.error)
105942
106461
  return emit(io, fail2("invalid_input", parsed.error));