@brainervirus/workit-codex 8.0.1 → 8.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workit",
3
- "version": "8.0.1",
3
+ "version": "8.1.1",
4
4
  "description": "Adaptive engineering workflow rails for Codex",
5
5
  "author": {
6
6
  "name": "BrainerVirus"
@@ -6625,7 +6625,7 @@ import { existsSync as existsSync9, realpathSync as realpathSync7, statSync as s
6625
6625
  import path21 from "node:path";
6626
6626
 
6627
6627
  // packages/workit-core/src/core.ts
6628
- import { spawnSync as spawnSync12 } from "node:child_process";
6628
+ import { spawnSync as spawnSync14 } from "node:child_process";
6629
6629
  import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
6630
6630
  import path19 from "node:path";
6631
6631
 
@@ -20315,73 +20315,8 @@ var exportDigest = (bundle) => sha256({
20315
20315
  sourceWorkspaceId: bundle.sourceWorkspaceId,
20316
20316
  task: bundle.task
20317
20317
  });
20318
- // packages/workit-core/src/core/git.ts
20319
- import { execFileSync } from "node:child_process";
20320
- var run = (cwd, args) => {
20321
- try {
20322
- const stdout = execFileSync("git", args, {
20323
- cwd,
20324
- encoding: "utf8",
20325
- stdio: ["pipe", "pipe", "pipe"]
20326
- });
20327
- return { stdout: stdout.trimEnd(), stderr: "", exitCode: 0 };
20328
- } catch (error) {
20329
- const e = error;
20330
- return {
20331
- stdout: (e.stdout ?? "").trimEnd(),
20332
- stderr: (e.stderr ?? "").trimEnd(),
20333
- exitCode: e.status ?? 1
20334
- };
20335
- }
20336
- };
20337
- var gitContext = (workspaceRoot, paths = []) => {
20338
- const cwd = workspaceRoot;
20339
- let failure;
20340
- const runHere = (args) => {
20341
- const result = run(cwd, args);
20342
- if (!failure && result.exitCode !== 0) {
20343
- failure = { stderr: result.stderr, exitCode: result.exitCode };
20344
- }
20345
- return result.stdout;
20346
- };
20347
- const branch = runHere(["rev-parse", "--abbrev-ref", "HEAD"]) || "unknown";
20348
- const status_short = runHere(["status", "--porcelain"]);
20349
- const staged = [];
20350
- const unstaged = [];
20351
- const untracked = [];
20352
- for (const line of status_short.split(`
20353
- `).filter(Boolean)) {
20354
- const code = line.slice(0, 2);
20355
- const file = line.slice(3);
20356
- if (code === "??") {
20357
- untracked.push(file);
20358
- } else {
20359
- if (code[0] !== " " && code[0] !== "?")
20360
- staged.push(file);
20361
- if (code[1] !== " " && code[1] !== "?")
20362
- unstaged.push(file);
20363
- }
20364
- }
20365
- const pathArgs = paths.length ? ["--", ...paths] : [];
20366
- const diff_stat = runHere(["diff", "--stat", ...pathArgs]);
20367
- const cached_stat = runHere(["diff", "--cached", "--stat", ...pathArgs]);
20368
- const stagedSet = new Set(staged);
20369
- const unstagedSet = new Set(unstaged);
20370
- const partial_staged = [...stagedSet].filter((f) => unstagedSet.has(f));
20371
- return {
20372
- workspace_root: cwd,
20373
- branch,
20374
- status_short,
20375
- staged,
20376
- unstaged,
20377
- untracked,
20378
- diff_stat: [diff_stat, cached_stat].filter(Boolean).join(`
20379
- `),
20380
- partial_staged: partial_staged.length > 0,
20381
- partial_staged_files: partial_staged,
20382
- ...failure ? { stderr: failure.stderr, exitCode: failure.exitCode } : {}
20383
- };
20384
- };
20318
+ // packages/workit-core/src/core/branch-policy.ts
20319
+ import { spawnSync as spawnSync7 } from "node:child_process";
20385
20320
 
20386
20321
  // packages/workit-core/src/core/config.ts
20387
20322
  import {
@@ -20507,6 +20442,7 @@ var DEFAULTS = {
20507
20442
  },
20508
20443
  commitPolicy: { preset: "conventional" }
20509
20444
  };
20445
+ var unchosenBranchPolicy = new WeakSet([DEFAULTS]);
20510
20446
  var readSafe = (p) => {
20511
20447
  try {
20512
20448
  return readFileSync3(p, "utf8");
@@ -20531,22 +20467,21 @@ var parseConfigResult = (raw, file) => {
20531
20467
  const locale = LOCALE_RE.test(String(input.locale ?? "")) ? input.locale : DEFAULTS.locale;
20532
20468
  const preset = Object.hasOwn(PRESETS, input.branchPolicy?.preset) ? input.branchPolicy?.preset : "gitflow";
20533
20469
  const commitPreset = COMMIT_PRESETS.includes(String(input.commitPolicy?.preset ?? "")) ? input.commitPolicy?.preset : "conventional";
20534
- return {
20535
- status: "valid",
20536
- path: file,
20537
- config: {
20538
- locale,
20539
- localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
20540
- branchPolicy: mergePreset(preset, {
20541
- allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
20542
- protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
20543
- }, DEFAULTS),
20544
- commitPolicy: {
20545
- preset: commitPreset,
20546
- ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
20547
- }
20470
+ const config = {
20471
+ locale,
20472
+ localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
20473
+ branchPolicy: mergePreset(preset, {
20474
+ allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
20475
+ protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
20476
+ }, DEFAULTS),
20477
+ commitPolicy: {
20478
+ preset: commitPreset,
20479
+ ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
20548
20480
  }
20549
20481
  };
20482
+ if (!Object.hasOwn(PRESETS, input.branchPolicy?.preset))
20483
+ unchosenBranchPolicy.add(config);
20484
+ return { status: "valid", path: file, config };
20550
20485
  };
20551
20486
  var readConfigTyped = (dir) => {
20552
20487
  const file = path12.join(dir ?? configDir(), "config.json");
@@ -20560,9 +20495,11 @@ var readConfig = () => {
20560
20495
  };
20561
20496
  var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path12.join(configDir(), "vcs.json");
20562
20497
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
20563
- var resolveBranchPolicy = (config, workspace) => {
20498
+ var resolveBranchPolicy = (config, workspace, repoPreset) => {
20564
20499
  const wp = workspace?.branchPolicy ?? {};
20565
- const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
20500
+ const workspacePreset = Object.hasOwn(PRESETS, wp.preset);
20501
+ const detected = !workspacePreset && unchosenBranchPolicy.has(config) ? repoPreset?.() ?? null : null;
20502
+ const preset = workspacePreset ? wp.preset : detected ?? config.branchPolicy?.preset ?? "gitflow";
20566
20503
  const merged = mergePreset(preset, {
20567
20504
  allowed: wp.allowed,
20568
20505
  protectedNames: wp.protected
@@ -20571,9 +20508,10 @@ var resolveBranchPolicy = (config, workspace) => {
20571
20508
  return {
20572
20509
  preset,
20573
20510
  allowed,
20574
- protected: new Set(merged.protected.map((p) => p.toLowerCase())),
20511
+ protected: new Set([...merged.protected, ...detected ? PRESETS.gitflow.protected : []].map((p) => p.toLowerCase())),
20575
20512
  integration: wp.integration === "merge" ? "merge" : "pr",
20576
- defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop"
20513
+ defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop",
20514
+ detected: detected !== null
20577
20515
  };
20578
20516
  };
20579
20517
  var COMMIT_PRESETS = [
@@ -20585,7 +20523,140 @@ var COMMIT_PRESETS = [
20585
20523
  "auto"
20586
20524
  ];
20587
20525
 
20526
+ // packages/workit-core/src/core/branch-policy.ts
20527
+ var PREFIXES = {
20528
+ feature: "feature/*",
20529
+ bugfix: "bugfix/*",
20530
+ release: "release/*",
20531
+ hotfix: "hotfix/*"
20532
+ };
20533
+ var branchNames = (workspaceRoot) => {
20534
+ const r = spawnSync7("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/remotes/origin"], { cwd: workspaceRoot, encoding: "utf8" });
20535
+ if (r.status !== 0)
20536
+ return new Set;
20537
+ return new Set((r.stdout ?? "").split(`
20538
+ `).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
20539
+ };
20540
+ var detectBranchPolicy = (workspaceRoot) => {
20541
+ const names = branchNames(workspaceRoot);
20542
+ const develop = names.has("develop");
20543
+ const main = names.has("main");
20544
+ const master = names.has("master");
20545
+ const root = main ? "main" : master ? "master" : null;
20546
+ if (develop) {
20547
+ return {
20548
+ preset: "gitflow",
20549
+ developBranch: "develop",
20550
+ integration: "merge",
20551
+ protected: root ? [root, "develop"] : ["develop"],
20552
+ allowed: [...PRESETS.gitflow.allowed],
20553
+ prefixes: PREFIXES
20554
+ };
20555
+ }
20556
+ if (root === "main") {
20557
+ return {
20558
+ preset: "github-flow",
20559
+ developBranch: null,
20560
+ integration: "pr",
20561
+ protected: [root],
20562
+ allowed: ["*"],
20563
+ prefixes: PREFIXES
20564
+ };
20565
+ }
20566
+ if (root === "master") {
20567
+ return {
20568
+ preset: "trunk-based",
20569
+ developBranch: null,
20570
+ integration: "pr",
20571
+ protected: [root],
20572
+ allowed: ["*"],
20573
+ prefixes: PREFIXES
20574
+ };
20575
+ }
20576
+ return {
20577
+ preset: "gitflow",
20578
+ developBranch: null,
20579
+ integration: "merge",
20580
+ protected: [],
20581
+ allowed: [],
20582
+ prefixes: PREFIXES
20583
+ };
20584
+ };
20585
+ var repoBranchPreset = (workspaceRoot) => {
20586
+ const detected = detectBranchPolicy(workspaceRoot);
20587
+ return detected.developBranch || detected.allowed.length ? detected.preset : null;
20588
+ };
20589
+
20590
+ // packages/workit-core/src/core/git.ts
20591
+ import { execFileSync } from "node:child_process";
20592
+ var run = (cwd, args) => {
20593
+ try {
20594
+ const stdout = execFileSync("git", args, {
20595
+ cwd,
20596
+ encoding: "utf8",
20597
+ stdio: ["pipe", "pipe", "pipe"]
20598
+ });
20599
+ return { stdout: stdout.trimEnd(), stderr: "", exitCode: 0 };
20600
+ } catch (error) {
20601
+ const e = error;
20602
+ return {
20603
+ stdout: (e.stdout ?? "").trimEnd(),
20604
+ stderr: (e.stderr ?? "").trimEnd(),
20605
+ exitCode: e.status ?? 1
20606
+ };
20607
+ }
20608
+ };
20609
+ var gitContext = (workspaceRoot, paths = []) => {
20610
+ const cwd = workspaceRoot;
20611
+ let failure;
20612
+ const runHere = (args) => {
20613
+ const result = run(cwd, args);
20614
+ if (!failure && result.exitCode !== 0) {
20615
+ failure = { stderr: result.stderr, exitCode: result.exitCode };
20616
+ }
20617
+ return result.stdout;
20618
+ };
20619
+ const branch = runHere(["rev-parse", "--abbrev-ref", "HEAD"]) || "unknown";
20620
+ const status_short = runHere(["status", "--porcelain"]);
20621
+ const staged = [];
20622
+ const unstaged = [];
20623
+ const untracked = [];
20624
+ for (const line of status_short.split(`
20625
+ `).filter(Boolean)) {
20626
+ const code = line.slice(0, 2);
20627
+ const file = line.slice(3);
20628
+ if (code === "??") {
20629
+ untracked.push(file);
20630
+ } else {
20631
+ if (code[0] !== " " && code[0] !== "?")
20632
+ staged.push(file);
20633
+ if (code[1] !== " " && code[1] !== "?")
20634
+ unstaged.push(file);
20635
+ }
20636
+ }
20637
+ const pathArgs = paths.length ? ["--", ...paths] : [];
20638
+ const diff_stat = runHere(["diff", "--stat", ...pathArgs]);
20639
+ const cached_stat = runHere(["diff", "--cached", "--stat", ...pathArgs]);
20640
+ const stagedSet = new Set(staged);
20641
+ const unstagedSet = new Set(unstaged);
20642
+ const partial_staged = [...stagedSet].filter((f) => unstagedSet.has(f));
20643
+ return {
20644
+ workspace_root: cwd,
20645
+ branch,
20646
+ status_short,
20647
+ staged,
20648
+ unstaged,
20649
+ untracked,
20650
+ diff_stat: [diff_stat, cached_stat].filter(Boolean).join(`
20651
+ `),
20652
+ partial_staged: partial_staged.length > 0,
20653
+ partial_staged_files: partial_staged,
20654
+ ...failure ? { stderr: failure.stderr, exitCode: failure.exitCode } : {}
20655
+ };
20656
+ };
20657
+
20588
20658
  // packages/workit-core/src/core/workspaces.ts
20659
+ import { spawnSync as spawnSync8 } from "node:child_process";
20589
20660
  import { createHash as createHash8 } from "node:crypto";
20590
20661
  import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
20591
20662
  import path13 from "node:path";
@@ -20830,6 +20901,53 @@ var validateWorkspaceGlob = (glob) => {
20830
20901
  }
20831
20902
  return { ok: true };
20832
20903
  };
20904
+
20905
+ class WorkspaceConfigError extends Error {
20906
+ name = "WorkspaceConfigError";
20907
+ }
20908
+ var mainCheckouts = new Map;
20909
+ var mainCheckoutOf = (cwd) => {
20910
+ const cached = mainCheckouts.get(cwd);
20911
+ if (cached !== undefined)
20912
+ return cached;
20913
+ const env = { ...process.env };
20914
+ for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
20915
+ delete env[name];
20916
+ const git = (args) => {
20917
+ const run = spawnSync8("git", args, {
20918
+ cwd,
20919
+ env,
20920
+ encoding: "utf8",
20921
+ timeout: 5000,
20922
+ windowsHide: true
20923
+ });
20924
+ return run.status === 0 ? run.stdout : null;
20925
+ };
20926
+ let main = null;
20927
+ const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
20928
+ const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
20929
+ if (gitDir && common && path13.resolve(gitDir) !== path13.resolve(common)) {
20930
+ const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
20931
+ main = listed?.trim() || (path13.basename(common) === ".git" ? path13.dirname(common) : common);
20932
+ }
20933
+ mainCheckouts.set(cwd, main);
20934
+ return main;
20935
+ };
20936
+ var matchingEntries = (cwd, entries) => {
20937
+ const match = (where) => {
20938
+ const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
20939
+ return entries.filter((entry) => {
20940
+ const glob = entry.glob.replaceAll("\\", "/");
20941
+ const canonical = canonicalGlob(glob);
20942
+ return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
20943
+ });
20944
+ };
20945
+ const direct = match(cwd);
20946
+ if (direct.length)
20947
+ return direct;
20948
+ const main = mainCheckoutOf(cwd);
20949
+ return main ? match(main) : direct;
20950
+ };
20833
20951
  var realpathOf = (p) => {
20834
20952
  const real = realpathSync3.native ?? realpathSync3;
20835
20953
  try {
@@ -20854,43 +20972,29 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
20854
20972
  if (named.length === 1)
20855
20973
  return named[0];
20856
20974
  if (named.length > 1)
20857
- throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
20975
+ throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
20858
20976
  if (matches.length > 0) {
20859
- throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
20977
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
20860
20978
  }
20861
- throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
20979
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
20862
20980
  }
20863
20981
  if (matches.length < 2)
20864
20982
  return matches[0];
20865
20983
  const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
20866
20984
  const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
20867
20985
  if (mostSpecific.length > 1) {
20868
- throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
20986
+ throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
20869
20987
  }
20870
20988
  return mostSpecific[0];
20871
20989
  };
20872
20990
  var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
20873
- const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
20874
- const matches = [];
20875
- for (const ws of entries) {
20876
- const glob = ws.glob.replaceAll("\\", "/");
20877
- const canonical = canonicalGlob(glob);
20878
- let matched = false;
20879
- for (const target of targets) {
20880
- if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
20881
- matched = true;
20882
- break;
20883
- }
20884
- }
20885
- if (matched)
20886
- matches.push(ws);
20887
- }
20991
+ const matches = matchingEntries(cwd, entries);
20888
20992
  return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
20889
20993
  };
20890
20994
  var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
20891
20995
  const result = readWorkspacesResult(dir);
20892
20996
  if (result.status === "malformed" || result.status === "invalid")
20893
- throw new Error(result.error);
20997
+ throw new WorkspaceConfigError(result.error);
20894
20998
  return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
20895
20999
  };
20896
21000
  var runtimeWorkspacePolicySchema = (kind) => {
@@ -20922,31 +21026,26 @@ var matchRuntimeWorkspace = (cwd) => {
20922
21026
  } catch (error) {
20923
21027
  if (error.code === "ENOENT")
20924
21028
  return null;
20925
- throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
21029
+ throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
20926
21030
  }
20927
21031
  let parsed;
20928
21032
  try {
20929
21033
  parsed = JSON.parse(raw);
20930
21034
  } catch {
20931
- throw new Error(`${file} is not valid JSON`);
21035
+ throw new WorkspaceConfigError(`${file} is not valid JSON`);
20932
21036
  }
20933
21037
  const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
20934
21038
  if (!validated.success) {
20935
21039
  const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
20936
- throw new Error(`${file} has invalid workspace matching data: ${detail}`);
21040
+ throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
20937
21041
  }
20938
21042
  const entries = validated.data.workspaces;
20939
21043
  for (const entry of entries ?? []) {
20940
21044
  const glob = validateWorkspaceGlob(entry.glob);
20941
21045
  if (!glob.ok)
20942
- throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
21046
+ throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
20943
21047
  }
20944
- const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
20945
- const matches = (entries ?? []).filter((entry) => {
20946
- const glob = entry.glob.replaceAll("\\", "/");
20947
- const canonical = canonicalGlob(glob);
20948
- return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
20949
- });
21048
+ const matches = matchingEntries(cwd, entries ?? []);
20950
21049
  const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
20951
21050
  const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
20952
21051
  return selected ? { file, selected } : null;
@@ -20963,7 +21062,7 @@ var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
20963
21062
  const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
20964
21063
  if (!policy.success) {
20965
21064
  const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
20966
- throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
21065
+ throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
20967
21066
  }
20968
21067
  return policy.data;
20969
21068
  };
@@ -20974,10 +21073,10 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
20974
21073
  const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
20975
21074
  const profileName = requestedProfile?.trim() || candidate.defaultProfile;
20976
21075
  if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
20977
- throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
21076
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
20978
21077
  const profile = profileName ? candidate.profiles?.[profileName] : undefined;
20979
21078
  if (profileName && !profile)
20980
- throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
21079
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
20981
21080
  const profilePolicy = profile?.[key];
20982
21081
  const workspacePolicy = candidate[key];
20983
21082
  return {
@@ -20990,18 +21089,18 @@ function workspaceDefaultTarget(cwd, globalDefault) {
20990
21089
  const wp = ws?.branchPolicy ?? {};
20991
21090
  const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
20992
21091
  const selected = resolveRuntimeWorkspacePolicy(cwd, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined);
20993
- const policyDefault = resolveBranchPolicy(readConfig(), selected.policy ? { branchPolicy: selected.policy } : null).defaultTargetBranch;
21092
+ const policyDefault = resolveBranchPolicy(readConfig(), selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(cwd)).defaultTargetBranch;
20994
21093
  return String(ws?.vcs?.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : globalDefault ?? policyDefault) ?? "develop");
20995
21094
  }
20996
21095
 
20997
21096
  // packages/workit-core/src/core/vcs-config.ts
20998
21097
  import fs12 from "node:fs";
20999
21098
  import path14 from "node:path";
21000
- import { spawnSync as spawnSync8 } from "node:child_process";
21099
+ import { spawnSync as spawnSync10 } from "node:child_process";
21001
21100
 
21002
21101
  // packages/workit-core/src/core/release-tracks.ts
21003
21102
  import { createHash as createHash9 } from "node:crypto";
21004
- import { spawnSync as spawnSync7 } from "node:child_process";
21103
+ import { spawnSync as spawnSync9 } from "node:child_process";
21005
21104
  var KNOWN_FIELDS = new Set([
21006
21105
  "strategy",
21007
21106
  "productionBranch",
@@ -21273,7 +21372,7 @@ function resolveReleaseTrack(input) {
21273
21372
  return fallback(`${branch} has the same or unrelated fork points with ${[...new Set(newest.map((fork) => `${fork.ref} (${fork.track.name === IMPLICIT ? "workspace default" : fork.track.name})`))].join(" and ")}`);
21274
21373
  }
21275
21374
  var git4 = (cwd, args) => {
21276
- const run = spawnSync7("git", args, {
21375
+ const run = spawnSync9("git", args, {
21277
21376
  cwd,
21278
21377
  encoding: "utf8",
21279
21378
  timeout: 20000,
@@ -21388,7 +21487,7 @@ function resolveTrackFor(cwd, options) {
21388
21487
  var workspacesPath = () => path14.join(configDir(), "workspaces.json");
21389
21488
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
21390
21489
  var remoteProvider = (cwd) => {
21391
- const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
21490
+ const r = spawnSync10("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
21392
21491
  if (r.status !== 0)
21393
21492
  return null;
21394
21493
  const url = (r.stdout ?? "").trim();
@@ -21425,7 +21524,7 @@ var remoteEndpoint = (remote) => {
21425
21524
  var configuredSshEndpoint = (host) => {
21426
21525
  if (host.startsWith("-"))
21427
21526
  return null;
21428
- const resolved = spawnSync8("ssh", ["-G", host], {
21527
+ const resolved = spawnSync10("ssh", ["-G", host], {
21429
21528
  encoding: "utf8",
21430
21529
  timeout: 5000,
21431
21530
  maxBuffer: 1024 * 1024,
@@ -21598,7 +21697,7 @@ function mergedPrStyle(limit = 6, cwd) {
21598
21697
  `).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
21599
21698
  descriptionPreview: desc.slice(0, 600)
21600
21699
  });
21601
- const remoteResult = spawnSync8("git", ["remote", "get-url", "--push", "--all", "origin"], {
21700
+ const remoteResult = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
21602
21701
  cwd,
21603
21702
  encoding: "utf8"
21604
21703
  });
@@ -21614,7 +21713,7 @@ function mergedPrStyle(limit = 6, cwd) {
21614
21713
  if (!hostingApiHostMatches(remote, host))
21615
21714
  return { ok: false, error: "PR style target does not match the provider host" };
21616
21715
  if (provider === "gitlab") {
21617
- const run = (args) => spawnSync8("glab", ["api", ...args], {
21716
+ const run = (args) => spawnSync10("glab", ["api", ...args], {
21618
21717
  cwd,
21619
21718
  encoding: "utf8",
21620
21719
  env
@@ -21634,7 +21733,7 @@ function mergedPrStyle(limit = 6, cwd) {
21634
21733
  });
21635
21734
  }
21636
21735
  } else if (provider === "github") {
21637
- const r = spawnSync8("gh", [
21736
+ const r = spawnSync10("gh", [
21638
21737
  "pr",
21639
21738
  "list",
21640
21739
  "--repo",
@@ -23571,7 +23670,7 @@ class WorkitCore {
23571
23670
  }
23572
23671
  }
23573
23672
  // packages/workit-core/src/core/repo-context.ts
23574
- import { spawnSync as spawnSync9 } from "node:child_process";
23673
+ import { spawnSync as spawnSync11 } from "node:child_process";
23575
23674
  import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
23576
23675
  import path16 from "node:path";
23577
23676
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
@@ -23600,7 +23699,7 @@ var invalidRange = (cwd) => ({
23600
23699
  cwd
23601
23700
  });
23602
23701
  var runGit = (cwd, args) => {
23603
- const result = spawnSync9("git", args, { cwd, encoding: "utf8" });
23702
+ const result = spawnSync11("git", args, { cwd, encoding: "utf8" });
23604
23703
  return {
23605
23704
  stdout: (result.stdout ?? "").trimEnd(),
23606
23705
  stderr: (result.stderr ?? "").trimEnd(),
@@ -24041,12 +24140,12 @@ function releaseNotesContext(root, rangeOrTag) {
24041
24140
  }
24042
24141
 
24043
24142
  // packages/workit-core/src/core/tracker-issues.ts
24044
- import { spawnSync as spawnSync11 } from "node:child_process";
24143
+ import { spawnSync as spawnSync13 } from "node:child_process";
24045
24144
 
24046
24145
  // packages/workit-core/src/core/pr-create.ts
24047
24146
  import fs13 from "node:fs";
24048
24147
  import path17 from "node:path";
24049
- import { spawnSync as spawnSync10 } from "node:child_process";
24148
+ import { spawnSync as spawnSync12 } from "node:child_process";
24050
24149
  // packages/workit-core/src/forge/pr-body.ts
24051
24150
  function parseGhIssue(value) {
24052
24151
  const m = /issues\/(\d+)/.exec(value);
@@ -24071,7 +24170,7 @@ function whichOnPath(tool) {
24071
24170
  }
24072
24171
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
24073
24172
  var pushRemote = (cwd) => {
24074
- const result = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
24173
+ const result = spawnSync12("git", ["remote", "get-url", "--push", "--all", "origin"], {
24075
24174
  cwd,
24076
24175
  encoding: "utf8"
24077
24176
  });
@@ -24102,7 +24201,7 @@ var defaultCli = (bin, available) => {
24102
24201
  if (!available)
24103
24202
  return null;
24104
24203
  return (args, root) => {
24105
- const out = spawnSync11(bin, args, {
24204
+ const out = spawnSync13(bin, args, {
24106
24205
  cwd: root,
24107
24206
  encoding: "utf8",
24108
24207
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -24620,7 +24719,7 @@ function resolveInside(root, candidate) {
24620
24719
  }
24621
24720
  function run2(root, executable, args, env = {}) {
24622
24721
  const cwd = realpathSync5(root);
24623
- const result = spawnSync12(executable, args, {
24722
+ const result = spawnSync14(executable, args, {
24624
24723
  cwd,
24625
24724
  encoding: "utf8",
24626
24725
  env: { ...process.env, ...env }
@@ -27404,7 +27503,7 @@ var signals = new Map;
27404
27503
  var cache = new Map;
27405
27504
  // packages/workit-core/src/hooks/write-gate.ts
27406
27505
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
27407
- var PREFIXES = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
27506
+ var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
27408
27507
  var cache2 = new Map;
27409
27508
  // packages/workit-core/src/hooks/hosts/fields.ts
27410
27509
  var SHELLS = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
@@ -13822,6 +13822,7 @@ var DEFAULTS = {
13822
13822
  },
13823
13823
  commitPolicy: { preset: "conventional" }
13824
13824
  };
13825
+ var unchosenBranchPolicy = new WeakSet([DEFAULTS]);
13825
13826
  var readSafe = (p) => {
13826
13827
  try {
13827
13828
  return readFileSync3(p, "utf8");
@@ -13846,22 +13847,21 @@ var parseConfigResult = (raw, file) => {
13846
13847
  const locale = LOCALE_RE.test(String(input.locale ?? "")) ? input.locale : DEFAULTS.locale;
13847
13848
  const preset = Object.hasOwn(PRESETS, input.branchPolicy?.preset) ? input.branchPolicy?.preset : "gitflow";
13848
13849
  const commitPreset = COMMIT_PRESETS.includes(String(input.commitPolicy?.preset ?? "")) ? input.commitPolicy?.preset : "conventional";
13849
- return {
13850
- status: "valid",
13851
- path: file,
13852
- config: {
13853
- locale,
13854
- localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
13855
- branchPolicy: mergePreset(preset, {
13856
- allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
13857
- protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
13858
- }, DEFAULTS),
13859
- commitPolicy: {
13860
- preset: commitPreset,
13861
- ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
13862
- }
13850
+ const config = {
13851
+ locale,
13852
+ localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
13853
+ branchPolicy: mergePreset(preset, {
13854
+ allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
13855
+ protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
13856
+ }, DEFAULTS),
13857
+ commitPolicy: {
13858
+ preset: commitPreset,
13859
+ ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
13863
13860
  }
13864
13861
  };
13862
+ if (!Object.hasOwn(PRESETS, input.branchPolicy?.preset))
13863
+ unchosenBranchPolicy.add(config);
13864
+ return { status: "valid", path: file, config };
13865
13865
  };
13866
13866
  var readConfigTyped = (dir) => {
13867
13867
  const file = path12.join(dir ?? configDir(), "config.json");
@@ -13874,9 +13874,11 @@ var readConfig = () => {
13874
13874
  return result.config ?? DEFAULTS;
13875
13875
  };
13876
13876
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
13877
- var resolveBranchPolicy = (config, workspace) => {
13877
+ var resolveBranchPolicy = (config, workspace, repoPreset) => {
13878
13878
  const wp = workspace?.branchPolicy ?? {};
13879
- const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
13879
+ const workspacePreset = Object.hasOwn(PRESETS, wp.preset);
13880
+ const detected = !workspacePreset && unchosenBranchPolicy.has(config) ? repoPreset?.() ?? null : null;
13881
+ const preset = workspacePreset ? wp.preset : detected ?? config.branchPolicy?.preset ?? "gitflow";
13880
13882
  const merged = mergePreset(preset, {
13881
13883
  allowed: wp.allowed,
13882
13884
  protectedNames: wp.protected
@@ -13885,9 +13887,10 @@ var resolveBranchPolicy = (config, workspace) => {
13885
13887
  return {
13886
13888
  preset,
13887
13889
  allowed,
13888
- protected: new Set(merged.protected.map((p) => p.toLowerCase())),
13890
+ protected: new Set([...merged.protected, ...detected ? PRESETS.gitflow.protected : []].map((p) => p.toLowerCase())),
13889
13891
  integration: wp.integration === "merge" ? "merge" : "pr",
13890
- defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop"
13892
+ defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop",
13893
+ detected: detected !== null
13891
13894
  };
13892
13895
  };
13893
13896
  var COMMIT_PRESETS = [
@@ -13910,9 +13913,77 @@ var resolveCommitPolicy = (config, workspace) => {
13910
13913
  };
13911
13914
 
13912
13915
  // packages/workit-core/src/core/workspaces.ts
13916
+ import { spawnSync as spawnSync8 } from "node:child_process";
13913
13917
  import { createHash as createHash8 } from "node:crypto";
13914
13918
  import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
13915
13919
  import path13 from "node:path";
13920
+
13921
+ // packages/workit-core/src/core/branch-policy.ts
13922
+ import { spawnSync as spawnSync7 } from "node:child_process";
13923
+ var PREFIXES = {
13924
+ feature: "feature/*",
13925
+ bugfix: "bugfix/*",
13926
+ release: "release/*",
13927
+ hotfix: "hotfix/*"
13928
+ };
13929
+ var branchNames = (workspaceRoot) => {
13930
+ const r = spawnSync7("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/remotes/origin"], { cwd: workspaceRoot, encoding: "utf8" });
13931
+ if (r.status !== 0)
13932
+ return new Set;
13933
+ return new Set((r.stdout ?? "").split(`
13934
+ `).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
13935
+ };
13936
+ var detectBranchPolicy = (workspaceRoot) => {
13937
+ const names = branchNames(workspaceRoot);
13938
+ const develop = names.has("develop");
13939
+ const main = names.has("main");
13940
+ const master = names.has("master");
13941
+ const root = main ? "main" : master ? "master" : null;
13942
+ if (develop) {
13943
+ return {
13944
+ preset: "gitflow",
13945
+ developBranch: "develop",
13946
+ integration: "merge",
13947
+ protected: root ? [root, "develop"] : ["develop"],
13948
+ allowed: [...PRESETS.gitflow.allowed],
13949
+ prefixes: PREFIXES
13950
+ };
13951
+ }
13952
+ if (root === "main") {
13953
+ return {
13954
+ preset: "github-flow",
13955
+ developBranch: null,
13956
+ integration: "pr",
13957
+ protected: [root],
13958
+ allowed: ["*"],
13959
+ prefixes: PREFIXES
13960
+ };
13961
+ }
13962
+ if (root === "master") {
13963
+ return {
13964
+ preset: "trunk-based",
13965
+ developBranch: null,
13966
+ integration: "pr",
13967
+ protected: [root],
13968
+ allowed: ["*"],
13969
+ prefixes: PREFIXES
13970
+ };
13971
+ }
13972
+ return {
13973
+ preset: "gitflow",
13974
+ developBranch: null,
13975
+ integration: "merge",
13976
+ protected: [],
13977
+ allowed: [],
13978
+ prefixes: PREFIXES
13979
+ };
13980
+ };
13981
+ var repoBranchPreset = (workspaceRoot) => {
13982
+ const detected = detectBranchPolicy(workspaceRoot);
13983
+ return detected.developBranch || detected.allowed.length ? detected.preset : null;
13984
+ };
13985
+
13986
+ // packages/workit-core/src/core/workspaces.ts
13916
13987
  var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
13917
13988
  var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
13918
13989
  var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
@@ -14154,6 +14225,53 @@ var validateWorkspaceGlob = (glob) => {
14154
14225
  }
14155
14226
  return { ok: true };
14156
14227
  };
14228
+
14229
+ class WorkspaceConfigError extends Error {
14230
+ name = "WorkspaceConfigError";
14231
+ }
14232
+ var mainCheckouts = new Map;
14233
+ var mainCheckoutOf = (cwd) => {
14234
+ const cached = mainCheckouts.get(cwd);
14235
+ if (cached !== undefined)
14236
+ return cached;
14237
+ const env = { ...process.env };
14238
+ for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
14239
+ delete env[name];
14240
+ const git = (args) => {
14241
+ const run = spawnSync8("git", args, {
14242
+ cwd,
14243
+ env,
14244
+ encoding: "utf8",
14245
+ timeout: 5000,
14246
+ windowsHide: true
14247
+ });
14248
+ return run.status === 0 ? run.stdout : null;
14249
+ };
14250
+ let main = null;
14251
+ const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
14252
+ const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
14253
+ if (gitDir && common && path13.resolve(gitDir) !== path13.resolve(common)) {
14254
+ const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
14255
+ main = listed?.trim() || (path13.basename(common) === ".git" ? path13.dirname(common) : common);
14256
+ }
14257
+ mainCheckouts.set(cwd, main);
14258
+ return main;
14259
+ };
14260
+ var matchingEntries = (cwd, entries) => {
14261
+ const match = (where) => {
14262
+ const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
14263
+ return entries.filter((entry) => {
14264
+ const glob = entry.glob.replaceAll("\\", "/");
14265
+ const canonical = canonicalGlob(glob);
14266
+ return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
14267
+ });
14268
+ };
14269
+ const direct = match(cwd);
14270
+ if (direct.length)
14271
+ return direct;
14272
+ const main = mainCheckoutOf(cwd);
14273
+ return main ? match(main) : direct;
14274
+ };
14157
14275
  var realpathOf = (p) => {
14158
14276
  const real = realpathSync3.native ?? realpathSync3;
14159
14277
  try {
@@ -14178,43 +14296,29 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
14178
14296
  if (named.length === 1)
14179
14297
  return named[0];
14180
14298
  if (named.length > 1)
14181
- throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
14299
+ throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
14182
14300
  if (matches.length > 0) {
14183
- throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
14301
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
14184
14302
  }
14185
- throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
14303
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
14186
14304
  }
14187
14305
  if (matches.length < 2)
14188
14306
  return matches[0];
14189
14307
  const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
14190
14308
  const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
14191
14309
  if (mostSpecific.length > 1) {
14192
- throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
14310
+ throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
14193
14311
  }
14194
14312
  return mostSpecific[0];
14195
14313
  };
14196
14314
  var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
14197
- const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
14198
- const matches = [];
14199
- for (const ws of entries) {
14200
- const glob = ws.glob.replaceAll("\\", "/");
14201
- const canonical = canonicalGlob(glob);
14202
- let matched = false;
14203
- for (const target of targets) {
14204
- if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
14205
- matched = true;
14206
- break;
14207
- }
14208
- }
14209
- if (matched)
14210
- matches.push(ws);
14211
- }
14315
+ const matches = matchingEntries(cwd, entries);
14212
14316
  return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
14213
14317
  };
14214
14318
  var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
14215
14319
  const result = readWorkspacesResult(dir);
14216
14320
  if (result.status === "malformed" || result.status === "invalid")
14217
- throw new Error(result.error);
14321
+ throw new WorkspaceConfigError(result.error);
14218
14322
  return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
14219
14323
  };
14220
14324
  var runtimeWorkspacePolicySchema = (kind) => {
@@ -14245,31 +14349,26 @@ var matchRuntimeWorkspace = (cwd) => {
14245
14349
  } catch (error) {
14246
14350
  if (error.code === "ENOENT")
14247
14351
  return null;
14248
- throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
14352
+ throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
14249
14353
  }
14250
14354
  let parsed;
14251
14355
  try {
14252
14356
  parsed = JSON.parse(raw);
14253
14357
  } catch {
14254
- throw new Error(`${file} is not valid JSON`);
14358
+ throw new WorkspaceConfigError(`${file} is not valid JSON`);
14255
14359
  }
14256
14360
  const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
14257
14361
  if (!validated.success) {
14258
14362
  const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
14259
- throw new Error(`${file} has invalid workspace matching data: ${detail}`);
14363
+ throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
14260
14364
  }
14261
14365
  const entries = validated.data.workspaces;
14262
14366
  for (const entry of entries ?? []) {
14263
14367
  const glob = validateWorkspaceGlob(entry.glob);
14264
14368
  if (!glob.ok)
14265
- throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
14369
+ throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
14266
14370
  }
14267
- const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
14268
- const matches = (entries ?? []).filter((entry) => {
14269
- const glob = entry.glob.replaceAll("\\", "/");
14270
- const canonical = canonicalGlob(glob);
14271
- return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
14272
- });
14371
+ const matches = matchingEntries(cwd, entries ?? []);
14273
14372
  const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
14274
14373
  const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
14275
14374
  return selected ? { file, selected } : null;
@@ -14286,7 +14385,7 @@ var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
14286
14385
  const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
14287
14386
  if (!policy.success) {
14288
14387
  const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
14289
- throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
14388
+ throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
14290
14389
  }
14291
14390
  return policy.data;
14292
14391
  };
@@ -14297,10 +14396,10 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
14297
14396
  const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
14298
14397
  const profileName = requestedProfile?.trim() || candidate.defaultProfile;
14299
14398
  if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
14300
- throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
14399
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
14301
14400
  const profile = profileName ? candidate.profiles?.[profileName] : undefined;
14302
14401
  if (profileName && !profile)
14303
- throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
14402
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
14304
14403
  const profilePolicy = profile?.[key];
14305
14404
  const workspacePolicy = candidate[key];
14306
14405
  return {
@@ -16468,7 +16567,7 @@ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
16468
16567
  const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
16469
16568
  const config = readConfig();
16470
16569
  return kind === "branch" ? {
16471
- branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null)),
16570
+ branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(workspaceRoot))),
16472
16571
  provenance: { branchPolicy: selected.source }
16473
16572
  } : {
16474
16573
  commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
@@ -16665,11 +16764,11 @@ var segmentsOf = (command) => {
16665
16764
  flushSegment();
16666
16765
  return segments;
16667
16766
  };
16668
- var PREFIXES = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
16767
+ var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
16669
16768
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
16670
16769
  var editsOf = (input) => {
16671
16770
  let words = input;
16672
- while (words.length && (PREFIXES.has(words[0]) || /^\w+=/.test(words[0])))
16771
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
16673
16772
  words = words.slice(1);
16674
16773
  const [verb, ...args] = words;
16675
16774
  const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-codex",
3
- "version": "8.0.1",
3
+ "version": "8.1.1",
4
4
  "private": false,
5
5
  "description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, and sixteen method skills",
6
6
  "keywords": [
@@ -38,8 +38,8 @@
38
38
  "build": "bun scripts/build.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@brainervirus/workit-core": "^8.0.1",
42
- "@brainervirus/workit-mcp": "^8.0.1"
41
+ "@brainervirus/workit-core": "^8.1.1",
42
+ "@brainervirus/workit-mcp": "^8.1.1"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=24"