@brainervirus/workit-cursor 7.0.0 → 7.2.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.
@@ -2,7 +2,7 @@
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
4
  "description": "Adaptive Workit workflow with shared MCP tools and native Cursor hooks",
5
- "version": "7.0.0",
5
+ "version": "7.2.0",
6
6
  "author": {
7
7
  "name": "Cristhofer Pincetti"
8
8
  },
@@ -10081,14 +10081,14 @@ var diagnosticLogger, PRESETS, mergePreset = (preset, input = {}, current = {
10081
10081
  if (result.status === "malformed")
10082
10082
  throw new Error(result.error);
10083
10083
  return result.config ?? DEFAULTS;
10084
- }, resolveBranchPolicy = (config, workspace) => {
10084
+ }, branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"), resolveBranchPolicy = (config, workspace) => {
10085
10085
  const wp = workspace?.branchPolicy ?? {};
10086
10086
  const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
10087
10087
  const merged = mergePreset(preset, {
10088
10088
  allowed: wp.allowed,
10089
10089
  protectedNames: wp.protected
10090
10090
  }, config);
10091
- const allowed = merged.allowed.map((p) => new RegExp(`^${p.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"));
10091
+ const allowed = merged.allowed.map(branchGlobPattern);
10092
10092
  return {
10093
10093
  preset,
10094
10094
  allowed,
@@ -10334,7 +10334,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
10334
10334
  profiles: record(nonBlank, profileSchema).optional()
10335
10335
  }).passthrough();
10336
10336
  return entrySchema;
10337
- }, runtimeWorkspaceIndexSchema, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
10337
+ }, runtimeWorkspaceIndexSchema, matchRuntimeWorkspace = (cwd) => {
10338
10338
  const file = path6.join(configDir(), "workspaces.json");
10339
10339
  let raw;
10340
10340
  try {
@@ -10369,8 +10369,15 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
10369
10369
  });
10370
10370
  const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
10371
10371
  const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
10372
- if (!selected)
10372
+ return selected ? { file, selected } : null;
10373
+ }, resolveRuntimeReleaseTracks = (cwd) => {
10374
+ const matched = matchRuntimeWorkspace(cwd);
10375
+ return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
10376
+ }, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
10377
+ const matched = matchRuntimeWorkspace(cwd);
10378
+ if (!matched)
10373
10379
  return null;
10380
+ const { file, selected } = matched;
10374
10381
  const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
10375
10382
  if (!policy.success) {
10376
10383
  const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
@@ -10431,26 +10438,31 @@ var init_workspaces = __esm(() => {
10431
10438
  object({ kind: literal("manual") }).strict()
10432
10439
  ]);
10433
10440
  releaseTrackSchema = object({
10434
- strategy: branchPreset,
10441
+ strategy: branchPreset.optional(),
10435
10442
  productionBranch: nonBlank,
10436
10443
  integrationBranch: nonBlank,
10437
- naming: object({ feature: nonBlank, release: nonBlank, hotfix: nonBlank }).strict(),
10438
- baseBranch: nonBlank,
10439
- mergeBackBranches: array(nonBlank),
10440
- pullRequestTarget: nonBlank,
10441
- tagNamespace: string2(),
10442
- versionSource: versionSourceSchema,
10443
- requiredChecks: array(nonBlank)
10444
- }).strict().superRefine((track, context) => {
10444
+ naming: object({
10445
+ feature: nonBlank.optional(),
10446
+ release: nonBlank.optional(),
10447
+ hotfix: nonBlank.optional()
10448
+ }).passthrough().optional(),
10449
+ baseBranch: nonBlank.optional(),
10450
+ mergeBackBranches: array(nonBlank).optional(),
10451
+ pullRequestTarget: nonBlank.optional(),
10452
+ tagNamespace: string2().optional(),
10453
+ versionSource: versionSourceSchema.optional(),
10454
+ requiredChecks: array(nonBlank).optional(),
10455
+ critical: array(nonBlank).optional()
10456
+ }).passthrough().superRefine((track, context) => {
10445
10457
  for (const [field, values] of [
10446
- ["mergeBackBranches", track.mergeBackBranches],
10447
- ["requiredChecks", track.requiredChecks]
10458
+ ["mergeBackBranches", track.mergeBackBranches ?? []],
10459
+ ["requiredChecks", track.requiredChecks ?? []]
10448
10460
  ]) {
10449
10461
  if (new Set(values).size !== values.length) {
10450
10462
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
10451
10463
  }
10452
10464
  }
10453
- if (track.versionSource.kind === "package-json" && (path6.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
10465
+ if (track.versionSource?.kind === "package-json" && (path6.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
10454
10466
  context.addIssue({
10455
10467
  code: "custom",
10456
10468
  path: ["versionSource", "path"],
@@ -10468,7 +10480,7 @@ var init_workspaces = __esm(() => {
10468
10480
  account: nonBlank.optional()
10469
10481
  }).passthrough().optional(),
10470
10482
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
10471
- defaultEndpoint: _enum(["commit", "pr"]).optional(),
10483
+ defaultEndpoint: unknown().optional(),
10472
10484
  verification: _enum(["self", "independent"]).optional(),
10473
10485
  autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
10474
10486
  kind,
@@ -10520,6 +10532,46 @@ var init_workspaces = __esm(() => {
10520
10532
  // packages/workit-core/src/autonomy.ts
10521
10533
  import os3 from "node:os";
10522
10534
  import path7 from "node:path";
10535
+ function endpointOf(entry) {
10536
+ const raw = entry?.defaultEndpoint;
10537
+ if (raw === undefined)
10538
+ return { raw, endpoint: DEFAULT_ENDPOINT };
10539
+ if (isDefaultEndpoint(raw))
10540
+ return { raw, endpoint: raw };
10541
+ return {
10542
+ raw,
10543
+ endpoint: DEFAULT_ENDPOINT,
10544
+ issue: `defaultEndpoint ${JSON.stringify(raw)} is not one of ${DEFAULT_ENDPOINTS.join(", ")}; using ${DEFAULT_ENDPOINT}`
10545
+ };
10546
+ }
10547
+ function effectiveEndpoint(endpoint, context) {
10548
+ if (endpoint === "commit")
10549
+ return { endpoint };
10550
+ const missing = (kind) => `${kind} grant missing; ${grantHint(context.workspace, kind)}`;
10551
+ if (context.grants.push === false)
10552
+ return { endpoint: "commit", reason: missing("push") };
10553
+ if (context.grants.pr === false)
10554
+ return { endpoint: "commit", reason: missing("pr") };
10555
+ if (context.configured.length > 0 && !context.accountConfigured)
10556
+ return {
10557
+ endpoint: "commit",
10558
+ reason: `vcs.account missing; set vcs.account for workspace "${context.workspace ?? "?"}" in ~/.config/workit/workspaces.json`
10559
+ };
10560
+ if (endpoint === "merged" && context.grants.merge === false)
10561
+ return { endpoint: "green", reason: missing("merge") };
10562
+ return { endpoint };
10563
+ }
10564
+ function endpointView(entry, context) {
10565
+ const { raw, endpoint, issue } = endpointOf(entry);
10566
+ const effective = effectiveEndpoint(endpoint, context);
10567
+ return {
10568
+ defaultEndpoint: endpoint,
10569
+ configuredEndpoint: raw ?? null,
10570
+ effectiveEndpoint: effective.endpoint,
10571
+ ...effective.reason ? { endpointReason: effective.reason } : {},
10572
+ ...issue ? { endpointIssue: issue } : {}
10573
+ };
10574
+ }
10523
10575
  function configuredGrants(entry) {
10524
10576
  const record = entry ?? {};
10525
10577
  const raw = record.autonomy;
@@ -10545,18 +10597,28 @@ function resolveAutonomy(cwd) {
10545
10597
  source: "default",
10546
10598
  accountConfigured: false,
10547
10599
  defaultEndpoint: DEFAULT_ENDPOINT,
10600
+ configuredEndpoint: null,
10601
+ effectiveEndpoint: DEFAULT_ENDPOINT,
10548
10602
  verification: DEFAULT_VERIFICATION,
10549
10603
  note: override
10550
10604
  };
10551
10605
  const workspace = resolveWorkspaceFrom(cwd, grantsDir(), process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
10552
10606
  const { grants, source } = configuredGrants(workspace);
10607
+ const full = { ...DEFAULT_GRANTS, ...grants };
10608
+ const configured = GRANT_KINDS.filter((kind) => grants[kind] !== undefined);
10609
+ const accountConfigured = Boolean(workspace?.vcs?.account);
10553
10610
  return {
10554
10611
  workspace: workspace?.name ?? null,
10555
- grants: { ...DEFAULT_GRANTS, ...grants },
10556
- configured: GRANT_KINDS.filter((kind) => grants[kind] !== undefined),
10612
+ grants: full,
10613
+ configured,
10557
10614
  source,
10558
- accountConfigured: Boolean(workspace?.vcs?.account),
10559
- defaultEndpoint: endpointOf(workspace),
10615
+ accountConfigured,
10616
+ ...endpointView(workspace, {
10617
+ workspace: workspace?.name ?? null,
10618
+ grants: full,
10619
+ configured,
10620
+ accountConfigured
10621
+ }),
10560
10622
  verification: verificationOf(workspace)
10561
10623
  };
10562
10624
  }
@@ -10569,7 +10631,7 @@ var accountHome = () => {
10569
10631
  }, grantsHome, grantsDir = () => path7.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
10570
10632
  const configured = path7.resolve(resolveConfigDir());
10571
10633
  return configured === path7.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path7.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
10572
- }, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINT = "commit", DEFAULT_VERIFICATION = "self", verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self", endpointOf = (entry) => entry?.defaultEndpoint === "pr" ? "pr" : "commit", isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
10634
+ }, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), DEFAULT_VERIFICATION = "self", verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self", isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
10573
10635
  if (value === true)
10574
10636
  return { push: true, pr: true, merge: "verified" };
10575
10637
  if (!Array.isArray(value))
@@ -10581,7 +10643,7 @@ var accountHome = () => {
10581
10643
  else if (item === "merge")
10582
10644
  grants.merge = "verified";
10583
10645
  return Object.keys(grants).length > 0 ? grants : null;
10584
- };
10646
+ }, suggested = (kind) => kind === "merge" ? "verified" : "true", grantHint = (workspace, kind) => `ask the user to run, in their own terminal: workit grant set ${workspace ?? "<workspace>"} ${kind}=${suggested(kind)}`;
10585
10647
  var init_autonomy = __esm(() => {
10586
10648
  init_config();
10587
10649
  init_safe_write();
@@ -10595,6 +10657,7 @@ var init_autonomy = __esm(() => {
10595
10657
  release: false,
10596
10658
  rerun: true
10597
10659
  });
10660
+ DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
10598
10661
  });
10599
10662
 
10600
10663
  // node_modules/@openclaw/fs-safe/dist/errors.js
@@ -14291,10 +14354,10 @@ Autonomy contract:
14291
14354
  Stop only for a new consequential choice, a host denial, a conflicting edit
14292
14355
  or an unresolved blocker; report open gaps and uncertain workers.
14293
14356
  - No endpoint named: stop at a local commit on a policy-compliant branch and
14294
- state the next command. Push and open a PR only when the request implies
14295
- delivery (fix, implement, ship) and \`workit grant show\` reports
14296
- \`defaultEndpoint\` \`pr\`; otherwise the endpoint is \`commit\`. PRs open,
14297
- CI green and verified is the most autonomy allows, never the default target.
14357
+ state the next command. If the request implies delivery (fix, implement,
14358
+ ship), go to the effective endpoint in \`workit grant show\`: \`pr\` opens
14359
+ the PR; \`green\` babysits it without asking (workit-ship) to merge-ready,
14360
+ never merging; \`merged\` also lands it with \`workit pr merge\`.
14298
14361
  Merge and release need a workspace grant; a blocked verb names its unblock.
14299
14362
  Only the user raises a grant, from their own terminal (\`workit grant set\`);
14300
14363
  never edit workspaces.json or route around a grant_required.
@@ -16225,11 +16288,150 @@ var init_commit_flavors = () => {};
16225
16288
  // packages/workit-core/src/core/git.ts
16226
16289
  var init_git = () => {};
16227
16290
 
16291
+ // packages/workit-core/src/core/release-tracks.ts
16292
+ function readReleaseTracks(raw) {
16293
+ const out = { tracks: [], issues: [], error: null, protectedBranches: [] };
16294
+ if (raw === undefined || raw === null)
16295
+ return out;
16296
+ if (!isRecord3(raw)) {
16297
+ out.issues.push("releaseTracks is not an object; ignored");
16298
+ return out;
16299
+ }
16300
+ const fatal = [];
16301
+ const protectedNames = new Set;
16302
+ for (const [name, entry] of Object.entries(raw).toSorted(([a], [b]) => a < b ? -1 : 1)) {
16303
+ const label = `release track ${JSON.stringify(name)}`;
16304
+ if (!isRecord3(entry)) {
16305
+ out.issues.push(`${label} is not an object; ignored`);
16306
+ continue;
16307
+ }
16308
+ for (const branch of namedBranches(entry))
16309
+ protectedNames.add(branch);
16310
+ if (!name.trim()) {
16311
+ out.issues.push("a release track with a blank name was ignored");
16312
+ continue;
16313
+ }
16314
+ let critical = [];
16315
+ if (Array.isArray(entry.critical))
16316
+ critical = entry.critical.filter((item) => typeof item === "string");
16317
+ else if (entry.critical !== undefined)
16318
+ out.issues.push(`${label} critical: must be a list of field names; ignored`);
16319
+ const isCritical = (field) => critical.some((item) => field === item || field.startsWith(`${item}.`));
16320
+ const problem = (field, message) => {
16321
+ if (isCritical(field))
16322
+ fatal.push(`${label} ${field}: ${message}`);
16323
+ else
16324
+ out.issues.push(`${label} ${field}: ${message}`);
16325
+ };
16326
+ for (const key of Object.keys(entry))
16327
+ if (!KNOWN_FIELDS.has(key))
16328
+ problem(key, "unknown field, ignored");
16329
+ const productionBranch = branchName(entry.productionBranch);
16330
+ const integrationBranch = branchName(entry.integrationBranch);
16331
+ if (!productionBranch)
16332
+ problem("productionBranch", "missing or not a branch name");
16333
+ if (!integrationBranch)
16334
+ problem("integrationBranch", "missing or not a branch name");
16335
+ const optionalBranch = (field) => {
16336
+ if (entry[field] === undefined)
16337
+ return null;
16338
+ const value = branchName(entry[field]);
16339
+ if (!value)
16340
+ problem(field, "not a branch name; using the integration branch");
16341
+ return value;
16342
+ };
16343
+ const baseBranch = optionalBranch("baseBranch");
16344
+ const pullRequestTarget = optionalBranch("pullRequestTarget");
16345
+ let mergeBackBranches = [];
16346
+ if (entry.mergeBackBranches !== undefined) {
16347
+ const list = Array.isArray(entry.mergeBackBranches) ? entry.mergeBackBranches.map(branchName) : [null];
16348
+ if (list.some((item) => item === null))
16349
+ problem("mergeBackBranches", "must be a list of branch names; ignored");
16350
+ else
16351
+ mergeBackBranches = [...new Set(list)];
16352
+ }
16353
+ const naming = {};
16354
+ if (entry.naming !== undefined) {
16355
+ if (!isRecord3(entry.naming))
16356
+ problem("naming", "not an object; ignored");
16357
+ else
16358
+ for (const kind of ["feature", "release", "hotfix"]) {
16359
+ const template = entry.naming[kind];
16360
+ if (template === undefined)
16361
+ continue;
16362
+ if (typeof template !== "string" || !template.trim())
16363
+ problem(`naming.${kind}`, "not a template string; ignored");
16364
+ else if (!literalOf(template))
16365
+ problem(`naming.${kind}`, "needs literal text besides placeholders; ignored");
16366
+ else
16367
+ naming[kind] = template.trim();
16368
+ }
16369
+ }
16370
+ if (!productionBranch || !integrationBranch) {
16371
+ out.issues.push(`${label} was skipped: it needs a productionBranch and integrationBranch`);
16372
+ continue;
16373
+ }
16374
+ out.tracks.push({
16375
+ name,
16376
+ productionBranch,
16377
+ integrationBranch,
16378
+ baseBranch: baseBranch ?? integrationBranch,
16379
+ pullRequestTarget: pullRequestTarget ?? integrationBranch,
16380
+ mergeBackBranches,
16381
+ naming
16382
+ });
16383
+ }
16384
+ out.protectedBranches = [...protectedNames];
16385
+ if (fatal.length)
16386
+ out.error = `workspace releaseTracks cannot be read by this Workit (${fatal.join("; ")}); upgrade Workit or fix the track`;
16387
+ return out;
16388
+ }
16389
+ function workspaceReleaseTracks(cwd) {
16390
+ const raw = resolveRuntimeReleaseTracks(cwd);
16391
+ return { workspace: raw?.workspace ?? null, ...readReleaseTracks(raw?.releaseTracks) };
16392
+ }
16393
+ var ReleaseTrackError, KNOWN_FIELDS, BRANCH_FIELDS, PLACEHOLDER, HAS_PLACEHOLDER, isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), branchName = (value) => typeof value === "string" && value.trim() && !value.trim().startsWith("-") ? value.trim() : null, namedBranches = (entry) => [
16394
+ ...BRANCH_FIELDS.map((field) => branchName(entry[field])),
16395
+ ...Array.isArray(entry.mergeBackBranches) ? entry.mergeBackBranches.map(branchName) : []
16396
+ ].filter((name) => name !== null), literalOf = (template) => template.replace(PLACEHOLDER, "").replace(/[*/]/gu, "").trim(), trackBranches = (tracks) => [
16397
+ ...new Set(tracks.flatMap((track) => [
16398
+ track.productionBranch,
16399
+ track.integrationBranch,
16400
+ track.baseBranch,
16401
+ track.pullRequestTarget,
16402
+ ...track.mergeBackBranches
16403
+ ]))
16404
+ ], namingGlob = (template) => HAS_PLACEHOLDER.test(template) ? template.replace(PLACEHOLDER, "*") : `${template.replace(/\/?\*?$/u, "")}/*`, memo3;
16405
+ var init_release_tracks = __esm(() => {
16406
+ init_rev();
16407
+ init_config();
16408
+ init_workspaces();
16409
+ ReleaseTrackError = class ReleaseTrackError extends Error {
16410
+ };
16411
+ KNOWN_FIELDS = new Set([
16412
+ "strategy",
16413
+ "productionBranch",
16414
+ "integrationBranch",
16415
+ "naming",
16416
+ "baseBranch",
16417
+ "mergeBackBranches",
16418
+ "pullRequestTarget",
16419
+ "tagNamespace",
16420
+ "versionSource",
16421
+ "requiredChecks",
16422
+ "critical"
16423
+ ]);
16424
+ BRANCH_FIELDS = ["productionBranch", "integrationBranch", "baseBranch", "pullRequestTarget"];
16425
+ PLACEHOLDER = /\{[^}]*\}/gu;
16426
+ HAS_PLACEHOLDER = /\{[^}]*\}/u;
16427
+ memo3 = new Map;
16428
+ });
16429
+
16228
16430
  // packages/workit-core/src/core/vcs-config.ts
16229
16431
  var init_vcs_config = __esm(() => {
16230
16432
  init_config();
16231
16433
  init_workspaces();
16232
- init_branch();
16434
+ init_release_tracks();
16233
16435
  });
16234
16436
 
16235
16437
  // packages/workit-core/src/core/branch.ts
@@ -16238,14 +16440,37 @@ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
16238
16440
  const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
16239
16441
  const config = readConfig();
16240
16442
  return kind === "branch" ? {
16241
- branchPolicy: resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null),
16443
+ branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null)),
16242
16444
  provenance: { branchPolicy: selected.source }
16243
16445
  } : {
16244
16446
  commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
16245
16447
  provenance: { commitPolicy: selected.source }
16246
16448
  };
16247
16449
  }
16248
- var validateBranchNamePolicy = (name, policy, source) => {
16450
+ var withReleaseTracks = (workspaceRoot, policy) => {
16451
+ const read = workspaceReleaseTracks(workspaceRoot);
16452
+ if (read.error)
16453
+ throw new ReleaseTrackError(read.error);
16454
+ if (read.tracks.length === 0 && read.protectedBranches.length === 0)
16455
+ return policy;
16456
+ const known = new Set(policy.allowed.map((pattern) => pattern.source));
16457
+ const allowed = [...policy.allowed];
16458
+ for (const glob of read.tracks.flatMap((track) => Object.values(track.naming).map(namingGlob))) {
16459
+ const pattern = branchGlobPattern(glob);
16460
+ if (!known.has(pattern.source)) {
16461
+ known.add(pattern.source);
16462
+ allowed.push(pattern);
16463
+ }
16464
+ }
16465
+ return {
16466
+ ...policy,
16467
+ allowed,
16468
+ protected: new Set([
16469
+ ...policy.protected,
16470
+ ...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
16471
+ ])
16472
+ };
16473
+ }, validateBranchNamePolicy = (name, policy, source) => {
16249
16474
  const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
16250
16475
  if (policy.protected.has(name.toLowerCase())) {
16251
16476
  const correction = `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
@@ -16305,6 +16530,7 @@ var init_branch = __esm(() => {
16305
16530
  init_config();
16306
16531
  init_workspaces();
16307
16532
  init_vcs_config();
16533
+ init_release_tracks();
16308
16534
  });
16309
16535
 
16310
16536
  // packages/workit-core/src/core/route-intent.ts
@@ -16598,7 +16824,7 @@ var init_write_gate = __esm(() => {
16598
16824
  // packages/workit-core/src/hooks/hosts/fields.ts
16599
16825
  import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
16600
16826
  import path17 from "node:path";
16601
- var isRecord3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
16827
+ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
16602
16828
  if (!nonEmpty2(value) || !path17.isAbsolute(value) || !existsSync6(value))
16603
16829
  return null;
16604
16830
  try {
@@ -16625,7 +16851,7 @@ var isRecord3 = (value) => value !== null && typeof value === "object" && !Array
16625
16851
  const joined = argv.map(quoteArg).join(" ");
16626
16852
  return nonEmpty2(joined) ? joined : null;
16627
16853
  }, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, writePaths = (input) => {
16628
- if (!isRecord3(input))
16854
+ if (!isRecord4(input))
16629
16855
  return [];
16630
16856
  const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
16631
16857
  for (const value of Object.values(input))
@@ -16757,7 +16983,7 @@ var init_handle = __esm(() => {
16757
16983
  });
16758
16984
 
16759
16985
  // packages/workit-core/src/hooks/hosts/claude-code.ts
16760
- var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, toolCommand = (value) => isRecord3(value.tool_input) ? commandText(value.tool_input.command) : null, eventOf = (name, value) => {
16986
+ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null, eventOf = (name, value) => {
16761
16987
  const toolUseId = optionalText(value.tool_use_id);
16762
16988
  switch (name) {
16763
16989
  case "SessionStart":
@@ -16786,7 +17012,7 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, toolCommand = (value)
16786
17012
  const command = toolCommand(value);
16787
17013
  if (!SHELL_TOOLS.has(String(value.tool_name)) || !command)
16788
17014
  return { ok: false, error: "only shell PostToolUse is mapped" };
16789
- const response = isRecord3(value.tool_response) ? value.tool_response : {};
17015
+ const response = isRecord4(value.tool_response) ? value.tool_response : {};
16790
17016
  return {
16791
17017
  ok: true,
16792
17018
  event: {
@@ -16961,14 +17187,14 @@ var init_claude_code = __esm(() => {
16961
17187
  claudeCodeAdapter = {
16962
17188
  descriptor: CLAUDE_CODE_DESCRIPTOR,
16963
17189
  parse(raw) {
16964
- const native = isRecord3(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
17190
+ const native = isRecord4(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
16965
17191
  const fail = (error) => ({
16966
17192
  ok: false,
16967
17193
  error,
16968
17194
  native,
16969
17195
  event: native ? EVENTS[native] : null
16970
17196
  });
16971
- if (!isRecord3(raw) || !native)
17197
+ if (!isRecord4(raw) || !native)
16972
17198
  return fail("hook_event_name is required");
16973
17199
  if (!nonEmpty2(raw.session_id))
16974
17200
  return fail("session_id is required");
@@ -17005,7 +17231,7 @@ function detectCodexSurface(env) {
17005
17231
  return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
17006
17232
  }
17007
17233
  var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase()), SOURCES2, parseCodexHookInput = (value) => {
17008
- if (!isRecord3(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
17234
+ if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
17009
17235
  return { ok: false, error: "hook_event_name is required" };
17010
17236
  const event = value.hook_event_name;
17011
17237
  const cwd = existingDirectory(value.cwd);
@@ -17013,7 +17239,7 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
17013
17239
  return { ok: false, error: "cwd must be an existing absolute directory" };
17014
17240
  if (event === "PreToolUse" && !nonEmpty2(value.tool_name))
17015
17241
  return { ok: false, error: "tool_name is required" };
17016
- const command = isRecord3(value.tool_input) ? commandText(value.tool_input.command) : null;
17242
+ const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
17017
17243
  if (event === "PreToolUse" && isShellTool(value.tool_name) && !command)
17018
17244
  return { ok: false, error: "tool_input.command is required for shell tools" };
17019
17245
  if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
@@ -17200,7 +17426,7 @@ var init_codex = __esm(() => {
17200
17426
  const host = detectCodexSurface(env);
17201
17427
  const parsed = parseCodexHookInput(raw);
17202
17428
  if (!parsed.ok) {
17203
- const native = isRecord3(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
17429
+ const native = isRecord4(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
17204
17430
  return { ok: false, error: parsed.error, native, event: native ? EVENTS2[native] : null };
17205
17431
  }
17206
17432
  const input = parsed.data;
@@ -17230,7 +17456,7 @@ var init_codex = __esm(() => {
17230
17456
  // packages/workit-core/src/hooks/hosts/cursor.ts
17231
17457
  import path18 from "node:path";
17232
17458
  var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
17233
- if (!isRecord3(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
17459
+ if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
17234
17460
  return { ok: false, error: "hook_event_name is required" };
17235
17461
  const roots = value.workspace_roots;
17236
17462
  if (!Array.isArray(roots) || roots.length === 0 || !roots.every(nonEmpty2))
@@ -17442,7 +17668,7 @@ var init_cursor = __esm(() => {
17442
17668
  parse(raw) {
17443
17669
  const parsed = parseCursorHookInput(raw);
17444
17670
  if (!parsed.ok) {
17445
- const native = isRecord3(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
17671
+ const native = isRecord4(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
17446
17672
  return { ok: false, error: parsed.error, native, event: native ? EVENTS3[native] : null };
17447
17673
  }
17448
17674
  const input = parsed.data;