@brainervirus/workit-core 0.8.2 → 0.8.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "private": false,
5
5
  "description": "Workit — workflow rails for agentic coding: specs, plans, YouTrack, CI-gated commits (shared core)",
6
6
  "keywords": [
@@ -147,9 +147,15 @@ export function prCreate(env: NodeJS.ProcessEnv, cwd: string): Record<string, an
147
147
  // target is validated against the resolved branch policy so a PR can never
148
148
  // be aimed at a protected or disallowed branch.
149
149
  const targetOverride = env.WF_PR_TARGET;
150
- const target =
151
- targetOverride || String(cfg.defaultTargetBranch ?? policy.defaultTargetBranch ?? "develop");
152
- if (targetOverride) {
150
+ const resolvedDefault = String(
151
+ cfg.defaultTargetBranch ?? policy.defaultTargetBranch ?? "develop",
152
+ );
153
+ const target = targetOverride || resolvedDefault;
154
+ // CA-06: an explicit override equal to the resolved default (e.g. WF_PR_TARGET
155
+ // "main" under github-flow) is authoritative — the same value flows
156
+ // unvalidated from config, so it must not be rejected as a protected
157
+ // override. Genuine differing overrides keep the strict validation.
158
+ if (targetOverride && targetOverride !== resolvedDefault) {
153
159
  const { allowed, protected: protectedTargets } = policy;
154
160
  if (protectedTargets.has(targetOverride.toLowerCase()))
155
161
  return {
@@ -283,6 +289,31 @@ export function prCreate(env: NodeJS.ProcessEnv, cwd: string): Record<string, an
283
289
  // uv_spawn fails with ENOENT even though the CLI is on PATH.
284
290
  cmdEnv = { ...process.env, PATH: process.env.PATH ?? "", GITLAB_TOKEN: token };
285
291
  } else {
292
+ // T2: GitHub has no --push flag — push the branch first so `gh pr create`
293
+ // never runs against an unpushed branch when pushBranch is enabled.
294
+ if (push) {
295
+ if (!branch) {
296
+ return {
297
+ error: "push failed",
298
+ provider,
299
+ mode: "push",
300
+ targetBranch: target,
301
+ stderr: "empty current branch (detached HEAD or unborn HEAD)",
302
+ };
303
+ }
304
+ const pushRes = spawnSync("git", ["push", "-u", "origin", branch], {
305
+ cwd: root,
306
+ encoding: "utf8",
307
+ });
308
+ if (pushRes.status !== 0) {
309
+ return {
310
+ error: "push failed",
311
+ provider,
312
+ targetBranch: target,
313
+ stderr: (pushRes.stderr ?? "").slice(0, 800),
314
+ };
315
+ }
316
+ }
286
317
  cmd = ["gh", "pr", "create", "--title", title, "--base", target];
287
318
  if (finalBody) cmd.push("--body", finalBody);
288
319
  if (draft) cmd.push("--draft");
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
- import { configDir } from "./config";
4
+ import { configDir, PRESETS } from "./config";
5
5
  import { resolveWorkspace } from "./workspaces";
6
6
  import { resolveBranchPolicyFor } from "./branch";
7
7
  // Ports of scripts/vcs/config.sh + verify-token.sh + token-create-urls.sh + merged-style.sh.
@@ -90,10 +90,17 @@ export function vcsConfig(mode: "load" | "summary" | "resolve", cwd?: string): R
90
90
  // and resolve so both surfaces stay consistent.
91
91
  // CA-09: the one resolver wrapper — same policy resolution every consumer
92
92
  // uses, so the tightened gate in branch-policy-resolver.test.ts stays green.
93
+ // CA-02: a matched workspace's OWN branchPolicy default beats any global
94
+ // vcs.json default (PR #43: global develop shadowed the personal github-flow
95
+ // main). Explicit workspace vcs.defaultTargetBranch stays authoritative; a
96
+ // workspace without a branchPolicy still falls back to the global vcs.json
97
+ // default, and unmatched repos keep it too.
98
+ const wp = (ws?.branchPolicy ?? {}) as Record<string, any>;
99
+ const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
100
+ const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
93
101
  const defaultTarget = String(
94
102
  wsVcs.defaultTargetBranch ??
95
- cfg.defaultTargetBranch ??
96
- resolveBranchPolicyFor(root).defaultTargetBranch ??
103
+ (hasWorkspacePolicy ? policyDefault : (cfg.defaultTargetBranch ?? policyDefault)) ??
97
104
  "develop",
98
105
  );
99
106
  const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;