@brainervirus/workit-cursor 8.17.1 → 8.19.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.
@@ -8574,7 +8574,7 @@ function pushRemoteName(cwd, branch) {
8574
8574
 
8575
8575
  // packages/workit-core/src/ledger.ts
8576
8576
  import { spawnSync as spawnSync8 } from "node:child_process";
8577
- import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
8577
+ import { createHash as createHash8, randomBytes as randomBytes2 } from "node:crypto";
8578
8578
  import fs6 from "node:fs";
8579
8579
  import path8 from "node:path";
8580
8580
 
@@ -10322,10 +10322,227 @@ function vcsConfig(mode, cwd, options = {}) {
10322
10322
  }
10323
10323
 
10324
10324
  // packages/workit-core/src/host-session.ts
10325
- import { createHash as createHash6, randomBytes, timingSafeEqual } from "node:crypto";
10325
+ import { createHash as createHash7, randomBytes, timingSafeEqual } from "node:crypto";
10326
10326
  import fs5 from "node:fs";
10327
10327
  import path7 from "node:path";
10328
10328
 
10329
+ // packages/workit-core/src/core/branch.ts
10330
+ import { createHash as createHash6 } from "node:crypto";
10331
+ var withReleaseTracks = (workspaceRoot, policy) => {
10332
+ const read = workspaceReleaseTracks(workspaceRoot);
10333
+ if (read.error)
10334
+ throw new ReleaseTrackError(read.error);
10335
+ if (read.tracks.length === 0 && read.protectedBranches.length === 0)
10336
+ return policy;
10337
+ const known = new Set(policy.allowed.map((pattern) => pattern.source));
10338
+ const allowed = [...policy.allowed];
10339
+ for (const glob of read.tracks.flatMap((track) => Object.values(track.naming).map(namingGlob))) {
10340
+ const pattern = branchGlobPattern(glob);
10341
+ if (!known.has(pattern.source)) {
10342
+ known.add(pattern.source);
10343
+ allowed.push(pattern);
10344
+ }
10345
+ }
10346
+ return {
10347
+ ...policy,
10348
+ allowed,
10349
+ protected: new Set([
10350
+ ...policy.protected,
10351
+ ...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
10352
+ ])
10353
+ };
10354
+ };
10355
+ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
10356
+ const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
10357
+ const config = readConfig();
10358
+ return kind === "branch" ? {
10359
+ branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(workspaceRoot))),
10360
+ provenance: { branchPolicy: selected.source }
10361
+ } : {
10362
+ commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
10363
+ provenance: { commitPolicy: selected.source }
10364
+ };
10365
+ }
10366
+ var KIND_SYNONYMS = {
10367
+ fix: "bugfix",
10368
+ fixes: "bugfix",
10369
+ bug: "bugfix",
10370
+ bugs: "bugfix",
10371
+ bugfixes: "bugfix",
10372
+ feat: "feature",
10373
+ features: "feature",
10374
+ chore: "feature",
10375
+ docs: "feature",
10376
+ doc: "feature",
10377
+ refactor: "feature",
10378
+ test: "feature",
10379
+ tests: "feature",
10380
+ ci: "feature",
10381
+ build: "feature",
10382
+ perf: "feature",
10383
+ style: "feature",
10384
+ deps: "feature",
10385
+ task: "feature",
10386
+ enhancement: "feature",
10387
+ hot: "hotfix",
10388
+ hotfixes: "hotfix",
10389
+ releases: "release",
10390
+ rel: "release"
10391
+ };
10392
+ var allowedKinds = (allowed) => [
10393
+ ...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
10394
+ ];
10395
+ var editDistance = (a, b) => {
10396
+ const row = Array.from({ length: b.length + 1 }, (_, index) => index);
10397
+ for (let i = 1;i <= a.length; i++) {
10398
+ let previous = row[0];
10399
+ row[0] = i;
10400
+ for (let j = 1;j <= b.length; j++) {
10401
+ const saved = row[j];
10402
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
10403
+ previous = saved;
10404
+ }
10405
+ }
10406
+ return row[b.length];
10407
+ };
10408
+ var suggestBranchName = (name, kinds) => {
10409
+ if (!kinds.length)
10410
+ return null;
10411
+ const slash = name.indexOf("/");
10412
+ const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
10413
+ const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
10414
+ const synonym = KIND_SYNONYMS[prefix];
10415
+ const near = kinds.map((kind) => ({ kind, distance: editDistance(prefix, kind) })).filter((entry) => prefix && entry.distance <= 2).toSorted((a, b) => a.distance - b.distance)[0]?.kind;
10416
+ const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
10417
+ return `${kind}/${rest}`;
10418
+ };
10419
+ var kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", ");
10420
+ var aliasNote = (kinds) => {
10421
+ const notes = [
10422
+ ["fix", "bugfix"],
10423
+ ["chore", "feature"]
10424
+ ].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
10425
+ return notes.length ? ` (${notes.join(", ")})` : "";
10426
+ };
10427
+ var validateBranchNamePolicy = (name, policy, source) => {
10428
+ const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
10429
+ const kinds = allowedKinds(policy.allowed);
10430
+ if (policy.protected.has(name.toLowerCase())) {
10431
+ const correction = kinds.length ? `choose a non-protected branch of an allowed kind (${kindList(kinds)}), e.g. ${kinds[0]}/<slug>: workit git branch --kind ${kinds[0]} --slug <slug>` : `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
10432
+ return {
10433
+ ok: false,
10434
+ rule: "protected_ref",
10435
+ source,
10436
+ attempted: name,
10437
+ correction,
10438
+ error: `protected_ref: branch ${JSON.stringify(name)} is protected by ${source}; ${correction}`
10439
+ };
10440
+ }
10441
+ if (!policy.allowed.some((pattern) => pattern.test(name))) {
10442
+ const suggestion = suggestBranchName(name, kinds);
10443
+ const correction = suggestion && policy.allowed.some((pattern) => pattern.test(suggestion)) ? `use ${JSON.stringify(suggestion)}; the allowed kinds are ${kindList(kinds)} (workit git branch ${suggestion})` : `choose a branch matching one of: ${allowed.join(", ")}`;
10444
+ return {
10445
+ ok: false,
10446
+ rule: "allowed_pattern",
10447
+ source,
10448
+ attempted: name,
10449
+ correction,
10450
+ error: `allowed_pattern: branch ${JSON.stringify(name)} violates ${source} policy; ${correction}`
10451
+ };
10452
+ }
10453
+ return { ok: true };
10454
+ };
10455
+ var branchPolicySnapshotFor = (workspaceRoot) => {
10456
+ const resolved = effectiveWorkspacePolicy(workspaceRoot, "branch");
10457
+ const branchPolicy = resolved.branchPolicy;
10458
+ const source = resolved.provenance.branchPolicy;
10459
+ const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
10460
+ const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
10461
+ const fingerprint = createHash6("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
10462
+ return {
10463
+ branchPolicy,
10464
+ namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
10465
+ source,
10466
+ fingerprint
10467
+ };
10468
+ };
10469
+ var validateBranchNameFor = (workspaceRoot, name) => {
10470
+ try {
10471
+ const resolved = branchPolicySnapshotFor(workspaceRoot);
10472
+ return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
10473
+ } catch (error) {
10474
+ const correction = "repair or select a valid workspace policy before creating a branch";
10475
+ return {
10476
+ ok: false,
10477
+ rule: "policy_unavailable",
10478
+ source: "workspaces.json",
10479
+ attempted: name,
10480
+ correction,
10481
+ error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
10482
+ };
10483
+ }
10484
+ };
10485
+ var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
10486
+ var gitFlowOf = (root, integration) => {
10487
+ try {
10488
+ if (resolveBranchPolicyFor(root).preset !== "gitflow")
10489
+ return null;
10490
+ const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
10491
+ const names = branchNames(root);
10492
+ const production = productionBranchOf(names);
10493
+ const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
10494
+ return { develop, production, developExists: names.has(develop) };
10495
+ } catch {
10496
+ return null;
10497
+ }
10498
+ };
10499
+ var releaseBranchesOf = (cwd) => {
10500
+ try {
10501
+ const resolved = resolveBranchPolicyFor(cwd);
10502
+ const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
10503
+ return {
10504
+ preset: resolved.preset,
10505
+ branches: flow ? [flow.production, flow.develop] : [],
10506
+ production: flow ? flow.production : resolved.defaultTargetBranch
10507
+ };
10508
+ } catch {
10509
+ return { preset: "unknown", branches: [], production: null };
10510
+ }
10511
+ };
10512
+ var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
10513
+ var branchGuidance = (cwd) => {
10514
+ try {
10515
+ const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
10516
+ const kinds = allowedKinds(patterns);
10517
+ const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
10518
+ const anyName = patterns.some((pattern) => pattern.source === "^.*$");
10519
+ const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
10520
+ const allowed = anyName ? "Any non-protected branch name is allowed here." : `${kinds.length ? `Allowed branch kinds here: ${kindList(kinds)}${aliasNote(kinds)}` : "Allowed branch names here:"}${others.length ? `${kinds.length ? "; also" : ""} names matching ${others.join(", ")}` : ""}; anything else is denied.`;
10521
+ const resolved = vcsConfig("resolve", cwd);
10522
+ const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
10523
+ const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
10524
+ if (flow?.developExists)
10525
+ return `${allowed} git-flow: feature/, bugfix/ and release/ start from origin/${flow.develop}, hotfix/ from origin/${flow.production}; feature/ and bugfix/ land on ${flow.develop}, release/ and hotfix/ on ${flow.production}.`;
10526
+ const names = branchNames(cwd);
10527
+ const configured = String(resolved.baseBranch ?? integration ?? "") || null;
10528
+ const trunk = productionBranchOf(names);
10529
+ const base = configured && names.has(configured) ? `Branch from origin/${configured}.` : names.has(trunk) ? `Branch from origin/${trunk}${configured ? ` (the default target ${configured} does not exist here)` : ""}.` : "No base branch exists here yet; pass the base explicitly.";
10530
+ return `${allowed} ${base}`;
10531
+ } catch {
10532
+ return null;
10533
+ }
10534
+ };
10535
+ var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
10536
+ var policy = (root) => resolveBranchPolicyFor(root);
10537
+ var isProtected = (root, name) => policy(root).protected.has(name.toLowerCase());
10538
+ var isProtectedTarget = (root, name) => {
10539
+ try {
10540
+ return isProtected(root, name);
10541
+ } catch {
10542
+ return true;
10543
+ }
10544
+ };
10545
+
10329
10546
  // packages/workit-core/src/core/logger.ts
10330
10547
  import os3 from "node:os";
10331
10548
  import path6 from "node:path";
@@ -10415,7 +10632,7 @@ function hostSessionFromEnv(env) {
10415
10632
  }
10416
10633
  var ISSUED_PER_LEAD = 64;
10417
10634
  var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3";
10418
- var hash = (value) => createHash6("sha256").update(value).digest("hex").slice(0, 32);
10635
+ var hash = (value) => createHash7("sha256").update(value).digest("hex").slice(0, 32);
10419
10636
  var userIssuedRoot = () => path7.join(resolveStateDir(), "issued");
10420
10637
  var userTokenRoot = () => path7.join(resolveStateDir(), "issued-tokens");
10421
10638
  function migrateRepoIssued(cwd) {
@@ -10465,7 +10682,7 @@ var removeIfEmpty = (dir) => {
10465
10682
  fs5.rmdirSync(dir);
10466
10683
  } catch {}
10467
10684
  };
10468
- var tokenHash = (token) => createHash6("sha256").update(token).digest("hex");
10685
+ var tokenHash = (token) => createHash7("sha256").update(token).digest("hex");
10469
10686
  function recordIssuedSession(cwd, lead, issued) {
10470
10687
  const token = randomBytes(18).toString("hex");
10471
10688
  try {
@@ -10485,6 +10702,21 @@ function recordIssuedSession(cwd, lead, issued) {
10485
10702
  return null;
10486
10703
  }
10487
10704
  }
10705
+ function judgeBranch(cwd) {
10706
+ const branch = currentBranch(cwd);
10707
+ return branch && !isProtectedTarget(cwd, branch) ? branch : null;
10708
+ }
10709
+ function verdictCommand(options) {
10710
+ const kind = options.judge === "verifier" ? "<live|unit>" : options.judge === "reviewer" ? "review" : "<live|unit|review>";
10711
+ return [
10712
+ "workit ledger verdict <verified|failed|blocked>",
10713
+ `--kind ${kind}`,
10714
+ `--branch ${options.branch ?? "<branch>"}`,
10715
+ options.session ? `--session ${options.session}` : null,
10716
+ options.token ? `--token=${options.token}` : null,
10717
+ '--how "<what you ran and saw>"'
10718
+ ].filter(Boolean).join(" ");
10719
+ }
10488
10720
  function pruneIssued(dir, own, tokens) {
10489
10721
  try {
10490
10722
  const entries = fs5.readdirSync(dir);
@@ -10642,7 +10874,7 @@ function diffHash(cwd, base, head) {
10642
10874
  ], GIT_TIMEOUTS.worktree);
10643
10875
  if (!diff.ok || !diff.stdout)
10644
10876
  return null;
10645
- const hash = createHash7("sha256");
10877
+ const hash = createHash8("sha256");
10646
10878
  for (const line of diff.stdout.split(`
10647
10879
  `)) {
10648
10880
  if (line.startsWith("index "))
@@ -10777,7 +11009,7 @@ function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
10777
11009
  ledgerLock.release(lock, token);
10778
11010
  }
10779
11011
  }
10780
- var sha2562 = (text) => createHash7("sha256").update(text).digest("hex");
11012
+ var sha2562 = (text) => createHash8("sha256").update(text).digest("hex");
10781
11013
  var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
10782
11014
  ${body}`);
10783
11015
  var TAIL_BYTES = 64 * 1024;
@@ -11528,7 +11760,7 @@ var productDecision = (task, ledger) => {
11528
11760
  };
11529
11761
  const since = Date.parse(task.createdAt);
11530
11762
  const branch = ledger.branch();
11531
- const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
11763
+ const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
11532
11764
  return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
11533
11765
  met: false,
11534
11766
  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>"`)',
@@ -12288,7 +12520,7 @@ var suggested = (kind) => kind === "merge" ? "verified" : "true";
12288
12520
  var grantHint = (workspace, kind) => `ask the user to run, in their own terminal: workit grant set ${workspace ?? "<workspace>"} ${kind}=${suggested(kind)}`;
12289
12521
 
12290
12522
  // packages/workit-core/src/core/task-store.ts
12291
- import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
12523
+ import { createHash as createHash10, randomUUID as randomUUID3 } from "node:crypto";
12292
12524
  import * as fs11 from "node:fs";
12293
12525
  import path15 from "node:path";
12294
12526
 
@@ -13178,7 +13410,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
13178
13410
  });
