@brainervirus/workit-claude-code 7.7.0 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workit.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@brainervirus/workit-cli",
68
- version: "7.7.0",
68
+ version: "8.0.1",
69
69
  private: false,
70
70
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
71
71
  keywords: [
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
103
103
  },
104
104
  dependencies: {
105
105
  "@babel/parser": "7.29.9",
106
- "@brainervirus/workit-core": "^7.7.0",
107
- "@brainervirus/workit-cursor": "^7.7.0",
108
- "@brainervirus/workit-opencode": "^7.7.0",
106
+ "@brainervirus/workit-core": "^8.0.1",
107
+ "@brainervirus/workit-cursor": "^8.0.1",
108
+ "@brainervirus/workit-opencode": "^8.0.1",
109
109
  "@inkjs/ui": "2.0.0",
110
110
  ink: "7.1.1",
111
111
  react: "19.2.8"
@@ -13267,7 +13267,7 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }) {
13267
13267
  sessions.add(session);
13268
13268
  return sessions;
13269
13269
  }
13270
- function effectiveOf(rows, authors) {
13270
+ function effectiveOf(rows, authors, sameCode) {
13271
13271
  let effective = null;
13272
13272
  for (const row of rows) {
13273
13273
  if (!effective) {
@@ -13278,7 +13278,7 @@ function effectiveOf(rows, authors) {
13278
13278
  const effIndependent = independenceReasons(effective, authors).length === 0;
13279
13279
  if (effIndependent && !rowIndependent)
13280
13280
  continue;
13281
- if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session)
13281
+ if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
13282
13282
  continue;
13283
13283
  effective = row;
13284
13284
  }
@@ -13309,16 +13309,22 @@ function checkVerdicts(cwd, branch, rows) {
13309
13309
  const head = branchHead(cwd, branch);
13310
13310
  const fallbackBase = defaultBase(cwd);
13311
13311
  const keyCache = new Map;
13312
- const keyFor = (base) => {
13313
- if (!base || !head)
13312
+ const keysAt = (base, at) => {
13313
+ if (!base || !at)
13314
13314
  return { patchId: null, diffHash: null };
13315
- let key = keyCache.get(base);
13315
+ const cacheKey = `${base}\x00${at}`;
13316
+ let key = keyCache.get(cacheKey);
13316
13317
  if (!key) {
13317
- key = { patchId: patchId(cwd, base, head), diffHash: diffHash(cwd, base, head) };
13318
- keyCache.set(base, key);
13318
+ key = { patchId: patchId(cwd, base, at), diffHash: diffHash(cwd, base, at) };
13319
+ keyCache.set(cacheKey, key);
13319
13320
  }
13320
13321
  return key;
13321
13322
  };
13323
+ const keyFor = (base) => keysAt(base, head);
13324
+ const sameCode = (failed, row) => verdictBasis(failed, {
13325
+ head: row.head,
13326
+ ...keysAt(failed.base ?? fallbackBase, row.head)
13327
+ }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
13322
13328
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head });
13323
13329
  const byKind = new Map;
13324
13330
  for (const row of rows)
@@ -13328,7 +13334,7 @@ function checkVerdicts(cwd, branch, rows) {
13328
13334
  }
13329
13335
  const verdicts = [];
13330
13336
  for (const [kind, list] of byKind) {
13331
- const row = effectiveOf(list, authors);
13337
+ const row = effectiveOf(list, authors, sameCode);
13332
13338
  if (!row)
13333
13339
  continue;
13334
13340
  let basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
@@ -13354,11 +13360,11 @@ function checkVerdicts(cwd, branch, rows) {
13354
13360
  verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
13355
13361
  const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
13356
13362
  const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
13357
- const newestAccepted = verdicts.findLast((entry) => entry.accepted) ?? null;
13363
+ const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
13358
13364
  const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
13359
13365
  accepted: false,
13360
13366
  verdict: failing?.verdict ?? null,
13361
- reasons: failing ? ["failing_verdict"] : newestCurrent?.reasons ?? ["no_verdict"]
13367
+ reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
13362
13368
  };
13363
13369
  const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
13364
13370
  return {
@@ -13587,6 +13593,9 @@ function summarizeRow(row) {
13587
13593
  case "pr.merged":
13588
13594
  summary = `#${row.pr ?? "?"} ${text("method")} at ${(row.head ?? "?").slice(0, 12)}${text("mergeSha") ? ` -> ${text("mergeSha").slice(0, 12)}` : ""}`;
13589
13595
  break;
13596
+ case "merge.unverified":
13597
+ summary = `#${row.pr ?? "?"} unverified merge requested at ${(row.head ?? "?").slice(0, 12)} (reason: ${text("reason")})`;
13598
+ break;
13590
13599
  case "delivery.verified":
13591
13600
  summary = `${text("expect")} delivered at ${(row.head ?? "?").slice(0, 12)}`;
13592
13601
  break;
@@ -13911,7 +13920,7 @@ var init_ledger = __esm(() => {
13911
13920
  return false;
13912
13921
  }
13913
13922
  };
13914
- OBSERVED_ONLY = new Set(["check", ...PR_ROW_TYPES]);
13923
+ OBSERVED_ONLY = new Set(["check", "merge.unverified", ...PR_ROW_TYPES]);
13915
13924
  });
13916
13925
 
13917
13926
  // packages/workit-core/src/core/policy/derive.ts
@@ -16656,7 +16665,7 @@ function requireGrant(cwd, kind, options = {}) {
16656
16665
  allowed: true,
16657
16666
  kind,
16658
16667
  source: configured ? autonomy.source : "default",
16659
- requireVerdict: kind === "merge" && value !== true,
16668
+ allowUnverified: kind === "merge" && value === true,
16660
16669
  workspace
16661
16670
  };
16662
16671
  }
@@ -19123,20 +19132,30 @@ async function parseTaskArgs(argv, deps) {
19123
19132
  const judge = [];
19124
19133
  const seen = new Set;
19125
19134
  for (let i = 2;i < argv.length; i += 1) {
19126
- const token = argv[i];
19135
+ if (argv[i] === "--") {
19136
+ const extra = argv.slice(i + 1);
19137
+ if (extra.length)
19138
+ return parseUsage(`${family} ${actionName} takes no positional arguments (got: ${extra.join(" ")})`, json);
19139
+ break;
19140
+ }
19141
+ const eq = argv[i].startsWith("--") ? argv[i].indexOf("=") : -1;
19142
+ const token = eq > 0 ? argv[i].slice(0, eq) : argv[i];
19143
+ const inline = eq > 0 ? argv[i].slice(eq + 1) : undefined;
19127
19144
  if (family === "policy" && (token === "--judge" || token === "--ref")) {
19128
19145
  if (token === "--ref") {
19129
- const value = argv[++i];
19146
+ const value = inline ?? argv[++i];
19130
19147
  if (value === undefined || !value.trim())
19131
19148
  return parseUsage("--ref requires a value", json);
19132
19149
  judge.push(`ref=${value}`);
19133
19150
  continue;
19134
19151
  }
19152
+ if (inline !== undefined)
19153
+ judge.push(inline);
19135
19154
  while (argv[i + 1] !== undefined && !argv[i + 1].startsWith("--"))
19136
19155
  judge.push(argv[++i]);
19137
19156
  continue;
19138
19157
  }
19139
- if (token === "--json" || token === "--confirm") {
19158
+ if ((token === "--json" || token === "--confirm") && inline === undefined) {
19140
19159
  if (seen.has(token))
19141
19160
  return parseUsage(`duplicate argument: ${token}`, json);
19142
19161
  seen.add(token);
@@ -19145,8 +19164,8 @@ async function parseTaskArgs(argv, deps) {
19145
19164
  continue;
19146
19165
  }
19147
19166
  if (!["--payload", "--task", "--revision", "--workspace-revision", "--view", "--actor"].includes(token))
19148
- return parseUsage(`unknown argument: ${token}`, json);
19149
- const value = argv[++i];
19167
+ return parseUsage(`unknown argument: ${argv[i]}`, json);
19168
+ const value = inline ?? argv[++i];
19150
19169
  if (value === undefined || value.trim() === "" && token !== "--workspace-revision")
19151
19170
  return parseUsage(`${token} requires a value`, json);
19152
19171
  if (seen.has(token))
@@ -19205,6 +19224,12 @@ async function parseHandoffArgs(argv, deps) {
19205
19224
  const seen = new Set;
19206
19225
  for (let i = 0;i < argv.length; i += 1) {
19207
19226
  const token = argv[i];
19227
+ if (token === "--") {
19228
+ const extra = argv.slice(i + 1);
19229
+ if (extra.length)
19230
+ return parseUsage(`handoff takes no positional arguments (got: ${extra.join(" ")})`, json);
19231
+ break;
19232
+ }
19208
19233
  if (token === "--json") {
19209
19234
  if (seen.has(token))
19210
19235
  return parseUsage(`duplicate argument: ${token}`, json);
@@ -19212,14 +19237,18 @@ async function parseHandoffArgs(argv, deps) {
19212
19237
  json = true;
19213
19238
  continue;
19214
19239
  }
19215
- if (token !== "--task")
19240
+ const inline = token.startsWith("--task=") ? token.slice("--task=".length) : undefined;
19241
+ if (token !== "--task" && inline === undefined)
19216
19242
  return parseUsage(`unknown argument: ${token}`, json);
19217
- if (taskId !== undefined || argv[i + 1] === undefined || argv[i + 1].trim() === "")
19243
+ const value = inline ?? argv[i + 1];
19244
+ if (taskId !== undefined || value === undefined || value.trim() === "")
19218
19245
  return parseUsage("--task requires one non-empty value", json);
19219
- if (seen.has(token))
19220
- return parseUsage(`duplicate argument: ${token}`, json);
19221
- seen.add(token);
19222
- taskId = argv[++i];
19246
+ if (seen.has("--task"))
19247
+ return parseUsage("duplicate argument: --task", json);
19248
+ seen.add("--task");
19249
+ taskId = value;
19250
+ if (inline === undefined)
19251
+ i += 1;
19223
19252
  }
19224
19253
  if (!taskId)
19225
19254
  return parseUsage("handoff requires --task <id>", json);
@@ -19406,13 +19435,14 @@ var init_task = __esm(() => {
19406
19435
 
19407
19436
  // packages/workit-cli/src/verbs/task.ts
19408
19437
  function isImplicitTaskForm(argv) {
19409
- const [action, first] = argv;
19438
+ const [action, ...rest] = argv;
19410
19439
  if (!action)
19411
19440
  return false;
19412
19441
  if (IMPLICIT2.has(action))
19413
19442
  return true;
19443
+ const first = rest.find((arg) => arg !== "--json");
19414
19444
  if (action === "start")
19415
- return first !== undefined && !first.startsWith("--");
19445
+ return first !== undefined && (first === "--" || !first.startsWith("--"));
19416
19446
  if (action === "close")
19417
19447
  return !argv.includes("--task") && !argv.includes("--payload");
19418
19448
  return false;
@@ -19421,6 +19451,10 @@ function parse4(argv) {
19421
19451
  const flags = { positionals: [], values: new Map, switches: new Set };
19422
19452
  for (let index = 0;index < argv.length; index += 1) {
19423
19453
  const arg = argv[index];
19454
+ if (arg === "--") {
19455
+ flags.positionals.push(...argv.slice(index + 1));
19456
+ break;
19457
+ }
19424
19458
  const [flag, inline] = arg.startsWith("--") && arg.includes("=") ? arg.split(/=(.*)/su) : [arg];
19425
19459
  if (SWITCHES.has(flag)) {
19426
19460
  flags.switches.add(flag);
@@ -34280,7 +34314,7 @@ function getSgrPrefix(code) {
34280
34314
  if (code.startsWith("›")) {
34281
34315
  return "›";
34282
34316
  }
34283
- return `${ESCAPE}${ANSI_CSI2}`;
34317
+ return `${ESCAPE2}${ANSI_CSI2}`;
34284
34318
  }
34285
34319
  function createSgrCode(prefix, values) {
34286
34320
  return `${prefix}${values.join(";")}${ANSI_SGR_TERMINATOR2}`;
@@ -34289,7 +34323,7 @@ function getSgrFragments(code) {
34289
34323
  const fragments = [];
34290
34324
  const sgrPrefix = getSgrPrefix(code);
34291
34325
  let parameterString;
34292
- if (code.startsWith(`${ESCAPE}${ANSI_CSI2}`)) {
34326
+ if (code.startsWith(`${ESCAPE2}${ANSI_CSI2}`)) {
34293
34327
  parameterString = code.slice(2, -1);
34294
34328
  } else if (code.startsWith("›")) {
34295
34329
  parameterString = code.slice(1, -1);
@@ -34450,7 +34484,7 @@ function parseHyperlinkCode(string, index) {
34450
34484
  endIndex: sequenceIndex + 1
34451
34485
  };
34452
34486
  }
34453
- if (character === ESCAPE && string[sequenceIndex + 1] === ANSI_OSC_TERMINATOR) {
34487
+ if (character === ESCAPE2 && string[sequenceIndex + 1] === ANSI_OSC_TERMINATOR) {
34454
34488
  const code = string.slice(index, sequenceIndex + 2);
34455
34489
  const action = sequenceIndex === uriStart + 1 ? "close" : "open";
34456
34490
  return {
@@ -34533,7 +34567,7 @@ function parseControlStringCode(string, index) {
34533
34567
  if (supportsBellTerminator && string[sequenceIndex] === ANSI_BELL) {
34534
34568
  return createControlParseResult(string.slice(index, sequenceIndex + 1), sequenceIndex + 1);
34535
34569
  }
34536
- if (string[sequenceIndex] === ESCAPE && string[sequenceIndex + 1] === ANSI_OSC_TERMINATOR) {
34570
+ if (string[sequenceIndex] === ESCAPE2 && string[sequenceIndex + 1] === ANSI_OSC_TERMINATOR) {
34537
34571
  return createControlParseResult(string.slice(index, sequenceIndex + 2), sequenceIndex + 2);
34538
34572
  }
34539
34573
  if (string[sequenceIndex] === C1_STRING_TERMINATOR) {
@@ -34758,7 +34792,7 @@ function tokenizeAnsi(string, { endCharacter = Number.POSITIVE_INFINITY } = {})
34758
34792
  }
34759
34793
  return tokens;
34760
34794
  }
34761
- var ESCAPE_CODE_POINT = 27, C1_DCS_CODE_POINT = 144, C1_SOS_CODE_POINT = 152, C1_CSI_CODE_POINT = 155, C1_ST_CODE_POINT = 156, C1_OSC_CODE_POINT = 157, C1_PM_CODE_POINT = 158, C1_APC_CODE_POINT = 159, ESCAPES2, ESCAPE = "\x1B", ANSI_BELL = "\x07", ANSI_CSI2 = "[", ANSI_OSC2 = "]", ANSI_DCS = "P", ANSI_SOS = "X", ANSI_PM = "^", ANSI_APC = "_", ANSI_SGR_TERMINATOR2 = "m", ANSI_OSC_TERMINATOR = "\\", ANSI_STRING_TERMINATOR, C1_OSC = "", C1_STRING_TERMINATOR = "œ", ANSI_HYPERLINK_ESC_PREFIX, ANSI_HYPERLINK_C1_PREFIX, ANSI_HYPERLINK_ESC_CLOSE, ANSI_HYPERLINK_C1_CLOSE, CODE_POINT_0, CODE_POINT_9, CODE_POINT_SEMICOLON, CODE_POINT_COLON, CODE_POINT_CSI_PARAMETER_START, CODE_POINT_CSI_PARAMETER_END, CODE_POINT_CSI_INTERMEDIATE_START, CODE_POINT_CSI_INTERMEDIATE_END, CODE_POINT_CSI_FINAL_START, CODE_POINT_CSI_FINAL_END, REGIONAL_INDICATOR_SYMBOL_LETTER_A = 127462, REGIONAL_INDICATOR_SYMBOL_LETTER_Z = 127487, SGR_RESET_CODE = 0, SGR_EXTENDED_FOREGROUND_CODE = 38, SGR_DEFAULT_FOREGROUND_CODE = 39, SGR_EXTENDED_BACKGROUND_CODE = 48, SGR_DEFAULT_BACKGROUND_CODE = 49, SGR_COLOR_TYPE_ANSI_256 = 5, SGR_COLOR_TYPE_TRUECOLOR = 2, SGR_ANSI_256_FRAGMENT_LENGTH = 3, SGR_TRUECOLOR_FRAGMENT_LENGTH = 5, SGR_ANSI_256_LAST_PARAMETER_OFFSET = 2, SGR_TRUECOLOR_LAST_PARAMETER_OFFSET = 4, VARIATION_SELECTOR_16_CODE_POINT = 65039, COMBINING_ENCLOSING_KEYCAP_CODE_POINT = 8419, EMOJI_PRESENTATION_GRAPHEME_REGEX, GRAPHEME_SEGMENTER, endCodeNumbers;
34795
+ var ESCAPE_CODE_POINT = 27, C1_DCS_CODE_POINT = 144, C1_SOS_CODE_POINT = 152, C1_CSI_CODE_POINT = 155, C1_ST_CODE_POINT = 156, C1_OSC_CODE_POINT = 157, C1_PM_CODE_POINT = 158, C1_APC_CODE_POINT = 159, ESCAPES2, ESCAPE2 = "\x1B", ANSI_BELL = "\x07", ANSI_CSI2 = "[", ANSI_OSC2 = "]", ANSI_DCS = "P", ANSI_SOS = "X", ANSI_PM = "^", ANSI_APC = "_", ANSI_SGR_TERMINATOR2 = "m", ANSI_OSC_TERMINATOR = "\\", ANSI_STRING_TERMINATOR, C1_OSC = "", C1_STRING_TERMINATOR = "œ", ANSI_HYPERLINK_ESC_PREFIX, ANSI_HYPERLINK_C1_PREFIX, ANSI_HYPERLINK_ESC_CLOSE, ANSI_HYPERLINK_C1_CLOSE, CODE_POINT_0, CODE_POINT_9, CODE_POINT_SEMICOLON, CODE_POINT_COLON, CODE_POINT_CSI_PARAMETER_START, CODE_POINT_CSI_PARAMETER_END, CODE_POINT_CSI_INTERMEDIATE_START, CODE_POINT_CSI_INTERMEDIATE_END, CODE_POINT_CSI_FINAL_START, CODE_POINT_CSI_FINAL_END, REGIONAL_INDICATOR_SYMBOL_LETTER_A = 127462, REGIONAL_INDICATOR_SYMBOL_LETTER_Z = 127487, SGR_RESET_CODE = 0, SGR_EXTENDED_FOREGROUND_CODE = 38, SGR_DEFAULT_FOREGROUND_CODE = 39, SGR_EXTENDED_BACKGROUND_CODE = 48, SGR_DEFAULT_BACKGROUND_CODE = 49, SGR_COLOR_TYPE_ANSI_256 = 5, SGR_COLOR_TYPE_TRUECOLOR = 2, SGR_ANSI_256_FRAGMENT_LENGTH = 3, SGR_TRUECOLOR_FRAGMENT_LENGTH = 5, SGR_ANSI_256_LAST_PARAMETER_OFFSET = 2, SGR_TRUECOLOR_LAST_PARAMETER_OFFSET = 4, VARIATION_SELECTOR_16_CODE_POINT = 65039, COMBINING_ENCLOSING_KEYCAP_CODE_POINT = 8419, EMOJI_PRESENTATION_GRAPHEME_REGEX, GRAPHEME_SEGMENTER, endCodeNumbers;
34762
34796
  var init_tokenize_ansi = __esm(() => {
34763
34797
  init_ansi_styles();
34764
34798
  init_is_fullwidth_code_point();
@@ -34772,8 +34806,8 @@ var init_tokenize_ansi = __esm(() => {
34772
34806
  C1_PM_CODE_POINT,
34773
34807
  C1_APC_CODE_POINT
34774
34808
  ]);
34775
- ANSI_STRING_TERMINATOR = `${ESCAPE}${ANSI_OSC_TERMINATOR}`;
34776
- ANSI_HYPERLINK_ESC_PREFIX = `${ESCAPE}${ANSI_OSC2}8;`;
34809
+ ANSI_STRING_TERMINATOR = `${ESCAPE2}${ANSI_OSC_TERMINATOR}`;
34810
+ ANSI_HYPERLINK_ESC_PREFIX = `${ESCAPE2}${ANSI_OSC2}8;`;
34777
34811
  ANSI_HYPERLINK_C1_PREFIX = `${C1_OSC}8;`;
34778
34812
  ANSI_HYPERLINK_ESC_CLOSE = `${ANSI_HYPERLINK_ESC_PREFIX};`;
34779
34813
  ANSI_HYPERLINK_C1_CLOSE = `${ANSI_HYPERLINK_C1_PREFIX};`;
@@ -71015,18 +71049,14 @@ async function change(verb, argv, io, deps) {
71015
71049
  ]);
71016
71050
  }
71017
71051
  async function runGrant(argv, io, deps = defaultDeps) {
71018
- const [sub, ...rest] = argv;
71052
+ const end = argv.indexOf("--");
71053
+ const [sub, ...rest] = end < 0 ? argv : [...argv.slice(0, end), ...argv.slice(end + 1)];
71019
71054
  if (sub === "show")
71020
71055
  return show(rest, io);
71021
- if (sub === undefined || sub.startsWith("--") && sub !== "--help")
71056
+ if (sub === undefined || sub.startsWith("--"))
71022
71057
  return show(argv, io);
71023
71058
  if (sub === "set" || sub === "unset")
71024
71059
  return change(sub, rest, io, deps);
71025
- if (sub === "--help" || sub === "-h") {
71026
- io.stdout(`usage: ${USAGE2}
71027
- `);
71028
- return 0;
71029
- }
71030
71060
  return usage2(io, `unknown subcommand "${sub}"`);
71031
71061
  }
71032
71062
  var USAGE2 = "workit grant show [<workspace>] [--all] | grant set <workspace> <kind>=<true|false|verified>… [defaultEndpoint=commit|pr|green|merged] [verification=self|independent] | grant unset <workspace> <kind>… (kinds: push, pr, merge, release, rerun, defaultEndpoint, verification)", AGENT_ENV, agentMarker = (env) => AGENT_ENV.find((name) => env[name]) ?? Object.keys(env).find((name) => name.startsWith("CODEX_") && env[name]), defaultDeps, parseValue = (raw) => raw === "true" || raw === "yes" || raw === "on" ? true : raw === "false" || raw === "no" || raw === "off" ? false : raw === "verified" ? "verified" : null, STOPS_AT, endpointLabel = (view) => view.effectiveEndpoint === view.defaultEndpoint ? view.defaultEndpoint : `${view.defaultEndpoint} (effective: ${view.effectiveEndpoint}${view.endpointReason ? `, ${view.endpointReason}` : ""})`, describe2 = (grants, configured, endpoint, verification) => [
@@ -72339,13 +72369,19 @@ async function createPullRequest(cwd, resolved, input, sleep) {
72339
72369
  head: sha,
72340
72370
  created,
72341
72371
  draft: input.draft,
72342
- recorded: row.ok ? { id: String(row.value.id) } : { error: row.error }
72372
+ recorded: row.ok ? { id: String(row.value.id) } : { error: row.error },
72373
+ next: createNext(cwd)
72343
72374
  });
72344
72375
  }
72345
72376
  async function mergePullRequest(cwd, resolved, input, sleep) {
72346
72377
  const grant = requireGrant(cwd, "merge");
72347
72378
  if (!grant.allowed)
72348
72379
  return refuse(grant.error, grant.unblock, { reason: "grant_required" });
72380
+ const bypass = input.unverified ?? null;
72381
+ if (bypass && !bypass.reason.trim())
72382
+ return failure3("invalid_input", "--unverified needs --reason <why the user asked for it>");
72383
+ if (bypass && !grant.allowUnverified)
72384
+ return refuse(`unverified_refused: workspace ${grant.workspace ? `"${grant.workspace}"` : "(none)"} grants merge: "verified", which never merges without an accepted independent verdict`, "a non-author session verifies the head (workit-review), then merge without --unverified", { reason: "unverified_refused" });
72349
72385
  const number = selectPr(cwd, resolved, { pr: input.pr });
72350
72386
  if (!number.ok)
72351
72387
  return number;
@@ -72379,27 +72415,40 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
72379
72415
  if (reason)
72380
72416
  return refuse(`protected_branch: --delete-branch would delete ${branch}, which ${reason}; workit never deletes it`, `workit pr merge${input.pr ? ` --pr ${doc.number}` : ""} --method ${input.method} # without --delete-branch`, { reason: "protected_branch" });
72381
72417
  }
72418
+ const ledger = readLedger(cwd);
72419
+ if (!ledger.ok)
72420
+ return ledger;
72421
+ const check = checkVerdicts(cwd, branch, ledger.value.rows);
72422
+ const summary = { head: check.head, accepted: check.accepted, authors: check.authors };
72423
+ const covered = check.head === head && check.accepted.accepted;
72382
72424
  let verdict = {
72383
- required: grant.requireVerdict,
72384
- accepted: false,
72385
- verdictId: null
72425
+ required: true,
72426
+ accepted: covered,
72427
+ verdictId: covered && typeof check.accepted.verdict?.id === "string" ? check.accepted.verdict.id : null
72386
72428
  };
72387
- if (grant.requireVerdict) {
72388
- const ledger = readLedger(cwd);
72389
- if (!ledger.ok)
72390
- return ledger;
72391
- const check = checkVerdicts(cwd, branch, ledger.value.rows);
72392
- const summary = { head: check.head, accepted: check.accepted, authors: check.authors };
72393
- if (check.head !== head)
72394
- return refuse(`head_mismatch: the verdict check reads ${branch} at ${check.head?.slice(0, 12) ?? "(missing)"} but ${label} is at ${head.slice(0, 12)}`, `git fetch && git switch ${branch} && git merge --ff-only @{u} # then re-verify that head`, { reason: "head_mismatch", verdict: summary });
72395
- if (!check.accepted.accepted)
72396
- return refuse(`NEEDS_VERDICT: ${label} has no accepted independent verdict for ${head.slice(0, 12)} (${check.accepted.reasons.join(", ")})`, `an independent session verifies the head and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${branch}`, { reason: "needs_verdict", verdict: summary });
72397
- verdict = {
72398
- required: true,
72399
- accepted: true,
72400
- verdictId: typeof check.accepted.verdict?.id === "string" ? check.accepted.verdict.id : null
72401
- };
72402
- }
72429
+ let unverified = null;
72430
+ if (!covered && bypass && check.accepted.reasons.includes("failing_verdict"))
72431
+ return refuse(`failed_verdict: ${label} has a current independent failed verdict${typeof check.accepted.verdict?.id === "string" ? ` (${check.accepted.verdict.id})` : ""}; --unverified never merges over a rejection`, "a new independent verdict on the head supersedes it", { reason: "failed_verdict", verdict: summary });
72432
+ if (!covered && bypass) {
72433
+ const reason = bypass.reason.trim();
72434
+ const recorded = appendObserved(cwd, {
72435
+ type: "merge.unverified",
72436
+ actor: input.actor,
72437
+ branch,
72438
+ head,
72439
+ pr: doc.number,
72440
+ base: doc.base,
72441
+ reason,
72442
+ grant: grant.source
72443
+ });
72444
+ if (!recorded.ok)
72445
+ return failure3("failed", `unverified_unrecorded: the --unverified bypass could not be recorded (${recorded.error}); nothing was merged`);
72446
+ verdict = { required: false, accepted: false, verdictId: null };
72447
+ unverified = { reason, recorded: String(recorded.value.id) };
72448
+ } else if (check.head !== head)
72449
+ return refuse(`head_mismatch: the verdict check reads ${branch} at ${check.head?.slice(0, 12) ?? "(missing)"} but ${label} is at ${head.slice(0, 12)}`, `git fetch && git switch ${branch} && git merge --ff-only @{u} # then re-verify that head`, { reason: "head_mismatch", verdict: summary });
72450
+ else if (!covered)
72451
+ return refuse(`NEEDS_VERDICT: ${label} has no accepted independent verdict for ${head.slice(0, 12)} (${check.accepted.reasons.join(", ")})`, `an independent session verifies the head and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${branch}${grant.allowUnverified ? `; or, only if the user asks to merge unverified: workit pr merge --unverified --reason "<why>"` : ""}`, { reason: "needs_verdict", verdict: summary });
72403
72452
  const merged = resolved.forge.merge(doc.number, { sha: head, method: input.method });
72404
72453
  if (!merged.ok)
72405
72454
  return merged;
@@ -72437,6 +72486,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
72437
72486
  mergeSha: merged.data.mergeSha,
72438
72487
  method: input.method,
72439
72488
  verdictId: verdict.verdictId,
72489
+ ...unverified ? { unverified: unverified.recorded } : {},
72440
72490
  grant: grant.source
72441
72491
  });
72442
72492
  return {
@@ -72450,6 +72500,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
72450
72500
  method: input.method,
72451
72501
  mergeSha: merged.data.mergeSha,
72452
72502
  verdict,
72503
+ unverified,
72453
72504
  grant: { source: grant.source },
72454
72505
  deletedBranch,
72455
72506
  recorded: row.ok ? { id: String(row.value.id) } : { error: row.error },
@@ -72458,7 +72509,10 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
72458
72509
  }
72459
72510
  };
72460
72511
  }
72461
- var VERIFY_DELAYS_MS, noun = (resolved) => resolved.forge.kind === "github" ? "pull request" : "merge request", grantBlocked = (decision) => failure3("blocked", decision.error, decision.unblock), mergeBackFor = (cwd, base) => {
72512
+ var VERIFY_DELAYS_MS, noun = (resolved) => resolved.forge.kind === "github" ? "pull request" : "merge request", grantBlocked = (decision) => failure3("blocked", decision.error, decision.unblock), AFTER_REVIEW, createNext = (cwd) => {
72513
+ const endpoint = resolveAutonomy(cwd).effectiveEndpoint;
72514
+ return `a non-author verifies the head (workit-review); the endpoint is ${endpoint}, so ${AFTER_REVIEW[endpoint]}`;
72515
+ }, mergeBackFor = (cwd, base) => {
72462
72516
  try {
72463
72517
  const owners = workspaceReleaseTracks(cwd).tracks.filter((track) => track.productionBranch === base);
72464
72518
  if (owners.length !== 1)
@@ -72487,6 +72541,12 @@ var init_pr_ops = __esm(() => {
72487
72541
  init_ledger();
72488
72542
  init_report();
72489
72543
  VERIFY_DELAYS_MS = [1000, 2000, 4000];
72544
+ AFTER_REVIEW = {
72545
+ commit: "stop here; review comes when the user asks",
72546
+ pr: "stop here; review comes when the user asks",
72547
+ green: "babysit it to merge-ready, never merging",
72548
+ merged: "babysit it, then land it after verification"
72549
+ };
72490
72550
  NEXT_HINTS = {
72491
72551
  RESOLVE_CONFLICTS: "rebase onto the base, resolve, then workit git push --force-with-lease",
72492
72552
  REBASE: "rebase onto the base, then workit git push --force-with-lease",
@@ -73358,6 +73418,10 @@ function parseFlags(argv, spec) {
73358
73418
  const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
73359
73419
  for (let index = 0;index < argv.length; index += 1) {
73360
73420
  const arg = argv[index];
73421
+ if (arg === "--") {
73422
+ parsed.positionals.push(...argv.slice(index + 1));
73423
+ break;
73424
+ }
73361
73425
  if (arg === "--json")
73362
73426
  continue;
73363
73427
  if (!arg.startsWith("--")) {
@@ -73385,6 +73449,14 @@ function parseFlags(argv, spec) {
73385
73449
  }
73386
73450
  return parsed;
73387
73451
  }
73452
+ function unverifiedFlag(flags) {
73453
+ const reason = flags.values.reason?.trim();
73454
+ if (!flags.booleans.has("unverified"))
73455
+ return reason === undefined ? null : "--reason is only used with --unverified";
73456
+ if (!reason)
73457
+ return "--unverified needs --reason <why the user asked for it>";
73458
+ return reason.length > MAX_REASON ? `--reason is capped at ${MAX_REASON} characters` : { reason };
73459
+ }
73388
73460
  function positiveInt(value, flag) {
73389
73461
  if (value === undefined)
73390
73462
  return null;
@@ -73468,7 +73540,7 @@ function renderStatus(doc) {
73468
73540
  lines.push(`next: ${doc.next}`);
73469
73541
  return lines;
73470
73542
  }
73471
- var forgeDeps, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
73543
+ var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
73472
73544
  var init_forge_common = __esm(() => {
73473
73545
  init_resolve();
73474
73546
  init_vcs_config();
@@ -73614,13 +73686,23 @@ async function create4(argv, io) {
73614
73686
  ...data.releaseTrack?.name ? [`release track: ${data.releaseTrack.name} (${data.releaseTrack.detail})`] : [],
73615
73687
  ...(data.releaseTrack?.warnings ?? []).map((warning) => `note: ${warning}`),
73616
73688
  `head ${data.head.slice(0, 12)} verified on the forge`,
73617
- ..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : []
73689
+ ..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : [],
73690
+ `next: ${data.next}`
73618
73691
  ]);
73619
73692
  }
73620
73693
  async function merge3(argv, io) {
73621
- const flags = parseFlags(argv, { pr: "value", method: "value", "delete-branch": "boolean" });
73694
+ const flags = parseFlags(argv, {
73695
+ pr: "value",
73696
+ method: "value",
73697
+ "delete-branch": "boolean",
73698
+ unverified: "boolean",
73699
+ reason: "value"
73700
+ });
73622
73701
  if (typeof flags === "string")
73623
73702
  return usage3(io, flags, MERGE_USAGE);
73703
+ const unverified = unverifiedFlag(flags);
73704
+ if (typeof unverified === "string")
73705
+ return usage3(io, unverified, MERGE_USAGE);
73624
73706
  if (flags.positionals.length)
73625
73707
  return usage3(io, `unexpected argument ${flags.positionals[0]}`, MERGE_USAGE);
73626
73708
  const pr = positiveInt(flags.values.pr, "--pr");
@@ -73632,7 +73714,13 @@ async function merge3(argv, io) {
73632
73714
  const connected = connect(io);
73633
73715
  if (!connected.ok)
73634
73716
  return forgeFail(io, connected);
73635
- const result = await mergePullRequest(io.cwd, connected.data, { pr, method, deleteBranch: flags.booleans.has("delete-branch"), actor: actorFromEnv(io.env) }, forgeDeps.sleep);
73717
+ const result = await mergePullRequest(io.cwd, connected.data, {
73718
+ pr,
73719
+ method,
73720
+ deleteBranch: flags.booleans.has("delete-branch"),
73721
+ actor: actorFromEnv(io.env),
73722
+ ...unverified ? { unverified } : {}
73723
+ }, forgeDeps.sleep);
73636
73724
  if (!result.ok)
73637
73725
  return emit(io, fail(result.code, result.error, {
73638
73726
  unblock: result.unblock,
@@ -73640,7 +73728,7 @@ async function merge3(argv, io) {
73640
73728
  }));
73641
73729
  return emit(io, ok(result.data), (data) => [
73642
73730
  `merged ${connected.data.forge.kind === "github" ? "PR #" : "MR !"}${data.number} (${data.method}) at head ${data.head.slice(0, 12)}${data.mergeSha ? ` -> ${data.mergeSha.slice(0, 12)}` : ""}`,
73643
- data.verdict.required ? `verdict ${data.verdict.verdictId ?? "?"} accepted for that head` : "no verdict required (workspace grants merge: true)",
73731
+ data.unverified ? `UNVERIFIED: merged without a verdict (--unverified: ${data.unverified.reason}); ledger ${data.unverified.recorded}` : `verdict ${data.verdict.verdictId ?? "?"} accepted for that head`,
73644
73732
  ...data.mergeBack.length ? [
73645
73733
  `merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
73646
73734
  ] : [],
@@ -73658,7 +73746,7 @@ async function run10(argv, io) {
73658
73746
  return merge3(rest, io);
73659
73747
  return usage3(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE4);
73660
73748
  }
73661
- 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) [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--json]", USAGE4 = "workit pr status|create|merge ... (workit help pr)", METHODS2;
73749
+ 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) [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>] [--json]", USAGE4 = "workit pr status|create|merge ... (workit help pr)", METHODS2;
73662
73750
  var init_pr = __esm(() => {
73663
73751
  init_pr_body();
73664
73752
  init_pr_ops();
@@ -74945,7 +75033,7 @@ function repoMismatch(ctx, stack) {
74945
75033
  return stackFail("blocked", `repo_mismatch: stack ${stack.name} was planned on ${stack.forge ?? "?"} ${stack.repo ?? "?"}, but this checkout resolves to ${kind} ${repo}`, `workit stack plan --name ${stack.name} <branch…> # re-plan it here, or run from the right checkout`, { planned: { forge: stack.forge, repo: stack.repo }, resolved: { forge: kind, repo } });
74946
75034
  return null;
74947
75035
  }
74948
- function qualify(ctx, stack, entry, doc, verdict, grant, binding = null) {
75036
+ function qualify(ctx, stack, entry, doc, verdict, grant, binding = null, bypass = false) {
74949
75037
  const label = entry.pr ? noun2(ctx.resolved, entry.pr) : entry.branch;
74950
75038
  const stop = (reason, detail, unblock, ready = false) => ({
74951
75039
  ok: false,
@@ -74964,9 +75052,11 @@ function qualify(ctx, stack, entry, doc, verdict, grant, binding = null) {
74964
75052
  return stop("base_not_trunk", `${label} targets ${doc.base}, not ${stack.trunk}`, "workit stack sync");
74965
75053
  if (doc.next !== "READY")
74966
75054
  return stop("not_ready", `${label} is ${doc.next}${doc.blockers.length ? ` (${doc.blockers.join(", ")})` : ""}`, doc.next === "WAITING_CI" ? `workit ci wait --pr ${entry.pr}` : `workit pr status --pr ${entry.pr}`);
74967
- const requireVerdict = grant.allowed ? grant.requireVerdict : true;
74968
- if (requireVerdict && !verdict.accepted)
74969
- return stop(verdict.reasons.includes("no_verdict") ? "no_verdict" : "needs_verdict", `${label} has no accepted independent verdict for ${doc.head.sha.slice(0, 12)} (${verdict.reasons.join(", ") || "none"})`, `an independent session verifies ${entry.branch} and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${entry.branch}`);
75055
+ const allowUnverified = grant.allowed && grant.allowUnverified;
75056
+ if (bypass && allowUnverified && verdict.reasons.includes("failing_verdict"))
75057
+ return stop("failed_verdict", `${label} has a current independent failed verdict for ${doc.head.sha.slice(0, 12)}; --unverified never lands over a rejection`, "a new independent verdict on the head supersedes it");
75058
+ if (!(bypass && allowUnverified) && !verdict.accepted)
75059
+ return stop(verdict.reasons.includes("no_verdict") ? "no_verdict" : "needs_verdict", `${label} has no accepted independent verdict for ${doc.head.sha.slice(0, 12)} (${verdict.reasons.join(", ") || "none"})`, `an independent session verifies ${entry.branch} and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${entry.branch}${allowUnverified ? `; or, only if the user asks to land unverified: workit stack land --unverified --reason "<why>"` : ""}`);
74970
75060
  if (!grant.allowed)
74971
75061
  return stop("grant_required", `${label} is verified and ready, but ${grant.error}`, grant.unblock, true);
74972
75062
  return { ok: true };
@@ -75384,6 +75474,14 @@ async function landStack(ctx, stack, options) {
75384
75474
  const mismatch = repoMismatch(ctx, stack);
75385
75475
  if (mismatch)
75386
75476
  return failWith(mismatch);
75477
+ const bypass = options.unverified ?? null;
75478
+ if (bypass) {
75479
+ const grant = requireGrant(ctx.cwd, "merge");
75480
+ if (!grant.allowed)
75481
+ return failWith(stackFail("blocked", grant.error, grant.unblock, { reason: "grant_required" }));
75482
+ if (!grant.allowUnverified)
75483
+ return failWith(stackFail("blocked", `unverified_refused: workspace ${grant.workspace ? `"${grant.workspace}"` : "(none)"} grants merge: "verified", which never lands without an accepted independent verdict`, "a non-author session verifies each head (workit-review), then land without --unverified", { reason: "unverified_refused" }));
75484
+ }
75387
75485
  if (!options.dryRun) {
75388
75486
  const synced = syncStack(ctx.cwd, ctx, stack, { publish: true, dryRun: false, limit: 1 }, ctx.actor);
75389
75487
  if (!synced.ok)
@@ -75429,7 +75527,7 @@ async function landStack(ctx, stack, options) {
75429
75527
  const grant = requireGrant(ctx.cwd, "merge");
75430
75528
  const verdict = verdictOf(ctx.cwd, entry.branch, ledgerRows());
75431
75529
  const effective = options.dryRun && simulated.size > 0 && doc ? { ...doc, base: stack.trunk } : doc;
75432
- const q = qualify(ctx, stack, entry, effective, verdict, grant, doc ? prBinding(ctx, entry, doc) : null);
75530
+ const q = qualify(ctx, stack, entry, effective, verdict, grant, doc ? prBinding(ctx, entry, doc) : null, bypass !== null);
75433
75531
  if (!q.ok) {
75434
75532
  outcome.stoppedAt = {
75435
75533
  pr: entry.pr,
@@ -75452,7 +75550,8 @@ async function landStack(ctx, stack, options) {
75452
75550
  method: options.method,
75453
75551
  deleteBranch: false,
75454
75552
  actor: ctx.actor,
75455
- expect: { base: stack.trunk, branch: entry.branch }
75553
+ expect: { base: stack.trunk, branch: entry.branch },
75554
+ ...bypass && !verdict.accepted ? { unverified: bypass } : {}
75456
75555
  }, ctx.sleep);
75457
75556
  if (!merged.ok) {
75458
75557
  if (merged.code === "blocked" && "refusal" in merged && merged.refusal) {
@@ -75468,7 +75567,12 @@ async function landStack(ctx, stack, options) {
75468
75567
  }
75469
75568
  return failWith(stackFail(merged.code, merged.error, merged.unblock));
75470
75569
  }
75471
- outcome.landed.push({ pr, branch: entry.branch, mergeSha: merged.data.mergeSha });
75570
+ outcome.landed.push({
75571
+ pr,
75572
+ branch: entry.branch,
75573
+ mergeSha: merged.data.mergeSha,
75574
+ unverified: merged.data.unverified?.recorded ?? null
75575
+ });
75472
75576
  entry.merged = { pr, mergeSha: merged.data.mergeSha, at: new Date().toISOString() };
75473
75577
  const written = writeStack(ctx.cwd, stack);
75474
75578
  if (!written.ok)
@@ -75701,10 +75805,15 @@ async function land(argv, io) {
75701
75805
  max: "value",
75702
75806
  method: "value",
75703
75807
  timeout: "value",
75704
- interval: "value"
75808
+ interval: "value",
75809
+ unverified: "boolean",
75810
+ reason: "value"
75705
75811
  });
75706
75812
  if (typeof flags === "string")
75707
75813
  return usage3(io, flags, LAND_USAGE);
75814
+ const unverified = unverifiedFlag(flags);
75815
+ if (typeof unverified === "string")
75816
+ return usage3(io, unverified, LAND_USAGE);
75708
75817
  if (flags.positionals.length)
75709
75818
  return usage3(io, `unexpected argument ${flags.positionals[0]}`, LAND_USAGE);
75710
75819
  const max = positiveInt(flags.values.max, "--max");
@@ -75735,7 +75844,7 @@ async function land(argv, io) {
75735
75844
  resolved: connected.data,
75736
75845
  actor: actorFromEnv(io.env),
75737
75846
  sleep: forgeDeps.sleep
75738
- }, fresh.data ?? selected.data, { dryRun: flags.booleans.has("dry-run"), max, method, timeoutMs, intervalMs });
75847
+ }, fresh.data ?? selected.data, { dryRun: flags.booleans.has("dry-run"), max, method, timeoutMs, intervalMs, unverified });
75739
75848
  });
75740
75849
  if (!result.ok)
75741
75850
  return stackFailed(io, result);
@@ -75744,7 +75853,7 @@ async function land(argv, io) {
75744
75853
  ...data.dryRun ? [
75745
75854
  data.wouldLand.length ? `would land: ${data.wouldLand.map((item) => `${label(kind, item.pr)} (${item.branch})`).join(", ")}` : "would land nothing"
75746
75855
  ] : [
75747
- data.landed.length ? `landed: ${data.landed.map((item) => `${label(kind, item.pr)} (${item.branch}) -> ${short4(item.mergeSha)}`).join(", ")}` : "landed nothing"
75856
+ data.landed.length ? `landed: ${data.landed.map((item) => `${label(kind, item.pr)} (${item.branch}) -> ${short4(item.mergeSha)}${item.unverified ? ` UNVERIFIED (ledger ${item.unverified})` : ""}`).join(", ")}` : "landed nothing"
75748
75857
  ],
75749
75858
  ...data.retargeted.length ? [`retargeted: ${data.retargeted.map((pr) => label(kind, pr)).join(", ")}`] : [],
75750
75859
  ...data.restacked.length ? [`restacked: ${data.restacked.join(", ")}`] : [],
@@ -75764,7 +75873,7 @@ async function run14(argv, io) {
75764
75873
  return land(rest, io);
75765
75874
  return usage3(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE7);
75766
75875
  }
75767
- 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] [--timeout 20m] [--interval 30s] [--json]", USAGE7 = "workit stack plan|status|sync|land ... (workit help stack)", short4 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), label = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
75876
+ 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]", USAGE7 = "workit stack plan|status|sync|land ... (workit help stack)", short4 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), label = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
75768
75877
  `${data.dryRun ? "would sync" : "synced"} stack ${data.name}${data.merged.length ? `; merged: ${data.merged.map((item) => item.branch).join(", ")}` : ""}`,
75769
75878
  ...data.steps.map((step) => [
75770
75879
  ` ${step.branch} on ${step.parent}:`,
@@ -94739,6 +94848,10 @@ function parse7(argv) {
94739
94848
  };
94740
94849
  for (let index = 0;index < argv.length; index += 1) {
94741
94850
  const raw = argv[index];
94851
+ if (raw === "--") {
94852
+ options.paths.push(...argv.slice(index + 1));
94853
+ break;
94854
+ }
94742
94855
  const [flag, inline] = raw.startsWith("--") && raw.includes("=") ? [raw.slice(0, raw.indexOf("=")), raw.slice(raw.indexOf("=") + 1)] : [raw, undefined];
94743
94856
  if (flag === "--json")
94744
94857
  continue;
@@ -95310,7 +95423,7 @@ async function resumeBrief(argv, io) {
95310
95423
  return emit(io, ok({ ...data, recorded }), human2);
95311
95424
  }
95312
95425
  async function run21(argv, io) {
95313
- if (!argv.includes("--task"))
95426
+ if (!argv.some((arg) => arg === "--task" || arg.startsWith("--task=")))
95314
95427
  return resumeBrief(argv, io);
95315
95428
  await Promise.resolve().then(() => init_task());
95316
95429
  return runTaskCommand(["handoff", ...argv]);
@@ -95323,9 +95436,99 @@ var init_handoff = __esm(() => {
95323
95436
 
95324
95437
  // packages/workit-cli/src/main.ts
95325
95438
  init_package();
95326
- init_output();
95327
95439
  import path52 from "node:path";
95328
95440
 
95441
+ // packages/workit-cli/src/help.ts
95442
+ var HELP_FLAGS = new Set(["--help", "-h"]);
95443
+ var ESCAPE = "A value that starts with - goes as --flag=value (or after a bare --).";
95444
+ var options = (args) => {
95445
+ const end = args.indexOf("--");
95446
+ return end < 0 ? args : args.slice(0, end);
95447
+ };
95448
+ var wantsHelp = (args) => options(args).some((arg) => HELP_FLAGS.has(arg));
95449
+ var firstWord = (args) => options(args).find((arg) => !arg.startsWith("-"));
95450
+ var valueFlags = (lines) => {
95451
+ const out = new Map;
95452
+ const group = /(?<![\w-])(--?[a-zA-Z][\w-]*(?:\|--?[a-zA-Z][\w-]*)*) (?=[<"a-z0-9])/gu;
95453
+ for (const line of lines)
95454
+ for (const match of line.matchAll(group)) {
95455
+ const names = match[1].split("|");
95456
+ const long = names.find((name) => name.startsWith("--")) ?? names[0];
95457
+ for (const name of names)
95458
+ out.set(name, long);
95459
+ }
95460
+ return out;
95461
+ };
95462
+ function helpAsValue(args, lines) {
95463
+ const before = options(args);
95464
+ const takes = valueFlags(lines);
95465
+ for (let index = 1;index < before.length; index += 1) {
95466
+ const flag = before[index];
95467
+ const long = takes.get(before[index - 1]);
95468
+ if (HELP_FLAGS.has(flag) && long)
95469
+ return `\`${flag}\` looks like the value of ${before[index - 1]}; use ${long}=${flag} or put it after --`;
95470
+ }
95471
+ return null;
95472
+ }
95473
+ function takesPositionals(lines) {
95474
+ const flag = /(?<![\w-])--?[a-zA-Z][\w-]*(?:\|--?[a-zA-Z][\w-]*)*(?:[ =](?!-)(?:<[^>]*>|"[^"]*"|[\w.,@/|=-]+)…?)?/gu;
95475
+ return lines.some((line) => /<|"|\w…|\s…/u.test(line.replace(flag, "")));
95476
+ }
95477
+ var usageLines = (entry) => entry.subcommands?.length ? entry.subcommands.map((sub) => sub.usage) : [entry.usage];
95478
+ var examplesFor = (entry, sub) => (entry.examples ?? []).filter((example) => sub ? new RegExp(`^workit ${entry.name} ${sub.name}(?![\\w-])`, "u").test(example) : true);
95479
+ var exampleBlock = (examples) => examples.length ? ["", "examples:", ...examples.map((line) => ` ${line}`)] : [];
95480
+ function verbHelp(entry) {
95481
+ const subs = entry.subcommands ?? [];
95482
+ const usages = usageLines(entry);
95483
+ const examples = examplesFor(entry);
95484
+ const text = [
95485
+ ...subs.length ? [
95486
+ `workit ${entry.name}: ${entry.summary}`,
95487
+ "",
95488
+ ...subs.flatMap((sub) => [`usage: ${sub.usage}`, ` ${sub.summary}`])
95489
+ ] : [`usage: ${entry.usage}`, "", entry.summary],
95490
+ ...exampleBlock(examples),
95491
+ "",
95492
+ ...subs.length ? [`More: workit help ${entry.name} <${subs.map((sub) => sub.name).join("|")}>`] : [],
95493
+ ESCAPE
95494
+ ].join(`
95495
+ `);
95496
+ return {
95497
+ name: entry.name,
95498
+ group: entry.group,
95499
+ usage: usages[0],
95500
+ usages,
95501
+ summary: entry.summary,
95502
+ examples,
95503
+ text
95504
+ };
95505
+ }
95506
+ function subcommandHelp(entry, sub) {
95507
+ const examples = examplesFor(entry, sub);
95508
+ return {
95509
+ name: entry.name,
95510
+ group: entry.group,
95511
+ subcommand: sub.name,
95512
+ usage: sub.usage,
95513
+ usages: [sub.usage],
95514
+ summary: sub.summary,
95515
+ examples,
95516
+ text: [
95517
+ `usage: ${sub.usage}`,
95518
+ "",
95519
+ sub.summary,
95520
+ ...exampleBlock(examples),
95521
+ "",
95522
+ ...sub.aliases?.length ? [`aliases: ${sub.aliases.join(", ")}`] : [],
95523
+ ESCAPE
95524
+ ].join(`
95525
+ `)
95526
+ };
95527
+ }
95528
+
95529
+ // packages/workit-cli/src/main.ts
95530
+ init_output();
95531
+
95329
95532
  // packages/workit-cli/src/verbs/registry.ts
95330
95533
  var TASK_FAMILY_NAMES = [
95331
95534
  "task",
@@ -95336,13 +95539,97 @@ var TASK_FAMILY_NAMES = [
95336
95539
  "worker",
95337
95540
  "state"
95338
95541
  ];
95339
- var family = (name) => ({
95542
+ var FAMILY_OPTIONS = "[--task <id>] [--payload <JSON|@file|->] [--revision <r>] [--workspace-revision <r|null>] [--view <v>] [--actor <a>] [--confirm]";
95543
+ var FAMILY_ACTIONS = {
95544
+ task: {
95545
+ list: "List the workspace's tasks",
95546
+ inspect: "Show one task's full state",
95547
+ revise: "Change a task's intent or scope",
95548
+ progress: "Record progress on a task",
95549
+ pause: "Pause a task",
95550
+ resume: "Resume a paused task",
95551
+ start: "Start a task from a full payload",
95552
+ close: "Close a task by id"
95553
+ },
95554
+ policy: {
95555
+ assess: "Judge the branch's work and record the derived checks, verdicts and gates",
95556
+ preview: "Show what a judgment would derive, without recording it",
95557
+ explain: "Explain the policy in force"
95558
+ },
95559
+ evidence: { record: "Record evidence against a task's requirements" },
95560
+ finding: { record: "Record a finding", resolve: "Resolve a finding" },
95561
+ decision: { record: "Record a task decision", revoke: "Revoke a task decision" },
95562
+ worker: {
95563
+ assign: "Assign a worker to a task",
95564
+ report: "Report a worker's result",
95565
+ cancel: "Cancel a worker assignment"
95566
+ },
95567
+ state: { export: "Export a task's state (v1)", import: "Import an exported task state" }
95568
+ };
95569
+ var POLICY_JUDGE = "--judge risk=trivial|normal|high behavior=yes|no product-choice=yes|no plan=yes|no [--ref <path>]…";
95570
+ var TASK_IMPLICIT = [
95571
+ {
95572
+ name: "status",
95573
+ usage: "workit task status [--all]",
95574
+ summary: "The current branch's task, or every task with --all"
95575
+ },
95576
+ {
95577
+ name: "start",
95578
+ usage: `workit task start "<objective>" | task start ${FAMILY_OPTIONS}`,
95579
+ summary: "Start tracking the current branch's task (idempotent per branch)"
95580
+ },
95581
+ {
95582
+ name: "note",
95583
+ usage: 'workit task note [--next "<t>"] [--objective "<t>"] [--] "<text>"',
95584
+ summary: "Add a progress note to the current branch's task (creates it if needed)"
95585
+ },
95586
+ {
95587
+ name: "close",
95588
+ usage: `workit task close [--outcome verified|limited|stopped] [--summary "<t>"] [--confirm] | task close ${FAMILY_OPTIONS}`,
95589
+ summary: "Close the current branch's task, or a task by id"
95590
+ },
95591
+ {
95592
+ name: "adopt",
95593
+ usage: "workit task adopt <id>",
95594
+ summary: "Bind a migrated or other task to the current branch"
95595
+ }
95596
+ ];
95597
+ var familySubcommands = (name) => {
95598
+ const implicit = name === "task" ? TASK_IMPLICIT : [];
95599
+ const grammar = Object.entries(FAMILY_ACTIONS[name]).filter(([action]) => !implicit.some((entry) => entry.name === action)).map(([action, summary]) => ({
95600
+ name: action,
95601
+ usage: name === "policy" && action !== "explain" ? `workit policy ${action} ${POLICY_JUDGE} ${FAMILY_OPTIONS}` : `workit ${name} ${action} ${FAMILY_OPTIONS}`,
95602
+ summary
95603
+ }));
95604
+ return [...implicit, ...grammar];
95605
+ };
95606
+ var family = (name) => withSubcommands({
95340
95607
  name,
95341
95608
  group: "task",
95342
- usage: name === "task" ? 'workit task status [--all] | task start "<objective>" | task note "<text>" [--next "<t>"] | task close [--outcome verified|limited|stopped] | task adopt <id> | task <action> --payload <JSON>' : name === "policy" ? "workit policy assess|preview --judge risk=trivial|normal|high behavior=yes|no product-choice=yes|no plan=yes|no [--ref <path>] | policy explain" : `workit ${name} <action> [options]`,
95343
95609
  summary: name === "task" ? "The current branch's task (created by its first note, check or recording; no ids needed), or the task family" : name === "policy" ? "Judge the branch's work; Workit derives its checks, verdicts, decision and plan gates" : `Inspect and control a Workit task (${name} family)`,
95610
+ subcommands: familySubcommands(name),
95611
+ ...name === "task" ? {
95612
+ examples: [
95613
+ 'workit task note "parser done; wiring the router" --next "add the router test"',
95614
+ "workit task close --outcome verified"
95615
+ ]
95616
+ } : name === "policy" ? {
95617
+ examples: [
95618
+ "workit policy assess --judge risk=normal behavior=yes product-choice=no plan=no"
95619
+ ]
95620
+ } : {},
95344
95621
  load: async () => (await Promise.resolve().then(() => (init_family(), exports_family))).familyVerb(name)
95345
95622
  });
95623
+ function withSubcommands(entry, options = {}) {
95624
+ const names = entry.subcommands.map((sub) => sub.name).join("|");
95625
+ return {
95626
+ ...entry,
95627
+ usage: `workit ${entry.name} ${options.optional ? `[${names}]` : names} [options]`,
95628
+ ...options.optional ? { optionalSubcommand: true } : {}
95629
+ };
95630
+ }
95631
+ var VERIFY_OPTIONS = "[--expect pushed|pr|merged|released] [--pr <n> | --branch <b>] [--sha <sha>] [--tag <t>] [--package <name[@version]>]";
95632
+ var LEDGER_ACTOR = "[--branch <b> | --pr <n>] [--supersedes <id>] [--session <id> | --as <role>]";
95346
95633
  var VERBS = [
95347
95634
  {
95348
95635
  name: "init",
@@ -95354,28 +95641,28 @@ var VERBS = [
95354
95641
  {
95355
95642
  name: "upgrade",
95356
95643
  group: "setup",
95357
- usage: "workit upgrade [--hosts=a,b|none] [--cli] [--preview | --apply --confirm] [--json]",
95644
+ usage: "workit upgrade [--hosts=a,b|none] [--cli] [--preview | --apply --confirm]",
95358
95645
  summary: "Preview upgrades (--apply --confirm to apply)",
95359
95646
  load: () => Promise.resolve().then(() => (init_upgrade2(), exports_upgrade))
95360
95647
  },
95361
95648
  {
95362
95649
  name: "launch",
95363
95650
  group: "setup",
95364
- usage: "workit launch <host> [--auto-upgrade] [-- args]",
95651
+ usage: "workit launch <host> [--auto-upgrade] [-- <host args…>]",
95365
95652
  summary: "Upgrade before host startup, then start the host",
95366
95653
  load: () => Promise.resolve().then(() => (init_launch(), exports_launch))
95367
95654
  },
95368
95655
  {
95369
95656
  name: "doctor",
95370
95657
  group: "setup",
95371
- usage: "workit doctor [--json] [--fix-lock [--force [--yes]]]",
95658
+ usage: "workit doctor [--fix-lock [--force [--yes]]]",
95372
95659
  summary: "Verify the offline installation health (--fix-lock clears a stale workit store lock)",
95373
95660
  load: () => Promise.resolve().then(() => (init_doctor2(), exports_doctor))
95374
95661
  },
95375
95662
  {
95376
95663
  name: "gc",
95377
95664
  group: "setup",
95378
- usage: "workit gc [--dry-run] [--prune-recovery --yes] [--json]",
95665
+ usage: "workit gc [--dry-run] [--prune-recovery --yes]",
95379
95666
  summary: "Compact long task event logs, drop unreferenced blobs and old check logs; report (or prune) 2.x recovery copies",
95380
95667
  load: () => Promise.resolve().then(() => (init_gc(), exports_gc))
95381
95668
  },
@@ -95386,108 +95673,312 @@ var VERBS = [
95386
95673
  summary: "Remove workit host registrations interactively (~/.config/workit is kept)",
95387
95674
  load: () => Promise.resolve().then(() => (init_uninstall2(), exports_uninstall))
95388
95675
  },
95389
- {
95676
+ withSubcommands({
95390
95677
  name: "grant",
95391
95678
  group: "setup",
95392
- usage: "workit grant show [<workspace>] [--all] | grant set <workspace> <kind>=<true|false|verified>… | grant unset <workspace> <kind>…",
95393
95679
  summary: "Show or change a workspace's autonomy grants (push, pr, merge, release, rerun) and default endpoint; raising needs the user at a terminal",
95680
+ subcommands: [
95681
+ {
95682
+ name: "show",
95683
+ usage: "workit grant show [<workspace>] [--all]",
95684
+ summary: "Show the grants of the current (or named) workspace, or of every workspace"
95685
+ },
95686
+ {
95687
+ name: "set",
95688
+ usage: "workit grant set <workspace> <kind>=<true|false|verified>… [defaultEndpoint=commit|pr|green|merged] [verification=self|independent]",
95689
+ summary: "Change grants (kinds: push, pr, merge, release, rerun); raising one asks the user to confirm at a terminal"
95690
+ },
95691
+ {
95692
+ name: "unset",
95693
+ usage: "workit grant unset <workspace> <kind>…",
95694
+ summary: "Remove grants, falling back to the defaults"
95695
+ }
95696
+ ],
95697
+ examples: ["workit grant show", "workit grant set my-repo push=true pr=true"],
95394
95698
  load: () => Promise.resolve().then(() => (init_grant(), exports_grant))
95395
- },
95699
+ }, { optional: true }),
95396
95700
  ...TASK_FAMILY_NAMES.map(family),
95397
95701
  {
95398
95702
  name: "check",
95399
95703
  group: "delivery",
95400
- usage: "workit check <name> | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] -- <cmd…>",
95704
+ usage: "workit check <name> [--task <id>] [--base <ref>] | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--base <ref>] -- <cmd…>",
95401
95705
  summary: "Run a configured or ad-hoc check and record CLI-observed evidence (exit = the command's)",
95706
+ examples: ["workit check test", "workit check --name unit -- bun test test/unit"],
95402
95707
  load: () => Promise.resolve().then(() => (init_check(), exports_check))
95403
95708
  },
95404
- {
95709
+ withSubcommands({
95405
95710
  name: "pr",
95406
95711
  group: "delivery",
95407
- usage: "workit pr status [--pr <n>] | pr create [--base <b> | --track <t>] (--title <t> | --fill) [--draft] | pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch]",
95408
95712
  summary: "PR/MR state with failing log tails and next action; open a SHA-verified PR; merge only when READY, verified and granted",
95713
+ subcommands: [
95714
+ {
95715
+ name: "status",
95716
+ usage: "workit pr status [--pr <n> | --branch <b>] [--log-lines 60]",
95717
+ summary: "PR/MR state, failing check log tails and the next action"
95718
+ },
95719
+ {
95720
+ name: "create",
95721
+ usage: "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f>] | --fill) [--draft]",
95722
+ summary: "Open a PR/MR for the pushed branch, verified against the local HEAD"
95723
+ },
95724
+ {
95725
+ name: "merge",
95726
+ usage: "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>]",
95727
+ summary: "Merge when READY, verified by a non-author and granted"
95728
+ }
95729
+ ],
95730
+ examples: [
95731
+ 'workit pr create --title "fix(cli): answer help flags first" --body-file pr.md',
95732
+ "workit pr merge --method squash --delete-branch"
95733
+ ],
95409
95734
  load: () => Promise.resolve().then(() => (init_pr(), exports_pr))
95410
- },
95411
- {
95735
+ }),
95736
+ withSubcommands({
95412
95737
  name: "ci",
95413
95738
  group: "delivery",
95414
- usage: "workit ci wait [--pr <n>] [--head <sha>] [--timeout 20m] | ci rerun [--failed | --check <name>…] --reason flake|infra [--force]",
95415
95739
  summary: "Wait for CI on the PR head (exit 0/1/3/4), or rerun failed jobs once per head",
95740
+ subcommands: [
95741
+ {
95742
+ name: "wait",
95743
+ usage: "workit ci wait [--pr <n> | --branch <b>] [--head <sha>] [--timeout 20m] [--interval 30s]",
95744
+ summary: "Wait for CI on the PR head; exit 0 green, 1 red, 3 blocked, 4 still pending"
95745
+ },
95746
+ {
95747
+ name: "rerun",
95748
+ usage: "workit ci rerun [--pr <n> | --branch <b>] [--check <name>… | --failed] --reason flake|infra [--force]",
95749
+ summary: "Rerun failed jobs once per head, with a recorded reason"
95750
+ }
95751
+ ],
95752
+ examples: ["workit ci wait --timeout 30m", "workit ci rerun --failed --reason flake"],
95416
95753
  load: () => Promise.resolve().then(() => (init_ci(), exports_ci))
95417
- },
95418
- {
95754
+ }),
95755
+ withSubcommands({
95419
95756
  name: "git",
95420
95757
  group: "delivery",
95421
- usage: "workit git branch <name> [--base <b>] [--track <t>] | git commit -m <msg> [--all | -- <paths…>] | git push [--set-upstream] [--force-with-lease]",
95422
95758
  summary: "Policy-checked branch and commit (Workit-Session trailer); push with a lease and a verified remote tip",
95759
+ subcommands: [
95760
+ {
95761
+ name: "branch",
95762
+ usage: "workit git branch <name> | git branch --kind feature|bugfix|hotfix --slug <s> [--base <b>] [--track <t>] [--carry]",
95763
+ summary: "Create and switch to a branch from the trunk or a release track"
95764
+ },
95765
+ {
95766
+ name: "commit",
95767
+ usage: "workit git commit -m|--message <msg>… [-a|--all | [--] <paths…>]",
95768
+ summary: "Commit with a linted Conventional Commit message and the session trailer"
95769
+ },
95770
+ {
95771
+ name: "push",
95772
+ usage: "workit git push [-u|--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]]",
95773
+ summary: "Push the current branch and verify the remote tip (never a blind --force)"
95774
+ }
95775
+ ],
95776
+ examples: [
95777
+ "workit git branch --kind feature --slug help-everywhere",
95778
+ 'workit git commit -m "fix(cli): answer help flags first" --all',
95779
+ "workit git push --set-upstream"
95780
+ ],
95423
95781
  load: () => Promise.resolve().then(() => (init_git2(), exports_git))
95424
- },
95425
- {
95782
+ }),
95783
+ withSubcommands({
95426
95784
  name: "verify-delivery",
95427
95785
  group: "delivery",
95428
- usage: "workit verify-delivery [push|pr|merge|release] [--pr <n> | --branch <b>] [--sha <s>] [--tag <t>] [--package <p>]",
95429
95786
  summary: "Observe that a push, PR head, merge or release really landed on the remote (exit 1 when not)",
95787
+ subcommands: [
95788
+ ["push", "pushed", "the branch's remote tip is the local HEAD"],
95789
+ ["pr", null, "the PR head is the local HEAD"],
95790
+ ["merge", "merged", "the PR merged into its base"],
95791
+ ["release", "released", "the release tag and package exist"]
95792
+ ].map(([name, alias, what]) => ({
95793
+ name,
95794
+ ...alias ? { aliases: [alias] } : {},
95795
+ usage: `workit verify-delivery ${name} ${VERIFY_OPTIONS}`,
95796
+ summary: `Observe that ${what}`
95797
+ })),
95798
+ examples: ["workit verify-delivery push", "workit verify-delivery merge --pr 42"],
95430
95799
  load: () => Promise.resolve().then(() => (init_verify_delivery(), exports_verify_delivery))
95431
- },
95432
- {
95800
+ }, { optional: true }),
95801
+ withSubcommands({
95433
95802
  name: "stack",
95434
95803
  group: "delivery",
95435
- usage: "workit stack plan [<bottom> … <top>] | status | sync [--local] | land [--dry-run] [--max <n>] [--json]",
95436
95804
  summary: "Plan, restack (lease push + retarget) and land a base-branch stack of PRs, root first",
95805
+ subcommands: [
95806
+ {
95807
+ name: "plan",
95808
+ usage: "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>]",
95809
+ summary: "Record the stack's branches, bottom first"
95810
+ },
95811
+ {
95812
+ name: "status",
95813
+ usage: "workit stack status [--name <n>]",
95814
+ summary: "Each stacked PR's base, head, CI and verdict"
95815
+ },
95816
+ {
95817
+ name: "sync",
95818
+ usage: "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]…",
95819
+ summary: "Restack onto the moved bases, lease-push and retarget the PRs"
95820
+ },
95821
+ {
95822
+ name: "land",
95823
+ usage: "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--timeout 20m] [--interval 30s]",
95824
+ summary: "Merge the stack root first, restacking the rest as each lands"
95825
+ }
95826
+ ],
95827
+ examples: ["workit stack plan feature/a feature/b feature/c", "workit stack land --dry-run"],
95437
95828
  load: () => Promise.resolve().then(() => (init_stack2(), exports_stack))
95438
- },
95439
- {
95829
+ }),
95830
+ withSubcommands({
95440
95831
  name: "fanout",
95441
95832
  group: "delivery",
95442
- usage: "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] | fanout brief <slice> [--mode new|resume] [--attempt <n>] | fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] | fanout status [--name <n>] [--stuck-after 30m] [--offline] | fanout worktree create|release <slice> [--force] [--json]",
95443
95833
  summary: "Register parallel slices (complete briefs, disjoint scopes, shared-file owners), render each worker's brief with the standing orders, watch them (STUCK, PR, CI, verdict, landed, landing order), make and release slice worktrees, and gate fan-in: out-of-scope edits, trunk and sibling merge conflicts",
95834
+ subcommands: [
95835
+ {
95836
+ name: "plan",
95837
+ usage: "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead]",
95838
+ summary: "Register the slices of a plan file"
95839
+ },
95840
+ {
95841
+ name: "brief",
95842
+ usage: "workit fanout brief <slice> [--mode new|resume] [--attempt <n>] [--name <n>]",
95843
+ summary: "Render one worker's brief with the standing orders"
95844
+ },
95845
+ {
95846
+ name: "check",
95847
+ usage: "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline]",
95848
+ summary: "Gate fan-in: out-of-scope edits, trunk and sibling merge conflicts"
95849
+ },
95850
+ {
95851
+ name: "status",
95852
+ usage: "workit fanout status [--name <n>] [--stuck-after 30m] [--offline]",
95853
+ summary: "Each slice's state: STUCK, PR, CI, verdict, landed, landing order"
95854
+ },
95855
+ {
95856
+ name: "worktree",
95857
+ usage: "workit fanout worktree create|release <slice> [--name <n>] [--force]",
95858
+ summary: "Make or release a slice's worktree (--force applies to release only)"
95859
+ }
95860
+ ],
95861
+ examples: [
95862
+ "workit fanout plan plan.json --take-lead",
95863
+ "workit fanout status --stuck-after 20m"
95864
+ ],
95444
95865
  load: () => Promise.resolve().then(() => (init_fanout2(), exports_fanout))
95445
- },
95446
- {
95866
+ }),
95867
+ withSubcommands({
95447
95868
  name: "ledger",
95448
95869
  group: "delivery",
95449
- usage: "workit ledger decision|ruling|verdict|standing|list|check ...",
95450
95870
  summary: "Record and query decisions, rulings, SHA-keyed verdicts (patch-id carry-over across rebases) and a fanout's standing orders",
95871
+ subcommands: [
95872
+ {
95873
+ name: "decision",
95874
+ usage: `workit ledger decision "<what>" --why "<why>" [--ref <path|url>]… ${LEDGER_ACTOR}`,
95875
+ summary: "Record a decision and why"
95876
+ },
95877
+ {
95878
+ name: "ruling",
95879
+ usage: `workit ledger ruling "<what>" --why "<why>" --cost-if-wrong "<cost>" [--ref <path|url>]… ${LEDGER_ACTOR}`,
95880
+ summary: "Record a ruling on an open choice, with the cost of being wrong"
95881
+ },
95882
+ {
95883
+ name: "verdict",
95884
+ usage: `workit ledger verdict <result> --how "<method/evidence>" [--kind unit|live|perf|review] [--base <ref>] [--surface ui|cli|api] [--evidence <ref>]… [--self] ${LEDGER_ACTOR} | ledger verdict [<branch>]`,
95885
+ summary: "Record a SHA-keyed verdict on the branch head (result: verified, tests-verified, type-check-only, blocked, failed), or show the current verdicts"
95886
+ },
95887
+ {
95888
+ name: "list",
95889
+ aliases: ["show"],
95890
+ usage: "workit ledger list|show [--branch <b>] [--pr <n>] [--type <t>] [--last <n>]",
95891
+ summary: "List ledger rows, newest last"
95892
+ },
95893
+ {
95894
+ name: "check",
95895
+ usage: "workit ledger check [--pr <n> | --branch <b>]",
95896
+ summary: "Whether the branch head carries an accepted non-author verdict"
95897
+ },
95898
+ {
95899
+ name: "standing",
95900
+ usage: `workit ledger standing add "<order>" | standing list | standing clear [<id>] [--fanout <name>] [--supersedes <id>] [--session <id> | --as <role>]`,
95901
+ summary: "The lead's standing orders for a fanout's workers"
95902
+ },
95903
+ {
95904
+ name: "add",
95905
+ usage: 'workit ledger add decision|ruling|verdict "<what>" …',
95906
+ summary: "Same as the bare decision, ruling and verdict forms"
95907
+ }
95908
+ ],
95909
+ examples: [
95910
+ 'workit ledger decision "keep the registry as the help source" --why "one place to update"',
95911
+ 'workit ledger verdict verified --how "ran the CLI against a scratch repo" --kind live --as verifier'
95912
+ ],
95451
95913
  load: () => Promise.resolve().then(() => (init_ledger2(), exports_ledger))
95452
- },
95914
+ }),
95453
95915
  {
95454
95916
  name: "test-audit",
95455
95917
  group: "delivery",
95456
- usage: "workit test-audit [paths…] [--diff [base]] [--rule <ids>] [--min-severity <level>] [--fail-on <level>] [--mutate [--test-cmd <cmd {files}>] [--max-mutants <n>] [--budget <s>]] [--json]",
95918
+ usage: "workit test-audit [paths…] [--diff [<base>]] [--rule <ids>] [--min-severity <level>] [--fail-on <level>] [--mutate [--test-cmd <cmd {files}>] [--max-mutants <n>] [--budget <s>] [--timeout <s>]]",
95457
95919
  summary: "Flag tautological and low-value tests with a suggested independent oracle; --mutate checks changed lines with diff-scoped mutation",
95458
95920
  load: () => Promise.resolve().then(() => (init_test_audit(), exports_test_audit))
95459
95921
  },
95460
- {
95922
+ withSubcommands({
95461
95923
  name: "knowledge",
95462
95924
  group: "delivery",
95463
- usage: "workit knowledge lint [--json]",
95464
95925
  summary: "Lint agent knowledge files: AGENTS.md byte budget, broken local links, scaffold-only files, duplicated rules (exit 1 on findings)",
95926
+ subcommands: [
95927
+ {
95928
+ name: "lint",
95929
+ usage: "workit knowledge lint",
95930
+ summary: "Lint AGENTS.md and the knowledge files it links"
95931
+ }
95932
+ ],
95465
95933
  load: () => Promise.resolve().then(() => (init_knowledge2(), exports_knowledge))
95466
- },
95467
- {
95934
+ }),
95935
+ withSubcommands({
95468
95936
  name: "youtrack",
95469
95937
  group: "delivery",
95470
- usage: "workit youtrack note <ISSUE> (--markdown <t> | --file <p>) [--minutes <n>] | youtrack time|meeting <ISSUE> --minutes <n> [--text <t>] [--date auto|YYYY-MM-DD]",
95471
95938
  summary: "Post a YouTrack comment or log work time (host permission, no grant)",
95939
+ subcommands: [
95940
+ {
95941
+ name: "note",
95942
+ usage: "workit youtrack note <ISSUE> (--markdown <t> | --file <p>) [--minutes <n>] [--date auto|YYYY-MM-DD]",
95943
+ summary: "Post a comment, optionally logging time"
95944
+ },
95945
+ {
95946
+ name: "time",
95947
+ usage: "workit youtrack time <ISSUE> --minutes <n> [--text <t>] [--date auto|YYYY-MM-DD]",
95948
+ summary: "Log work time"
95949
+ },
95950
+ {
95951
+ name: "meeting",
95952
+ usage: "workit youtrack meeting <ISSUE> --minutes <n> --text <t> [--date auto|YYYY-MM-DD]",
95953
+ summary: "Log meeting time"
95954
+ }
95955
+ ],
95472
95956
  load: () => Promise.resolve().then(() => (init_youtrack2(), exports_youtrack))
95473
- },
95474
- {
95957
+ }),
95958
+ withSubcommands({
95475
95959
  name: "changelog",
95476
95960
  group: "delivery",
95477
- usage: "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]",
95478
95961
  summary: "Add entries under the Unreleased section of the changelog",
95962
+ subcommands: [
95963
+ {
95964
+ name: "apply",
95965
+ usage: "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]",
95966
+ summary: "Add entries (or only normalize the file)"
95967
+ }
95968
+ ],
95479
95969
  load: () => Promise.resolve().then(() => (init_changelog2(), exports_changelog))
95480
- },
95970
+ }),
95481
95971
  {
95482
95972
  name: "handoff",
95483
95973
  group: "delivery",
95484
- usage: "workit handoff [--note <t>] [--next <t>] [--record] | workit handoff --task <id>",
95974
+ usage: "workit handoff [--note <t>] [--next <t>] [--record] [--last <n>] | workit handoff --task <id>",
95485
95975
  summary: "Print a resume brief (branch, HEAD, dirty state, verdict, rulings, next command); --task exports v1 task state",
95486
95976
  load: () => Promise.resolve().then(() => (init_handoff(), exports_handoff))
95487
95977
  }
95488
95978
  ];
95489
95979
  var BY_NAME = new Map(VERBS.map((entry) => [entry.name, entry]));
95490
95980
  var findVerb = (name) => BY_NAME.get(name);
95981
+ var findSubcommand = (entry, name) => entry.subcommands?.find((sub) => sub.name === name || sub.aliases?.includes(name));
95491
95982
 
95492
95983
  // packages/workit-cli/src/main.ts
95493
95984
  var GROUP_TITLES = {
@@ -95519,20 +96010,17 @@ ${sections.join(`
95519
96010
  `)}
95520
96011
 
95521
96012
  Global flags:
95522
- --json Machine-readable output: {"ok","code","data","error"?,"unblock"?}
95523
- --cwd <dir> Run as if started in <dir>
95524
- --version Print the CLI version
95525
- help <cmd> Show usage for one command
96013
+ --json Machine-readable output: {"ok","code","data","error"?,"unblock"?}
96014
+ --cwd <dir> Run as if started in <dir>
96015
+ --version Print the CLI version
96016
+ -h, --help Show usage for the command and subcommand before it (runs nothing)
96017
+ help <cmd> [<sub>] The same, as a command
95526
96018
 
95527
96019
  Exit codes: 0 ok · 1 failed · 2 usage · 3 blocked · 4 busy/pending · 5 unavailable
95528
96020
 
95529
96021
  Run \`npx @brainervirus/workit-cli init\` to configure platforms, YouTrack, VCS and project hygiene.
95530
96022
  `;
95531
96023
  }
95532
- var verbHelp = (entry) => `usage: ${entry.usage}
95533
-
95534
- ${entry.summary}${entry.planned ? ` (coming in ${entry.planned})` : ""}
95535
- `;
95536
96024
  function parseGlobals(argv) {
95537
96025
  const rest = [];
95538
96026
  let json = false;
@@ -95578,18 +96066,38 @@ async function main(argv, overrides = {}, options = {}) {
95578
96066
  return emit(io, ok({ version: package_default.version }), (data) => data.version);
95579
96067
  }
95580
96068
  if (command === undefined || command === "help" || command === "--help" || command === "-h") {
95581
- const topic = command === "help" ? args.find((arg) => !arg.startsWith("-")) : undefined;
96069
+ const [topic, sub] = command === "help" ? words2(args) : [];
95582
96070
  if (topic) {
95583
96071
  const entry = findVerb(topic);
95584
96072
  if (!entry)
95585
96073
  return unknown2(io, topic);
95586
- return emit(io, ok(describe3(entry)), () => verbHelp(entry));
96074
+ if (sub === undefined)
96075
+ return help(io, verbHelp(entry));
96076
+ const found = findSubcommand(entry, sub);
96077
+ if (!found)
96078
+ return emit(io, fail("invalid_input", `unknown ${topic} subcommand "${sub}"`, {
96079
+ unblock: `workit help ${topic}`
96080
+ }));
96081
+ return help(io, subcommandHelp(entry, found));
95587
96082
  }
95588
96083
  return emit(io, ok({ version: package_default.version, verbs: listed().map(describe3) }), () => helpText());
95589
96084
  }
95590
96085
  const entry = findVerb(command);
95591
96086
  if (!entry)
95592
96087
  return unknown2(io, command);
96088
+ if (wantsHelp(verbArgs)) {
96089
+ const word = firstWord(verbArgs);
96090
+ const sub = word === undefined ? undefined : findSubcommand(entry, word);
96091
+ const asValue = helpAsValue(verbArgs, sub ? [sub.usage] : usageLines(entry));
96092
+ if (asValue)
96093
+ return emit(io, fail("invalid_input", asValue, {
96094
+ unblock: `workit help ${entry.name}${sub ? ` ${sub.name}` : ""}`
96095
+ }));
96096
+ return help(io, sub ? subcommandHelp(entry, sub) : verbHelp(entry));
96097
+ }
96098
+ const ended = endOfOptions(io, entry, args);
96099
+ if (typeof ended === "number")
96100
+ return ended;
95593
96101
  if (parsed.cwd !== null) {
95594
96102
  try {
95595
96103
  process.chdir(io.cwd);
@@ -95607,8 +96115,8 @@ async function main(argv, overrides = {}, options = {}) {
95607
96115
  `);
95608
96116
  const verb = await entry.load();
95609
96117
  if (!io.json)
95610
- return verb.run(args, io);
95611
- return runJsonPure(io, command, (pure) => verb.run(args, pure));
96118
+ return verb.run(ended, io);
96119
+ return runJsonPure(io, command, (pure) => verb.run(ended, pure));
95612
96120
  }
95613
96121
  var CODE_FOR_EXIT = {
95614
96122
  1: "failed",
@@ -95671,10 +96179,32 @@ var withJsonFlag = (args) => {
95671
96179
  return end < 0 ? [...args, "--json"] : [...args.slice(0, end), "--json", ...args.slice(end)];
95672
96180
  };
95673
96181
  var listed = () => VERBS.filter((entry) => !entry.planned);
96182
+ function endOfOptions(io, entry, args) {
96183
+ const end = args.indexOf("--");
96184
+ if (end < 0)
96185
+ return args;
96186
+ const word = firstWord(args);
96187
+ const sub = word === undefined ? undefined : findSubcommand(entry, word);
96188
+ const unblock = `workit help ${entry.name}${sub ? ` ${sub.name}` : ""}`;
96189
+ if (entry.subcommands?.length && !sub) {
96190
+ if (entry.optionalSubcommand || word !== undefined)
96191
+ return args;
96192
+ return emit(io, fail("invalid_input", `missing ${entry.name} subcommand`, { unblock }));
96193
+ }
96194
+ if (takesPositionals(sub ? [sub.usage] : [entry.usage]))
96195
+ return args;
96196
+ const extra = args.slice(end + 1);
96197
+ if (extra.length)
96198
+ return emit(io, fail("invalid_input", `${entry.name}${sub ? ` ${sub.name}` : ""} takes no positional arguments (got: ${extra.join(" ")})`, { unblock }));
96199
+ return args.slice(0, end);
96200
+ }
96201
+ var words2 = (args) => args.filter((arg) => !arg.startsWith("-"));
96202
+ var help = (io, doc) => emit(io, ok(doc), (data) => data.text);
95674
96203
  var describe3 = (entry) => ({
95675
96204
  name: entry.name,
95676
96205
  group: entry.group,
95677
96206
  usage: entry.usage,
96207
+ usages: usageLines(entry),
95678
96208
  summary: entry.summary,
95679
96209
  ...entry.planned ? { planned: entry.planned } : {}
95680
96210
  });