@brainervirus/workit-core 0.8.2 → 0.8.4
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 +1 -1
- package/src/core/flow-state.ts +14 -3
- package/src/core/pr-create.ts +34 -3
- package/src/core/reminder.ts +4 -2
- package/src/core/vcs-config.ts +10 -3
package/package.json
CHANGED
package/src/core/flow-state.ts
CHANGED
|
@@ -1271,9 +1271,20 @@ export class HostReceiptStore {
|
|
|
1271
1271
|
}
|
|
1272
1272
|
}
|
|
1273
1273
|
|
|
1274
|
-
/** Menu labels compare
|
|
1275
|
-
*
|
|
1276
|
-
|
|
1274
|
+
/** Menu labels compare semantically: hosts decorate choices with
|
|
1275
|
+
* parenthesized qualifiers ("Handoff (new session only)") that the enum does
|
|
1276
|
+
* not carry, so we strip them, trim, collapse whitespace, and lowercase both
|
|
1277
|
+
* sides before comparing. Only the comparison normalizes — the stored label
|
|
1278
|
+
* and evidence bytes are preserved verbatim. */
|
|
1279
|
+
const sameChoiceLabel = (a: string, b: string): boolean => normalizeLabel(a) === normalizeLabel(b);
|
|
1280
|
+
|
|
1281
|
+
const normalizeLabel = (s: string): string =>
|
|
1282
|
+
s
|
|
1283
|
+
.replace(/\s*\([^)]*\)/g, " ")
|
|
1284
|
+
.replace(/\s*\bfirst\b\s*$/i, " ")
|
|
1285
|
+
.replace(/[^a-z0-9]+/gi, " ")
|
|
1286
|
+
.trim()
|
|
1287
|
+
.toLowerCase();
|
|
1277
1288
|
|
|
1278
1289
|
/** Derive the evidence record from a consumed host receipt (AR-12). */
|
|
1279
1290
|
export const createOpenCodeEvidence = (receipt: HostReceipt): OpenCodeChoiceEvidence => ({
|
package/src/core/pr-create.ts
CHANGED
|
@@ -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
|
|
151
|
-
|
|
152
|
-
|
|
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");
|
package/src/core/reminder.ts
CHANGED
|
@@ -4,8 +4,10 @@ import { DESTINATION_MENU_LABELS, HANDOFF_DESTINATION_MARKER, SOURCE_MENU_LABELS
|
|
|
4
4
|
// the reminder PROSE like the other source surfaces (bootstrap.ts, session-start,
|
|
5
5
|
// superpowers-doc-contract.md, ask-question-only.mdc), never in the machine
|
|
6
6
|
// label tuple `SOURCE_MENU_LABELS` — the receipt label must stay exactly
|
|
7
|
-
// `Handoff` for the native-question match (AR-12).
|
|
8
|
-
|
|
7
|
+
// `Handoff` for the native-question match (AR-12). Exported so contract tests
|
|
8
|
+
// assert the rendered reminder against this single source of truth instead of
|
|
9
|
+
// re-deriving the mapping.
|
|
10
|
+
export const SOURCE_MENU_LABELS_DISPLAY = SOURCE_MENU_LABELS.map((label) =>
|
|
9
11
|
label === "Handoff" ? "Handoff (new session only)" : label,
|
|
10
12
|
);
|
|
11
13
|
|
package/src/core/vcs-config.ts
CHANGED
|
@@ -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;
|