@brainervirus/workit-codex 8.14.1 → 8.15.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workit",
3
- "version": "8.14.1",
3
+ "version": "8.15.0",
4
4
  "description": "Adaptive engineering workflow rails for Codex",
5
5
  "author": {
6
6
  "name": "BrainerVirus"
@@ -19627,20 +19627,19 @@ var workspaceConfigSchema = object({
19627
19627
  union([boolean2(), literal("verified")]).optional()
19628
19628
  ]))).passthrough().optional(),
19629
19629
  youtrack: object({ baseUrl: nonBlank.optional(), link_issues: boolean2().optional() }).passthrough().optional(),
19630
- issues: object({ provider: literal("github").optional(), link_on_pr: boolean2().optional() }).passthrough().optional(),
19630
+ issues: object({
19631
+ provider: _enum(["github", "gitlab", "youtrack"]).optional(),
19632
+ link: boolean2().optional(),
19633
+ relation: unknown().optional(),
19634
+ backlink: boolean2().optional(),
19635
+ link_on_pr: boolean2().optional()
19636
+ }).passthrough().optional(),
19631
19637
  branchPolicy: workspaceBranchPolicySchema.optional(),
19632
19638
  commitPolicy: workspaceCommitPolicySchema.optional(),
19633
19639
  defaultProfile: nonBlank.optional(),
19634
19640
  profiles: record(nonBlank, workspaceProfileSchema).optional(),
19635
19641
  releaseTracks: record(nonBlank, releaseTrackSchema).optional()