13179
13411
 
13180
13412
  // packages/workit-core/src/store/reduce.ts
13181
- import { createHash as createHash8 } from "node:crypto";
13413
+ import { createHash as createHash9 } from "node:crypto";
13182
13414
  var OPENING_TYPES = new Set([
13183
13415
  "task.opened",
13184
13416
  "task.imported",
@@ -13207,7 +13439,7 @@ var isObject5 = (value) => typeof value === "object" && value !== null && !Array
13207
13439
  var isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
13208
13440
  var BLOB_REF = "$blob";
13209
13441
  var blobRef = (value) => isObject5(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
13210
- var blobDigest = (value) => createHash8("sha256").update(JSON.stringify(value)).digest("hex");
13442
+ var blobDigest = (value) => createHash9("sha256").update(JSON.stringify(value)).digest("hex");
13211
13443
  var toBlob = (value, blobs) => {
13212
13444
  if (!isObject5(value))
13213
13445
  return value;
@@ -14436,7 +14668,7 @@ class TaskStore {
14436
14668
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
14437
14669
  }
14438
14670
  withKeyLock(key, operation) {
14439
- const digest = createHash9("sha256").update(key).digest("hex").slice(0, 32);
14671
+ const digest = createHash10("sha256").update(key).digest("hex").slice(0, 32);
14440
14672
  return this.withLock(path15.join(this.paths().dir, "keys", `${digest}.lock`), operation);
14441
14673
  }
14442
14674
  bindLocked(taskId, key, timestamp) {
@@ -15061,7 +15293,7 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
15061
15293
  var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
15062
15294
 
15063
15295
  // packages/workit-core/src/core/task-evaluation.ts
15064
- import { createHash as createHash10 } from "node:crypto";
15296
+ import { createHash as createHash11 } from "node:crypto";
15065
15297
  import * as fs13 from "node:fs";
15066
15298
  import { spawnSync as spawnSync10 } from "node:child_process";
15067
15299
  import path17 from "node:path";
@@ -15071,8 +15303,13 @@ import { spawnSync as spawnSync9 } from "node:child_process";
15071
15303
  import fs12 from "node:fs";
15072
15304
  import path16 from "node:path";
15073
15305
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
15074
- var CHECKS_FILE = "workit.checks.json";
15075
- var checksConfigFile = (root) => path16.join(root, CHECKS_FILE);
15306
+ var CHECKS_FILE = ".workit/checks.json";
15307
+ var LEGACY_CHECKS_FILE = "workit.checks.json";
15308
+ function checksConfigFile(root) {
15309
+ const current = path16.join(root, CHECKS_FILE);
15310
+ const legacy = path16.join(root, LEGACY_CHECKS_FILE);
15311
+ return !fs12.existsSync(current) && fs12.existsSync(legacy) ? legacy : current;
15312
+ }
15076
15313
  function repoHeavyCommands(root) {
15077
15314
  try {
15078
15315
  const value = JSON.parse(fs12.readFileSync(checksConfigFile(root), "utf8"));
@@ -15196,19 +15433,25 @@ var GATES = ["testing", "verification"];
15196
15433
  function loadCheckConfig(cwd) {
15197
15434
  const root = checkRoot(cwd);
15198
15435
  const file = checksConfigFile(root);
15436
+ const legacy = path16.basename(file) === LEGACY_CHECKS_FILE;
15437
+ const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
15438
+ const note = legacy ? {
15439
+ note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
15440
+ } : {};
15199
15441
  const none = (error = null) => ({
15200
- source: error ? CHECKS_FILE : "none",
15442
+ source: error ? label : "none",
15201
15443
  root,
15202
15444
  checks: [],
15203
15445
  gates: {},
15204
- error
15446
+ error,
15447
+ ...note
15205
15448
  });
15206
15449
  let text = null;
15207
15450
  try {
15208
15451
  text = fs12.readFileSync(file, "utf8");
15209
15452
  } catch (error) {
15210
15453
  if (error.code !== "ENOENT")
15211
- return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
15454
+ return none(`cannot read ${label}: ${error.message}`);
15212
15455
  }
15213
15456
  if (text === null) {
15214
15457
  const checks = detectedChecks(root);
@@ -15218,33 +15461,33 @@ function loadCheckConfig(cwd) {
15218
15461
  try {
15219
15462
  parsed = JSON.parse(text);
15220
15463
  } catch {
15221
- return none(`${CHECKS_FILE} is not valid JSON`);
15464
+ return none(`${label} is not valid JSON`);
15222
15465
  }
15223
15466
  if (!isRecord3(parsed) || !isRecord3(parsed.checks))
15224
- return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
15467
+ return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
15225
15468
  const checks = [];
15226
15469
  for (const [name, value] of Object.entries(parsed.checks)) {
15227
15470
  if (!CHECK_NAME.test(name))
15228
- return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
15471
+ return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
15229
15472
  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");
15230
15473
  if (argv instanceof Error)
15231
- return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
15474
+ return none(`${label}: check "${name}": ${argv.message}`);
15232
15475
  checks.push({ name, argv, accepts: [argv] });
15233
15476
  }
15234
15477
  const gates = {};
15235
15478
  if (parsed.gates !== undefined) {
15236
15479
  if (!isRecord3(parsed.gates))
15237
- return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
15480
+ return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
15238
15481
  for (const [gate, value] of Object.entries(parsed.gates)) {
15239
15482
  if (!GATES.includes(gate))
15240
- return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
15483
+ return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
15241
15484
  const names = typeof value === "string" ? [value] : value;
15242
15485
  if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
15243
- return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
15486
+ return none(`${label}: gates.${gate} must name configured checks`);
15244
15487
  gates[gate] = names;
15245
15488
  }
15246
15489
  }
15247
- return { source: CHECKS_FILE, root, checks, gates, error: null, resources: parsed.resources };
15490
+ return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
15248
15491
  }
15249
15492
  var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
15250
15493
  function matchesNamedCheck(config, name, argv, cwd) {
@@ -15283,7 +15526,7 @@ function checkCommand(args, version, onPath = workitAvailable()) {
15283
15526
  }
15284
15527
 
15285
15528
  // packages/workit-core/src/core/task-evaluation.ts
15286
- var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
15529
+ var digestBytes = (value) => createHash11("sha256").update(value).digest("hex");
15287
15530
  var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
15288
15531
  var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
15289
15532
  var canonicalPath = (value) => {
@@ -15711,7 +15954,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
15711
15954
  return {
15712
15955
  satisfied: [],
15713
15956
  considered,
15714
- 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())}`
15957
+ 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())}`
15715
15958
  };
15716
15959
  const satisfied = fresh.filter(named);
15717
15960
  if (satisfied.length)
@@ -17932,7 +18175,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
17932
18175
  };
17933
18176
  });
17934
18177
  // packages/workit-core/src/hooks/context.ts
17935
- import { createHash as createHash11 } from "node:crypto";
18178
+ import { createHash as createHash12 } from "node:crypto";
17936
18179
  import fs14 from "node:fs";
17937
18180
  import path18 from "node:path";
17938
18181
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
@@ -18088,7 +18331,7 @@ var turnContextText = (input, descriptor) => {
18088
18331
  }
18089
18332
  };
18090
18333
  var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
18091
- var digestOf = (text) => createHash11("sha256").update(text).digest("hex");
18334
+ var digestOf = (text) => createHash12("sha256").update(text).digest("hex");
18092
18335
  var turnMarker = (input) => {
18093
18336
  if (!input.session.id)
18094
18337
  return null;
@@ -18167,214 +18410,6 @@ import fs16 from "node:fs";
18167
18410
  import os5 from "node:os";
18168
18411
  import path20 from "node:path";
18169
18412
 
18170
- // packages/workit-core/src/core/branch.ts
18171
- import { createHash as createHash12 } from "node:crypto";
18172
- var withReleaseTracks = (workspaceRoot, policy) => {
18173
- const read = workspaceReleaseTracks(workspaceRoot);
18174
- if (read.error)
18175
- throw new ReleaseTrackError(read.error);
18176
- if (read.tracks.length === 0 && read.protectedBranches.length === 0)
18177
- return policy;
18178
- const known = new Set(policy.allowed.map((pattern) => pattern.source));
18179
- const allowed = [...policy.allowed];
18180
- for (const glob of read.tracks.flatMap((track) => Object.values(track.naming).map(namingGlob))) {
18181
- const pattern = branchGlobPattern(glob);
18182
- if (!known.has(pattern.source)) {
18183
- known.add(pattern.source);
18184
- allowed.push(pattern);
18185
- }
18186
- }
18187
- return {
18188
- ...policy,
18189
- allowed,
18190
- protected: new Set([
18191
- ...policy.protected,
18192
- ...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
18193
- ])
18194
- };
18195
- };
18196
- function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
18197
- const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
18198
- const config = readConfig();
18199
- return kind === "branch" ? {
18200
- branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(workspaceRoot))),
18201
- provenance: { branchPolicy: selected.source }
18202
- } : {
18203
- commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
18204
- provenance: { commitPolicy: selected.source }
18205
- };
18206
- }
18207
- var KIND_SYNONYMS = {
18208
- fix: "bugfix",
18209
- fixes: "bugfix",
18210
- bug: "bugfix",
18211
- bugs: "bugfix",
18212
- bugfixes: "bugfix",
18213
- feat: "feature",
18214
- features: "feature",
18215
- chore: "feature",
18216
- docs: "feature",
18217
- doc: "feature",
18218
- refactor: "feature",
18219
- test: "feature",
18220
- tests: "feature",
18221
- ci: "feature",
18222
- build: "feature",
18223
- perf: "feature",
18224
- style: "feature",
18225
- deps: "feature",
18226
- task: "feature",
18227
- enhancement: "feature",
18228
- hot: "hotfix",
18229
- hotfixes: "hotfix",
18230
- releases: "release",
18231
- rel: "release"
18232
- };
18233
- var allowedKinds = (allowed) => [
18234
- ...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
18235
- ];
18236
- var editDistance = (a, b) => {
18237
- const row = Array.from({ length: b.length + 1 }, (_, index) => index);
18238
- for (let i = 1;i <= a.length; i++) {
18239
- let previous = row[0];
18240
- row[0] = i;
18241
- for (let j = 1;j <= b.length; j++) {
18242
- const saved = row[j];
18243
- row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
18244
- previous = saved;
18245
- }
18246
- }
18247
- return row[b.length];
18248
- };
18249
- var suggestBranchName = (name, kinds) => {
18250
- if (!kinds.length)
18251
- return null;
18252
- const slash = name.indexOf("/");
18253
- const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
18254
- const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
18255
- const synonym = KIND_SYNONYMS[prefix];
18256
- const near = kinds.map((kind) => ({ kind, distance: editDistance(prefix, kind) })).filter((entry) => prefix && entry.distance <= 2).toSorted((a, b) => a.distance - b.distance)[0]?.kind;
18257
- const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
18258
- return `${kind}/${rest}`;
18259
- };
18260
- var kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", ");
18261
- var aliasNote = (kinds) => {
18262
- const notes = [
18263
- ["fix", "bugfix"],
18264
- ["chore", "feature"]
18265
- ].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
18266
- return notes.length ? ` (${notes.join(", ")})` : "";
18267
- };
18268
- var validateBranchNamePolicy = (name, policy, source) => {
18269
- const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
18270
- const kinds = allowedKinds(policy.allowed);
18271
- if (policy.protected.has(name.toLowerCase())) {
18272
- const correction = kinds.length ? `choose a non-protected branch of an allowed kind (${kindList(kinds)}), e.g. ${kinds[0]}/<slug>: workit git branch --kind ${kinds[0]} --slug <slug>` : `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
18273
- return {
18274
- ok: false,
18275
- rule: "protected_ref",
18276
- source,
18277
- attempted: name,
18278
- correction,
18279
- error: `protected_ref: branch ${JSON.stringify(name)} is protected by ${source}; ${correction}`
18280
- };
18281
- }
18282
- if (!policy.allowed.some((pattern) => pattern.test(name))) {
18283
- const suggestion = suggestBranchName(name, kinds);
18284
- const correction = suggestion && policy.allowed.some((pattern) => pattern.test(suggestion)) ? `use ${JSON.stringify(suggestion)}; the allowed kinds are ${kindList(kinds)} (workit git branch ${suggestion})` : `choose a branch matching one of: ${allowed.join(", ")}`;
18285
- return {
18286
- ok: false,
18287
- rule: "allowed_pattern",
18288
- source,
18289
- attempted: name,
18290
- correction,
18291
- error: `allowed_pattern: branch ${JSON.stringify(name)} violates ${source} policy; ${correction}`
18292
- };
18293
- }
18294
- return { ok: true };
18295
- };
18296
- var branchPolicySnapshotFor = (workspaceRoot) => {
18297
- const resolved = effectiveWorkspacePolicy(workspaceRoot, "branch");
18298
- const branchPolicy = resolved.branchPolicy;
18299
- const source = resolved.provenance.branchPolicy;
18300
- const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
18301
- const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
18302
- const fingerprint = createHash12("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
18303
- return {
18304
- branchPolicy,
18305
- namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
18306
- source,
18307
- fingerprint
18308
- };
18309
- };
18310
- var validateBranchNameFor = (workspaceRoot, name) => {
18311
- try {
18312
- const resolved = branchPolicySnapshotFor(workspaceRoot);
18313
- return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
18314
- } catch (error) {
18315
- const correction = "repair or select a valid workspace policy before creating a branch";
18316
- return {
18317
- ok: false,
18318
- rule: "policy_unavailable",
18319
- source: "workspaces.json",
18320
- attempted: name,
18321
- correction,
18322
- error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
18323
- };
18324
- }
18325
- };
18326
- var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
18327
- var gitFlowOf = (root, integration) => {
18328
- try {
18329
- if (resolveBranchPolicyFor(root).preset !== "gitflow")
18330
- return null;
18331
- const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
18332
- const names = branchNames(root);
18333
- const production = productionBranchOf(names);
18334
- const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
18335
- return { develop, production, developExists: names.has(develop) };
18336
- } catch {
18337
- return null;
18338
- }
18339
- };
18340
- var releaseBranchesOf = (cwd) => {
18341
- try {
18342
- const resolved = resolveBranchPolicyFor(cwd);
18343
- const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
18344
- return {
18345
- preset: resolved.preset,
18346
- branches: flow ? [flow.production, flow.develop] : [],
18347
- production: flow ? flow.production : resolved.defaultTargetBranch
18348
- };
18349
- } catch {
18350
- return { preset: "unknown", branches: [], production: null };
18351
- }
18352
- };
18353
- var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
18354
- var branchGuidance = (cwd) => {
18355
- try {
18356
- const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
18357
- const kinds = allowedKinds(patterns);
18358
- const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
18359
- const anyName = patterns.some((pattern) => pattern.source === "^.*$");
18360
- const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
18361
- const allowed = anyName ? "Any non-protected branch name is allowed here." : `${kinds.length ? `Allowed branch kinds here: ${kindList(kinds)}${aliasNote(kinds)}` : "Allowed branch names here:"}${others.length ? `${kinds.length ? "; also" : ""} names matching ${others.join(", ")}` : ""}; anything else is denied.`;
18362
- const resolved = vcsConfig("resolve", cwd);
18363
- const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
18364
- const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
18365
- if (flow?.developExists)
18366
- return `${allowed} git-flow: feature/, bugfix/ and release/ start from origin/${flow.develop}, hotfix/ from origin/${flow.production}; feature/ and bugfix/ land on ${flow.develop}, release/ and hotfix/ on ${flow.production}.`;
18367
- const names = branchNames(cwd);
18368
- const configured = String(resolved.baseBranch ?? integration ?? "") || null;
18369
- const trunk = productionBranchOf(names);
18370
- const base = configured && names.has(configured) ? `Branch from origin/${configured}.` : names.has(trunk) ? `Branch from origin/${trunk}${configured ? ` (the default target ${configured} does not exist here)` : ""}.` : "No base branch exists here yet; pass the base explicitly.";
18371
- return `${allowed} ${base}`;
18372
- } catch {
18373
- return null;
18374
- }
18375
- };
18376
- var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
18377
-
18378
18413
  // packages/workit-core/src/hooks/session-marker.ts
18379
18414
  import { createHash as createHash13 } from "node:crypto";
18380
18415
  import fs15 from "node:fs";
@@ -22368,9 +22403,14 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
22368
22403
  if (role !== "judge" || !event.agentId)
22369
22404
  return readOnly;
22370
22405
  const own = subagentSession(session, event.agentId);
22371
- const token = session ? recordIssuedSession(input.cwd || process.cwd(), session, own) : null;
22372
- const flags = token ? `--session ${own} --token ${token}` : `--session ${own}`;
22373
- return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> ${flags} ...\` so the ledger tells it apart from the author.${token ? " The token is this subagent's alone: never write it into a report, a file or a commit. --token needs a workit CLI at least as new as this plugin; if the CLI answers that --token is an unknown option, update the workit CLI (workit upgrade --cli) and record again." : ""}`;
22406
+ const cwd = input.cwd || process.cwd();
22407
+ const token = session ? recordIssuedSession(cwd, session, own) : null;
22408
+ if (session && !token)
22409
+ return `${readOnly} ${own} was already issued to an earlier subagent, so no token is handed out again and a verdict under it would be refused (unbound_session). Record no verdict as ${own}: spawn a fresh verifier or reviewer, which gets its own session and token.`;
22410
+ const judge = event.agentType.endsWith("reviewer") ? "reviewer" : "verifier";
22411
+ const line = verdictCommand({ branch: judgeBranch(cwd), judge, session: own, token });
22412
+ const pick = judge === "verifier" ? ", --kind live when you drove the real surface" : "";
22413
+ return `${readOnly} Its own Workit session is ${own}, so the ledger tells its verdicts apart from the author's. Record the verdict with exactly this command (pick the result${pick}, name the candidate branch if it differs, say what you ran): \`${line}\`.${token ? " The token is this subagent's alone: never write it into a report, a file or a commit. --token needs a workit CLI at least as new as this plugin; if the CLI answers that --token is an unknown option, update the workit CLI (workit upgrade --cli) and record again." : ""}`;
22374
22414
  };
22375
22415
  function handleHook(input, deps) {
22376
22416
  const { descriptor } = deps;