@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.
@@ -10331,6 +10331,221 @@ var init_vcs_config = __esm(() => {
10331
10331
  TRACKERS = new Set(["github", "gitlab", "youtrack"]);
10332
10332
  });
10333
10333
 
10334
+ // packages/workit-core/src/core/commit-flavors.ts
10335
+ var init_commit_flavors = () => {};
10336
+
10337
+ // packages/workit-core/src/core/git.ts
10338
+ var init_git = () => {};
10339
+
10340
+ // packages/workit-core/src/core/branch.ts
10341
+ import { createHash as createHash6 } from "node:crypto";
10342
+ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
10343
+ const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
10344
+ const config = readConfig();
10345
+ return kind === "branch" ? {
10346
+ branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(workspaceRoot))),
10347
+ provenance: { branchPolicy: selected.source }
10348
+ } : {
10349
+ commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
10350
+ provenance: { commitPolicy: selected.source }
10351
+ };
10352
+ }
10353
+ var withReleaseTracks = (workspaceRoot, policy) => {
10354
+ const read = workspaceReleaseTracks(workspaceRoot);
10355
+ if (read.error)
10356
+ throw new ReleaseTrackError(read.error);
10357
+ if (read.tracks.length === 0 && read.protectedBranches.length === 0)
10358
+ return policy;
10359
+ const known = new Set(policy.allowed.map((pattern) => pattern.source));
10360
+ const allowed = [...policy.allowed];
10361
+ for (const glob of read.tracks.flatMap((track) => Object.values(track.naming).map(namingGlob))) {
10362
+ const pattern = branchGlobPattern(glob);
10363
+ if (!known.has(pattern.source)) {
10364
+ known.add(pattern.source);
10365
+ allowed.push(pattern);
10366
+ }
10367
+ }
10368
+ return {
10369
+ ...policy,
10370
+ allowed,
10371
+ protected: new Set([
10372
+ ...policy.protected,
10373
+ ...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
10374
+ ])
10375
+ };
10376
+ }, KIND_SYNONYMS, allowedKinds = (allowed) => [
10377
+ ...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
10378
+ ], editDistance = (a, b) => {
10379
+ const row = Array.from({ length: b.length + 1 }, (_, index) => index);
10380
+ for (let i = 1;i <= a.length; i++) {
10381
+ let previous = row[0];
10382
+ row[0] = i;
10383
+ for (let j = 1;j <= b.length; j++) {
10384
+ const saved = row[j];
10385
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
10386
+ previous = saved;
10387
+ }
10388
+ }
10389
+ return row[b.length];
10390
+ }, suggestBranchName = (name, kinds) => {
10391
+ if (!kinds.length)
10392
+ return null;
10393
+ const slash = name.indexOf("/");
10394
+ const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
10395
+ const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
10396
+ const synonym = KIND_SYNONYMS[prefix];
10397
+ 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;
10398
+ const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
10399
+ return `${kind}/${rest}`;
10400
+ }, kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", "), aliasNote = (kinds) => {
10401
+ const notes = [
10402
+ ["fix", "bugfix"],
10403
+ ["chore", "feature"]
10404
+ ].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
10405
+ return notes.length ? ` (${notes.join(", ")})` : "";
10406
+ }, validateBranchNamePolicy = (name, policy, source) => {
10407
+ const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
10408
+ const kinds = allowedKinds(policy.allowed);
10409
+ if (policy.protected.has(name.toLowerCase())) {
10410
+ 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(", ")}`;
10411
+ return {
10412
+ ok: false,
10413
+ rule: "protected_ref",
10414
+ source,
10415
+ attempted: name,
10416
+ correction,
10417
+ error: `protected_ref: branch ${JSON.stringify(name)} is protected by ${source}; ${correction}`
10418
+ };
10419
+ }
10420
+ if (!policy.allowed.some((pattern) => pattern.test(name))) {
10421
+ const suggestion = suggestBranchName(name, kinds);
10422
+ 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(", ")}`;
10423
+ return {
10424
+ ok: false,
10425
+ rule: "allowed_pattern",
10426
+ source,
10427
+ attempted: name,
10428
+ correction,
10429
+ error: `allowed_pattern: branch ${JSON.stringify(name)} violates ${source} policy; ${correction}`
10430
+ };
10431
+ }
10432
+ return { ok: true };
10433
+ }, branchPolicySnapshotFor = (workspaceRoot) => {
10434
+ const resolved = effectiveWorkspacePolicy(workspaceRoot, "branch");
10435
+ const branchPolicy = resolved.branchPolicy;
10436
+ const source = resolved.provenance.branchPolicy;
10437
+ const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
10438
+ const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
10439
+ const fingerprint = createHash6("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
10440
+ return {
10441
+ branchPolicy,
10442
+ namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
10443
+ source,
10444
+ fingerprint
10445
+ };
10446
+ }, validateBranchNameFor = (workspaceRoot, name) => {
10447
+ try {
10448
+ const resolved = branchPolicySnapshotFor(workspaceRoot);
10449
+ return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
10450
+ } catch (error) {
10451
+ const correction = "repair or select a valid workspace policy before creating a branch";
10452
+ return {
10453
+ ok: false,
10454
+ rule: "policy_unavailable",
10455
+ source: "workspaces.json",
10456
+ attempted: name,
10457
+ correction,
10458
+ error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
10459
+ };
10460
+ }
10461
+ }, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy, gitFlowOf = (root, integration) => {
10462
+ try {
10463
+ if (resolveBranchPolicyFor(root).preset !== "gitflow")
10464
+ return null;
10465
+ const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
10466
+ const names = branchNames(root);
10467
+ const production = productionBranchOf(names);
10468
+ const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
10469
+ return { develop, production, developExists: names.has(develop) };
10470
+ } catch {
10471
+ return null;
10472
+ }
10473
+ }, releaseBranchesOf = (cwd) => {
10474
+ try {
10475
+ const resolved = resolveBranchPolicyFor(cwd);
10476
+ const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
10477
+ return {
10478
+ preset: resolved.preset,
10479
+ branches: flow ? [flow.production, flow.develop] : [],
10480
+ production: flow ? flow.production : resolved.defaultTargetBranch
10481
+ };
10482
+ } catch {
10483
+ return { preset: "unknown", branches: [], production: null };
10484
+ }
10485
+ }, releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase()), branchGuidance = (cwd) => {
10486
+ try {
10487
+ const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
10488
+ const kinds = allowedKinds(patterns);
10489
+ const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
10490
+ const anyName = patterns.some((pattern) => pattern.source === "^.*$");
10491
+ const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
10492
+ 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.`;
10493
+ const resolved = vcsConfig("resolve", cwd);
10494
+ const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
10495
+ const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
10496
+ if (flow?.developExists)
10497
+ 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}.`;
10498
+ const names = branchNames(cwd);
10499
+ const configured = String(resolved.baseBranch ?? integration ?? "") || null;
10500
+ const trunk = productionBranchOf(names);
10501
+ 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.";
10502
+ return `${allowed} ${base}`;
10503
+ } catch {
10504
+ return null;
10505
+ }
10506
+ }, globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1"), policy = (root) => resolveBranchPolicyFor(root), isProtected = (root, name) => policy(root).protected.has(name.toLowerCase()), isProtectedTarget = (root, name) => {
10507
+ try {
10508
+ return isProtected(root, name);
10509
+ } catch {
10510
+ return true;
10511
+ }
10512
+ };
10513
+ var init_branch = __esm(() => {
10514
+ init_branch_policy();
10515
+ init_commit_flavors();
10516
+ init_git();
10517
+ init_config();
10518
+ init_workspaces();
10519
+ init_vcs_config();
10520
+ init_release_tracks();
10521
+ KIND_SYNONYMS = {
10522
+ fix: "bugfix",
10523
+ fixes: "bugfix",
10524
+ bug: "bugfix",
10525
+ bugs: "bugfix",
10526
+ bugfixes: "bugfix",
10527
+ feat: "feature",
10528
+ features: "feature",
10529
+ chore: "feature",
10530
+ docs: "feature",
10531
+ doc: "feature",
10532
+ refactor: "feature",
10533
+ test: "feature",
10534
+ tests: "feature",
10535
+ ci: "feature",
10536
+ build: "feature",
10537
+ perf: "feature",
10538
+ style: "feature",
10539
+ deps: "feature",
10540
+ task: "feature",
10541
+ enhancement: "feature",
10542
+ hot: "hotfix",
10543
+ hotfixes: "hotfix",
10544
+ releases: "release",
10545
+ rel: "release"
10546
+ };
10547
+ });
10548
+
10334
10549
  // packages/workit-core/src/core/logger.ts