19636
19642
  }).passthrough().superRefine((workspace, context) => {
19637
- if (workspace.issues && workspace.vcs?.provider !== "github") {
19638
- context.addIssue({
19639
- code: "custom",
19640
- path: ["issues"],
19641
- message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
19642
- });
19643
- }
19644
19643
  if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
19645
19644
  context.addIssue({
19646
19645
  code: "custom",
@@ -20480,6 +20479,44 @@ var readVcsConfig = () => {
20480
20479
  }
20481
20480
  return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
20482
20481
  };
20482
+ var TRACKERS = new Set(["github", "gitlab", "youtrack"]);
20483
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
20484
+ var flag = (value) => typeof value === "boolean" ? value : null;
20485
+ var text2 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
20486
+ function issueTrackerOf(issues, youtrack) {
20487
+ const iss = asRecord(issues);
20488
+ const yt = asRecord(youtrack);
20489
+ const warnings = [];
20490
+ const named = typeof iss.provider === "string" && TRACKERS.has(iss.provider.toLowerCase()) ? iss.provider.toLowerCase() : null;
20491
+ if (iss.relation !== undefined && iss.relation !== "closes" && iss.relation !== "refs")
20492
+ warnings.push(`issues.relation ${JSON.stringify(iss.relation)} is not closes or refs; read as closes`);
20493
+ for (const key of ["repo", "host"])
20494
+ if (iss[key] !== undefined && text2(iss[key]) === null)
20495
+ warnings.push(`issues.${key} is not a non-empty string; ignored`);
20496
+ const common = {
20497
+ relation: iss.relation === "refs" ? "refs" : "closes",
20498
+ backlink: iss.backlink === true,
20499
+ baseUrl: text2(yt.baseUrl),
20500
+ repo: text2(iss.repo)?.replace(/^\/+|\/+$/gu, "") ?? null,
20501
+ host: text2(iss.host)?.toLowerCase() ?? null,
20502
+ warnings
20503
+ };
20504
+ const legacyGithub = named === "github" && flag(iss.link) === null;
20505
+ if (flag(yt.link_issues) === true && (named === null || legacyGithub))
20506
+ return { provider: "youtrack", link: true, ...common, source: "youtrack.link_issues" };
20507
+ if (named) {
20508
+ const link = flag(iss.link) ?? flag(iss.link_on_pr) ?? (named === "youtrack" ? flag(yt.link_issues) : null) ?? false;
20509
+ return {
20510
+ provider: named,
20511
+ link,
20512
+ ...common,
20513
+ source: flag(iss.link) === null && flag(iss.link_on_pr) !== null ? "issues.link_on_pr" : "issues"
20514
+ };
20515
+ }
20516
+ if (common.baseUrl)
20517
+ return { provider: "youtrack", link: false, ...common, source: "youtrack.baseUrl" };
20518
+ return null;
20519
+ }
20483
20520
  var trackSummary = (resolution) => resolution.status === "resolved" ? {
20484
20521
  name: resolution.track?.name ?? null,
20485
20522
  source: resolution.source,
@@ -20500,6 +20537,7 @@ function vcsConfig(mode, cwd, options = {}) {
20500
20537
  const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
20501
20538
  const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
20502
20539
  const workspaceDefault = workspaceDefaultTarget(root, cfg.defaultTargetBranch);
20540
+ const issueTracker = issueTrackerOf(wsIssues, wsYt);
20503
20541
  const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
20504
20542
  const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
20505
20543
  let issuesProvider = null;
@@ -20532,7 +20570,8 @@ function vcsConfig(mode, cwd, options = {}) {
20532
20570
  link_issues: linkIssues,
20533
20571
  youtrack_base_url: youtrackBaseUrl,
20534
20572
  issues_provider: issuesProvider,
20535
- link_on_pr: linkOnPr
20573
+ link_on_pr: linkOnPr,
20574
+ issue_tracker: issueTracker
20536
20575
  };
20537
20576
  }
20538
20577
  if (cfgStatus === "malformed") {
@@ -20558,7 +20597,8 @@ function vcsConfig(mode, cwd, options = {}) {
20558
20597
  link_issues: linkIssues,
20559
20598
  youtrack_base_url: youtrackBaseUrl,
20560
20599
  issues_provider: issuesProvider,
20561
- link_on_pr: linkOnPr
20600
+ link_on_pr: linkOnPr,
20601
+ issue_tracker: issueTracker
20562
20602
  };
20563
20603
  if (provider === "gitlab") {
20564
20604
  out.gitlab = {
@@ -20662,7 +20702,12 @@ var MAX_LINE_BYTES = 4096;
20662
20702
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
20663
20703
  var FAILING = new Set(["failed", "blocked"]);
20664
20704
  var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
20665
- var PR_ROW_TYPES = new Set(["pr.created", "pr.status", "pr.merged"]);
20705
+ var PR_ROW_TYPES = new Set([
20706
+ "pr.created",
20707
+ "pr.status",
20708
+ "pr.merged",
20709
+ "pr.linked"
20710
+ ]);
20666
20711
  var err = (code, error, unblock) => ({
20667
20712
  ok: false,
20668
20713
  code,
@@ -22799,7 +22844,7 @@ var RISK = {
22799
22844
  };
22800
22845
  var YES = new Set(["true", "yes", "y", "1", "on"]);
22801
22846
  var NO = new Set(["false", "no", "n", "0", "off", "none"]);
22802
- var flag = (value, unknownAs) => {
22847
+ var flag2 = (value, unknownAs) => {
22803
22848
  if (typeof value === "boolean")
22804
22849
  return { ok: true, value };
22805
22850
  if (typeof value === "number" && (value === 0 || value === 1))
@@ -22865,12 +22910,12 @@ function normalizeJudgment(raw, previous) {
22865
22910
  const items = Array.isArray(value) ? value : [value];
22866
22911
  judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
22867
22912
  } else {
22868
- if (name === "needsPlan" && typeof value === "string" && !flag(value, false).ok && value.trim()) {
22913
+ if (name === "needsPlan" && typeof value === "string" && !flag2(value, false).ok && value.trim()) {
22869
22914
  judgment.needsPlan = true;
22870
22915
  judgment.refs.push(refOf(value.trim()));
22871
22916
  continue;
22872
22917
  }
22873
- const parsed = flag(value, name === "behaviorChange");
22918
+ const parsed = flag2(value, name === "behaviorChange");
22874
22919
  if (parsed.ok)
22875
22920
  judgment[name] = parsed.value;
22876
22921
  else
@@ -9946,7 +9946,7 @@ var removeIfEmpty = (dir) => {
9946
9946
  };
9947
9947
  var tokenHash = (token) => createHash4("sha256").update(token).digest("hex");
9948
9948
  function recordIssuedSession(cwd, lead, issued) {
9949
- const token = randomBytes2(18).toString("base64url");
9949
+ const token = randomBytes2(18).toString("hex");
9950
9950
  try {
9951
9951
  migrateRepoIssued(cwd);
9952
9952
  const own = hash(issued);
@@ -12697,20 +12697,19 @@ var workspaceConfigSchema = object({
12697
12697
  union([boolean2(), literal("verified")]).optional()
12698
12698
  ]))).passthrough().optional(),
12699
12699
  youtrack: object({ baseUrl: nonBlank.optional(), link_issues: boolean2().optional() }).passthrough().optional(),
12700
- issues: object({ provider: literal("github").optional(), link_on_pr: boolean2().optional() }).passthrough().optional(),
12700
+ issues: object({
12701
+ provider: _enum(["github", "gitlab", "youtrack"]).optional(),
12702
+ link: boolean2().optional(),
12703
+ relation: unknown().optional(),
12704
+ backlink: boolean2().optional(),
12705
+ link_on_pr: boolean2().optional()
12706
+ }).passthrough().optional(),
12701
12707
  branchPolicy: workspaceBranchPolicySchema.optional(),
12702
12708
  commitPolicy: workspaceCommitPolicySchema.optional(),
12703
12709
  defaultProfile: nonBlank.optional(),
12704
12710
  profiles: record(nonBlank, workspaceProfileSchema).optional(),
12705
12711
  releaseTracks: record(nonBlank, releaseTrackSchema).optional()
12706
12712
  }).passthrough().superRefine((workspace, context) => {
12707
- if (workspace.issues && workspace.vcs?.provider !== "github") {
12708
- context.addIssue({
12709
- code: "custom",
12710
- path: ["issues"],
12711
- message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
12712
- });
12713
- }
12714
12713
  if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
12715
12714
  context.addIssue({
12716
12715
  code: "custom",
@@ -13512,6 +13511,44 @@ var readVcsConfig = () => {
13512
13511
  }
13513
13512
  return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
13514
13513
  };
13514
+ var TRACKERS = new Set(["github", "gitlab", "youtrack"]);
13515
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
13516
+ var flag = (value) => typeof value === "boolean" ? value : null;
13517
+ var text2 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
13518
+ function issueTrackerOf(issues, youtrack) {
13519
+ const iss = asRecord(issues);
13520
+ const yt = asRecord(youtrack);
13521
+ const warnings = [];
13522
+ const named = typeof iss.provider === "string" && TRACKERS.has(iss.provider.toLowerCase()) ? iss.provider.toLowerCase() : null;
13523
+ if (iss.relation !== undefined && iss.relation !== "closes" && iss.relation !== "refs")
13524
+ warnings.push(`issues.relation ${JSON.stringify(iss.relation)} is not closes or refs; read as closes`);
13525
+ for (const key of ["repo", "host"])
13526
+ if (iss[key] !== undefined && text2(iss[key]) === null)
13527
+ warnings.push(`issues.${key} is not a non-empty string; ignored`);
13528
+ const common = {
13529
+ relation: iss.relation === "refs" ? "refs" : "closes",
13530
+ backlink: iss.backlink === true,
13531
+ baseUrl: text2(yt.baseUrl),
13532
+ repo: text2(iss.repo)?.replace(/^\/+|\/+$/gu, "") ?? null,
13533
+ host: text2(iss.host)?.toLowerCase() ?? null,
13534
+ warnings
13535
+ };
13536
+ const legacyGithub = named === "github" && flag(iss.link) === null;
13537
+ if (flag(yt.link_issues) === true && (named === null || legacyGithub))
13538
+ return { provider: "youtrack", link: true, ...common, source: "youtrack.link_issues" };
13539
+ if (named) {
13540
+ const link = flag(iss.link) ?? flag(iss.link_on_pr) ?? (named === "youtrack" ? flag(yt.link_issues) : null) ?? false;
13541
+ return {
13542
+ provider: named,
13543
+ link,
13544
+ ...common,
13545
+ source: flag(iss.link) === null && flag(iss.link_on_pr) !== null ? "issues.link_on_pr" : "issues"
13546
+ };
13547
+ }
13548
+ if (common.baseUrl)
13549
+ return { provider: "youtrack", link: false, ...common, source: "youtrack.baseUrl" };
13550
+ return null;
13551
+ }
13515
13552
  var trackSummary = (resolution) => resolution.status === "resolved" ? {
13516
13553
  name: resolution.track?.name ?? null,
13517
13554
  source: resolution.source,
@@ -13532,6 +13569,7 @@ function vcsConfig(mode, cwd, options = {}) {
13532
13569
  const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
13533
13570
  const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
13534
13571
  const workspaceDefault = workspaceDefaultTarget(root, cfg.defaultTargetBranch);
13572
+ const issueTracker = issueTrackerOf(wsIssues, wsYt);
13535
13573
  const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
13536
13574
  const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
13537
13575
  let issuesProvider = null;
@@ -13564,7 +13602,8 @@ function vcsConfig(mode, cwd, options = {}) {
13564
13602
  link_issues: linkIssues,
13565
13603
  youtrack_base_url: youtrackBaseUrl,
13566
13604
  issues_provider: issuesProvider,
13567
- link_on_pr: linkOnPr
13605
+ link_on_pr: linkOnPr,
13606
+ issue_tracker: issueTracker
13568
13607
  };
13569
13608
  }
13570
13609
  if (cfgStatus === "malformed") {
@@ -13590,7 +13629,8 @@ function vcsConfig(mode, cwd, options = {}) {
13590
13629
  link_issues: linkIssues,
13591
13630
  youtrack_base_url: youtrackBaseUrl,
13592
13631
  issues_provider: issuesProvider,
13593
- link_on_pr: linkOnPr
13632
+ link_on_pr: linkOnPr,
13633
+ issue_tracker: issueTracker
13594
13634
  };
13595
13635
  if (provider === "gitlab") {
13596
13636
  out.gitlab = {
@@ -13609,7 +13649,12 @@ var MAX_LINE_BYTES = 4096;
13609
13649
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
13610
13650
  var FAILING = new Set(["failed", "blocked"]);
13611
13651
  var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
13612
- var PR_ROW_TYPES = new Set(["pr.created", "pr.status", "pr.merged"]);
13652
+ var PR_ROW_TYPES = new Set([
13653
+ "pr.created",
13654
+ "pr.status",
13655
+ "pr.merged",
13656
+ "pr.linked"
13657
+ ]);
13613
13658
  var err = (code, error, unblock) => ({
13614
13659
  ok: false,
13615
13660
  code,
@@ -15759,7 +15804,7 @@ var RISK = {
15759
15804
  };
15760
15805
  var YES = new Set(["true", "yes", "y", "1", "on"]);
15761
15806
  var NO = new Set(["false", "no", "n", "0", "off", "none"]);
15762
- var flag = (value, unknownAs) => {
15807
+ var flag2 = (value, unknownAs) => {
15763
15808
  if (typeof value === "boolean")
15764
15809
  return { ok: true, value };
15765
15810
  if (typeof value === "number" && (value === 0 || value === 1))
@@ -15825,12 +15870,12 @@ function normalizeJudgment(raw, previous) {
15825
15870
  const items = Array.isArray(value) ? value : [value];
15826
15871
  judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
15827
15872
  } else {
15828
- if (name === "needsPlan" && typeof value === "string" && !flag(value, false).ok && value.trim()) {
15873
+ if (name === "needsPlan" && typeof value === "string" && !flag2(value, false).ok && value.trim()) {
15829
15874
  judgment.needsPlan = true;
15830
15875
  judgment.refs.push(refOf(value.trim()));
15831
15876
  continue;
15832
15877
  }
15833
- const parsed = flag(value, name === "behaviorChange");
15878
+ const parsed = flag2(value, name === "behaviorChange");
15834
15879
  if (parsed.ok)
15835
15880
  judgment[name] = parsed.value;
15836
15881
  else
@@ -37,6 +37,6 @@ if (quiet)
37
37
  process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
38
38
  `);
39
39
  else {
40
- const { runCodexHook } = await import("./workit-hook-m4bfzykw.js");
40
+ const { runCodexHook } = await import("./workit-hook-v4a1y79w.js");
41
41
  await runCodexHook([payload]);
42
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-codex",
3
- "version": "8.14.1",
3
+ "version": "8.15.0",
4
4
  "private": false,
5
5
  "description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, method skills, and verifier/reviewer/implementer agents",
6
6
  "keywords": [
@@ -39,8 +39,8 @@
39
39
  "build": "bun scripts/build.ts"
40
40
  },
41
41
  "devDependencies": {
42
- "@brainervirus/workit-core": "^8.14.1",
43
- "@brainervirus/workit-mcp": "^8.14.1"
42
+ "@brainervirus/workit-core": "^8.15.0",
43
+ "@brainervirus/workit-mcp": "^8.15.0"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=24"
@@ -33,8 +33,8 @@ that repeats because the base keeps moving, or after 3 failed fix attempts on
33
33
  the same check.
34
34
 
35
35
  1. **Open.** `workit git push`, then `workit pr create --title "<title>"
36
- --body-file <f|->` (idempotent; body: `references/pr-body.md`; edits:
37
- `workit pr edit`). A stack: `workit stack plan <bottom> ... <top>`, one
36
+ --body-file <f|->` plus `--issue <#n|ID>` per issue (`--refs`: no close; idempotent;
37
+ body: `references/pr-body.md`; edits: `workit pr edit`). A stack: `workit stack plan <bottom> ... <top>`, one
38
38
  `workit pr create --base <parent> --fill` per branch, then
39
39
  `workit stack sync`. Finish the whole stack before babysitting any PR.
40
40
  2. **Read state** with `workit pr status --json`. `REVIEW` alone is a pending