@brainervirus/workit-cli 8.17.0 → 8.18.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.
Files changed (2) hide show
  1. package/dist/index.js +172 -37
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@brainervirus/workit-cli",
68
- version: "8.17.0",
68
+ version: "8.18.0",
69
69
  private: false,
70
70
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
71
71
  keywords: [
@@ -103,13 +103,13 @@ var init_package = __esm(() => {
103
103
  },
104
104
  dependencies: {
105
105
  "@babel/parser": "7.29.9",
106
- "@brainervirus/workit-cursor": "^8.17.0",
106
+ "@brainervirus/workit-cursor": "^8.18.0",
107
107
  "@inkjs/ui": "2.0.0",
108
108
  ink: "7.1.1",
109
109
  react: "19.2.8"
110
110
  },
111
111
  devDependencies: {
112
- "@brainervirus/workit-core": "^8.17.0"
112
+ "@brainervirus/workit-core": "^8.18.0"
113
113
  },
114
114
  engines: {
115
115
  node: ">=24"
@@ -170,6 +170,20 @@ var init_output = __esm(() => {
170
170
  import { spawnSync } from "node:child_process";
171
171
  import fs from "node:fs";
172
172
  import path from "node:path";
173
+ function checksConfigFile(root) {
174
+ const current = path.join(root, CHECKS_FILE);
175
+ const legacy = path.join(root, LEGACY_CHECKS_FILE);
176
+ return !fs.existsSync(current) && fs.existsSync(legacy) ? legacy : current;
177
+ }
178
+ function ensureWorkitDir(root) {
179
+ const dir = path.join(root, ".workit");
180
+ fs.mkdirSync(dir, { recursive: true });
181
+ const ignore = path.join(dir, ".gitignore");
182
+ if (!fs.existsSync(ignore))
183
+ fs.writeFileSync(ignore, `*
184
+ `);
185
+ return dir;
186
+ }
173
187
  function repoHeavyCommands(root) {
174
188
  try {
175
189
  const value = JSON.parse(fs.readFileSync(checksConfigFile(root), "utf8"));
@@ -261,19 +275,25 @@ function detectedChecks(root) {
261
275
  function loadCheckConfig(cwd) {
262
276
  const root = checkRoot(cwd);
263
277
  const file = checksConfigFile(root);
278
+ const legacy = path.basename(file) === LEGACY_CHECKS_FILE;
279
+ const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
280
+ const note = legacy ? {
281
+ note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
282
+ } : {};
264
283
  const none = (error = null) => ({
265
- source: error ? CHECKS_FILE : "none",
284
+ source: error ? label : "none",
266
285
  root,
267
286
  checks: [],
268
287
  gates: {},
269
- error
288
+ error,
289
+ ...note
270
290
  });
271
291
  let text = null;
272
292
  try {
273
293
  text = fs.readFileSync(file, "utf8");
274
294
  } catch (error) {
275
295
  if (error.code !== "ENOENT")
276
- return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
296
+ return none(`cannot read ${label}: ${error.message}`);
277
297
  }
278
298
  if (text === null) {
279
299
  const checks = detectedChecks(root);
@@ -283,33 +303,33 @@ function loadCheckConfig(cwd) {
283
303
  try {
284
304
  parsed = JSON.parse(text);
285
305
  } catch {
286
- return none(`${CHECKS_FILE} is not valid JSON`);
306
+ return none(`${label} is not valid JSON`);
287
307
  }
288
308
  if (!isRecord(parsed) || !isRecord(parsed.checks))
289
- return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
309
+ return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
290
310
  const checks = [];
291
311
  for (const [name, value] of Object.entries(parsed.checks)) {
292
312
  if (!CHECK_NAME.test(name))
293
- return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
313
+ return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
294
314
  const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
295
315
  if (argv instanceof Error)
296
- return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
316
+ return none(`${label}: check "${name}": ${argv.message}`);
297
317
  checks.push({ name, argv, accepts: [argv] });
298
318
  }
299
319
  const gates = {};
300
320
  if (parsed.gates !== undefined) {
301
321
  if (!isRecord(parsed.gates))
302
- return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
322
+ return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
303
323
  for (const [gate, value] of Object.entries(parsed.gates)) {
304
324
  if (!GATES.includes(gate))
305
- return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
325
+ return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
306
326
  const names = typeof value === "string" ? [value] : value;
307
327
  if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
308
- return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
328
+ return none(`${label}: gates.${gate} must name configured checks`);
309
329
  gates[gate] = names;
310
330
  }
311
331
  }
312
- return { source: CHECKS_FILE, root, checks, gates, error: null, resources: parsed.resources };
332
+ return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
313
333
  }
314
334
  function matchesNamedCheck(config, name, argv, cwd) {
315
335
  if (config.error || name === null || cwd !== ".")
@@ -344,7 +364,7 @@ function workitAvailable(env = process.env) {
344
364
  function checkCommand(args, version, onPath = workitAvailable()) {
345
365
  return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
346
366
  }
347
- var SCRIPT_CHECKS, CHECKS_FILE = "workit.checks.json", checksConfigFile = (root) => path.join(root, CHECKS_FILE), isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git = (cwd, args) => {
367
+ var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git = (cwd, args) => {
348
368
  const run = spawnSync("git", args, {
349
369
  cwd,
350
370
  encoding: "utf8",
@@ -15647,6 +15667,10 @@ function checkSupersede(context, type, row = {}) {
15647
15667
  return problem ? err("invalid_input", `--supersedes: ${problem}`) : { ok: true, value: undefined };
15648
15668
  }
15649
15669
  function recordDecision(context, input) {
15670
+ if (input.by !== undefined && input.by !== "decider")
15671
+ return err("invalid_input", `--by takes "decider", got "${input.by}"`);
15672
+ if (input.by === undefined && input.views?.length)
15673
+ return err("invalid_input", "--view needs --by decider");
15650
15674
  const what = required2(input.what, "<what>");
15651
15675
  if (!what.ok)
15652
15676
  return what;
@@ -15661,7 +15685,9 @@ function recordDecision(context, input) {
15661
15685
  type: "decision",
15662
15686
  what: what.value,
15663
15687
  why: why.value,
15664
- refs: input.refs ?? []
15688
+ refs: input.refs ?? [],
15689
+ ...input.by === "decider" ? { by: "decider" } : {},
15690
+ ...input.views?.length ? { views: input.views } : {}
15665
15691
  }, { now: context.now });
15666
15692
  }
15667
15693
  function recordRuling(context, input) {
@@ -15828,15 +15854,19 @@ function rowLabels(row) {
15828
15854
  labels.push("ignored");
15829
15855
  if (row.type === "verdict" && row.self === true)
15830
15856
  labels.push("self");
15857
+ if (row.type === "decision" && row.by === "decider")
15858
+ labels.push("decider");
15831
15859
  return labels;
15832
15860
  }
15833
15861
  function summarizeRow(row) {
15834
15862
  const text = (key) => str2(row[key]) ?? "";
15835
15863
  let summary;
15836
15864
  switch (row.type) {
15837
- case "decision":
15838
- summary = `${text("what")} (why: ${text("why")})`;
15865
+ case "decision": {
15866
+ const views = Array.isArray(row.views) ? row.views.map(String) : [];
15867
+ summary = `${row.by === "decider" ? "[decider] " : ""}${text("what")} (why: ${text("why")}${views.length ? `; views: ${views.join(" | ")}` : ""})`;
15839
15868
  break;
15869
+ }
15840
15870
  case "ruling":
15841
15871
  summary = `${text("what")} (why: ${text("why")}; cost if wrong: ${text("costIfWrong")})`;
15842
15872
  break;
@@ -16445,7 +16475,7 @@ var RULES, derived2 = (spec) => ({
16445
16475
  };
16446
16476
  const since = Date.parse(task.createdAt);
16447
16477
  const branch = ledger.branch();
16448
- const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
16478
+ const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
16449
16479
  return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
16450
16480
  met: false,
16451
16481
  reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
@@ -16680,7 +16710,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
16680
16710
  return {
16681
16711
  satisfied: [],
16682
16712
  considered,
16683
- reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE} (committed, so it shows in the diff), e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
16713
+ reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}, e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
16684
16714
  };
16685
16715
  const satisfied = fresh.filter(named);
16686
16716
  if (satisfied.length)
@@ -66219,6 +66249,7 @@ import {
66219
66249
  readdirSync as readdirSync8,
66220
66250
  readFileSync as readFileSync14,
66221
66251
  readlinkSync as readlinkSync2,
66252
+ renameSync as renameSync4,
66222
66253
  rmSync as rmSync3,
66223
66254
  statSync as statSync5,
66224
66255
  unlinkSync as unlinkSync3,
@@ -66257,6 +66288,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
66257
66288
  cursorMcp: options.cursorMcp ?? path34.join(home, ".cursor", "mcp.json"),
66258
66289
  cursorPluginDir: options.cursorPluginDir ?? path34.join(home, ".cursor", "plugins", "local", "workit"),
66259
66290
  cursorHeartbeatDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
66291
+ hookStateDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
66260
66292
  codexHome: options.codexHome,
66261
66293
  piAgentDir: options.piAgentDir,
66262
66294
  env,
@@ -67951,6 +67983,79 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
67951
67983
  detail: `heavy-check scopes do not apply their limits: ${probe.reason}${delegate}`,
67952
67984
  fix: SCOPE_DELEGATE_FIX
67953
67985
  };
67986
+ }, DAY_MS, HOOK_SKIPS_FILE = "hook-skips.jsonl", countHookSkips = (stateDir, since) => {
67987
+ const counts = { skip: 0, late: 0 };
67988
+ let text = "";
67989
+ try {
67990
+ text = readFileSync14(path34.join(stateDir, HOOK_SKIPS_FILE), "utf8");
67991
+ } catch {
67992
+ return counts;
67993
+ }
67994
+ for (const line of text.split(`
67995
+ `)) {
67996
+ try {
67997
+ const row = JSON.parse(line);
67998
+ if (!(Date.parse(String(row.at)) >= since))
67999
+ continue;
68000
+ if (row.outcome === "skip")
68001
+ counts.skip += 1;
68002
+ else if (row.outcome === "late")
68003
+ counts.late += 1;
68004
+ } catch {}
68005
+ }
68006
+ return counts;
68007
+ }, checkHookDeadline = (res) => {
68008
+ const file = path34.join(res.hookStateDir, HOOK_SKIPS_FILE);
68009
+ const { skip, late } = countHookSkips(res.hookStateDir, Date.now() - DAY_MS);
68010
+ const lateText = late > 0 ? `; ${late} answered past it in full` : "";
68011
+ if (skip > 0)
68012
+ return {
68013
+ id: "hook_deadline",
68014
+ status: "warn",
68015
+ detail: `${skip} hook${skip === 1 ? "" : "s"} skipped past the hook deadline in the last day (host under load: Workit context, gates and nudges were not applied)${lateText} (${file})`,
68016
+ fix: "Lower the machine load: run heavy commands through `workit check` (the lease serializes them) and fewer parallel agents; WORKIT_HOOK_DEADLINE_MS raises the deadline"
68017
+ };
68018
+ if (late > 0)
68019
+ return {
68020
+ id: "hook_deadline",
68021
+ status: "info",
68022
+ detail: `no hook skipped in the last day${lateText} (${file})`
68023
+ };
68024
+ return {
68025
+ id: "hook_deadline",
68026
+ status: "pass",
68027
+ detail: "no hook passed its deadline in the last day"
68028
+ };
68029
+ }, checkChecksConfig = (res) => {
68030
+ const root = checkRoot(res.cwd);
68031
+ const current = existsSync14(path34.join(root, CHECKS_FILE));
68032
+ const legacy = existsSync14(path34.join(root, LEGACY_CHECKS_FILE));
68033
+ if (!legacy)
68034
+ return {
68035
+ id: "checks_config",
68036
+ status: "pass",
68037
+ detail: current ? `${CHECKS_FILE} is the checks config` : "no per-repo checks config at the root"
68038
+ };
68039
+ return {
68040
+ id: "checks_config",
68041
+ status: "warn",
68042
+ detail: current ? `${LEGACY_CHECKS_FILE} at the repo root is ignored: ${CHECKS_FILE} wins` : `${LEGACY_CHECKS_FILE} at the repo root is a legacy location, still read`,
68043
+ fix: current ? `delete ${LEGACY_CHECKS_FILE} once ${CHECKS_FILE} has what you need` : "workit doctor --fix (moves it to .workit/checks.json)"
68044
+ };
68045
+ }, fixChecksConfig = (options = {}) => {
68046
+ const root = checkRoot(resolve(options).cwd);
68047
+ const from = path34.join(root, LEGACY_CHECKS_FILE);
68048
+ const to = path34.join(root, CHECKS_FILE);
68049
+ if (!existsSync14(from))
68050
+ return { action: "none", detail: `no ${LEGACY_CHECKS_FILE} at ${root}` };
68051
+ if (existsSync14(to))
68052
+ return {
68053
+ action: "skipped",
68054
+ detail: `${CHECKS_FILE} already exists; ${LEGACY_CHECKS_FILE} left in place`
68055
+ };
68056
+ ensureWorkitDir(root);
68057
+ renameSync4(from, to);
68058
+ return { action: "moved", detail: `${LEGACY_CHECKS_FILE} -> ${CHECKS_FILE}` };
67954
68059
  }, CLAUDE_PLUGIN_PACKAGE = "@brainervirus/workit-claude-code", versionBehind = (version, latest) => version !== latest && semverAtLeast(latest, version), checkClaudePlugin = (res) => {
67955
68060
  const installs = claudeWorkitInstalls(res.home, res.env, res.cwd);
67956
68061
  if (installs.length === 0)
@@ -68137,6 +68242,7 @@ var init_doctor = __esm(() => {
68137
68242
  init_branch_policy();
68138
68243
  init_config();
68139
68244
  init_logger();
68245
+ init_check_config();
68140
68246
  init_heavy_scope();
68141
68247
  init_package_root();
68142
68248
  init_registration();
@@ -68148,6 +68254,7 @@ var init_doctor = __esm(() => {
68148
68254
  AGENT_MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
68149
68255
  canonicalCursorHook = cursorHooksEntry().command;
68150
68256
  SILENT_HOOK_GRACE_MS = 2 * 60000;
68257
+ DAY_MS = 24 * 60 * 60 * 1000;
68151
68258
  SEMVER = /\d+\.\d+\.\d+/;
68152
68259
  workitVersions = new Map;
68153
68260
  RUN_CHECKS = [
@@ -68176,7 +68283,9 @@ var init_doctor = __esm(() => {
68176
68283
  checkGithubIdentity,
68177
68284
  checkGitLabIdentity,
68178
68285
  checkLogWritable,
68179
- checkHeavyScope
68286
+ checkHeavyScope,
68287
+ checkHookDeadline,
68288
+ checkChecksConfig
68180
68289
  ];
68181
68290
  INSTALLER_REQUIRED = new Set([
68182
68291
  "runtime",
@@ -68201,7 +68310,7 @@ import {
68201
68310
  readFileSync as readFileSync15,
68202
68311
  readdirSync as readdirSync9,
68203
68312
  realpathSync as realpathSync7,
68204
- renameSync as renameSync4,
68313
+ renameSync as renameSync5,
68205
68314
  rmSync as rmSync4,
68206
68315
  statSync as statSync6,
68207
68316
  writeFileSync as writeFileSync8
@@ -68490,7 +68599,7 @@ function applyMutation(m) {
68490
68599
  detail: "workspace config changed while applying the preview; rebuild and review it"
68491
68600
  };
68492
68601
  }
68493
- renameSync4(tmp, m.path);
68602
+ renameSync5(tmp, m.path);
68494
68603
  } catch (error) {
68495
68604
  rmSync4(tmp, { force: true });
68496
68605
  return {
@@ -68683,12 +68792,12 @@ function copyPluginDir(src, dest) {
68683
68792
  throw new Error("staged adapter resolved to a symlink — refusing fragile cache install");
68684
68793
  }
68685
68794
  if (hadDir)
68686
- renameSync4(dest, backup);
68795
+ renameSync5(dest, backup);
68687
68796
  try {
68688
- renameSync4(stage, dest);
68797
+ renameSync5(stage, dest);
68689
68798
  } catch (error) {
68690
68799
  if (hadDir && !existsSync15(dest))
68691
- renameSync4(backup, dest);
68800
+ renameSync5(backup, dest);
68692
68801
  throw error;
68693
68802
  }
68694
68803
  rmSync4(backup, { recursive: true, force: true });
@@ -69211,7 +69320,7 @@ var init_setup = __esm(() => {
69211
69320
  });
69212
69321
 
69213
69322
  // packages/workit-core/src/core/workspace-branch-policy.ts
69214
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
69323
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
69215
69324
  import path36 from "node:path";
69216
69325
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
69217
69326
  function applyWorkspaceBranchPolicy(opts) {
@@ -69289,7 +69398,7 @@ function applyWorkspaceBranchPolicy(opts) {
69289
69398
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
69290
69399
  };
69291
69400
  }
69292
- renameSync5(tmp, wsPath);
69401
+ renameSync6(tmp, wsPath);
69293
69402
  } catch (error) {
69294
69403
  try {
69295
69404
  rmSync5(tmp, { force: true });
@@ -73890,6 +73999,17 @@ async function run6(argv, io) {
73890
73999
  };
73891
74000
  }
73892
74001
  }
74002
+ let checksConfig = null;
74003
+ if (argv.includes("--fix")) {
74004
+ try {
74005
+ checksConfig = fixChecksConfig({ host: "cli", cwd: io.cwd });
74006
+ } catch (error) {
74007
+ checksConfig = {
74008
+ action: "failed",
74009
+ detail: error instanceof Error ? error.message : String(error)
74010
+ };
74011
+ }
74012
+ }
73893
74013
  const report = runDoctor({
73894
74014
  host: "cli",
73895
74015
  cwd: io.cwd,
@@ -73912,7 +74032,7 @@ async function run6(argv, io) {
73912
74032
  budget: knowledge.budget,
73913
74033
  findings: knowledge.findings.length
73914
74034
  } : { unavailable: true };
73915
- io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, ...sessionHook ? { sessionHook } : {}, knowledge: summary }, null, 2)}
74035
+ io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, ...sessionHook ? { sessionHook } : {}, ...checksConfig ? { checksConfig } : {}, knowledge: summary }, null, 2)}
73916
74036
  `);
73917
74037
  return report.exitCode;
73918
74038
  }
@@ -73923,6 +74043,9 @@ async function run6(argv, io) {
73923
74043
  }
73924
74044
  if (sessionHook)
73925
74045
  io.stdout(`fix: session hook ${sessionHook.action}: ${sessionHook.detail}
74046
+ `);
74047
+ if (checksConfig && checksConfig.action !== "none")
74048
+ io.stdout(`fix: checks config ${checksConfig.action}: ${checksConfig.detail}
73926
74049
  `);
73927
74050
  io.stdout(`workit doctor — ${report.ok ? "healthy" : "problems found"} (${report.offline ? "offline" : "online"})
73928
74051
  `);
@@ -75778,6 +75901,9 @@ async function run10(argv, io) {
75778
75901
  if (options instanceof Error)
75779
75902
  return emit(io, fail("invalid_input", options.message, { unblock: USAGE3 }));
75780
75903
  const config = loadCheckConfig(io.cwd);
75904
+ if (config.note)
75905
+ io.stderr(`note: ${config.note}
75906
+ `);
75781
75907
  const top = checkRoot(io.cwd);
75782
75908
  let command;
75783
75909
  let runCwd = io.cwd;
@@ -75792,7 +75918,7 @@ async function run10(argv, io) {
75792
75918
  if (!check)
75793
75919
  return emit(io, fail("not_found", `no configured check named "${name}"`, {
75794
75920
  data: { configured: config.checks.map((item) => item.name), source: config.source },
75795
- unblock: config.checks.length ? `workit check ${config.checks.map((item) => item.name).join("|")}` : `add {"checks":{"${name}":"<command>"}} to workit.checks.json, or run workit check --name ${name} -- <cmd…>`
75921
+ unblock: config.checks.length ? `workit check ${config.checks.map((item) => item.name).join("|")}` : `add {"checks":{"${name}":"<command>"}} to .workit/checks.json, or run workit check --name ${name} -- <cmd…>`
75796
75922
  }));
75797
75923
  command = check.argv;
75798
75924
  runCwd = config.root;
@@ -84749,7 +84875,8 @@ var DEFAULT_TIMEBOX = "30 minutes", REPORT, identityOf = (host) => {
84749
84875
  ' 2. Decide ambiguities yourself: `workit ledger ruling "<what>" --why "<why>" --cost-if-wrong "<cost>"`; stop only for an irreversible, security-sensitive or out-of-worktree action.',
84750
84876
  plain ? ' 3. Commit with plain `git commit -m <msg> --trailer "Workit-Session: <the WORKIT_SESSION_ID in your env>" --trailer "<your host attribution trailer>" -- <paths in SCOPE>`; push only if this brief says so.' : " 3. Commit with `workit git commit -m <msg> -- <paths in SCOPE>`; push only if this brief says so.",
84751
84877
  " 4. Never record a verdict on your own work.",
84752
- " 5. Run tests, lint, typecheck and builds only through `workit check` (a named check, or `workit check --heavy -- <cmd>`): one machine-wide lease queues every agent, so never a hand-made flock; `workit lock status` shows who holds it."
84878
+ " 5. Run tests, lint, typecheck and builds only through `workit check` (a named check, or `workit check --heavy -- <cmd>`): one machine-wide lease queues every agent, so never a hand-made flock; `workit lock status` shows who holds it.",
84879
+ " 6. A failed review gets one fix round and one recheck. Still failing: stop and report the open finding, never a third round (the lead spawns a decider). A product choice is the user's: report it, do not decide it."
84753
84880
  ];
84754
84881
  }, sessionSafe = (text) => text.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), SESSION_MAX = 128;
84755
84882
  var init_fanout_brief = __esm(() => {
@@ -85843,7 +85970,13 @@ async function run19(argv, io) {
85843
85970
  return result;
85844
85971
  };
85845
85972
  if (sub === "decision")
85846
- return fromResult(io, await recorded(recordDecision(context, { what: text, why: values.why, refs: values.ref })), (row) => `recorded decision ${row.id}`);
85973
+ return fromResult(io, await recorded(recordDecision(context, {
85974
+ what: text,
85975
+ why: values.why,
85976
+ refs: values.ref,
85977
+ by: values.by,
85978
+ views: values.view
85979
+ })), (row) => `recorded decision ${row.id}`);
85847
85980
  if (sub === "ruling")
85848
85981
  return fromResult(io, await recorded(recordRuling(context, {
85849
85982
  what: text,
@@ -86016,6 +86149,8 @@ var init_ledger2 = __esm(() => {
86016
86149
  why: { type: "string" },
86017
86150
  "cost-if-wrong": { type: "string" },
86018
86151
  ref: { type: "string", multiple: true },
86152
+ by: { type: "string" },
86153
+ view: { type: "string", multiple: true },
86019
86154
  how: { type: "string" },
86020
86155
  kind: { type: "string" },
86021
86156
  surface: { type: "string" },
@@ -102036,7 +102171,7 @@ import {
102036
102171
  readFileSync as readFileSync24,
102037
102172
  readlinkSync as readlinkSync3,
102038
102173
  realpathSync as realpathSync9,
102039
- renameSync as renameSync6,
102174
+ renameSync as renameSync7,
102040
102175
  rmSync as rmSync8,
102041
102176
  statSync as statSync9,
102042
102177
  symlinkSync,
@@ -102213,7 +102348,7 @@ function writeInside(copy, file, content) {
102213
102348
  }
102214
102349
  const temp = `${target}.workit-mutant-${process.pid}`;
102215
102350
  writeFileSync11(temp, content);
102216
- renameSync6(temp, target);
102351
+ renameSync7(temp, target);
102217
102352
  return true;
102218
102353
  }
102219
102354
  function copyWorkingTree(repo) {
@@ -102642,7 +102777,7 @@ usage: ${USAGE12}`));
102642
102777
  const runner = testCommand(repo, io.cwd, options.testCmd);
102643
102778
  if (!runner)
102644
102779
  return emit(io, fail("unavailable", "no test command for --mutate", {
102645
- unblock: 'workit test-audit --mutate --test-cmd "<runner> {files}" (or configure a `test` check in workit.checks.json)'
102780
+ unblock: 'workit test-audit --mutate --test-cmd "<runner> {files}" (or configure a `test` check in .workit/checks.json)'
102646
102781
  }));
102647
102782
  try {
102648
102783
  mutation = await runMutation({
@@ -103540,8 +103675,8 @@ var VERBS = [
103540
103675
  subcommands: [
103541
103676
  {
103542
103677
  name: "decision",
103543
- usage: `workit ledger decision "<what>" --why "<why>" [--ref <path|url>]… ${LEDGER_ACTOR}`,
103544
- summary: "Record a decision and why"
103678
+ usage: `workit ledger decision "<what>" --why "<why>" [--by decider [--view "<view>"]…] [--ref <path|url>]… ${LEDGER_ACTOR}`,
103679
+ summary: "Record a decision and why; --by decider marks a tie-breaker agent's call (not the user's answer, not a verdict)"
103545
103680
  },
103546
103681
  {
103547
103682
  name: "ruling",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-cli",
3
- "version": "8.17.0",
3
+ "version": "8.18.0",
4
4
  "private": false,
5
5
  "description": "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
6
6
  "keywords": [
@@ -38,13 +38,13 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@babel/parser": "7.29.9",
41
- "@brainervirus/workit-cursor": "^8.17.0",
41
+ "@brainervirus/workit-cursor": "^8.18.0",
42
42
  "@inkjs/ui": "2.0.0",
43
43
  "ink": "7.1.1",
44
44
  "react": "19.2.8"
45
45
  },
46
46
  "devDependencies": {
47
- "@brainervirus/workit-core": "^8.17.0"
47
+ "@brainervirus/workit-core": "^8.18.0"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=24"