10335
10550
  import os3 from "node:os";
10336
10551
  import path6 from "node:path";
@@ -10392,7 +10607,7 @@ var init_logger = __esm(() => {
10392
10607
  });
10393
10608
 
10394
10609
  // packages/workit-core/src/host-session.ts
10395
- import { createHash as createHash6, randomBytes, timingSafeEqual } from "node:crypto";
10610
+ import { createHash as createHash7, randomBytes, timingSafeEqual } from "node:crypto";
10396
10611
  import fs5 from "node:fs";
10397
10612
  import path7 from "node:path";
10398
10613
  function hostSessionFromEnv(env) {
@@ -10467,6 +10682,21 @@ function recordIssuedSession(cwd, lead, issued) {
10467
10682
  return null;
10468
10683
  }
10469
10684
  }
10685
+ function judgeBranch(cwd) {
10686
+ const branch = currentBranch(cwd);
10687
+ return branch && !isProtectedTarget(cwd, branch) ? branch : null;
10688
+ }
10689
+ function verdictCommand(options) {
10690
+ const kind = options.judge === "verifier" ? "<live|unit>" : options.judge === "reviewer" ? "review" : "<live|unit|review>";
10691
+ return [
10692
+ "workit ledger verdict <verified|failed|blocked>",
10693
+ `--kind ${kind}`,
10694
+ `--branch ${options.branch ?? "<branch>"}`,
10695
+ options.session ? `--session ${options.session}` : null,
10696
+ options.token ? `--token=${options.token}` : null,
10697
+ '--how "<what you ran and saw>"'
10698
+ ].filter(Boolean).join(" ");
10699
+ }
10470
10700
  function pruneIssued(dir, own, tokens) {
10471
10701
  try {
10472
10702
  const entries = fs5.readdirSync(dir);
@@ -10488,7 +10718,7 @@ function issuedBinding(cwd, claimed) {
10488
10718
  return null;
10489
10719
  return leadBound ? "token" : "legacy";
10490
10720
  }
10491
- var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, ISSUED_PER_LEAD = 64, ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3", hash = (value) => createHash6("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () => path7.join(resolveStateDir(), "issued"), userTokenRoot = () => path7.join(resolveStateDir(), "issued-tokens"), safeList = (dir) => {
10721
+ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, ISSUED_PER_LEAD = 64, ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3", hash = (value) => createHash7("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () => path7.join(resolveStateDir(), "issued"), userTokenRoot = () => path7.join(resolveStateDir(), "issued-tokens"), safeList = (dir) => {
10492
10722
  try {
10493
10723
  return fs5.readdirSync(dir);
10494
10724
  } catch {
@@ -10498,7 +10728,7 @@ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "")
10498
10728
  try {
10499
10729
  fs5.rmdirSync(dir);
10500
10730
  } catch {}
10501
- }, tokenHash = (token) => createHash6("sha256").update(token).digest("hex"), issuedRecord = (cwd, claimed) => {
10731
+ }, tokenHash = (token) => createHash7("sha256").update(token).digest("hex"), issuedRecord = (cwd, claimed) => {
10502
10732
  const cut = claimed.lastIndexOf(":");
10503
10733
  if (cut <= 0)
10504
10734
  return null;
@@ -10521,7 +10751,9 @@ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "")
10521
10751
  }
10522
10752
  }, OWN_CHILD_SHELL;
10523
10753
  var init_host_session = __esm(() => {
10754
+ init_branch();
10524
10755
  init_logger();
10756
+ init_rev();
10525
10757
  init_paths();
10526
10758
  HOST_SESSION_VARS = [
10527
10759
  ["OPENCODE_SESSION_ID", () => "opencode"],
@@ -10537,7 +10769,7 @@ var init_host_session = __esm(() => {
10537
10769
 
10538
10770
  // packages/workit-core/src/ledger.ts
10539
10771
  import { spawnSync as spawnSync8 } from "node:child_process";
10540
- import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
10772
+ import { createHash as createHash8, randomBytes as randomBytes2 } from "node:crypto";
10541
10773
  import fs6 from "node:fs";
10542
10774
  import path8 from "node:path";
10543
10775
  function resolveCommit(cwd, ref) {
@@ -10593,7 +10825,7 @@ function diffHash(cwd, base, head) {
10593
10825
  ], GIT_TIMEOUTS.worktree);
10594
10826
  if (!diff.ok || !diff.stdout)
10595
10827
  return null;
10596
- const hash = createHash7("sha256");
10828
+ const hash = createHash8("sha256");
10597
10829
  for (const line of diff.stdout.split(`
10598
10830
  `)) {
10599
10831
  if (line.startsWith("index "))
@@ -11196,7 +11428,7 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, PASSING, FAILING, STRONG_RESULTS,
11196
11428
  } catch {}
11197
11429
  }
11198
11430
  return null;
11199
- }, ledgerLock, sha2562 = (text) => createHash7("sha256").update(text).digest("hex"), chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
11431
+ }, ledgerLock, sha2562 = (text) => createHash8("sha256").update(text).digest("hex"), chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
11200
11432
  ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
11201
11433
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
11202
11434
  const buffer = Buffer.alloc(span);
@@ -11526,7 +11758,7 @@ var RULES, derived2 = (spec) => ({
11526
11758
  };
11527
11759
  const since = Date.parse(task.createdAt);
11528
11760
  const branch = ledger.branch();
11529
- const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
11761
+ const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
11530
11762
  return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
11531
11763
  met: false,
11532
11764
  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>"`)',
@@ -13162,7 +13394,7 @@ var isPlainObject2 = (value) => typeof value === "object" && value !== null && !
13162
13394
  });
13163
13395
 
13164
13396
  // packages/workit-core/src/store/reduce.ts
13165
- import { createHash as createHash8 } from "node:crypto";
13397
+ import { createHash as createHash9 } from "node:crypto";
13166
13398
  function reduce(state, event, blobs) {
13167
13399
  const data = event.data;
13168
13400
  const known = KNOWN_TYPES.has(event.type);
@@ -13229,7 +13461,7 @@ function eventType(before, after) {
13229
13461
  return "task.noted";
13230
13462
  return "task.updated";
13231
13463
  }
13232
- var OPENING_TYPES, KNOWN_TYPES, isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string"), BLOB_REF = "$blob", 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, blobDigest = (value) => createHash8("sha256").update(JSON.stringify(value)).digest("hex"), toBlob = (value, blobs) => {
13464
+ var OPENING_TYPES, KNOWN_TYPES, isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string"), BLOB_REF = "$blob", 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, blobDigest = (value) => createHash9("sha256").update(JSON.stringify(value)).digest("hex"), toBlob = (value, blobs) => {
13233
13465
  if (!isObject5(value))
13234
13466
  return value;
13235
13467
  const digest = blobDigest(value);
@@ -13300,7 +13532,7 @@ var init_notes = __esm(() => {
13300
13532
  });
13301
13533
 
13302
13534
  // packages/workit-core/src/core/task-store.ts
13303
- import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
13535
+ import { createHash as createHash10, randomUUID as randomUUID3 } from "node:crypto";
13304
13536
  import * as fs11 from "node:fs";
13305
13537
  import path15 from "node:path";
13306
13538
 
@@ -14264,7 +14496,7 @@ class TaskStore {
14264
14496
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
14265
14497
  }
14266
14498
  withKeyLock(key, operation) {
14267
- const digest = createHash9("sha256").update(key).digest("hex").slice(0, 32);
14499
+ const digest = createHash10("sha256").update(key).digest("hex").slice(0, 32);
14268
14500
  return this.withLock(path15.join(this.paths().dir, "keys", `${digest}.lock`), operation);
14269
14501
  }
14270
14502
  bindLocked(taskId, key, timestamp) {
@@ -15033,6 +15265,11 @@ var init_workers = __esm(() => {
15033
15265
  import { spawnSync as spawnSync9 } from "node:child_process";
15034
15266
  import fs12 from "node:fs";
15035
15267
  import path16 from "node:path";
15268
+ function checksConfigFile(root) {
15269
+ const current = path16.join(root, CHECKS_FILE);
15270
+ const legacy = path16.join(root, LEGACY_CHECKS_FILE);
15271
+ return !fs12.existsSync(current) && fs12.existsSync(legacy) ? legacy : current;
15272
+ }
15036
15273
  function repoHeavyCommands(root) {
15037
15274
  try {
15038
15275
  const value = JSON.parse(fs12.readFileSync(checksConfigFile(root), "utf8"));
@@ -15124,19 +15361,25 @@ function detectedChecks(root) {
15124
15361
  function loadCheckConfig(cwd) {
15125
15362
  const root = checkRoot(cwd);
15126
15363
  const file = checksConfigFile(root);
15364
+ const legacy = path16.basename(file) === LEGACY_CHECKS_FILE;
15365
+ const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
15366
+ const note = legacy ? {
15367
+ note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
15368
+ } : {};
15127
15369
  const none = (error = null) => ({
15128
- source: error ? CHECKS_FILE : "none",
15370
+ source: error ? label : "none",
15129
15371
  root,
15130
15372
  checks: [],
15131
15373
  gates: {},
15132
- error
15374
+ error,
15375
+ ...note
15133
15376
  });
15134
15377
  let text = null;
15135
15378
  try {
15136
15379
  text = fs12.readFileSync(file, "utf8");
15137
15380
  } catch (error) {
15138
15381
  if (error.code !== "ENOENT")
15139
- return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
15382
+ return none(`cannot read ${label}: ${error.message}`);
15140
15383
  }
15141
15384
  if (text === null) {
15142
15385
  const checks = detectedChecks(root);
@@ -15146,33 +15389,33 @@ function loadCheckConfig(cwd) {
15146
15389
  try {
15147
15390
  parsed = JSON.parse(text);
15148
15391
  } catch {
15149
- return none(`${CHECKS_FILE} is not valid JSON`);
15392
+ return none(`${label} is not valid JSON`);
15150
15393
  }
15151
15394
  if (!isRecord3(parsed) || !isRecord3(parsed.checks))
15152
- return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
15395
+ return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
15153
15396
  const checks = [];
15154
15397
  for (const [name, value] of Object.entries(parsed.checks)) {
15155
15398
  if (!CHECK_NAME.test(name))
15156
- return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
15399
+ return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
15157
15400
  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");
15158
15401
  if (argv instanceof Error)
15159
- return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
15402
+ return none(`${label}: check "${name}": ${argv.message}`);
15160
15403
  checks.push({ name, argv, accepts: [argv] });
15161
15404
  }
15162
15405
  const gates = {};
15163
15406
  if (parsed.gates !== undefined) {
15164
15407
  if (!isRecord3(parsed.gates))
15165
- return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
15408
+ return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
15166
15409
  for (const [gate, value] of Object.entries(parsed.gates)) {
15167
15410
  if (!GATES.includes(gate))
15168
- return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
15411
+ return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
15169
15412
  const names = typeof value === "string" ? [value] : value;
15170
15413
  if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
15171
- return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
15414
+ return none(`${label}: gates.${gate} must name configured checks`);
15172
15415
  gates[gate] = names;
15173
15416
  }
15174
15417
  }
15175
- return { source: CHECKS_FILE, root, checks, gates, error: null, resources: parsed.resources };
15418
+ return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
15176
15419
  }
15177
15420
  function matchesNamedCheck(config, name, argv, cwd) {
15178
15421
  if (config.error || name === null || cwd !== ".")
@@ -15207,7 +15450,7 @@ function workitAvailable(env = process.env) {
15207
15450
  function checkCommand(args, version, onPath = workitAvailable()) {
15208
15451
  return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
15209
15452
  }
15210
- var SCRIPT_CHECKS, CHECKS_FILE = "workit.checks.json", checksConfigFile = (root) => path16.join(root, CHECKS_FILE), isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git4 = (cwd, args) => {
15453
+ var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git4 = (cwd, args) => {
15211
15454
  const run = spawnSync9("git", args, {
15212
15455
  cwd,
15213
15456
  encoding: "utf8",
@@ -15241,7 +15484,7 @@ var init_check_config = __esm(() => {
15241
15484
  });
15242
15485
 
15243
15486
  // packages/workit-core/src/core/task-evaluation.ts
15244
- import { createHash as createHash10 } from "node:crypto";
15487
+ import { createHash as createHash11 } from "node:crypto";
15245
15488
  import * as fs13 from "node:fs";
15246
15489
  import { spawnSync as spawnSync10 } from "node:child_process";
15247
15490
  import path17 from "node:path";
@@ -15423,7 +15666,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
15423
15666
  return {
15424
15667
  satisfied: [],
15425
15668
  considered,
15426
- 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())}`
15669
+ 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())}`
15427
15670
  };
15428
15671
  const satisfied = fresh.filter(named);
15429
15672
  if (satisfied.length)
@@ -15695,7 +15938,7 @@ function boundTaskSummary(summary) {
15695
15938
  brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
15696
15939
  return brief;
15697
15940
  }
15698
- var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`), relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".", canonicalPath = (value) => {
15941
+ var digestBytes = (value) => createHash11("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`), relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".", canonicalPath = (value) => {
15699
15942
  if (typeof value !== "string" || !value)
15700
15943
  return null;
15701
15944
  if (value.includes("\\") || value.split("/").includes(".."))
@@ -17861,7 +18104,7 @@ var support = (descriptor, axis) => {
17861
18104
  });
17862
18105
 
17863
18106
  // packages/workit-core/src/hooks/context.ts
17864
- import { createHash as createHash11 } from "node:crypto";
18107
+ import { createHash as createHash12 } from "node:crypto";
17865
18108
  import fs14 from "node:fs";
17866
18109
  import path18 from "node:path";
17867
18110
  function sessionCompactContext(store, session, context, selection = "session-bound") {
@@ -17996,7 +18239,7 @@ ${options.addendum}` : ""}
17996
18239
  } catch {
17997
18240
  return null;
17998
18241
  }
17999
- }, TURN_TTL_MS, digestOf = (text) => createHash11("sha256").update(text).digest("hex"), turnMarker = (input) => {
18242
+ }, TURN_TTL_MS, digestOf = (text) => createHash12("sha256").update(text).digest("hex"), turnMarker = (input) => {
18000
18243
  if (!input.session.id)
18001
18244
  return null;
18002
18245
  try {
@@ -18070,215 +18313,6 @@ var init_context = __esm(() => {
18070
18313
  TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
18071
18314
  });
18072
18315
 
18073
- // packages/workit-core/src/core/commit-flavors.ts
18074
- var init_commit_flavors = () => {};
18075
-
18076
- // packages/workit-core/src/core/git.ts
18077
- var init_git = () => {};
18078
-
18079
- // packages/workit-core/src/core/branch.ts
18080
- import { createHash as createHash12 } from "node:crypto";
18081
- function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
18082
- const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
18083
- const config = readConfig();
18084
- return kind === "branch" ? {
18085
- branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(workspaceRoot))),
18086
- provenance: { branchPolicy: selected.source }
18087
- } : {
18088
- commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
18089
- provenance: { commitPolicy: selected.source }
18090
- };
18091
- }
18092
- var withReleaseTracks = (workspaceRoot, policy) => {
18093
- const read = workspaceReleaseTracks(workspaceRoot);
18094
- if (read.error)
18095
- throw new ReleaseTrackError(read.error);
18096
- if (read.tracks.length === 0 && read.protectedBranches.length === 0)
18097
- return policy;
18098
- const known = new Set(policy.allowed.map((pattern) => pattern.source));
18099
- const allowed = [...policy.allowed];
18100
- for (const glob of read.tracks.flatMap((track) => Object.values(track.naming).map(namingGlob))) {
18101
- const pattern = branchGlobPattern(glob);
18102
- if (!known.has(pattern.source)) {
18103
- known.add(pattern.source);
18104
- allowed.push(pattern);
18105
- }
18106
- }
18107
- return {
18108
- ...policy,
18109
- allowed,
18110
- protected: new Set([
18111
- ...policy.protected,
18112
- ...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
18113
- ])
18114
- };
18115
- }, KIND_SYNONYMS, allowedKinds = (allowed) => [
18116
- ...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
18117
- ], editDistance = (a, b) => {
18118
- const row = Array.from({ length: b.length + 1 }, (_, index) => index);
18119
- for (let i = 1;i <= a.length; i++) {
18120
- let previous = row[0];
18121
- row[0] = i;
18122
- for (let j = 1;j <= b.length; j++) {
18123
- const saved = row[j];
18124
- row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
18125
- previous = saved;
18126
- }
18127
- }
18128
- return row[b.length];
18129
- }, suggestBranchName = (name, kinds) => {
18130
- if (!kinds.length)
18131
- return null;
18132
- const slash = name.indexOf("/");
18133
- const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
18134
- const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
18135
- const synonym = KIND_SYNONYMS[prefix];
18136
- 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;
18137
- const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
18138
- return `${kind}/${rest}`;
18139
- }, kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", "), aliasNote = (kinds) => {
18140
- const notes = [
18141
- ["fix", "bugfix"],
18142
- ["chore", "feature"]
18143
- ].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
18144
- return notes.length ? ` (${notes.join(", ")})` : "";
18145
- }, validateBranchNamePolicy = (name, policy, source) => {
18146
- const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
18147
- const kinds = allowedKinds(policy.allowed);
18148
- if (policy.protected.has(name.toLowerCase())) {
18149
- 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(", ")}`;
18150
- return {
18151
- ok: false,
18152
- rule: "protected_ref",
18153
- source,
18154
- attempted: name,
18155
- correction,
18156
- error: `protected_ref: branch ${JSON.stringify(name)} is protected by ${source}; ${correction}`
18157
- };
18158
- }
18159
- if (!policy.allowed.some((pattern) => pattern.test(name))) {
18160
- const suggestion = suggestBranchName(name, kinds);
18161
- 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(", ")}`;
18162
- return {
18163
- ok: false,
18164
- rule: "allowed_pattern",
18165
- source,
18166
- attempted: name,
18167
- correction,
18168
- error: `allowed_pattern: branch ${JSON.stringify(name)} violates ${source} policy; ${correction}`
18169
- };
18170
- }
18171
- return { ok: true };
18172
- }, branchPolicySnapshotFor = (workspaceRoot) => {
18173
- const resolved = effectiveWorkspacePolicy(workspaceRoot, "branch");
18174
- const branchPolicy = resolved.branchPolicy;
18175
- const source = resolved.provenance.branchPolicy;
18176
- const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
18177
- const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
18178
- const fingerprint = createHash12("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
18179
- return {
18180
- branchPolicy,
18181
- namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
18182
- source,
18183
- fingerprint
18184
- };
18185
- }, validateBranchNameFor = (workspaceRoot, name) => {
18186
- try {
18187
- const resolved = branchPolicySnapshotFor(workspaceRoot);
18188
- return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
18189
- } catch (error) {
18190
- const correction = "repair or select a valid workspace policy before creating a branch";
18191
- return {
18192
- ok: false,
18193
- rule: "policy_unavailable",
18194
- source: "workspaces.json",
18195
- attempted: name,
18196
- correction,
18197
- error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
18198
- };
18199
- }
18200
- }, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy, gitFlowOf = (root, integration) => {
18201
- try {
18202
- if (resolveBranchPolicyFor(root).preset !== "gitflow")
18203
- return null;
18204
- const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
18205
- const names = branchNames(root);
18206
- const production = productionBranchOf(names);
18207
- const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
18208
- return { develop, production, developExists: names.has(develop) };
18209
- } catch {
18210
- return null;
18211
- }
18212
- }, releaseBranchesOf = (cwd) => {
18213
- try {
18214
- const resolved = resolveBranchPolicyFor(cwd);
18215
- const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
18216
- return {
18217
- preset: resolved.preset,
18218
- branches: flow ? [flow.production, flow.develop] : [],
18219
- production: flow ? flow.production : resolved.defaultTargetBranch
18220
- };
18221
- } catch {
18222
- return { preset: "unknown", branches: [], production: null };
18223
- }
18224
- }, releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase()), branchGuidance = (cwd) => {
18225
- try {
18226
- const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
18227
- const kinds = allowedKinds(patterns);
18228
- const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
18229
- const anyName = patterns.some((pattern) => pattern.source === "^.*$");
18230
- const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
18231
- 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.`;
18232
- const resolved = vcsConfig("resolve", cwd);
18233
- const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
18234
- const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
18235
- if (flow?.developExists)
18236
- 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}.`;
18237
- const names = branchNames(cwd);
18238
- const configured = String(resolved.baseBranch ?? integration ?? "") || null;
18239
- const trunk = productionBranchOf(names);
18240
- 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.";
18241
- return `${allowed} ${base}`;
18242
- } catch {
18243
- return null;
18244
- }
18245
- }, globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
18246
- var init_branch = __esm(() => {
18247
- init_branch_policy();
18248
- init_commit_flavors();
18249
- init_git();
18250
- init_config();
18251
- init_workspaces();
18252
- init_vcs_config();
18253
- init_release_tracks();
18254
- KIND_SYNONYMS = {
18255
- fix: "bugfix",
18256
- fixes: "bugfix",
18257
- bug: "bugfix",
18258
- bugs: "bugfix",
18259
- bugfixes: "bugfix",
18260
- feat: "feature",
18261
- features: "feature",
18262
- chore: "feature",
18263
- docs: "feature",
18264
- doc: "feature",
18265
- refactor: "feature",
18266
- test: "feature",
18267
- tests: "feature",
18268
- ci: "feature",
18269
- build: "feature",
18270
- perf: "feature",
18271
- style: "feature",
18272
- deps: "feature",
18273
- task: "feature",
18274
- enhancement: "feature",
18275
- hot: "hotfix",
18276
- hotfixes: "hotfix",
18277
- releases: "release",
18278
- rel: "release"
18279
- };
18280
- });
18281
-
18282
18316
  // packages/workit-core/src/hooks/session-marker.ts
18283
18317
  import { createHash as createHash13 } from "node:crypto";
18284
18318
  import fs15 from "node:fs";
@@ -22221,9 +22255,14 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
22221
22255
  if (role !== "judge" || !event.agentId)
22222
22256
  return readOnly;
22223
22257
  const own = subagentSession(session, event.agentId);
22224
- const token = session ? recordIssuedSession(input.cwd || process.cwd(), session, own) : null;
22225
- const flags = token ? `--session ${own} --token ${token}` : `--session ${own}`;
22226
- 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." : ""}`;
22258
+ const cwd = input.cwd || process.cwd();
22259
+ const token = session ? recordIssuedSession(cwd, session, own) : null;
22260
+ if (session && !token)
22261
+ 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.`;
22262
+ const judge = event.agentType.endsWith("reviewer") ? "reviewer" : "verifier";
22263
+ const line = verdictCommand({ branch: judgeBranch(cwd), judge, session: own, token });
22264
+ const pick = judge === "verifier" ? ", --kind live when you drove the real surface" : "";
22265
+ 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." : ""}`;
22227
22266
  }, failureDecision = (descriptor, event, error) => {
22228
22267
  if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
22229
22268
  return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE5;