@brainervirus/workit-cli 2.0.2 → 2.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.
Files changed (3) hide show
  1. package/README.md +36 -7
  2. package/dist/index.js +2337 -289
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -11780,7 +11780,7 @@ var require_react_reconciler = __commonJS(function(exports, module) {
11780
11780
  }
11781
11781
  });
11782
11782
 
11783
- // node_modules/ink/node_modules/ws/lib/constants.js
11783
+ // node_modules/ws/lib/constants.js
11784
11784
  var require_constants2 = __commonJS(function(exports, module) {
11785
11785
  var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
11786
11786
  var hasBlob = typeof Blob !== "undefined";
@@ -11800,7 +11800,7 @@ var require_constants2 = __commonJS(function(exports, module) {
11800
11800
  };
11801
11801
  });
11802
11802
 
11803
- // node_modules/ink/node_modules/ws/lib/buffer-util.js
11803
+ // node_modules/ws/lib/buffer-util.js
11804
11804
  var require_buffer_util = __commonJS(function(exports, module) {
11805
11805
  var { EMPTY_BUFFER } = require_constants2();
11806
11806
  var FastBuffer = Buffer[Symbol.species];
@@ -11878,7 +11878,7 @@ var require_buffer_util = __commonJS(function(exports, module) {
11878
11878
  }
11879
11879
  });
11880
11880
 
11881
- // node_modules/ink/node_modules/ws/lib/limiter.js
11881
+ // node_modules/ws/lib/limiter.js
11882
11882
  var require_limiter = __commonJS(function(exports, module) {
11883
11883
  var kDone = Symbol("kDone");
11884
11884
  var kRun = Symbol("kRun");
@@ -11910,7 +11910,7 @@ var require_limiter = __commonJS(function(exports, module) {
11910
11910
  module.exports = Limiter;
11911
11911
  });
11912
11912
 
11913
- // node_modules/ink/node_modules/ws/lib/permessage-deflate.js
11913
+ // node_modules/ws/lib/permessage-deflate.js
11914
11914
  var require_permessage_deflate = __commonJS(function(exports, module) {
11915
11915
  var zlib = __require("zlib");
11916
11916
  var bufferUtil = require_buffer_util();
@@ -12174,7 +12174,7 @@ var require_permessage_deflate = __commonJS(function(exports, module) {
12174
12174
  }
12175
12175
  });
12176
12176
 
12177
- // node_modules/ink/node_modules/ws/lib/validation.js
12177
+ // node_modules/ws/lib/validation.js
12178
12178
  var require_validation = __commonJS(function(exports, module) {
12179
12179
  var { isUtf8 } = __require("buffer");
12180
12180
  var { hasBlob } = require_constants2();
@@ -12361,7 +12361,7 @@ var require_validation = __commonJS(function(exports, module) {
12361
12361
  }
12362
12362
  });
12363
12363
 
12364
- // node_modules/ink/node_modules/ws/lib/receiver.js
12364
+ // node_modules/ws/lib/receiver.js
12365
12365
  var require_receiver = __commonJS(function(exports, module) {
12366
12366
  var { Writable } = __require("stream");
12367
12367
  var PerMessageDeflate = require_permessage_deflate();
@@ -12755,7 +12755,7 @@ var require_receiver = __commonJS(function(exports, module) {
12755
12755
  module.exports = Receiver;
12756
12756
  });
12757
12757
 
12758
- // node_modules/ink/node_modules/ws/lib/sender.js
12758
+ // node_modules/ws/lib/sender.js
12759
12759
  var require_sender = __commonJS(function(exports, module) {
12760
12760
  var { Duplex } = __require("stream");
12761
12761
  var { randomFillSync } = __require("crypto");
@@ -13114,7 +13114,7 @@ var require_sender = __commonJS(function(exports, module) {
13114
13114
  }
13115
13115
  });
13116
13116
 
13117
- // node_modules/ink/node_modules/ws/lib/event-target.js
13117
+ // node_modules/ws/lib/event-target.js
13118
13118
  var require_event_target = __commonJS(function(exports, module) {
13119
13119
  var { kForOnEventAttribute, kListener } = require_constants2();
13120
13120
  var kCode = Symbol("kCode");
@@ -13265,7 +13265,7 @@ var require_event_target = __commonJS(function(exports, module) {
13265
13265
  }
13266
13266
  });
13267
13267
 
13268
- // node_modules/ink/node_modules/ws/lib/extension.js
13268
+ // node_modules/ws/lib/extension.js
13269
13269
  var require_extension = __commonJS(function(exports, module) {
13270
13270
  var { tokenChars } = require_validation();
13271
13271
  function push(dest, name, elem) {
@@ -13430,7 +13430,7 @@ var require_extension = __commonJS(function(exports, module) {
13430
13430
  module.exports = { format, parse };
13431
13431
  });
13432
13432
 
13433
- // node_modules/ink/node_modules/ws/lib/websocket.js
13433
+ // node_modules/ws/lib/websocket.js
13434
13434
  var require_websocket = __commonJS(function(exports, module) {
13435
13435
  var EventEmitter = __require("events");
13436
13436
  var https = __require("https");
@@ -14202,7 +14202,7 @@ var require_websocket = __commonJS(function(exports, module) {
14202
14202
  }
14203
14203
  });
14204
14204
 
14205
- // node_modules/ink/node_modules/ws/lib/stream.js
14205
+ // node_modules/ws/lib/stream.js
14206
14206
  var require_stream = __commonJS(function(exports, module) {
14207
14207
  var WebSocket = require_websocket();
14208
14208
  var { Duplex } = __require("stream");
@@ -14305,7 +14305,7 @@ var require_stream = __commonJS(function(exports, module) {
14305
14305
  module.exports = createWebSocketStream;
14306
14306
  });
14307
14307
 
14308
- // node_modules/ink/node_modules/ws/lib/subprotocol.js
14308
+ // node_modules/ws/lib/subprotocol.js
14309
14309
  var require_subprotocol = __commonJS(function(exports, module) {
14310
14310
  var { tokenChars } = require_validation();
14311
14311
  function parse(header) {
@@ -14350,7 +14350,7 @@ var require_subprotocol = __commonJS(function(exports, module) {
14350
14350
  module.exports = { parse };
14351
14351
  });
14352
14352
 
14353
- // node_modules/ink/node_modules/ws/lib/websocket-server.js
14353
+ // node_modules/ws/lib/websocket-server.js
14354
14354
  var require_websocket_server = __commonJS(function(exports, module) {
14355
14355
  var EventEmitter = __require("events");
14356
14356
  var http = __require("http");
@@ -14660,7 +14660,7 @@ var require_websocket_server = __commonJS(function(exports, module) {
14660
14660
  }
14661
14661
  });
14662
14662
 
14663
- // node_modules/ink/node_modules/ws/wrapper.mjs
14663
+ // node_modules/ws/wrapper.mjs
14664
14664
  var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
14665
14665
  var init_wrapper = __esm(() => {
14666
14666
  import_stream = __toESM(require_stream(), 1);
@@ -49793,13 +49793,6 @@ var workspaceConfigSchema = object({
49793
49793
  profiles: record(nonBlank, workspaceProfileSchema).optional(),
49794
49794
  releaseTracks: record(nonBlank, releaseTrackSchema).optional()
49795
49795
  }).passthrough().superRefine((workspace, context) => {
49796
- if (workspace.youtrack && workspace.vcs?.provider !== "gitlab") {
49797
- context.addIssue({
49798
- code: "custom",
49799
- path: ["youtrack"],
49800
- message: `YouTrack issue linking requires the gitlab provider, got ${workspace.vcs?.provider ?? "unset"}`
49801
- });
49802
- }
49803
49796
  if (workspace.issues && workspace.vcs?.provider !== "github") {
49804
49797
  context.addIssue({
49805
49798
  code: "custom",
@@ -49992,13 +49985,10 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
49992
49985
  }
49993
49986
  return mostSpecific[0];
49994
49987
  };
49995
- var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
49996
- const result = readWorkspacesResult(dir);
49997
- if (result.status === "malformed" || result.status === "invalid")
49998
- throw new Error(result.error);
49988
+ var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
49999
49989
  const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
50000
49990
  const matches = [];
50001
- for (const ws of result.entries) {
49991
+ for (const ws of entries) {
50002
49992
  const glob = ws.glob.replaceAll("\\", "/");
50003
49993
  const canonical = canonicalGlob(glob);
50004
49994
  let matched = false;
@@ -50013,6 +50003,12 @@ var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
50013
50003
  }
50014
50004
  return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
50015
50005
  };
50006
+ var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
50007
+ const result = readWorkspacesResult(dir);
50008
+ if (result.status === "malformed" || result.status === "invalid")
50009
+ throw new Error(result.error);
50010
+ return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
50011
+ };
50016
50012
  var runtimeWorkspacePolicySchema = (kind) => {
50017
50013
  const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
50018
50014
  const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object({
@@ -50096,6 +50092,48 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
50096
50092
  source: profilePolicy ? `profile:${profileName}` : workspacePolicy ? `workspace:${candidate.name}` : "user-default"
50097
50093
  };
50098
50094
  };
50095
+ var resolveWorkspaceProfile = (workspace, requestedProfile) => {
50096
+ const profileName = requestedProfile ?? workspace.defaultProfile ?? null;
50097
+ const profile = profileName ? workspace.profiles?.[profileName] : undefined;
50098
+ if (profileName && !profile) {
50099
+ return {
50100
+ status: "invalid",
50101
+ error: `workspace ${JSON.stringify(workspace.name)} has no profile ${JSON.stringify(profileName)}`
50102
+ };
50103
+ }
50104
+ const effectiveWorkspace = profile ? {
50105
+ ...workspace,
50106
+ ...profile.branchPolicy ? { branchPolicy: profile.branchPolicy } : {},
50107
+ ...profile.commitPolicy ? { commitPolicy: profile.commitPolicy } : {}
50108
+ } : workspace;
50109
+ return {
50110
+ status: "resolved",
50111
+ profileName,
50112
+ workspace: effectiveWorkspace,
50113
+ provenance: {
50114
+ branchPolicy: profile?.branchPolicy ? `profile:${profileName}` : workspace.branchPolicy ? `workspace:${workspace.name}` : "user-default",
50115
+ commitPolicy: profile?.commitPolicy ? `profile:${profileName}` : workspace.commitPolicy ? `workspace:${workspace.name}` : "user-default"
50116
+ }
50117
+ };
50118
+ };
50119
+ var resolveWorkspacePolicy = (config, workspace, profileName) => {
50120
+ const selected = workspace ? resolveWorkspaceProfile(workspace, profileName) : {
50121
+ status: "resolved",
50122
+ profileName: null,
50123
+ workspace: null,
50124
+ provenance: { branchPolicy: "user-default", commitPolicy: "user-default" }
50125
+ };
50126
+ if (selected.status === "invalid")
50127
+ return selected;
50128
+ return {
50129
+ status: "resolved",
50130
+ workspace: selected.workspace,
50131
+ profileName: selected.profileName,
50132
+ branchPolicy: resolveBranchPolicy(config, selected.workspace),
50133
+ commitPolicy: resolveCommitPolicy(config, selected.workspace),
50134
+ provenance: selected.provenance
50135
+ };
50136
+ };
50099
50137
 
50100
50138
  // packages/workit-core/src/core/gitignore.ts
50101
50139
  var GITIGNORE_ENTRIES = [
@@ -50607,20 +50645,20 @@ function writeFileAtomic(file, content, mode) {
50607
50645
  import {
50608
50646
  copyFileSync as copyFileSync2,
50609
50647
  cpSync as cpSync3,
50610
- existsSync as existsSync10,
50648
+ existsSync as existsSync11,
50611
50649
  lstatSync as lstatSync4,
50612
50650
  mkdirSync as mkdirSync5,
50613
50651
  mkdtempSync,
50614
- readFileSync as readFileSync11,
50615
- readdirSync as readdirSync4,
50652
+ readFileSync as readFileSync12,
50653
+ readdirSync as readdirSync5,
50616
50654
  realpathSync as realpathSync2,
50617
50655
  renameSync as renameSync2,
50618
50656
  rmSync,
50619
- statSync as statSync3,
50657
+ statSync as statSync4,
50620
50658
  writeFileSync as writeFileSync5
50621
50659
  } from "node:fs";
50622
- import os6 from "node:os";
50623
- import path13 from "node:path";
50660
+ import os7 from "node:os";
50661
+ import path14 from "node:path";
50624
50662
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
50625
50663
 
50626
50664
  // packages/workit-core/src/core/branch-policy.ts
@@ -50729,7 +50767,7 @@ var named = (s, name) => s === name || s.startsWith(`${name}@`);
50729
50767
  function isWorkitPlugin(value) {
50730
50768
  const s = String(value).replaceAll("\\", "/");
50731
50769
  const url = s.startsWith("file://") || s.startsWith("git+file://");
50732
- const pkgPath = s.includes("/packages/workit-opencode/") || s.includes("/packages/workit-cursor/") || s.includes("/node_modules/@brainervirus/workit-opencode/") || s.includes("/node_modules/@brainervirus/workit-cursor/");
50770
+ const pkgPath = /\/(?:packages\/workit-(?:opencode|cursor)|node_modules\/@brainervirus\/workit-(?:opencode|cursor))(?:\/|$)/.test(s);
50733
50771
  return named(s, "workflow-toolkit") || named(s, "workflow-toolkit-opencode") || named(s, "local/workflow-toolkit") || named(s, "workit") || named(s, "local/workit") || named(s, "@brainervirus/workit-opencode") || named(s, "@brainervirus/workit-cursor") || url && pkgPath || s.startsWith("git+file://") && s.includes("workflow-toolkit");
50734
50772
  }
50735
50773
  function mergeOpenCodePlugins(plugin, pin) {
@@ -53344,9 +53382,10 @@ var parsesAsConfigObject = (p) => {
53344
53382
  }
53345
53383
  };
53346
53384
  var pluginEntries = (cfg) => {
53347
- const plugin = cfg?.plugin;
53348
- const list = Array.isArray(plugin) ? plugin : typeof plugin === "string" ? [plugin] : [];
53349
- return list.map(String).filter(isWorkitPlugin);
53385
+ const entries = [cfg?.plugins, cfg?.plugin].flatMap((value) => Array.isArray(value) ? value : typeof value === "string" ? [value] : []);
53386
+ return [
53387
+ ...new Set(entries.filter((entry) => typeof entry === "string" && isWorkitPlugin(entry)))
53388
+ ];
53350
53389
  };
53351
53390
  var commandOnPath = (name, env) => {
53352
53391
  const dirs = (env.PATH ?? process.env.PATH ?? "").split(path12.delimiter);
@@ -54603,6 +54642,351 @@ var runDoctor = (options = {}) => {
54603
54642
  return report;
54604
54643
  };
54605
54644
 
54645
+ // packages/workit-core/src/core/host-install.ts
54646
+ import { spawnSync as spawnSync3 } from "node:child_process";
54647
+ import { accessSync, constants as constants2, existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
54648
+ import os6 from "node:os";
54649
+ import path13 from "node:path";
54650
+ function hostCommand(name, args, options = {}) {
54651
+ const executable = findHostExecutable(name, options);
54652
+ if (!executable)
54653
+ throw new Error(`${name} executable was not found on PATH or supported user bin directories`);
54654
+ if (process.platform === "win32" && /\.(?:cmd|bat)$/i.test(executable)) {
54655
+ const node = findHostExecutable("node", options);
54656
+ if (!node)
54657
+ throw new Error(`Node.js is required to run the ${name} Windows shim`);
54658
+ const script = resolveWindowsShimScript(executable);
54659
+ if (!script)
54660
+ throw new Error(`could not resolve the ${name} Windows shim to a JavaScript entrypoint`);
54661
+ return { command: node, args: [script, ...args], cwd: options.cwd, purpose: `${name} CLI` };
54662
+ }
54663
+ return { command: executable, args, cwd: options.cwd, purpose: `${name} CLI` };
54664
+ }
54665
+ function resolveWindowsShimEntry(contents, shim, exists = existsSync10) {
54666
+ const root = path13.win32.dirname(shim);
54667
+ const matches = contents.matchAll(/(["']?)([^\r\n"']*node_modules[\\/][^\r\n"']+?\.js)\1/gi);
54668
+ for (const match of matches) {
54669
+ const raw = match[2].replace(/%~?dp0%?/gi, root);
54670
+ const candidate = path13.win32.isAbsolute(raw) ? raw : path13.win32.resolve(root, raw);
54671
+ if (exists(candidate))
54672
+ return candidate;
54673
+ }
54674
+ return null;
54675
+ }
54676
+ var resolveWindowsShimScript = (shim) => {
54677
+ try {
54678
+ const contents = readFileSync11(shim, "utf8");
54679
+ return resolveWindowsShimEntry(contents, shim);
54680
+ } catch {
54681
+ return null;
54682
+ }
54683
+ };
54684
+ var MARKETPLACE = "https://github.com/BrainerVirus/workit.git";
54685
+ var CODEX_MARKETPLACE_NAME = "workflow-toolkit";
54686
+ var validPackageVersion = (value) => value === "latest" || /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value);
54687
+ var nodePrerequisite = (options) => {
54688
+ const node = findHostExecutable("node", options);
54689
+ if (!node)
54690
+ throw new Error("Workit host packages require Node.js 24+; install Node.js and retry");
54691
+ return {
54692
+ command: node,
54693
+ args: [
54694
+ "-e",
54695
+ 'const v=Number(process.versions.node.split(".")[0]);if(v<24){console.error(`Workit host packages require Node.js 24+ (found ${process.versions.node})`);process.exit(1)}'
54696
+ ],
54697
+ cwd: options.cwd,
54698
+ purpose: "Check the Node.js 24+ package runtime requirement"
54699
+ };
54700
+ };
54701
+ function findHostExecutable(name, options = {}) {
54702
+ const env = options.env ?? process.env;
54703
+ const home = options.home ?? env.HOME ?? os6.homedir();
54704
+ const suffixes = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
54705
+ const dirs = (env.PATH ?? "").split(path13.delimiter).filter(Boolean);
54706
+ dirs.push(path13.join(home, ".local", "bin"), path13.join(home, ".asdf", "shims"));
54707
+ for (const [base, tail] of [
54708
+ [path13.join(home, ".local", "share", "fnm", "node-versions"), path13.join("installation", "bin")],
54709
+ [path13.join(home, ".nvm", "versions", "node"), "bin"]
54710
+ ]) {
54711
+ try {
54712
+ for (const entry of readdirSync4(base, { withFileTypes: true })) {
54713
+ if (entry.isDirectory())
54714
+ dirs.push(path13.join(base, entry.name, tail));
54715
+ }
54716
+ } catch {}
54717
+ }
54718
+ for (const dir of dirs) {
54719
+ for (const suffix of suffixes) {
54720
+ const candidate = path13.join(dir, `${name}${suffix}`);
54721
+ try {
54722
+ if (statSync3(candidate).isFile()) {
54723
+ if (process.platform === "win32")
54724
+ return candidate;
54725
+ accessSync(candidate, constants2.X_OK);
54726
+ return candidate;
54727
+ }
54728
+ } catch {}
54729
+ }
54730
+ }
54731
+ return null;
54732
+ }
54733
+ var isPiWorkitInstalled = (home, env = process.env) => {
54734
+ const settings = path13.join(env.PI_CODING_AGENT_DIR ?? path13.join(home, ".pi", "agent"), "settings.json");
54735
+ try {
54736
+ const document2 = JSON.parse(readFileSync11(settings, "utf8"));
54737
+ return (document2.packages ?? []).some((entry) => {
54738
+ const source = entry && typeof entry === "object" && !Array.isArray(entry) ? entry.source : entry;
54739
+ const value = String(source).replaceAll("\\", "/").replace(/\/+$/, "");
54740
+ return /^npm:@brainervirus\/workit-pi(?:@[^/]+)?$/.test(value) || value === "@brainervirus/workit-pi" || /(?:^|\/)packages\/workit-pi$/.test(value);
54741
+ });
54742
+ } catch {
54743
+ return false;
54744
+ }
54745
+ };
54746
+ var isCanonicalCodexPackage = (root) => {
54747
+ for (const file of [
54748
+ path13.join(root, ".codex-plugin", "plugin.json"),
54749
+ path13.join(root, "package.json")
54750
+ ]) {
54751
+ try {
54752
+ const manifest = JSON.parse(readFileSync11(file, "utf8"));
54753
+ const repository = typeof manifest.repository === "string" ? manifest.repository : manifest.repository?.url;
54754
+ if ((manifest.name === "workit" || manifest.name === "@brainervirus/workit-codex") && repository?.replace(/\.git$/, "").toLowerCase() === "https://github.com/brainervirus/workit")
54755
+ return true;
54756
+ } catch {}
54757
+ }
54758
+ return false;
54759
+ };
54760
+ var isCodexWorkitInstalled = (home, env = process.env) => {
54761
+ const codexHome = env.CODEX_HOME ?? path13.join(home, ".codex");
54762
+ const isPackageOrVersionDirectory = (root) => {
54763
+ if (isCanonicalCodexPackage(root))
54764
+ return true;
54765
+ try {
54766
+ return readdirSync4(root, { withFileTypes: true }).some((entry) => entry.isDirectory() && isCanonicalCodexPackage(path13.join(root, entry.name)));
54767
+ } catch {
54768
+ return false;
54769
+ }
54770
+ };
54771
+ if (isPackageOrVersionDirectory(path13.join(codexHome, "plugins", "workit")))
54772
+ return true;
54773
+ const root = path13.join(codexHome, "plugins", "cache");
54774
+ try {
54775
+ for (const marketplace of readdirSync4(root, { withFileTypes: true })) {
54776
+ if (!marketplace.isDirectory())
54777
+ continue;
54778
+ if (isPackageOrVersionDirectory(path13.join(root, marketplace.name, "workit")))
54779
+ return true;
54780
+ }
54781
+ } catch {
54782
+ return false;
54783
+ }
54784
+ return false;
54785
+ };
54786
+ function installedHostApp(host, home, env = process.env, platform = process.platform, exists = existsSync10) {
54787
+ const candidates = [];
54788
+ const pathApi = platform === "win32" ? path13.win32 : path13;
54789
+ if (platform === "darwin") {
54790
+ const app = host === "cursor" ? "Cursor.app" : host === "codex" ? "Codex.app" : host === "opencode" ? "OpenCode.app" : "";
54791
+ if (app)
54792
+ candidates.push(`/Applications/${app}`, pathApi.join(home, "Applications", app));
54793
+ } else if (platform === "win32") {
54794
+ const local = env.LOCALAPPDATA ?? pathApi.join(home, "AppData", "Local");
54795
+ if (host === "cursor")
54796
+ candidates.push(pathApi.join(local, "Programs", "Cursor", "Cursor.exe"));
54797
+ if (host === "codex")
54798
+ candidates.push(pathApi.join(local, "Programs", "Codex", "Codex.exe"));
54799
+ if (host === "opencode")
54800
+ candidates.push(pathApi.join(local, "Programs", "OpenCode", "OpenCode.exe"));
54801
+ }
54802
+ return candidates.some(exists);
54803
+ }
54804
+ function planHostInstall(host, options = {}) {
54805
+ const env = options.env ?? process.env;
54806
+ const home = options.home ?? env.HOME ?? os6.homedir();
54807
+ const cwd = options.cwd ?? process.cwd();
54808
+ const version = options.packageVersion ?? "latest";
54809
+ if (!validPackageVersion(version))
54810
+ throw new Error(`unsupported package version selector: ${version}`);
54811
+ if (options.alreadyInstalled && options.mode !== "upgrade")
54812
+ return [];
54813
+ const upgrading = options.mode === "upgrade";
54814
+ if (host === "codex") {
54815
+ if (!upgrading && isCodexWorkitInstalled(home, env))
54816
+ return [];
54817
+ const base = hostCommand("codex", [], { home, cwd, env });
54818
+ return [
54819
+ nodePrerequisite({ home, cwd, env }),
54820
+ {
54821
+ ...base,
54822
+ args: [...base.args, "plugin", "marketplace", "list"],
54823
+ purpose: "Check whether the Workit marketplace is already registered"
54824
+ },
54825
+ {
54826
+ ...base,
54827
+ args: [
54828
+ ...base.args,
54829
+ "plugin",
54830
+ "marketplace",
54831
+ upgrading ? "upgrade" : "add",
54832
+ ...upgrading ? [CODEX_MARKETPLACE_NAME] : [MARKETPLACE]
54833
+ ],
54834
+ purpose: upgrading ? "Refresh the Workit Codex marketplace" : "Register the Workit Codex marketplace",
54835
+ ...upgrading ? {} : { skipWhenOutputIncludes: CODEX_MARKETPLACE_NAME }
54836
+ },
54837
+ {
54838
+ ...base,
54839
+ args: [...base.args, "plugin", "add", `workit@${CODEX_MARKETPLACE_NAME}`],
54840
+ purpose: upgrading ? "Refresh the Workit Codex plugin" : "Install the Workit Codex plugin"
54841
+ }
54842
+ ];
54843
+ }
54844
+ if (host === "pi") {
54845
+ if (!upgrading && isPiWorkitInstalled(home, env))
54846
+ return [];
54847
+ const base = hostCommand("pi", [], { home, cwd, env });
54848
+ return [
54849
+ nodePrerequisite({ home, cwd, env }),
54850
+ {
54851
+ ...base,
54852
+ args: upgrading ? [...base.args, "update", "--extension", "npm:@brainervirus/workit-pi"] : [...base.args, "install", `npm:@brainervirus/workit-pi@${version}`],
54853
+ purpose: upgrading ? "Update the Workit Pi package" : "Install the Workit Pi package in personal Pi settings"
54854
+ }
54855
+ ];
54856
+ }
54857
+ if (host === "cursor") {
54858
+ const node = findHostExecutable("node", { home, env });
54859
+ if (!node)
54860
+ throw new Error("Workit host packages require Node.js 24+; install Node.js and retry");
54861
+ const npm = findHostExecutable("npm", { home, env });
54862
+ const npmCli = npm ? findNpmCli(npm) : null;
54863
+ if (!npmCli)
54864
+ throw new Error("npm CLI could not be resolved; install Node.js 24+ and npm, then retry");
54865
+ const stage = path13.join(home, ".cursor", "plugins", "local", ".workit-package-bootstrap");
54866
+ const target = options.targetDir ?? path13.join(home, ".cursor", "plugins", "local", "workit");
54867
+ return [
54868
+ nodePrerequisite({ home, cwd, env }),
54869
+ {
54870
+ command: node,
54871
+ args: [
54872
+ npmCli,
54873
+ "install",
54874
+ "--prefix",
54875
+ stage,
54876
+ "--no-save",
54877
+ "--package-lock=false",
54878
+ `@brainervirus/workit-cursor@${version}`
54879
+ ],
54880
+ cwd,
54881
+ purpose: upgrading ? "Download the current Workit Cursor adapter for the managed local plugin directory" : "Download the Workit Cursor adapter package for the managed local plugin directory",
54882
+ packageRoot: path13.join(stage, "node_modules", "@brainervirus", "workit-cursor"),
54883
+ packageTarget: target
54884
+ }
54885
+ ];
54886
+ }
54887
+ if (host === "opencode") {
54888
+ if (upgrading)
54889
+ throw new Error("OpenCode CLI cannot target the Workit server plugin for a scoped update; upgrade is unsupported and the current registration was preserved");
54890
+ return [];
54891
+ }
54892
+ return [];
54893
+ }
54894
+ function runHostInstall(commands, runner, context = {}) {
54895
+ const results = [];
54896
+ let packageRoot;
54897
+ let outputHistory = "";
54898
+ for (const command of commands) {
54899
+ if (command.skipWhenOutputIncludes && outputHistory.includes(command.skipWhenOutputIncludes)) {
54900
+ results.push({
54901
+ command,
54902
+ result: { exitCode: 0, stdout: "", stderr: "already registered; skipped" }
54903
+ });
54904
+ continue;
54905
+ }
54906
+ const result = runner ? runner(command) : runHostCommand(command, context);
54907
+ results.push({ command, result });
54908
+ if (result.exitCode !== 0)
54909
+ return { ok: false, results };
54910
+ outputHistory += `
54911
+ ${result.stdout}
54912
+ ${result.stderr}`;
54913
+ if (command.packageRoot && command.packageTarget) {
54914
+ try {
54915
+ if (!existsSync10(command.packageRoot)) {
54916
+ return {
54917
+ ok: false,
54918
+ results: [
54919
+ {
54920
+ command,
54921
+ result: {
54922
+ ...result,
54923
+ exitCode: 1,
54924
+ stderr: "npm completed without producing the expected Cursor package"
54925
+ }
54926
+ }
54927
+ ]
54928
+ };
54929
+ } else {
54930
+ packageRoot = command.packageRoot;
54931
+ }
54932
+ } catch (error) {
54933
+ return {
54934
+ ok: false,
54935
+ results: [
54936
+ {
54937
+ command,
54938
+ result: {
54939
+ ...result,
54940
+ exitCode: 1,
54941
+ stderr: error instanceof Error ? error.message : String(error)
54942
+ }
54943
+ }
54944
+ ]
54945
+ };
54946
+ }
54947
+ }
54948
+ }
54949
+ return { ok: true, results, packageRoot };
54950
+ }
54951
+ function runHostCommand(step, context = {}) {
54952
+ if (process.platform === "win32" && /\.(?:cmd|bat)$/i.test(step.command)) {
54953
+ return {
54954
+ exitCode: 1,
54955
+ stdout: "",
54956
+ stderr: `Cannot safely execute batch shim ${step.command}; resolve its Node.js CLI entrypoint and retry`
54957
+ };
54958
+ }
54959
+ const result = spawnSync3(step.command, step.args, {
54960
+ cwd: step.cwd,
54961
+ env: (() => {
54962
+ const home = context.home ?? context.env?.HOME ?? os6.homedir();
54963
+ const env = { ...process.env, ...context.env, HOME: home };
54964
+ env.CODEX_HOME = context.env?.CODEX_HOME ?? path13.join(home, ".codex");
54965
+ env.PI_CODING_AGENT_DIR = context.env?.PI_CODING_AGENT_DIR ?? path13.join(home, ".pi", "agent");
54966
+ env.XDG_CONFIG_HOME = context.env?.XDG_CONFIG_HOME ?? path13.join(home, ".config");
54967
+ return env;
54968
+ })(),
54969
+ encoding: "utf8",
54970
+ timeout: 60000,
54971
+ windowsHide: true,
54972
+ shell: false
54973
+ });
54974
+ return {
54975
+ exitCode: result.status ?? (result.error ? 1 : 1),
54976
+ stdout: result.stdout ?? "",
54977
+ stderr: result.stderr ?? result.error?.message ?? ""
54978
+ };
54979
+ }
54980
+ var findNpmCli = (npm) => {
54981
+ const dir = path13.dirname(npm);
54982
+ const candidates = [
54983
+ path13.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
54984
+ path13.join(dir, "..", "node_modules", "npm", "bin", "npm-cli.js"),
54985
+ path13.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
54986
+ ];
54987
+ return candidates.find((candidate) => existsSync10(candidate)) ?? null;
54988
+ };
54989
+
54606
54990
  // packages/workit-core/src/core/setup.ts
54607
54991
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
54608
54992
  function parseList(raw) {
@@ -54682,7 +55066,7 @@ var resolveTokenPath = (configured, fallback, override) => {
54682
55066
  };
54683
55067
  var readConfigRecord = (file) => {
54684
55068
  try {
54685
- const value = JSON.parse(readFileSync11(file, "utf8"));
55069
+ const value = JSON.parse(readFileSync12(file, "utf8"));
54686
55070
  return isRecord2(value) ? value : null;
54687
55071
  } catch {
54688
55072
  return null;
@@ -54703,11 +55087,12 @@ function buildSetupPreview(values, opts = {}) {
54703
55087
  const current = readConfigFromDir(state.configDir);
54704
55088
  mutations.push({
54705
55089
  type: "merge-json",
54706
- path: path13.join(state.configDir, "config.json"),
55090
+ path: path14.join(state.configDir, "config.json"),
54707
55091
  value: {
54708
55092
  locale: values.locale,
54709
55093
  localeOptions: current.localeOptions,
54710
55094
  timezone: values.timezone,
55095
+ ...values.commitPolicy === undefined ? {} : { commitPolicy: values.commitPolicy },
54711
55096
  branchPolicy: mergePreset(values.branchPreset, {
54712
55097
  allowed: values.branchPreset === "custom" ? parseList(values.branchAllowed) : undefined,
54713
55098
  protectedNames: values.branchPreset === "custom" ? parseList(values.branchProtected) : undefined
@@ -54729,17 +55114,17 @@ function buildSetupPreview(values, opts = {}) {
54729
55114
  if (blocked.length === 0) {
54730
55115
  mutations.push({
54731
55116
  type: "update-workspaces",
54732
- path: path13.join(state.configDir, "workspaces.json"),
55117
+ path: path14.join(state.configDir, "workspaces.json"),
54733
55118
  entries: values.workspaces,
54734
55119
  expectedRevision: diskWorkspaceResult.revision
54735
55120
  });
54736
55121
  }
54737
55122
  }
54738
55123
  if (values.baseUrl.trim()) {
54739
- const ytPath = path13.join(state.configDir, "youtrack.json");
55124
+ const ytPath = path14.join(state.configDir, "youtrack.json");
54740
55125
  const ytExisting = readConfigRecord(ytPath);
54741
55126
  const ytConfigured = configuredTokenPath(ytExisting, "tokenFile");
54742
- const tokenPath = resolveTokenPath(ytConfigured, path13.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
55127
+ const tokenPath = resolveTokenPath(ytConfigured, path14.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
54743
55128
  const draft = youtrackDraft(values, tokenPath);
54744
55129
  if (ytConfigured !== null && tokenPath !== ytConfigured) {
54745
55130
  delete draft.tokenFile;
@@ -54755,7 +55140,7 @@ function buildSetupPreview(values, opts = {}) {
54755
55140
  path: ytPath,
54756
55141
  value: draft
54757
55142
  });
54758
- if (existsSync10(tokenPath))
55143
+ if (existsSync11(tokenPath))
54759
55144
  preserved.push(tokenPath);
54760
55145
  else
54761
55146
  mutations.push({
@@ -54767,7 +55152,7 @@ function buildSetupPreview(values, opts = {}) {
54767
55152
  });
54768
55153
  }
54769
55154
  if (values.vcsProvider !== "skip") {
54770
- const vcsPath = path13.join(state.configDir, "vcs.json");
55155
+ const vcsPath = path14.join(state.configDir, "vcs.json");
54771
55156
  mutations.push({
54772
55157
  type: "merge-json",
54773
55158
  path: vcsPath,
@@ -54781,16 +55166,45 @@ function buildSetupPreview(values, opts = {}) {
54781
55166
  if (platform === "opencode") {
54782
55167
  mutations.push({ type: "register-platform", platform, path: paths.opencodeConfig });
54783
55168
  } else if (platform === "cursor") {
55169
+ const cursorRoot = adapterRoot("cursor", {
55170
+ ...paths,
55171
+ dev: opts.dev ?? env.WORKFLOW_TOOLKIT_DEV ?? null
55172
+ });
55173
+ if (cursorRoot === null) {
55174
+ const commands = planHostInstall("cursor", {
55175
+ home: paths.home,
55176
+ cwd: paths.cwd,
55177
+ env,
55178
+ packageVersion: "latest",
55179
+ targetDir: paths.cursorPluginDir
55180
+ });
55181
+ mutations.push({
55182
+ type: "install-host",
55183
+ platform: "cursor",
55184
+ path: path14.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
55185
+ commands
55186
+ });
55187
+ }
54784
55188
  mutations.push({ type: "install-adapter", platform, path: paths.cursorPluginDir });
54785
55189
  mutations.push({ type: "register-platform", platform, path: paths.cursorSettings });
54786
55190
  mutations.push({ type: "register-platform", platform, path: paths.cursorMcp });
55191
+ } else if (platform === "codex" || platform === "pi") {
55192
+ const commands = planHostInstall(platform, { home: paths.home, cwd: paths.cwd, env });
55193
+ if (commands.length > 0) {
55194
+ mutations.push({
55195
+ type: "install-host",
55196
+ platform,
55197
+ path: platform === "codex" ? path14.join(env.CODEX_HOME ?? path14.join(paths.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : path14.join(env.PI_CODING_AGENT_DIR ?? path14.join(paths.home, ".pi", "agent"), "settings.json"),
55198
+ commands
55199
+ });
55200
+ }
54787
55201
  }
54788
55202
  }
54789
55203
  }
54790
55204
  if (values.applyProject) {
54791
- const root = path13.resolve(opts.cwd ?? process.cwd());
54792
- const gitignorePath = path13.join(root, ".gitignore");
54793
- const existing = existsSync10(gitignorePath) ? readFileSync11(gitignorePath, "utf8") : "";
55205
+ const root = path14.resolve(opts.cwd ?? process.cwd());
55206
+ const gitignorePath = path14.join(root, ".gitignore");
55207
+ const existing = existsSync11(gitignorePath) ? readFileSync12(gitignorePath, "utf8") : "";
54794
55208
  const existingLines = new Set(existing.split(`
54795
55209
  `).map((l) => l.trim()).filter(Boolean));
54796
55210
  const entries = GITIGNORE_ENTRIES.filter((e) => e.trim() !== "" && !existingLines.has(e.trim()));
@@ -54812,16 +55226,16 @@ function buildSetupPreview(values, opts = {}) {
54812
55226
  }
54813
55227
  var resolveSetupPaths = (options, configDir) => {
54814
55228
  const env = options.env ?? process.env;
54815
- const home = options.home ?? env.HOME ?? os6.homedir();
55229
+ const home = options.home ?? env.HOME ?? os7.homedir();
54816
55230
  return {
54817
55231
  home,
54818
55232
  configDir,
54819
55233
  cwd: options.cwd ?? process.cwd(),
54820
55234
  env,
54821
- opencodeConfig: options.opencodeConfig ?? path13.join(home, ".config", "opencode", "opencode.json"),
54822
- cursorSettings: options.cursorSettings ?? path13.join(home, ".cursor", "settings.json"),
54823
- cursorMcp: options.cursorMcp ?? path13.join(home, ".cursor", "mcp.json"),
54824
- cursorPluginDir: options.cursorPluginDir ?? path13.join(home, ".cursor", "plugins", "local", "workit")
55235
+ opencodeConfig: options.opencodeConfig ?? path14.join(home, ".config", "opencode", "opencode.json"),
55236
+ cursorSettings: options.cursorSettings ?? path14.join(home, ".cursor", "settings.json"),
55237
+ cursorMcp: options.cursorMcp ?? path14.join(home, ".cursor", "mcp.json"),
55238
+ cursorPluginDir: options.cursorPluginDir ?? path14.join(home, ".cursor", "plugins", "local", "workit")
54825
55239
  };
54826
55240
  };
54827
55241
  function resolveApply(preview, options) {
@@ -54835,9 +55249,9 @@ var isRecord2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
54835
55249
  var readExisting = (p) => {
54836
55250
  let raw;
54837
55251
  try {
54838
- raw = readFileSync11(p, "utf8");
55252
+ raw = readFileSync12(p, "utf8");
54839
55253
  } catch {
54840
- if (existsSync10(p))
55254
+ if (existsSync11(p))
54841
55255
  return { kind: "malformed", error: `${p} is not readable` };
54842
55256
  return { kind: "missing" };
54843
55257
  }
@@ -54861,7 +55275,7 @@ function deepMerge(base, patch) {
54861
55275
  }
54862
55276
  var readFileSafe = (p) => {
54863
55277
  try {
54864
- return readFileSync11(p, "utf8");
55278
+ return readFileSync12(p, "utf8");
54865
55279
  } catch {
54866
55280
  return null;
54867
55281
  }
@@ -54877,7 +55291,7 @@ var workspaceEntriesAlreadyMatch = (contents, file, entries) => {
54877
55291
  }
54878
55292
  };
54879
55293
  function applyMutation(m) {
54880
- const dir = path13.dirname(m.path);
55294
+ const dir = path14.dirname(m.path);
54881
55295
  switch (m.type) {
54882
55296
  case "create-file": {
54883
55297
  mkdirSync5(dir, { recursive: true });
@@ -55026,7 +55440,7 @@ function applyMutation(m) {
55026
55440
  detail: "already configured"
55027
55441
  };
55028
55442
  }
55029
- mkdirSync5(path13.dirname(m.path), { recursive: true });
55443
+ mkdirSync5(path14.dirname(m.path), { recursive: true });
55030
55444
  writeFileSync5(m.path, JSON.stringify(merged, null, 2) + `
55031
55445
  `, "utf8");
55032
55446
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
@@ -55035,28 +55449,33 @@ function applyMutation(m) {
55035
55449
  }
55036
55450
  var isAdapter = (root, platform) => {
55037
55451
  try {
55038
- const pkg = JSON.parse(readFileSync11(path13.join(root, "package.json"), "utf8"));
55452
+ const pkg = JSON.parse(readFileSync12(path14.join(root, "package.json"), "utf8"));
55039
55453
  return pkg.name === `@brainervirus/workit-${platform}`;
55040
55454
  } catch {
55041
55455
  return false;
55042
55456
  }
55043
55457
  };
55044
- function adapterRoot(platform, res) {
55458
+ function adapterRoot(platform, res, preferInstalledAdapter = false) {
55045
55459
  const candidates = [];
55460
+ const stagedCursor = path14.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
55461
+ if (platform === "cursor" && preferInstalledAdapter)
55462
+ candidates.push(stagedCursor);
55046
55463
  if (res.dev) {
55047
- candidates.push(path13.join(res.dev, "packages", `workit-${platform}`));
55464
+ candidates.push(path14.join(res.dev, "packages", `workit-${platform}`));
55048
55465
  } else {
55049
- candidates.push(path13.join(packageRoot(), "..", `workit-${platform}`));
55050
- let dir = path13.resolve(res.cwd);
55466
+ candidates.push(path14.join(packageRoot(), "..", `workit-${platform}`));
55467
+ let dir = path14.resolve(res.cwd);
55051
55468
  while (true) {
55052
- candidates.push(path13.join(dir, "packages", `workit-${platform}`));
55053
- const parent = path13.dirname(dir);
55469
+ candidates.push(path14.join(dir, "packages", `workit-${platform}`));
55470
+ const parent = path14.dirname(dir);
55054
55471
  if (parent === dir)
55055
55472
  break;
55056
55473
  dir = parent;
55057
55474
  }
55058
55475
  }
55059
- candidates.push(path13.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
55476
+ candidates.push(path14.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
55477
+ if (platform === "cursor")
55478
+ candidates.push(stagedCursor);
55060
55479
  for (const candidate of candidates) {
55061
55480
  if (isAdapter(candidate, platform))
55062
55481
  return candidate;
@@ -55073,16 +55492,16 @@ var resolveOpenCodePin = (root, opts = {}) => {
55073
55492
  if (!isDevOpenCodeAdapterRoot(root, opts.dev))
55074
55493
  return OPENCODE_NPM_PIN;
55075
55494
  for (const rel of ["src/plugin.ts", "dist/plugin.js"]) {
55076
- const entry = path13.join(root, rel);
55077
- if (existsSync10(entry))
55495
+ const entry = path14.join(root, rel);
55496
+ if (existsSync11(entry))
55078
55497
  return `file://${entry}`;
55079
55498
  }
55080
55499
  return null;
55081
55500
  };
55082
55501
  var opencodePin = (root, res) => resolveOpenCodePin(root, { dev: res.dev });
55083
55502
  function applyOpenCode(root, res) {
55084
- const pin = opencodePin(root, res);
55085
- if (!pin) {
55503
+ const resolvedPin = opencodePin(root, res);
55504
+ if (!resolvedPin) {
55086
55505
  return {
55087
55506
  platform: "opencode",
55088
55507
  file: res.opencodeConfig,
@@ -55090,7 +55509,7 @@ function applyOpenCode(root, res) {
55090
55509
  detail: "workit-opencode package has no loadable plugin entry (src/plugin.ts or dist/plugin.js)"
55091
55510
  };
55092
55511
  }
55093
- mkdirSync5(path13.dirname(res.opencodeConfig), { recursive: true });
55512
+ mkdirSync5(path14.dirname(res.opencodeConfig), { recursive: true });
55094
55513
  const existing = readExisting(res.opencodeConfig);
55095
55514
  if (existing.kind === "malformed") {
55096
55515
  return {
@@ -55100,7 +55519,17 @@ function applyOpenCode(root, res) {
55100
55519
  detail: `cannot merge into malformed config: ${existing.error} — repair or remove the file`
55101
55520
  };
55102
55521
  }
55103
- const merged = mergeOpenCodeConfig(existing.kind === "record" ? existing.value : {}, pin);
55522
+ const configuredPlugins = existing.kind === "record" ? Array.isArray(existing.value.plugins) ? existing.value.plugins : typeof existing.value.plugins === "string" ? [existing.value.plugins] : typeof existing.value.plugin === "string" ? [existing.value.plugin] : Array.isArray(existing.value.plugin) ? existing.value.plugin : [] : [];
55523
+ const configuredPin = configuredPlugins.find((entry) => typeof entry === "string" && isWorkitPlugin(entry));
55524
+ const pin = configuredPin ?? resolvedPin;
55525
+ const base = existing.kind === "record" ? existing.value : {};
55526
+ const merged = existing.kind === "record" && Object.hasOwn(existing.value, "plugins") ? (() => {
55527
+ const plugins = mergeOpenCodePlugins(existing.value.plugins, pin);
55528
+ return {
55529
+ config: { ...existing.value, plugins: plugins.config },
55530
+ changed: plugins.changed.length > 0 ? ["plugins"] : []
55531
+ };
55532
+ })() : mergeOpenCodeConfig(base, pin);
55104
55533
  if (merged.changed.length === 0) {
55105
55534
  return {
55106
55535
  platform: "opencode",
@@ -55125,22 +55554,22 @@ function applyOpenCode(root, res) {
55125
55554
  }
55126
55555
  var samePluginContent = (src, dest, relative = "") => {
55127
55556
  try {
55128
- for (const entry of readdirSync4(src, { withFileTypes: true })) {
55557
+ for (const entry of readdirSync5(src, { withFileTypes: true })) {
55129
55558
  if (entry.name === "node_modules")
55130
55559
  continue;
55131
- const source = path13.join(src, entry.name);
55132
- const installed = path13.join(dest, entry.name);
55560
+ const source = path14.join(src, entry.name);
55561
+ const installed = path14.join(dest, entry.name);
55133
55562
  if (entry.isDirectory()) {
55134
- if (!statSync3(installed).isDirectory() || !samePluginContent(source, installed, path13.join(relative, entry.name)))
55563
+ if (!statSync4(installed).isDirectory() || !samePluginContent(source, installed, path14.join(relative, entry.name)))
55135
55564
  return false;
55136
- } else if (!readFileSync11(source).equals(readFileSync11(installed))) {
55565
+ } else if (!readFileSync12(source).equals(readFileSync12(installed))) {
55137
55566
  return false;
55138
55567
  }
55139
55568
  }
55140
- for (const entry of readdirSync4(dest, { withFileTypes: true })) {
55141
- if (existsSync10(path13.join(src, entry.name)))
55569
+ for (const entry of readdirSync5(dest, { withFileTypes: true })) {
55570
+ if (existsSync11(path14.join(src, entry.name)))
55142
55571
  continue;
55143
- const rel = path13.join(relative, entry.name);
55572
+ const rel = path14.join(relative, entry.name);
55144
55573
  if (rel === ".workit-root")
55145
55574
  continue;
55146
55575
  if (relative === "rules" && entry.isFile() && entry.name.endsWith(".mdc"))
@@ -55154,43 +55583,43 @@ var samePluginContent = (src, dest, relative = "") => {
55154
55583
  };
55155
55584
  var preservedCursorRules = (src, dest) => {
55156
55585
  const preserved = new Map;
55157
- const sourceRules = path13.join(src, "rules");
55158
- const installedRules = path13.join(dest, "rules");
55586
+ const sourceRules = path14.join(src, "rules");
55587
+ const installedRules = path14.join(dest, "rules");
55159
55588
  try {
55160
- for (const entry of readdirSync4(installedRules, { withFileTypes: true })) {
55589
+ for (const entry of readdirSync5(installedRules, { withFileTypes: true })) {
55161
55590
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
55162
55591
  continue;
55163
- if (existsSync10(path13.join(sourceRules, entry.name)))
55592
+ if (existsSync11(path14.join(sourceRules, entry.name)))
55164
55593
  continue;
55165
- preserved.set(entry.name, readFileSync11(path13.join(installedRules, entry.name)));
55594
+ preserved.set(entry.name, readFileSync12(path14.join(installedRules, entry.name)));
55166
55595
  }
55167
55596
  } catch {}
55168
55597
  return preserved;
55169
55598
  };
55170
55599
  function copyPluginDir(src, dest) {
55171
55600
  const realSrc = realpathSync2(src);
55172
- const marker = path13.join(dest, ".workit-root");
55173
- const destIsLink = existsSync10(dest) && lstatSync4(dest).isSymbolicLink();
55601
+ const marker = path14.join(dest, ".workit-root");
55602
+ const destIsLink = existsSync11(dest) && lstatSync4(dest).isSymbolicLink();
55174
55603
  const synced = !destIsLink && readFileSafe(marker)?.trim() === realSrc && samePluginContent(realSrc, dest);
55175
55604
  if (synced)
55176
55605
  return "Skipped";
55177
- const hadDir = existsSync10(dest);
55606
+ const hadDir = existsSync11(dest);
55178
55607
  const rules = preservedCursorRules(realSrc, dest);
55179
- const parent = path13.dirname(dest);
55608
+ const parent = path14.dirname(dest);
55180
55609
  mkdirSync5(parent, { recursive: true });
55181
- const swap = mkdtempSync(path13.join(parent, `.${path13.basename(dest)}.swap-`));
55182
- const stage = path13.join(swap, "stage");
55183
- const backup = path13.join(swap, "backup");
55610
+ const swap = mkdtempSync(path14.join(parent, `.${path14.basename(dest)}.swap-`));
55611
+ const stage = path14.join(swap, "stage");
55612
+ const backup = path14.join(swap, "backup");
55184
55613
  try {
55185
55614
  cpSync3(realSrc, stage, {
55186
55615
  recursive: true,
55187
- filter: (entry) => !path13.relative(realSrc, entry).split(path13.sep).includes("node_modules")
55616
+ filter: (entry) => !path14.relative(realSrc, entry).split(path14.sep).includes("node_modules")
55188
55617
  });
55189
55618
  for (const [name, content] of rules) {
55190
- mkdirSync5(path13.join(stage, "rules"), { recursive: true });
55191
- writeFileSync5(path13.join(stage, "rules", name), content);
55619
+ mkdirSync5(path14.join(stage, "rules"), { recursive: true });
55620
+ writeFileSync5(path14.join(stage, "rules", name), content);
55192
55621
  }
55193
- writeFileSync5(path13.join(stage, ".workit-root"), realSrc + `
55622
+ writeFileSync5(path14.join(stage, ".workit-root"), realSrc + `
55194
55623
  `, "utf8");
55195
55624
  if (!samePluginContent(realSrc, stage))
55196
55625
  throw new Error("staged adapter content is incomplete");
@@ -55202,7 +55631,7 @@ function copyPluginDir(src, dest) {
55202
55631
  try {
55203
55632
  renameSync2(stage, dest);
55204
55633
  } catch (error) {
55205
- if (hadDir && !existsSync10(dest))
55634
+ if (hadDir && !existsSync11(dest))
55206
55635
  renameSync2(backup, dest);
55207
55636
  throw error;
55208
55637
  }
@@ -55213,26 +55642,26 @@ function copyPluginDir(src, dest) {
55213
55642
  return hadDir ? "Configured" : "Installed";
55214
55643
  }
55215
55644
  var removeLegacyCursorDir = (res) => {
55216
- const legacy = path13.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
55217
- if (legacy === res.cursorPluginDir || !existsSync10(legacy))
55645
+ const legacy = path14.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
55646
+ if (legacy === res.cursorPluginDir || !existsSync11(legacy))
55218
55647
  return;
55219
- const legacyRules = path13.join(legacy, "rules");
55220
- if (existsSync10(legacyRules)) {
55648
+ const legacyRules = path14.join(legacy, "rules");
55649
+ if (existsSync11(legacyRules)) {
55221
55650
  try {
55222
- mkdirSync5(path13.join(res.cursorPluginDir, "rules"), { recursive: true });
55223
- for (const entry of readdirSync4(legacyRules, { withFileTypes: true })) {
55651
+ mkdirSync5(path14.join(res.cursorPluginDir, "rules"), { recursive: true });
55652
+ for (const entry of readdirSync5(legacyRules, { withFileTypes: true })) {
55224
55653
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
55225
55654
  continue;
55226
- const target = path13.join(res.cursorPluginDir, "rules", entry.name);
55227
- if (!existsSync10(target))
55228
- copyFileSync2(path13.join(legacyRules, entry.name), target);
55655
+ const target = path14.join(res.cursorPluginDir, "rules", entry.name);
55656
+ if (!existsSync11(target))
55657
+ copyFileSync2(path14.join(legacyRules, entry.name), target);
55229
55658
  }
55230
55659
  } catch {}
55231
55660
  }
55232
55661
  rmSync(legacy, { recursive: true, force: true });
55233
55662
  };
55234
55663
  function applyCursorSettings(root, res) {
55235
- mkdirSync5(path13.dirname(res.cursorSettings), { recursive: true });
55664
+ mkdirSync5(path14.dirname(res.cursorSettings), { recursive: true });
55236
55665
  const settingsExisting = readExisting(res.cursorSettings);
55237
55666
  if (settingsExisting.kind === "malformed") {
55238
55667
  return {
@@ -55260,7 +55689,7 @@ function applyCursorSettings(root, res) {
55260
55689
  };
55261
55690
  }
55262
55691
  function applyCursorMcp(root, res) {
55263
- mkdirSync5(path13.dirname(res.cursorMcp), { recursive: true });
55692
+ mkdirSync5(path14.dirname(res.cursorMcp), { recursive: true });
55264
55693
  const mcpExisting = readExisting(res.cursorMcp);
55265
55694
  if (mcpExisting.kind === "malformed") {
55266
55695
  return {
@@ -55270,7 +55699,18 @@ function applyCursorMcp(root, res) {
55270
55699
  detail: `cannot merge into malformed config: ${mcpExisting.error} — repair or remove the file`
55271
55700
  };
55272
55701
  }
55273
- const mcp = mergeCursorMcp(mcpExisting.kind === "record" ? mcpExisting.value : {}, "workit", cursorMcpServerEntry(res.cursorPluginDir));
55702
+ const mcpBase = mcpExisting.kind === "record" ? mcpExisting.value : {};
55703
+ const server = cursorMcpServerEntry(res.cursorPluginDir);
55704
+ const servers = isRecord2(mcpBase.mcpServers) ? mcpBase.mcpServers : {};
55705
+ const current = isRecord2(servers.workit) ? servers.workit : {};
55706
+ const previousArgs = Array.isArray(current.args) ? current.args.map(String) : [];
55707
+ const previousPackage = previousArgs.find((arg) => arg.startsWith("--package=@brainervirus/workit-cursor@") && arg !== `--package=${CURSOR_RUNTIME_PACKAGE}`);
55708
+ if (previousPackage) {
55709
+ const index = server.args.findIndex((arg) => arg.startsWith("--package="));
55710
+ if (index >= 0)
55711
+ server.args[index] = previousPackage;
55712
+ }
55713
+ const mcp = mergeCursorMcp(mcpBase, "workit", server);
55274
55714
  if (mcp.changed.length === 0) {
55275
55715
  return {
55276
55716
  platform: "cursor",
@@ -55288,9 +55728,9 @@ function applyCursorMcp(root, res) {
55288
55728
  };
55289
55729
  }
55290
55730
  function verifyPlatform(platform, res) {
55291
- if (platform === "opencode" && !existsSync10(res.opencodeConfig))
55731
+ if (platform === "opencode" && !existsSync11(res.opencodeConfig))
55292
55732
  return null;
55293
- if (platform === "cursor" && !existsSync10(res.cursorSettings) && !existsSync10(res.cursorMcp)) {
55733
+ if (platform === "cursor" && !existsSync11(res.cursorSettings) && !existsSync11(res.cursorMcp)) {
55294
55734
  return null;
55295
55735
  }
55296
55736
  return runDoctor({
@@ -55355,6 +55795,51 @@ function applySetupPreview(preview, options = {}) {
55355
55795
  const rootFor = new Map;
55356
55796
  let cursorCopyOk = false;
55357
55797
  for (const mutation of preview.mutations) {
55798
+ if (mutation.type === "install-host") {
55799
+ const expectedPath = mutation.platform === "cursor" ? path14.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap") : mutation.platform === "codex" ? path14.join(res.env.CODEX_HOME ?? path14.join(res.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : mutation.platform === "pi" ? path14.join(res.env.PI_CODING_AGENT_DIR ?? path14.join(res.home, ".pi", "agent"), "settings.json") : "";
55800
+ if (mutation.path !== expectedPath) {
55801
+ entries.push({
55802
+ platform: mutation.platform,
55803
+ file: mutation.path,
55804
+ status: "Failed",
55805
+ detail: `preview/apply path mismatch: apply resolved ${expectedPath}, the previewed host install was ${mutation.path} — rebuild the preview with the same options`
55806
+ });
55807
+ continue;
55808
+ }
55809
+ const run = options.runHostCommand ?? ((step) => runHostCommand(step, { home: res.home, env: res.env }));
55810
+ const installed = runHostInstall(mutation.commands, run);
55811
+ for (const { command, result } of installed.results) {
55812
+ entries.push({
55813
+ platform: mutation.platform,
55814
+ file: command.command,
55815
+ status: result.exitCode === 0 ? "Installed" : "Failed",
55816
+ detail: result.exitCode === 0 ? `${command.purpose} (${command.args.join(" ")})` : `${command.purpose} failed (${command.args.join(" ")}): ${result.stderr || `exit ${result.exitCode}`}`
55817
+ });
55818
+ }
55819
+ if (!installed.ok && installed.results.length === 0) {
55820
+ entries.push({
55821
+ platform: mutation.platform,
55822
+ file: mutation.platform,
55823
+ status: "Failed",
55824
+ detail: "host installation did not run"
55825
+ });
55826
+ }
55827
+ if (!installed.ok)
55828
+ continue;
55829
+ if (mutation.platform === "cursor" && installed.packageRoot) {
55830
+ rootFor.set("cursor", installed.packageRoot);
55831
+ }
55832
+ const verified = mutation.platform === "codex" ? isCodexWorkitInstalled(res.home, res.env) : mutation.platform === "pi" ? isPiWorkitInstalled(res.home, res.env) : true;
55833
+ if (!verified) {
55834
+ entries.push({
55835
+ platform: mutation.platform,
55836
+ file: mutation.platform,
55837
+ status: "Failed",
55838
+ detail: "the host command succeeded, but Workit registration was not found; inspect the host's native package list and retry"
55839
+ });
55840
+ }
55841
+ continue;
55842
+ }
55358
55843
  if (mutation.type !== "register-platform" && mutation.type !== "install-adapter") {
55359
55844
  entries.push(applyMutation(mutation));
55360
55845
  continue;
@@ -55372,7 +55857,7 @@ function applySetupPreview(preview, options = {}) {
55372
55857
  }
55373
55858
  let root = rootFor.get(platform);
55374
55859
  if (root === undefined) {
55375
- root = adapterRoot(platform, res);
55860
+ root = adapterRoot(platform, res, options.preferInstalledAdapter);
55376
55861
  rootFor.set(platform, root);
55377
55862
  }
55378
55863
  if (root === null) {
@@ -55468,7 +55953,7 @@ var setupCompletionGuidance = () => [
55468
55953
  ];
55469
55954
  function applyWorkspaceBranchPolicy(opts) {
55470
55955
  const { workspace_root, env = process.env } = opts;
55471
- const dir = path13.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
55956
+ const dir = path14.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
55472
55957
  const {
55473
55958
  status,
55474
55959
  path: wsPath,
@@ -55480,7 +55965,7 @@ function applyWorkspaceBranchPolicy(opts) {
55480
55965
  if (status === "malformed" || status === "invalid")
55481
55966
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
55482
55967
  const detection = detectBranchPolicy(workspace_root);
55483
- const name = String(env.WORKFLOW_BP_NAME ?? path13.basename(workspace_root));
55968
+ const name = String(env.WORKFLOW_BP_NAME ?? path14.basename(workspace_root));
55484
55969
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
55485
55970
  const policy = {
55486
55971
  preset: detection.preset,
@@ -55529,7 +56014,7 @@ function applyWorkspaceBranchPolicy(opts) {
55529
56014
  ok: false,
55530
56015
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
55531
56016
  };
55532
- mkdirSync5(path13.dirname(wsPath), { recursive: true });
56017
+ mkdirSync5(path14.dirname(wsPath), { recursive: true });
55533
56018
  const tmp = `${wsPath}.${process.pid}.tmp`;
55534
56019
  try {
55535
56020
  writeFileSync5(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
@@ -55588,6 +56073,201 @@ function validateBaseUrl(url) {
55588
56073
  return "base URL must use https";
55589
56074
  return null;
55590
56075
  }
56076
+ function workspaceEditorValue(workspace, field) {
56077
+ const [section, key, leaf] = field.split(".");
56078
+ if (section === "defaultProfile")
56079
+ return workspace.defaultProfile ?? "";
56080
+ if (section === "vcs") {
56081
+ if (key === "account")
56082
+ return workspace.vcs?.account ?? "";
56083
+ return workspace.vcs?.defaultTargetBranch ?? "";
56084
+ }
56085
+ if (section === "youtrack")
56086
+ return key === "baseUrl" ? workspace.youtrack?.baseUrl ?? "" : workspace.youtrack?.link_issues === undefined ? "inherit" : String(workspace.youtrack.link_issues);
56087
+ if (section === "issues")
56088
+ return workspace.issues?.link_on_pr === undefined ? "inherit" : String(workspace.issues.link_on_pr);
56089
+ if (section === "branchPolicy") {
56090
+ const value = workspace.branchPolicy;
56091
+ if (key === "preset")
56092
+ return value?.preset ?? "inherit";
56093
+ if (key === "allowed")
56094
+ return value?.allowed?.join(", ") ?? "";
56095
+ if (key === "protected")
56096
+ return value?.protected?.join(", ") ?? "";
56097
+ if (key === "developBranch")
56098
+ return value?.developBranch ?? "";
56099
+ if (key === "integration")
56100
+ return value?.integration ?? "merge";
56101
+ if (key === "prefixes")
56102
+ return value?.prefixes?.[leaf] ?? "";
56103
+ }
56104
+ if (section === "commitPolicy") {
56105
+ if (key === "preset")
56106
+ return workspace.commitPolicy?.preset ?? "inherit";
56107
+ if (key === "pattern")
56108
+ return workspace.commitPolicy?.pattern ?? "";
56109
+ }
56110
+ return "";
56111
+ }
56112
+ function setWorkspaceEditorValue(workspace, field, value, defaults = {}) {
56113
+ const [section, key, leaf] = field.split(".");
56114
+ const list = value.split(",").map((item) => item.trim()).filter(Boolean);
56115
+ if (section === "defaultProfile")
56116
+ return { ...workspace, defaultProfile: value.trim() || undefined };
56117
+ if (section === "vcs")
56118
+ return workspace.vcs ? { ...workspace, vcs: { ...workspace.vcs, [key]: value.trim() || undefined } } : workspace;
56119
+ if (section === "youtrack") {
56120
+ const current = workspace.youtrack ?? {};
56121
+ if (key === "baseUrl")
56122
+ return { ...workspace, youtrack: { ...current, baseUrl: value.trim() || undefined } };
56123
+ if (!value || value === "inherit") {
56124
+ const { link_issues: _discard, ...rest } = current;
56125
+ return { ...workspace, youtrack: Object.keys(rest).length ? rest : undefined };
56126
+ }
56127
+ return { ...workspace, youtrack: { ...current, link_issues: value === "true" } };
56128
+ }
56129
+ if (section === "issues") {
56130
+ const current = workspace.issues ?? { provider: "github" };
56131
+ if (!value || value === "inherit") {
56132
+ const { link_on_pr: _discard, ...rest } = current;
56133
+ return { ...workspace, issues: Object.keys(rest).length ? rest : undefined };
56134
+ }
56135
+ return {
56136
+ ...workspace,
56137
+ issues: { ...current, provider: "github", link_on_pr: value === "true" }
56138
+ };
56139
+ }
56140
+ if (section === "branchPolicy") {
56141
+ if (key === "preset" && (!value.trim() || value === "inherit")) {
56142
+ const { branchPolicy: _discard, ...rest } = workspace;
56143
+ return rest;
56144
+ }
56145
+ const current = workspace.branchPolicy ?? { preset: defaults.branchPreset ?? "gitflow" };
56146
+ const branchPolicy = key === "preset" ? { ...current, preset: value || "gitflow" } : key === "allowed" ? { ...current, allowed: list } : key === "protected" ? { ...current, protected: list } : key === "developBranch" ? { ...current, developBranch: value.trim() || undefined } : key === "integration" ? { ...current, integration: value === "pr" ? "pr" : "merge" } : key === "prefixes" ? {
56147
+ ...current,
56148
+ prefixes: {
56149
+ feature: "feature/",
56150
+ bugfix: "bugfix/",
56151
+ release: "release/",
56152
+ hotfix: "hotfix/",
56153
+ ...current.prefixes,
56154
+ [leaf]: value.trim()
56155
+ }
56156
+ } : current;
56157
+ return { ...workspace, branchPolicy };
56158
+ }
56159
+ if (section === "commitPolicy") {
56160
+ if (key === "preset" && (!value.trim() || value === "inherit")) {
56161
+ const { commitPolicy: _discard, ...rest } = workspace;
56162
+ return rest;
56163
+ }
56164
+ const current = workspace.commitPolicy ?? { preset: defaults.commitPreset ?? "conventional" };
56165
+ return {
56166
+ ...workspace,
56167
+ commitPolicy: key === "pattern" ? { ...current, pattern: value || undefined } : { ...current, preset: value }
56168
+ };
56169
+ }
56170
+ return workspace;
56171
+ }
56172
+ var splitList = (value) => value.split(",").map((item) => item.trim()).filter(Boolean);
56173
+ var emptyTrack = () => ({
56174
+ strategy: "gitflow",
56175
+ productionBranch: "main",
56176
+ integrationBranch: "develop",
56177
+ naming: { feature: "feature/{name}", release: "release/{version}", hotfix: "hotfix/{name}" },
56178
+ baseBranch: "develop",
56179
+ mergeBackBranches: ["develop"],
56180
+ pullRequestTarget: "develop",
56181
+ tagNamespace: "",
56182
+ versionSource: { kind: "git-tag" },
56183
+ requiredChecks: []
56184
+ });
56185
+ function profileEditorValue(profile, field) {
56186
+ const [section, key, leaf] = field.split(".");
56187
+ if (section === "branchPolicy") {
56188
+ const value = profile?.branchPolicy;
56189
+ if (key === "prefixes")
56190
+ return value?.prefixes?.[leaf] ?? "";
56191
+ if (key === "allowed" || key === "protected")
56192
+ return value?.[key]?.join(", ") ?? "";
56193
+ return String(value?.[key] ?? "");
56194
+ }
56195
+ return String(profile?.commitPolicy?.[key] ?? "");
56196
+ }
56197
+ function setProfileEditorValue(profile, field, value, defaults = {}) {
56198
+ const [section, key, leaf] = field.split(".");
56199
+ if (section === "branchPolicy") {
56200
+ if (key === "preset" && (!value.trim() || value === "inherit")) {
56201
+ const { branchPolicy: _discard, ...rest } = profile;
56202
+ return rest;
56203
+ }
56204
+ const current = profile.branchPolicy ?? { preset: defaults.branchPreset ?? "gitflow" };
56205
+ let branchPolicy;
56206
+ if (key === "preset")
56207
+ branchPolicy = { ...current, preset: value || "gitflow" };
56208
+ else if (key === "allowed" || key === "protected")
56209
+ branchPolicy = { ...current, [key]: splitList(value) };
56210
+ else if (key === "prefixes")
56211
+ branchPolicy = {
56212
+ ...current,
56213
+ prefixes: {
56214
+ feature: "feature/",
56215
+ bugfix: "bugfix/",
56216
+ release: "release/",
56217
+ hotfix: "hotfix/",
56218
+ ...current.prefixes,
56219
+ [leaf]: value
56220
+ }
56221
+ };
56222
+ else if (key === "developBranch")
56223
+ branchPolicy = { ...current, developBranch: value || undefined };
56224
+ else
56225
+ branchPolicy = { ...current, integration: value === "pr" ? "pr" : "merge" };
56226
+ return { ...profile, branchPolicy };
56227
+ }
56228
+ const current = profile.commitPolicy ?? { preset: defaults.commitPreset ?? "conventional" };
56229
+ if (key === "preset" && (!value.trim() || value === "inherit")) {
56230
+ const { commitPolicy: _discard, ...rest } = profile;
56231
+ return rest;
56232
+ }
56233
+ return {
56234
+ ...profile,
56235
+ commitPolicy: key === "pattern" ? { ...current, pattern: value || undefined } : { ...current, preset: value || "conventional" }
56236
+ };
56237
+ }
56238
+ function trackEditorValue(track, field) {
56239
+ if (field.startsWith("naming."))
56240
+ return track.naming[field.slice(7)];
56241
+ if (field === "mergeBackBranches" || field === "requiredChecks")
56242
+ return track[field].join(", ");
56243
+ if (field.startsWith("versionSource.")) {
56244
+ const key = field.slice(14);
56245
+ if (key === "kind")
56246
+ return track.versionSource.kind;
56247
+ if (key === "path")
56248
+ return track.versionSource.kind === "package-json" ? track.versionSource.path : "";
56249
+ return track.versionSource.kind === "package-json" ? track.versionSource.field : "";
56250
+ }
56251
+ return String(track[field] ?? "");
56252
+ }
56253
+ function setTrackEditorValue(track, field, value) {
56254
+ if (field.startsWith("naming."))
56255
+ return { ...track, naming: { ...track.naming, [field.slice(7)]: value } };
56256
+ if (field === "mergeBackBranches" || field === "requiredChecks")
56257
+ return { ...track, [field]: splitList(value) };
56258
+ if (field.startsWith("versionSource.")) {
56259
+ const key = field.slice(14);
56260
+ const kind = key === "kind" ? value : track.versionSource.kind;
56261
+ const versionSource = kind === "package-json" ? {
56262
+ kind,
56263
+ path: key === "path" ? value : track.versionSource.kind === kind ? track.versionSource.path : "package.json",
56264
+ field: key === "field" ? value : track.versionSource.kind === kind ? track.versionSource.field : "version"
56265
+ } : kind === "manual" ? { kind } : { kind: "git-tag" };
56266
+ return { ...track, versionSource };
56267
+ }
56268
+ return { ...track, [field]: value };
56269
+ }
56270
+ var createReleaseTrack = emptyTrack;
55591
56271
  function loadWorkspaces() {
55592
56272
  return loadWorkspacesFrom(configDir());
55593
56273
  }
@@ -55605,8 +56285,8 @@ function applyWizardBranchPolicy(branchPolicy, workspace_root, env = process.env
55605
56285
  }
55606
56286
 
55607
56287
  // packages/workit-cli/src/wizard-state.ts
55608
- import { existsSync as existsSync11, statSync as statSync4 } from "node:fs";
55609
- import path14 from "node:path";
56288
+ import { existsSync as existsSync12, statSync as statSync5 } from "node:fs";
56289
+ import path15 from "node:path";
55610
56290
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
55611
56291
  var NEXT = {
55612
56292
  platforms: "locale",
@@ -55625,6 +56305,10 @@ var NEXT = {
55625
56305
  workspaceName: "workspaceGlob",
55626
56306
  workspaceGlob: "workspaceProvider",
55627
56307
  workspaceProvider: null,
56308
+ workspaceAdvanced: null,
56309
+ workspaceAdvancedValue: null,
56310
+ globalCommitPolicy: null,
56311
+ globalCommitPattern: null,
55628
56312
  branchPolicy: "project",
55629
56313
  branchPolicyDevelop: "branchPolicy",
55630
56314
  project: "summary",
@@ -55648,6 +56332,10 @@ var PREV = {
55648
56332
  workspaceName: "workspaces",
55649
56333
  workspaceGlob: "workspaceName",
55650
56334
  workspaceProvider: "workspaceGlob",
56335
+ workspaceAdvanced: "workspaces",
56336
+ workspaceAdvancedValue: "workspaceAdvanced",
56337
+ globalCommitPolicy: "workspaces",
56338
+ globalCommitPattern: "globalCommitPolicy",
55651
56339
  branchPolicy: "workspaces",
55652
56340
  branchPolicyDevelop: "branchPolicy",
55653
56341
  project: "branchPolicy",
@@ -55662,7 +56350,7 @@ function resolveBasePath(values, env = process.env) {
55662
56350
  }
55663
56351
  var isDirectory = (p) => {
55664
56352
  try {
55665
- return statSync4(p).isDirectory();
56353
+ return statSync5(p).isDirectory();
55666
56354
  } catch {
55667
56355
  return false;
55668
56356
  }
@@ -55671,9 +56359,9 @@ var basePathMessage = (value) => {
55671
56359
  const p = value.trim();
55672
56360
  if (!p)
55673
56361
  return "workspace root path is required";
55674
- return !path14.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
56362
+ return !path15.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
55675
56363
  };
55676
- var skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync11(path14.join(basePath, ".git"));
56364
+ var skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync12(path15.join(basePath, ".git"));
55677
56365
  function nextScreen(screen, preset, tracker, basePath) {
55678
56366
  let next = NEXT[screen];
55679
56367
  while (next && (skipsCustomBranch(next, preset) || skipsBranchPolicy(next, basePath) || skipsYoutrack(next, tracker) || skipsBasePath(next, basePath)))
@@ -55756,6 +56444,7 @@ function createInitialDraft(config = readConfig(), opts = {}) {
55756
56444
  branchPreset: config.branchPolicy.preset,
55757
56445
  branchAllowed: policy.allowed.join(", "),
55758
56446
  branchProtected: policy.protected.join(", "),
56447
+ commitPolicy: { ...config.commitPolicy },
55759
56448
  baseUrl: "",
55760
56449
  basePath: process.env.WORKFLOW_WORKSPACE_ROOT ?? "",
55761
56450
  issueTracker: "youtrack",
@@ -55793,23 +56482,17 @@ function reducer3(draft, action) {
55793
56482
  const next = decodeIssueTracker(action.value, draft.values.issueTracker);
55794
56483
  if (next === draft.values.issueTracker)
55795
56484
  return draft;
55796
- const stripIssues = (workspace) => {
55797
- const rest = { ...workspace };
55798
- delete rest.issues;
55799
- return rest;
55800
- };
55801
- const needsStrip = next !== "github" && (draft.values.workspaces.some((w) => w.issues !== undefined) || draft.workspaceDraft?.issues !== undefined);
55802
56485
  const errors = { ...draft.errors };
55803
56486
  delete errors.baseUrl;
55804
56487
  return {
55805
56488
  ...draft,
55806
56489
  errors,
55807
- workspaceDraft: needsStrip && draft.workspaceDraft ? stripIssues(draft.workspaceDraft) : draft.workspaceDraft,
56490
+ workspaceDraft: draft.workspaceDraft,
55808
56491
  values: {
55809
56492
  ...draft.values,
55810
56493
  issueTracker: next,
55811
56494
  baseUrl: next === "youtrack" ? draft.values.baseUrl : "",
55812
- workspaces: needsStrip ? draft.values.workspaces.map(stripIssues) : draft.values.workspaces
56495
+ workspaces: draft.values.workspaces
55813
56496
  }
55814
56497
  };
55815
56498
  }
@@ -55939,6 +56622,18 @@ function reducer3(draft, action) {
55939
56622
  errors: {}
55940
56623
  };
55941
56624
  }
56625
+ case "workspaceAdvancedOpen": {
56626
+ const entry = draft.values.workspaces[action.index];
56627
+ if (!entry)
56628
+ return draft;
56629
+ return {
56630
+ ...draft,
56631
+ screen: "workspaceAdvanced",
56632
+ workspaceIndex: action.index,
56633
+ workspaceDraft: entry.vcs ? { ...entry, vcs: { ...entry.vcs } } : { ...entry },
56634
+ workspaceEditorField: undefined
56635
+ };
56636
+ }
55942
56637
  case "workspaceRemove":
55943
56638
  return {
55944
56639
  ...draft,
@@ -55962,7 +56657,138 @@ function reducer3(draft, action) {
55962
56657
  ...draft,
55963
56658
  workspaceDraft: {
55964
56659
  ...base,
55965
- vcs: provider === "skip" ? undefined : { provider, defaultTargetBranch: vcs?.defaultTargetBranch }
56660
+ vcs: provider === "skip" ? undefined : { ...vcs, provider }
56661
+ }
56662
+ };
56663
+ }
56664
+ case "workspaceAdvancedSelect":
56665
+ return { ...draft, screen: "workspaceAdvancedValue", workspaceEditorField: action.field };
56666
+ case "workspaceAdvancedSet": {
56667
+ if (!draft.workspaceDraft)
56668
+ return draft;
56669
+ return {
56670
+ ...draft,
56671
+ workspaceDraft: setWorkspaceEditorValue(draft.workspaceDraft, action.field, action.value, {
56672
+ branchPreset: draft.values.branchPreset,
56673
+ commitPreset: draft.values.commitPolicy.preset
56674
+ }),
56675
+ screen: "workspaceAdvanced"
56676
+ };
56677
+ }
56678
+ case "globalCommitOpen":
56679
+ return { ...draft, screen: "globalCommitPolicy" };
56680
+ case "globalCommitEditPattern":
56681
+ return { ...draft, screen: "globalCommitPattern" };
56682
+ case "globalCommitPreset": {
56683
+ const valid = [
56684
+ "conventional",
56685
+ "gitmoji",
56686
+ "ticket-prefix",
56687
+ "freeform",
56688
+ "custom",
56689
+ "auto"
56690
+ ];
56691
+ if (!valid.includes(action.value))
56692
+ return draft;
56693
+ return {
56694
+ ...draft,
56695
+ values: {
56696
+ ...draft.values,
56697
+ commitPolicy: {
56698
+ ...draft.values.commitPolicy,
56699
+ preset: action.value
56700
+ }
56701
+ }
56702
+ };
56703
+ }
56704
+ case "globalCommitPattern":
56705
+ return {
56706
+ ...draft,
56707
+ values: {
56708
+ ...draft.values,
56709
+ commitPolicy: { ...draft.values.commitPolicy, pattern: action.value || undefined }
56710
+ }
56711
+ };
56712
+ case "globalCommitPatternDone":
56713
+ return { ...draft, screen: "globalCommitPolicy" };
56714
+ case "workspaceProfileCreate": {
56715
+ const name = action.name.trim();
56716
+ if (!draft.workspaceDraft || !name || draft.workspaceDraft.profiles?.[name])
56717
+ return draft;
56718
+ const profiles = {
56719
+ ...draft.workspaceDraft.profiles,
56720
+ [name]: { commitPolicy: { preset: "conventional" } }
56721
+ };
56722
+ return { ...draft, workspaceDraft: { ...draft.workspaceDraft, profiles } };
56723
+ }
56724
+ case "workspaceProfileDelete": {
56725
+ if (!draft.workspaceDraft?.profiles?.[action.name])
56726
+ return draft;
56727
+ const profiles = { ...draft.workspaceDraft.profiles };
56728
+ delete profiles[action.name];
56729
+ return {
56730
+ ...draft,
56731
+ workspaceDraft: {
56732
+ ...draft.workspaceDraft,
56733
+ profiles,
56734
+ defaultProfile: draft.workspaceDraft.defaultProfile === action.name ? undefined : draft.workspaceDraft.defaultProfile
56735
+ }
56736
+ };
56737
+ }
56738
+ case "workspaceProfileDefault": {
56739
+ if (!draft.workspaceDraft?.profiles?.[action.name])
56740
+ return draft;
56741
+ return { ...draft, workspaceDraft: { ...draft.workspaceDraft, defaultProfile: action.name } };
56742
+ }
56743
+ case "workspaceProfileSet": {
56744
+ const profile = draft.workspaceDraft?.profiles?.[action.name];
56745
+ if (!draft.workspaceDraft || !profile)
56746
+ return draft;
56747
+ return {
56748
+ ...draft,
56749
+ workspaceDraft: {
56750
+ ...draft.workspaceDraft,
56751
+ profiles: {
56752
+ ...draft.workspaceDraft.profiles,
56753
+ [action.name]: setProfileEditorValue(profile, action.field, action.value, {
56754
+ branchPreset: draft.values.branchPreset,
56755
+ commitPreset: draft.values.commitPolicy.preset
56756
+ })
56757
+ }
56758
+ }
56759
+ };
56760
+ }
56761
+ case "workspaceTrackCreate": {
56762
+ const name = action.name.trim();
56763
+ if (!draft.workspaceDraft || !name || draft.workspaceDraft.releaseTracks?.[name])
56764
+ return draft;
56765
+ return {
56766
+ ...draft,
56767
+ workspaceDraft: {
56768
+ ...draft.workspaceDraft,
56769
+ releaseTracks: { ...draft.workspaceDraft.releaseTracks, [name]: createReleaseTrack() }
56770
+ }
56771
+ };
56772
+ }
56773
+ case "workspaceTrackDelete": {
56774
+ if (!draft.workspaceDraft?.releaseTracks?.[action.name])
56775
+ return draft;
56776
+ const releaseTracks = { ...draft.workspaceDraft.releaseTracks };
56777
+ delete releaseTracks[action.name];
56778
+ return { ...draft, workspaceDraft: { ...draft.workspaceDraft, releaseTracks } };
56779
+ }
56780
+ case "workspaceTrackSet": {
56781
+ const track = draft.workspaceDraft?.releaseTracks?.[action.name];
56782
+ if (!draft.workspaceDraft || !track)
56783
+ return draft;
56784
+ return {
56785
+ ...draft,
56786
+ workspaceDraft: {
56787
+ ...draft.workspaceDraft,
56788
+ releaseTracks: {
56789
+ ...draft.workspaceDraft.releaseTracks,
56790
+ [action.name]: setTrackEditorValue(track, action.field, action.value)
56791
+ }
55966
56792
  }
55967
56793
  };
55968
56794
  }
@@ -56018,32 +56844,30 @@ function workspaceDraftText(draft, field, value) {
56018
56844
  }
56019
56845
 
56020
56846
  // packages/workit-core/src/core/detect-hosts.ts
56021
- import { existsSync as existsSync13, readdirSync as readdirSync5 } from "node:fs";
56022
- import os8 from "node:os";
56023
- import path16 from "node:path";
56847
+ import os9 from "node:os";
56024
56848
 
56025
56849
  // packages/workit-core/src/core/uninstall.ts
56026
- import { existsSync as existsSync12, readFileSync as readFileSync12, realpathSync as realpathSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
56027
- import os7 from "node:os";
56028
- import path15 from "node:path";
56850
+ import { existsSync as existsSync13, readFileSync as readFileSync13, realpathSync as realpathSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
56851
+ import os8 from "node:os";
56852
+ import path16 from "node:path";
56029
56853
  var resolveUninstallPaths = (options = {}) => {
56030
- const home = options.home ?? options.env?.HOME ?? os7.homedir();
56854
+ const home = options.home ?? options.env?.HOME ?? os8.homedir();
56031
56855
  return {
56032
- opencodeConfig: options.opencodeConfig ?? path15.join(home, ".config", "opencode", "opencode.json"),
56033
- cursorSettings: options.cursorSettings ?? path15.join(home, ".cursor", "settings.json"),
56034
- cursorMcp: options.cursorMcp ?? path15.join(home, ".cursor", "mcp.json"),
56035
- cursorPluginDir: options.cursorPluginDir ?? path15.join(home, ".cursor", "plugins", "local", "workit"),
56036
- codexPluginDir: path15.join(home, ".codex", "plugins", "workit"),
56037
- piConfig: path15.join(home, ".pi", "config.json")
56856
+ opencodeConfig: options.opencodeConfig ?? path16.join(home, ".config", "opencode", "opencode.json"),
56857
+ cursorSettings: options.cursorSettings ?? path16.join(home, ".cursor", "settings.json"),
56858
+ cursorMcp: options.cursorMcp ?? path16.join(home, ".cursor", "mcp.json"),
56859
+ cursorPluginDir: options.cursorPluginDir ?? path16.join(home, ".cursor", "plugins", "local", "workit"),
56860
+ codexPluginDir: path16.join(home, ".codex", "plugins", "workit"),
56861
+ piConfig: path16.join(home, ".pi", "config.json")
56038
56862
  };
56039
56863
  };
56040
56864
  var isRecord3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
56041
56865
  var readJsonRecord = (file) => {
56042
56866
  let raw;
56043
56867
  try {
56044
- raw = readFileSync12(file, "utf8");
56868
+ raw = readFileSync13(file, "utf8");
56045
56869
  } catch {
56046
- if (existsSync12(file))
56870
+ if (existsSync13(file))
56047
56871
  return { kind: "malformed", error: `${file} is not readable` };
56048
56872
  return { kind: "missing" };
56049
56873
  }
@@ -56055,8 +56879,8 @@ var readJsonRecord = (file) => {
56055
56879
  }
56056
56880
  };
56057
56881
  var stripTrailingSep = (p) => {
56058
- const j = path15.join(p);
56059
- return path15.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
56882
+ const j = path16.join(p);
56883
+ return path16.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
56060
56884
  };
56061
56885
  var CURSOR_LEGACY_IDENTITIES = ["workflow-toolkit", "local/workflow-toolkit"];
56062
56886
  function cleanCursorSettings(settings, pluginDir) {
@@ -56167,7 +56991,7 @@ function planUninstall(paths = {}) {
56167
56991
  const mcpExisting = readJsonRecord(res.cursorMcp);
56168
56992
  const settingsDirty = settingsExisting.kind === "malformed" || settingsExisting.kind === "record" && cleanCursorSettings(settingsExisting.value, res.cursorPluginDir).changed;
56169
56993
  const mcpDirty = mcpExisting.kind === "malformed" || mcpExisting.kind === "record" && cleanCursorMcp(mcpExisting.value).changed;
56170
- const dirExists = existsSync12(res.cursorPluginDir);
56994
+ const dirExists = existsSync13(res.cursorPluginDir);
56171
56995
  const actions = [];
56172
56996
  if (settingsDirty) {
56173
56997
  actions.push({
@@ -56195,7 +57019,7 @@ function planUninstall(paths = {}) {
56195
57019
  installed: settingsDirty || mcpDirty || dirExists,
56196
57020
  actions
56197
57021
  };
56198
- const codexInstalled = existsSync12(res.codexPluginDir);
57022
+ const codexInstalled = existsSync13(res.codexPluginDir);
56199
57023
  const codex = {
56200
57024
  host: "codex",
56201
57025
  installed: codexInstalled,
@@ -56217,8 +57041,8 @@ function planUninstall(paths = {}) {
56217
57041
  return { hosts: [opencode, cursor, codex, pi] };
56218
57042
  }
56219
57043
  var canonicalRemoveDirAllowed = (actionPath, res) => {
56220
- const expected = path15.resolve(res.cursorPluginDir);
56221
- if (!(actionPath === expected && path15.basename(expected) === "workit" && path15.basename(path15.dirname(expected)) === "local" && path15.basename(path15.dirname(path15.dirname(expected))) === "plugins")) {
57044
+ const expected = path16.resolve(res.cursorPluginDir);
57045
+ if (!(actionPath === expected && path16.basename(expected) === "workit" && path16.basename(path16.dirname(expected)) === "local" && path16.basename(path16.dirname(path16.dirname(expected))) === "plugins")) {
56222
57046
  return false;
56223
57047
  }
56224
57048
  try {
@@ -56242,7 +57066,7 @@ var applyEditJsonRemove = (target, cleaner) => {
56242
57066
  `;
56243
57067
  let current;
56244
57068
  try {
56245
- current = readFileSync12(target, "utf8");
57069
+ current = readFileSync13(target, "utf8");
56246
57070
  } catch (error) {
56247
57071
  return {
56248
57072
  status: "failed",
@@ -56270,12 +57094,12 @@ function applyUninstall(plan, paths = {}) {
56270
57094
  let status;
56271
57095
  let detail;
56272
57096
  if (action.kind === "remove-dir") {
56273
- const resolved = path15.resolve(action.path);
56274
- const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path15.resolve(res.codexPluginDir) && path15.basename(resolved) === "workit" && path15.basename(path15.dirname(resolved)) === "plugins";
57097
+ const resolved = path16.resolve(action.path);
57098
+ const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path16.resolve(res.codexPluginDir) && path16.basename(resolved) === "workit" && path16.basename(path16.dirname(resolved)) === "plugins";
56275
57099
  if (!allowed) {
56276
57100
  status = "failed";
56277
57101
  detail = `refusing to remove non-canonical plugin directory: ${resolved}`;
56278
- } else if (!existsSync12(resolved)) {
57102
+ } else if (!existsSync13(resolved)) {
56279
57103
  status = "skipped";
56280
57104
  detail = "directory already absent";
56281
57105
  } else {
@@ -56306,13 +57130,7 @@ function applyUninstall(plan, paths = {}) {
56306
57130
 
56307
57131
  // packages/workit-core/src/core/detect-hosts.ts
56308
57132
  var HOSTS = ["opencode", "cursor", "codex", "pi"];
56309
- var HOME_MARKERS = {
56310
- opencode: [path16.join(".config", "opencode")],
56311
- cursor: [".cursor"],
56312
- codex: [".codex"],
56313
- pi: [".pi"]
56314
- };
56315
- var WIZARD_HOSTS = ["opencode", "cursor"];
57133
+ var WIZARD_HOSTS = [...HOSTS];
56316
57134
  function preselectedPlatforms(detection) {
56317
57135
  return WIZARD_HOSTS.filter((host) => detection[host].detected);
56318
57136
  }
@@ -56326,52 +57144,19 @@ function emptyDetection() {
56326
57144
  }
56327
57145
  function detectHosts(options = {}) {
56328
57146
  const env = options.env ?? process.env;
56329
- const home = options.home ?? env.HOME ?? os8.homedir();
56330
- const plan = planUninstall(options);
57147
+ const home = options.home ?? env.HOME ?? os9.homedir();
57148
+ const plan = planUninstall({ ...options, home, env });
56331
57149
  const configuredByHost = new Map(plan.hosts.map((h) => [h.host, h.installed]));
56332
57150
  const found = emptyDetection();
56333
57151
  for (const host of HOSTS) {
56334
- const detected = cliFound(host, env, home) || HOME_MARKERS[host].some((m) => existsSync13(path16.join(home, m)));
57152
+ const detected = findHostExecutable(host, { home, env }) !== null || installedHostApp(host, home, env);
56335
57153
  found[host] = {
56336
57154
  detected,
56337
- configured: WIZARD_HOSTS.includes(host) && (configuredByHost.get(host) ?? false)
57155
+ configured: host === "codex" ? isCodexWorkitInstalled(home, env) || (configuredByHost.get(host) ?? false) : host === "pi" ? isPiWorkitInstalled(home, env) || (configuredByHost.get(host) ?? false) : configuredByHost.get(host) ?? false
56338
57156
  };
56339
57157
  }
56340
57158
  return found;
56341
57159
  }
56342
- function cliFound(name, env, home) {
56343
- if (commandOnPath(name, env))
56344
- return true;
56345
- const extra = managerBinDirs(home);
56346
- if (extra.length === 0)
56347
- return false;
56348
- return commandOnPath(name, {
56349
- ...env,
56350
- PATH: [...(env.PATH ?? "").split(path16.delimiter), ...extra].join(path16.delimiter)
56351
- });
56352
- }
56353
- var managerBinDirs = (home) => {
56354
- const dirs = [path16.join(home, ".local", "bin"), path16.join(home, ".asdf", "shims")];
56355
- const versioned = [
56356
- [path16.join(home, ".local", "share", "fnm", "node-versions"), path16.join("installation", "bin")],
56357
- [path16.join(home, ".nvm", "versions", "node"), "bin"]
56358
- ];
56359
- for (const [base, tail] of versioned) {
56360
- let entries;
56361
- try {
56362
- entries = readdirSync5(base, { withFileTypes: true });
56363
- } catch {
56364
- continue;
56365
- }
56366
- for (const entry of entries) {
56367
- try {
56368
- if (entry.isDirectory())
56369
- dirs.push(path16.join(base, entry.name, tail));
56370
- } catch {}
56371
- }
56372
- }
56373
- return dirs;
56374
- };
56375
57160
 
56376
57161
  // packages/workit-cli/src/search-select.tsx
56377
57162
  var import_react65 = __toESM(require_react(), 1);
@@ -56469,22 +57254,10 @@ function SearchSelect({
56469
57254
  var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
56470
57255
  var PLATFORM_LABELS = [
56471
57256
  { label: "OpenCode", value: "opencode" },
56472
- { label: "Cursor", value: "cursor" }
57257
+ { label: "Cursor", value: "cursor" },
57258
+ { label: "Codex", value: "codex" },
57259
+ { label: "Pi", value: "pi" }
56473
57260
  ];
56474
- function platformOptions(detection) {
56475
- return PLATFORM_LABELS.map(({ label, value }) => {
56476
- const found = detection[value];
56477
- const tag = found.configured ? " · already configured" : found.detected ? " · detected" : "";
56478
- return { label: `${label}${tag}`, value };
56479
- });
56480
- }
56481
- function externalHostGuidance(detection) {
56482
- return ["codex", "pi"].map((host) => {
56483
- const found = detection[host];
56484
- const status = found.configured ? "already configured" : found.detected ? "detected" : "not detected";
56485
- return host === "codex" ? `Codex · ${status} — plugin/hooks setup: https://github.com/BrainerVirus/workit#readme (Codex CLI / desktop)` : `Pi · ${status} — install separately: pi install @brainervirus/workit-pi`;
56486
- });
56487
- }
56488
57261
  var BRANCH_PRESETS = [
56489
57262
  { label: "GitFlow", value: "gitflow" },
56490
57263
  { label: "GitHub Flow", value: "github-flow" },
@@ -56522,6 +57295,99 @@ var ISSUE_TRACKERS = [
56522
57295
  { label: "GitLab Issues", value: "gitlab" },
56523
57296
  { label: "None", value: "none" }
56524
57297
  ];
57298
+ var WORKSPACE_ADVANCED_FIELDS = [
57299
+ { label: "Hosting provider account", value: "vcs.account" },
57300
+ { label: "Default target branch", value: "vcs.defaultTargetBranch" },
57301
+ { label: "Workspace YouTrack URL", value: "youtrack.baseUrl" },
57302
+ { label: "Link issues in YouTrack", value: "youtrack.link_issues" },
57303
+ { label: "Link GitHub issues on pull requests", value: "issues.link_on_pr" },
57304
+ { label: "Branch preset", value: "branchPolicy.preset" },
57305
+ { label: "Allowed branch patterns", value: "branchPolicy.allowed" },
57306
+ { label: "Protected branches", value: "branchPolicy.protected" },
57307
+ { label: "Develop branch", value: "branchPolicy.developBranch" },
57308
+ { label: "Integration method (pr or merge)", value: "branchPolicy.integration" },
57309
+ { label: "Feature prefix", value: "branchPolicy.prefixes.feature" },
57310
+ { label: "Bugfix prefix", value: "branchPolicy.prefixes.bugfix" },
57311
+ { label: "Release prefix", value: "branchPolicy.prefixes.release" },
57312
+ { label: "Hotfix prefix", value: "branchPolicy.prefixes.hotfix" },
57313
+ { label: "Commit policy preset", value: "commitPolicy.preset" },
57314
+ { label: "Custom commit pattern", value: "commitPolicy.pattern" },
57315
+ { label: "Default profile name", value: "defaultProfile" }
57316
+ ];
57317
+ var PROFILE_FIELDS = [
57318
+ { label: "Branch preset", value: "branchPolicy.preset" },
57319
+ { label: "Allowed patterns", value: "branchPolicy.allowed" },
57320
+ { label: "Protected branches", value: "branchPolicy.protected" },
57321
+ { label: "Develop branch", value: "branchPolicy.developBranch" },
57322
+ { label: "Integration (pr or merge)", value: "branchPolicy.integration" },
57323
+ { label: "Feature prefix", value: "branchPolicy.prefixes.feature" },
57324
+ { label: "Bugfix prefix", value: "branchPolicy.prefixes.bugfix" },
57325
+ { label: "Release prefix", value: "branchPolicy.prefixes.release" },
57326
+ { label: "Hotfix prefix", value: "branchPolicy.prefixes.hotfix" },
57327
+ { label: "Commit preset", value: "commitPolicy.preset" },
57328
+ { label: "Commit pattern", value: "commitPolicy.pattern" }
57329
+ ];
57330
+ var TRACK_FIELDS = [
57331
+ { label: "Strategy", value: "strategy" },
57332
+ { label: "Production branch", value: "productionBranch" },
57333
+ { label: "Integration branch", value: "integrationBranch" },
57334
+ { label: "Feature branch naming", value: "naming.feature" },
57335
+ { label: "Release branch naming", value: "naming.release" },
57336
+ { label: "Hotfix branch naming", value: "naming.hotfix" },
57337
+ { label: "Base branch", value: "baseBranch" },
57338
+ { label: "Merge-back branches", value: "mergeBackBranches" },
57339
+ { label: "Pull request target", value: "pullRequestTarget" },
57340
+ { label: "Tag namespace", value: "tagNamespace" },
57341
+ { label: "Version source (manual, git-tag, package-json)", value: "versionSource.kind" },
57342
+ { label: "Package.json path", value: "versionSource.path" },
57343
+ { label: "Package version field", value: "versionSource.field" },
57344
+ { label: "Required checks", value: "requiredChecks" }
57345
+ ];
57346
+ var enumFieldOptions = (field) => {
57347
+ if (field === "commitPolicy.preset")
57348
+ return [{ label: "Inherit global", value: "inherit" }, ...commitPresetOptions];
57349
+ if (field === "branchPolicy.preset")
57350
+ return [
57351
+ { label: "Inherit global", value: "inherit" },
57352
+ { label: "GitFlow", value: "gitflow" },
57353
+ { label: "GitHub Flow", value: "github-flow" },
57354
+ { label: "Trunk-based", value: "trunk-based" },
57355
+ { label: "Custom", value: "custom" }
57356
+ ];
57357
+ if (field === "strategy")
57358
+ return [
57359
+ { label: "GitFlow", value: "gitflow" },
57360
+ { label: "GitHub Flow", value: "github-flow" },
57361
+ { label: "Trunk-based", value: "trunk-based" },
57362
+ { label: "Custom", value: "custom" }
57363
+ ];
57364
+ if (field.endsWith(".integration"))
57365
+ return [
57366
+ { label: "Pull requests", value: "pr" },
57367
+ { label: "Merge commits", value: "merge" }
57368
+ ];
57369
+ if (field === "versionSource.kind")
57370
+ return [
57371
+ { label: "Package.json", value: "package-json" },
57372
+ { label: "Git tag", value: "git-tag" },
57373
+ { label: "Manual", value: "manual" }
57374
+ ];
57375
+ if (field === "youtrack.link_issues" || field === "issues.link_on_pr")
57376
+ return [
57377
+ { label: "Inherit global behavior", value: "inherit" },
57378
+ { label: "Enabled", value: "true" },
57379
+ { label: "Disabled", value: "false" }
57380
+ ];
57381
+ return null;
57382
+ };
57383
+ var commitPresetOptions = [
57384
+ { label: "Conventional commits", value: "conventional" },
57385
+ { label: "Gitmoji", value: "gitmoji" },
57386
+ { label: "Ticket prefix", value: "ticket-prefix" },
57387
+ { label: "Freeform", value: "freeform" },
57388
+ { label: "Custom pattern", value: "custom" },
57389
+ { label: "Auto-detect", value: "auto" }
57390
+ ];
56525
57391
  var TIMEZONE_FALLBACK = [
56526
57392
  "UTC",
56527
57393
  "America/New_York",
@@ -56560,6 +57426,8 @@ var TEXT_SCREENS = new Set([
56560
57426
  "basePath",
56561
57427
  "workspaceName",
56562
57428
  "workspaceGlob",
57429
+ "workspaceAdvancedValue",
57430
+ "globalCommitPattern",
56563
57431
  "branchPolicyDevelop"
56564
57432
  ]);
56565
57433
  var SEARCH_SCREENS = new Set(["locale", "timezone"]);
@@ -56604,6 +57472,479 @@ function SelectList({
56604
57472
  }, option.value, true, undefined, this))
56605
57473
  }, undefined, false, undefined, this);
56606
57474
  }
57475
+ function HostPicker({
57476
+ detection,
57477
+ selected,
57478
+ onChange,
57479
+ onSubmit
57480
+ }) {
57481
+ const available = PLATFORM_LABELS.filter(({ value }) => detection[value].detected).map(({ value }) => value);
57482
+ const options = [
57483
+ { label: "Select all available", value: "__all" },
57484
+ { label: "Clear all", value: "__none" },
57485
+ ...PLATFORM_LABELS.map(({ label, value }) => ({ label, value }))
57486
+ ];
57487
+ const [index, setIndex] = import_react66.useState(0);
57488
+ const indexRef = import_react66.useRef(index);
57489
+ const [chosen, setChosen] = import_react66.useState(selected);
57490
+ const chosenRef = import_react66.useRef(chosen);
57491
+ import_react66.useEffect(() => {
57492
+ indexRef.current = index;
57493
+ }, [index]);
57494
+ import_react66.useEffect(() => {
57495
+ chosenRef.current = chosen;
57496
+ }, [chosen]);
57497
+ const update = (next) => {
57498
+ chosenRef.current = next;
57499
+ setChosen(next);
57500
+ onChange(next);
57501
+ };
57502
+ use_input_default((input, key) => {
57503
+ if (key.upArrow || key.downArrow) {
57504
+ const next = Math.max(0, Math.min(options.length - 1, indexRef.current + (key.downArrow ? 1 : -1)));
57505
+ indexRef.current = next;
57506
+ setIndex(next);
57507
+ return;
57508
+ }
57509
+ if (input === " ") {
57510
+ const option = options[indexRef.current];
57511
+ if (option.value === "__all")
57512
+ update([...available]);
57513
+ else if (option.value === "__none")
57514
+ update([]);
57515
+ else if (detection[option.value].detected) {
57516
+ const next = chosenRef.current.includes(option.value) ? chosenRef.current.filter((host) => host !== option.value) : [...chosenRef.current, option.value];
57517
+ update(next);
57518
+ }
57519
+ } else if (key.return) {
57520
+ const option = options[indexRef.current];
57521
+ if (option.value === "__all")
57522
+ update([...available]);
57523
+ else if (option.value === "__none")
57524
+ update([]);
57525
+ onSubmit(chosenRef.current);
57526
+ }
57527
+ });
57528
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57529
+ flexDirection: "column",
57530
+ gap: 0,
57531
+ children: options.map((option, row) => {
57532
+ const host = option.value === "__all" || option.value === "__none" ? null : option.value;
57533
+ const detected = host === null || detection[host].detected;
57534
+ const checked = host !== null && chosen.includes(host);
57535
+ const label = host === null ? option.label : `${checked ? "[✓]" : "[ ]"} ${option.label}${detection[host].configured ? " · already configured" : detection[host].detected ? " · detected" : " · unavailable"}`;
57536
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57537
+ color: row === index ? "cyan" : undefined,
57538
+ dimColor: !detected,
57539
+ children: [
57540
+ row === index ? "❯ " : " ",
57541
+ label
57542
+ ]
57543
+ }, option.value, true, undefined, this);
57544
+ })
57545
+ }, undefined, false, undefined, this);
57546
+ }
57547
+ function WorkspaceCollectionEditor({
57548
+ draft,
57549
+ dispatch
57550
+ }) {
57551
+ const [mode, setMode] = import_react66.useState("main");
57552
+ const [name, setName] = import_react66.useState("");
57553
+ const [field, setField] = import_react66.useState(null);
57554
+ const [nameError, setNameError] = import_react66.useState("");
57555
+ const workspace = draft.workspaceDraft;
57556
+ const profileNames = Object.keys(workspace?.profiles ?? {});
57557
+ const trackNames = Object.keys(workspace?.releaseTracks ?? {});
57558
+ if (!workspace)
57559
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57560
+ color: "red",
57561
+ children: "Workspace draft is unavailable."
57562
+ }, undefined, false, undefined, this);
57563
+ if (mode === "profiles") {
57564
+ const options = [
57565
+ ...profileNames.flatMap((profile) => [
57566
+ { label: `Edit profile ${profile}`, value: `edit:${profile}` },
57567
+ { label: `Set ${profile} as default`, value: `default:${profile}` },
57568
+ { label: `Delete profile ${profile}`, value: `delete:${profile}` }
57569
+ ]),
57570
+ { label: "Create profile", value: "create" },
57571
+ { label: "Back to advanced settings", value: "back" }
57572
+ ];
57573
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57574
+ flexDirection: "column",
57575
+ gap: 1,
57576
+ children: [
57577
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57578
+ bold: true,
57579
+ children: [
57580
+ "Profiles · default: ",
57581
+ workspace.defaultProfile ?? "none"
57582
+ ]
57583
+ }, undefined, true, undefined, this),
57584
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57585
+ dimColor: true,
57586
+ children: "Profiles can override branch rules and commit conventions."
57587
+ }, undefined, false, undefined, this),
57588
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57589
+ options,
57590
+ value: "back",
57591
+ onSelect: (value) => {
57592
+ const profileName = value.slice(value.indexOf(":") + 1);
57593
+ if (value === "create") {
57594
+ setNameError("");
57595
+ setName("");
57596
+ setMode("profileName");
57597
+ } else if (value.startsWith("edit:")) {
57598
+ setName(profileName);
57599
+ setMode("profileEdit");
57600
+ } else if (value.startsWith("default:"))
57601
+ dispatch({ type: "workspaceProfileDefault", name: profileName });
57602
+ else if (value.startsWith("delete:"))
57603
+ dispatch({ type: "workspaceProfileDelete", name: profileName });
57604
+ else
57605
+ setMode("main");
57606
+ }
57607
+ }, undefined, false, undefined, this)
57608
+ ]
57609
+ }, undefined, true, undefined, this);
57610
+ }
57611
+ if (mode === "profileName")
57612
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57613
+ flexDirection: "column",
57614
+ gap: 1,
57615
+ children: [
57616
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57617
+ bold: true,
57618
+ children: "Create profile"
57619
+ }, undefined, false, undefined, this),
57620
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57621
+ dimColor: true,
57622
+ children: "Profile name:"
57623
+ }, undefined, false, undefined, this),
57624
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(TextInput, {
57625
+ onSubmit: (value) => {
57626
+ const next = value.trim();
57627
+ if (!next) {
57628
+ setNameError("Enter a profile name.");
57629
+ return;
57630
+ }
57631
+ if (workspace.profiles?.[next]) {
57632
+ setNameError(`Profile ${next} already exists. Choose a different name or edit it from the list.`);
57633
+ return;
57634
+ }
57635
+ dispatch({ type: "workspaceProfileCreate", name: next });
57636
+ setName(next);
57637
+ setMode("profileEdit");
57638
+ }
57639
+ }, undefined, false, undefined, this),
57640
+ nameError && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57641
+ color: "red",
57642
+ children: nameError
57643
+ }, undefined, false, undefined, this),
57644
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57645
+ dimColor: true,
57646
+ children: "Enter to create · Esc Back"
57647
+ }, undefined, false, undefined, this)
57648
+ ]
57649
+ }, undefined, true, undefined, this);
57650
+ if (mode === "profileEdit" || mode === "profileValue") {
57651
+ const selected = workspace.profiles?.[name];
57652
+ if (mode === "profileValue" && field) {
57653
+ const item = PROFILE_FIELDS.find((entry) => entry.value === field);
57654
+ const choices = enumFieldOptions(field);
57655
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57656
+ flexDirection: "column",
57657
+ gap: 1,
57658
+ children: [
57659
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57660
+ bold: true,
57661
+ children: [
57662
+ "Profile ",
57663
+ name,
57664
+ " · ",
57665
+ item?.label
57666
+ ]
57667
+ }, undefined, true, undefined, this),
57668
+ choices ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57669
+ options: choices,
57670
+ value: selected ? profileEditorValue(selected, field) : choices[0].value,
57671
+ onSelect: (value) => {
57672
+ dispatch({
57673
+ type: "workspaceProfileSet",
57674
+ name,
57675
+ field,
57676
+ value
57677
+ });
57678
+ setMode("profileEdit");
57679
+ }
57680
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(TextInput, {
57681
+ defaultValue: selected ? profileEditorValue(selected, field) : "",
57682
+ onSubmit: (value) => {
57683
+ dispatch({
57684
+ type: "workspaceProfileSet",
57685
+ name,
57686
+ field,
57687
+ value
57688
+ });
57689
+ setMode("profileEdit");
57690
+ }
57691
+ }, undefined, false, undefined, this),
57692
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57693
+ dimColor: true,
57694
+ children: "Enter to save · Esc Back"
57695
+ }, undefined, false, undefined, this)
57696
+ ]
57697
+ }, undefined, true, undefined, this);
57698
+ }
57699
+ const options = [
57700
+ ...PROFILE_FIELDS,
57701
+ { label: "Done editing", value: "done" }
57702
+ ];
57703
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57704
+ flexDirection: "column",
57705
+ gap: 1,
57706
+ children: [
57707
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57708
+ bold: true,
57709
+ children: [
57710
+ "Edit profile · ",
57711
+ name
57712
+ ]
57713
+ }, undefined, true, undefined, this),
57714
+ PROFILE_FIELDS.map((item) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57715
+ dimColor: true,
57716
+ children: [
57717
+ item.label,
57718
+ ": ",
57719
+ selected ? profileEditorValue(selected, item.value) || "unset" : ""
57720
+ ]
57721
+ }, item.value, true, undefined, this)),
57722
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57723
+ options,
57724
+ value: "done",
57725
+ onSelect: (value) => {
57726
+ if (value === "done")
57727
+ setMode("profiles");
57728
+ else {
57729
+ setField(value);
57730
+ setMode("profileValue");
57731
+ }
57732
+ }
57733
+ }, undefined, false, undefined, this)
57734
+ ]
57735
+ }, undefined, true, undefined, this);
57736
+ }
57737
+ if (mode === "tracks") {
57738
+ const options = [
57739
+ ...trackNames.flatMap((track) => [
57740
+ { label: `Edit release track ${track}`, value: `edit:${track}` },
57741
+ { label: `Delete release track ${track}`, value: `delete:${track}` }
57742
+ ]),
57743
+ { label: "Create release track", value: "create" },
57744
+ { label: "Back to advanced settings", value: "back" }
57745
+ ];
57746
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57747
+ flexDirection: "column",
57748
+ gap: 1,
57749
+ children: [
57750
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57751
+ bold: true,
57752
+ children: "Named release tracks"
57753
+ }, undefined, false, undefined, this),
57754
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57755
+ dimColor: true,
57756
+ children: "Each track has its own branches, naming, version source, and checks."
57757
+ }, undefined, false, undefined, this),
57758
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57759
+ options,
57760
+ value: "back",
57761
+ onSelect: (value) => {
57762
+ const trackName = value.slice(value.indexOf(":") + 1);
57763
+ if (value === "create") {
57764
+ setNameError("");
57765
+ setName("");
57766
+ setMode("trackName");
57767
+ } else if (value.startsWith("edit:")) {
57768
+ setName(trackName);
57769
+ setMode("trackEdit");
57770
+ } else if (value.startsWith("delete:"))
57771
+ dispatch({ type: "workspaceTrackDelete", name: trackName });
57772
+ else
57773
+ setMode("main");
57774
+ }
57775
+ }, undefined, false, undefined, this)
57776
+ ]
57777
+ }, undefined, true, undefined, this);
57778
+ }
57779
+ if (mode === "trackName")
57780
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57781
+ flexDirection: "column",
57782
+ gap: 1,
57783
+ children: [
57784
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57785
+ bold: true,
57786
+ children: "Create release track"
57787
+ }, undefined, false, undefined, this),
57788
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57789
+ dimColor: true,
57790
+ children: "Track name:"
57791
+ }, undefined, false, undefined, this),
57792
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(TextInput, {
57793
+ onSubmit: (value) => {
57794
+ const next = value.trim();
57795
+ if (!next) {
57796
+ setNameError("Enter a release-track name.");
57797
+ return;
57798
+ }
57799
+ if (workspace.releaseTracks?.[next]) {
57800
+ setNameError(`Release track ${next} already exists. Choose a different name or edit it from the list.`);
57801
+ return;
57802
+ }
57803
+ dispatch({ type: "workspaceTrackCreate", name: next });
57804
+ setName(next);
57805
+ setMode("trackEdit");
57806
+ }
57807
+ }, undefined, false, undefined, this),
57808
+ nameError && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57809
+ color: "red",
57810
+ children: nameError
57811
+ }, undefined, false, undefined, this),
57812
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57813
+ dimColor: true,
57814
+ children: "Enter to create · Esc Back"
57815
+ }, undefined, false, undefined, this)
57816
+ ]
57817
+ }, undefined, true, undefined, this);
57818
+ if (mode === "trackEdit" || mode === "trackValue") {
57819
+ const selected = workspace.releaseTracks?.[name];
57820
+ if (mode === "trackValue" && field) {
57821
+ const item = TRACK_FIELDS.find((entry) => entry.value === field);
57822
+ const choices = enumFieldOptions(field);
57823
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57824
+ flexDirection: "column",
57825
+ gap: 1,
57826
+ children: [
57827
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57828
+ bold: true,
57829
+ children: [
57830
+ "Release track ",
57831
+ name,
57832
+ " · ",
57833
+ item?.label
57834
+ ]
57835
+ }, undefined, true, undefined, this),
57836
+ choices ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57837
+ options: choices,
57838
+ value: selected ? trackEditorValue(selected, field) : choices[0].value,
57839
+ onSelect: (value) => {
57840
+ dispatch({
57841
+ type: "workspaceTrackSet",
57842
+ name,
57843
+ field,
57844
+ value
57845
+ });
57846
+ setMode("trackEdit");
57847
+ }
57848
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(TextInput, {
57849
+ defaultValue: selected ? trackEditorValue(selected, field) : "",
57850
+ onSubmit: (value) => {
57851
+ dispatch({
57852
+ type: "workspaceTrackSet",
57853
+ name,
57854
+ field,
57855
+ value
57856
+ });
57857
+ setMode("trackEdit");
57858
+ }
57859
+ }, undefined, false, undefined, this),
57860
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57861
+ dimColor: true,
57862
+ children: "Lists use comma-separated values · Enter to save · Esc Back"
57863
+ }, undefined, false, undefined, this)
57864
+ ]
57865
+ }, undefined, true, undefined, this);
57866
+ }
57867
+ const options = [...TRACK_FIELDS, { label: "Done editing", value: "done" }];
57868
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57869
+ flexDirection: "column",
57870
+ gap: 1,
57871
+ children: [
57872
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57873
+ bold: true,
57874
+ children: [
57875
+ "Edit release track · ",
57876
+ name
57877
+ ]
57878
+ }, undefined, true, undefined, this),
57879
+ TRACK_FIELDS.map((item) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57880
+ dimColor: true,
57881
+ children: [
57882
+ item.label,
57883
+ ": ",
57884
+ selected ? trackEditorValue(selected, item.value) || "unset" : ""
57885
+ ]
57886
+ }, item.value, true, undefined, this)),
57887
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57888
+ options,
57889
+ value: "done",
57890
+ onSelect: (value) => {
57891
+ if (value === "done")
57892
+ setMode("tracks");
57893
+ else {
57894
+ setField(value);
57895
+ setMode("trackValue");
57896
+ }
57897
+ }
57898
+ }, undefined, false, undefined, this)
57899
+ ]
57900
+ }, undefined, true, undefined, this);
57901
+ }
57902
+ const options = [
57903
+ ...WORKSPACE_ADVANCED_FIELDS,
57904
+ { label: "Manage profiles", value: "profiles" },
57905
+ { label: "Manage release tracks", value: "tracks" },
57906
+ { label: "Save workspace settings", value: "save" }
57907
+ ];
57908
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57909
+ flexDirection: "column",
57910
+ gap: 1,
57911
+ children: [
57912
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57913
+ bold: true,
57914
+ children: [
57915
+ "Advanced workspace settings · ",
57916
+ workspace.name
57917
+ ]
57918
+ }, undefined, true, undefined, this),
57919
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57920
+ dimColor: true,
57921
+ children: "Choose a setting to edit. Lists use comma-separated values; blank clears an override."
57922
+ }, undefined, false, undefined, this),
57923
+ [...WORKSPACE_ADVANCED_FIELDS].map(({ label, value }) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57924
+ dimColor: true,
57925
+ children: [
57926
+ label,
57927
+ ": ",
57928
+ workspaceEditorValue(workspace, value) || "inherited / unset"
57929
+ ]
57930
+ }, value, true, undefined, this)),
57931
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
57932
+ options,
57933
+ value: "save",
57934
+ onSelect: (value) => {
57935
+ if (value === "save")
57936
+ dispatch({ type: "workspaceSave" });
57937
+ else if (value === "profiles")
57938
+ setMode("profiles");
57939
+ else if (value === "tracks")
57940
+ setMode("tracks");
57941
+ else
57942
+ dispatch({ type: "workspaceAdvancedSelect", field: value });
57943
+ }
57944
+ }, undefined, false, undefined, this)
57945
+ ]
57946
+ }, undefined, true, undefined, this);
57947
+ }
56607
57948
  function effectivePolicy(values) {
56608
57949
  return mergePreset(values.branchPreset, {
56609
57950
  allowed: parseList(values.branchAllowed),
@@ -56815,6 +58156,8 @@ function describeMutation(m) {
56815
58156
  return `+ register ${m.platform}: ${m.path}`;
56816
58157
  case "install-adapter":
56817
58158
  return `+ copy adapter ${m.platform}: ${m.path}`;
58159
+ case "install-host":
58160
+ return `+ install ${m.platform}: ${m.commands.map((command) => command.command).join(", ")}`;
56818
58161
  case "set-token-path":
56819
58162
  return `+ change ${m.key} in ${m.path} → ${m.value}`;
56820
58163
  }
@@ -56870,28 +58213,21 @@ function Screen({
56870
58213
  children: "Step 1 — Platforms"
56871
58214
  }, undefined, false, undefined, this),
56872
58215
  /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
56873
- children: "This wizard configures OpenCode and Cursor only."
58216
+ children: "Choose the detected tools where Workit should be configured."
56874
58217
  }, undefined, false, undefined, this),
56875
58218
  /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
56876
58219
  dimColor: true,
56877
- children: "Select the tools to configure (space to toggle):"
58220
+ children: "Use Space to toggle, or choose Select all available / Clear all."
56878
58221
  }, undefined, false, undefined, this),
56879
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(MultiSelect, {
56880
- options: platformOptions(detection),
56881
- defaultValue: draft.values.platforms,
58222
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(HostPicker, {
58223
+ detection,
58224
+ selected: draft.values.platforms,
56882
58225
  onChange: (values) => dispatch({ type: "set", field: "platforms", value: values }),
56883
58226
  onSubmit: (values) => {
56884
58227
  dispatch({ type: "set", field: "platforms", value: values });
56885
58228
  dispatch({ type: "next" });
56886
58229
  }
56887
58230
  }, undefined, false, undefined, this),
56888
- externalHostGuidance(detection).map((guidance) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
56889
- children: guidance
56890
- }, guidance, false, undefined, this)),
56891
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
56892
- dimColor: true,
56893
- children: "For legacy installations, use `workit cutover` to preview migration."
56894
- }, undefined, false, undefined, this),
56895
58231
  draft.errors.platforms && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
56896
58232
  color: "red",
56897
58233
  children: draft.errors.platforms
@@ -57260,7 +58596,12 @@ function Screen({
57260
58596
  })),
57261
58597
  { label: "Add workspace", value: "add" },
57262
58598
  { label: `Use current project (${base})`, value: "current" },
57263
- { label: "Done", value: "done" }
58599
+ { label: "Done", value: "done" },
58600
+ { label: "Advanced global commit policy", value: "global-commit" },
58601
+ ...draft.values.workspaces.map((w, i) => ({
58602
+ label: `Advanced settings for ${w.name}`,
58603
+ value: `advanced:${i}`
58604
+ }))
57264
58605
  ];
57265
58606
  return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57266
58607
  flexDirection: "column",
@@ -57300,6 +58641,10 @@ function Screen({
57300
58641
  dispatch({ type: "workspaceEdit", index: Number(value.slice(5)) });
57301
58642
  else if (value.startsWith("remove:"))
57302
58643
  dispatch({ type: "workspaceRemove", index: Number(value.slice(7)) });
58644
+ else if (value.startsWith("advanced:"))
58645
+ dispatch({ type: "workspaceAdvancedOpen", index: Number(value.slice(9)) });
58646
+ else if (value === "global-commit")
58647
+ dispatch({ type: "globalCommitOpen" });
57303
58648
  else if (value === "add")
57304
58649
  dispatch({ type: "workspaceAdd" });
57305
58650
  else if (value === "current")
@@ -57424,6 +58769,135 @@ function Screen({
57424
58769
  }, undefined, false, undefined, this)
57425
58770
  ]
57426
58771
  }, undefined, true, undefined, this);
58772
+ case "workspaceAdvanced": {
58773
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(WorkspaceCollectionEditor, {
58774
+ draft,
58775
+ dispatch
58776
+ }, undefined, false, undefined, this);
58777
+ }
58778
+ case "workspaceAdvancedValue": {
58779
+ const field = draft.workspaceEditorField;
58780
+ const current = draft.workspaceDraft && field ? workspaceEditorValue(draft.workspaceDraft, field) : "";
58781
+ const label = WORKSPACE_ADVANCED_FIELDS.find((entry) => entry.value === field)?.label ?? "Setting";
58782
+ const choices = field ? enumFieldOptions(field) : null;
58783
+ const profileChoices = field === "defaultProfile" ? [
58784
+ { label: "Inherit / no default profile", value: "" },
58785
+ ...Object.keys(draft.workspaceDraft?.profiles ?? {}).map((name) => ({
58786
+ label: name,
58787
+ value: name
58788
+ }))
58789
+ ] : null;
58790
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
58791
+ flexDirection: "column",
58792
+ gap: 1,
58793
+ children: [
58794
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58795
+ bold: true,
58796
+ children: [
58797
+ "Advanced workspace settings · ",
58798
+ label
58799
+ ]
58800
+ }, undefined, true, undefined, this),
58801
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58802
+ dimColor: true,
58803
+ children: "Leave empty to clear this override. Use comma-separated values for lists."
58804
+ }, undefined, false, undefined, this),
58805
+ profileChoices ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
58806
+ options: profileChoices,
58807
+ value: current,
58808
+ onSelect: (value) => field && dispatch({ type: "workspaceAdvancedSet", field, value })
58809
+ }, undefined, false, undefined, this) : choices ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
58810
+ options: choices,
58811
+ value: current || choices[0].value,
58812
+ onSelect: (value) => field && dispatch({ type: "workspaceAdvancedSet", field, value })
58813
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(TextInput, {
58814
+ defaultValue: current,
58815
+ onSubmit: (value) => field && dispatch({ type: "workspaceAdvancedSet", field, value })
58816
+ }, undefined, false, undefined, this),
58817
+ field?.endsWith("integration") && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58818
+ dimColor: true,
58819
+ children: "Enter pr or merge."
58820
+ }, undefined, false, undefined, this),
58821
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58822
+ dimColor: true,
58823
+ children: "Enter to save · Esc Back"
58824
+ }, undefined, false, undefined, this)
58825
+ ]
58826
+ }, undefined, true, undefined, this);
58827
+ }
58828
+ case "globalCommitPolicy": {
58829
+ const options = [
58830
+ { label: "Conventional commits", value: "conventional" },
58831
+ { label: "Gitmoji", value: "gitmoji" },
58832
+ { label: "Ticket prefix", value: "ticket-prefix" },
58833
+ { label: "Freeform", value: "freeform" },
58834
+ { label: "Custom pattern", value: "custom" },
58835
+ { label: "Auto-detect", value: "auto" },
58836
+ { label: "Edit custom pattern…", value: "pattern" },
58837
+ { label: "Done", value: "done" }
58838
+ ];
58839
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
58840
+ flexDirection: "column",
58841
+ gap: 1,
58842
+ children: [
58843
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58844
+ bold: true,
58845
+ children: "Advanced global commit policy"
58846
+ }, undefined, false, undefined, this),
58847
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58848
+ dimColor: true,
58849
+ children: "Default convention used when a workspace has no commit override."
58850
+ }, undefined, false, undefined, this),
58851
+ draft.values.commitPolicy.pattern && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58852
+ dimColor: true,
58853
+ children: [
58854
+ "Custom pattern: ",
58855
+ draft.values.commitPolicy.pattern
58856
+ ]
58857
+ }, undefined, true, undefined, this),
58858
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(SelectList, {
58859
+ options,
58860
+ value: draft.values.commitPolicy.preset,
58861
+ onSelect: (value) => {
58862
+ if (value === "pattern")
58863
+ dispatch({ type: "globalCommitEditPattern" });
58864
+ else if (value === "done")
58865
+ dispatch({ type: "back" });
58866
+ else
58867
+ dispatch({ type: "globalCommitPreset", value });
58868
+ }
58869
+ }, undefined, false, undefined, this),
58870
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58871
+ dimColor: true,
58872
+ children: "Enter to select · b Back"
58873
+ }, undefined, false, undefined, this)
58874
+ ]
58875
+ }, undefined, true, undefined, this);
58876
+ }
58877
+ case "globalCommitPattern":
58878
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
58879
+ flexDirection: "column",
58880
+ gap: 1,
58881
+ children: [
58882
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58883
+ bold: true,
58884
+ children: "Advanced global commit policy · Custom pattern"
58885
+ }, undefined, false, undefined, this),
58886
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58887
+ dimColor: true,
58888
+ children: "Regular expression matched against the commit subject line."
58889
+ }, undefined, false, undefined, this),
58890
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(TextInput, {
58891
+ defaultValue: draft.values.commitPolicy.pattern ?? "",
58892
+ onChange: (value) => dispatch({ type: "globalCommitPattern", value }),
58893
+ onSubmit: () => dispatch({ type: "globalCommitPatternDone" })
58894
+ }, undefined, false, undefined, this),
58895
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
58896
+ dimColor: true,
58897
+ children: "Enter to save · Esc Back"
58898
+ }, undefined, false, undefined, this)
58899
+ ]
58900
+ }, undefined, true, undefined, this);
57427
58901
  case "branchPolicy":
57428
58902
  return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(BranchPolicyScreen, {
57429
58903
  draft,
@@ -57494,6 +58968,36 @@ function Screen({
57494
58968
  case "summary": {
57495
58969
  const policy = effectivePolicy(draft.values);
57496
58970
  const preview = buildSetupPreview(draft.values, { cwd: resolveBasePath(draft.values) });
58971
+ const policyPreview = (() => {
58972
+ try {
58973
+ const current = readConfig();
58974
+ const config = {
58975
+ ...current,
58976
+ branchPolicy: mergePreset(draft.values.branchPreset, {
58977
+ allowed: parseList(draft.values.branchAllowed),
58978
+ protectedNames: parseList(draft.values.branchProtected)
58979
+ }, current),
58980
+ commitPolicy: draft.values.commitPolicy
58981
+ };
58982
+ const checkout = resolveBasePath(draft.values);
58983
+ const workspace = resolveWorkspaceFromEntries(checkout, draft.values.workspaces);
58984
+ const resolved = resolveWorkspacePolicy(config, workspace);
58985
+ if (resolved.status === "invalid")
58986
+ return { error: resolved.error };
58987
+ return {
58988
+ error: null,
58989
+ workspace: workspace?.name ?? null,
58990
+ branchSource: resolved.provenance.branchPolicy,
58991
+ commitSource: resolved.provenance.commitPolicy,
58992
+ target: resolved.branchPolicy.defaultTargetBranch,
58993
+ branchPreset: resolved.branchPolicy.preset,
58994
+ commitPreset: resolved.commitPolicy.preset
58995
+ };
58996
+ } catch (error) {
58997
+ return { error: error instanceof Error ? error.message : String(error) };
58998
+ }
58999
+ })();
59000
+ const canApply = preview.ok && policyPreview.error === null;
57497
59001
  return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57498
59002
  flexDirection: "column",
57499
59003
  gap: 1,
@@ -57544,6 +59048,52 @@ function Screen({
57544
59048
  policy.protected.join(", ")
57545
59049
  ]
57546
59050
  }, undefined, true, undefined, this),
59051
+ policyPreview.error === null ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
59052
+ flexDirection: "column",
59053
+ gap: 0,
59054
+ children: [
59055
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59056
+ children: [
59057
+ "Sample checkout: ",
59058
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59059
+ color: "green",
59060
+ children: resolveBasePath(draft.values)
59061
+ }, undefined, false, undefined, this),
59062
+ policyPreview.workspace ? ` · workspace ${policyPreview.workspace}` : " · no matching workspace"
59063
+ ]
59064
+ }, undefined, true, undefined, this),
59065
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59066
+ children: [
59067
+ "Effective branch policy: ",
59068
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59069
+ color: "green",
59070
+ children: policyPreview.branchPreset
59071
+ }, undefined, false, undefined, this),
59072
+ " · target ",
59073
+ policyPreview.target,
59074
+ " · source ",
59075
+ policyPreview.branchSource
59076
+ ]
59077
+ }, undefined, true, undefined, this),
59078
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59079
+ children: [
59080
+ "Effective commit policy: ",
59081
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59082
+ color: "green",
59083
+ children: policyPreview.commitPreset
59084
+ }, undefined, false, undefined, this),
59085
+ " · source ",
59086
+ policyPreview.commitSource
59087
+ ]
59088
+ }, undefined, true, undefined, this)
59089
+ ]
59090
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59091
+ color: "red",
59092
+ children: [
59093
+ "Policy preview failed: ",
59094
+ policyPreview.error
59095
+ ]
59096
+ }, undefined, true, undefined, this),
57547
59097
  /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57548
59098
  children: [
57549
59099
  "YouTrack base URL:",
@@ -57592,7 +59142,7 @@ function Screen({
57592
59142
  }, o.envKey, true, undefined, this))
57593
59143
  ]
57594
59144
  }, undefined, true, undefined, this),
57595
- preview.ok ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
59145
+ canApply ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
57596
59146
  flexDirection: "column",
57597
59147
  gap: 0,
57598
59148
  children: [
@@ -57600,9 +59150,9 @@ function Screen({
57600
59150
  bold: true,
57601
59151
  children: "Will apply:"
57602
59152
  }, undefined, false, undefined, this),
57603
- preview.mutations.map((m) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
59153
+ preview.mutations.map((m, index) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57604
59154
  children: describeMutation(m)
57605
- }, `${m.type}:${m.path}`, false, undefined, this)),
59155
+ }, `${m.type}:${index}`, false, undefined, this)),
57606
59156
  preview.preserved.map((p) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57607
59157
  color: "green",
57608
59158
  children: [
@@ -57631,7 +59181,7 @@ function Screen({
57631
59181
  }, undefined, false, undefined, this)
57632
59182
  ]
57633
59183
  }, undefined, true, undefined, this),
57634
- preview.ok && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(ConfirmInput, {
59184
+ canApply && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(ConfirmInput, {
57635
59185
  defaultChoice: "confirm",
57636
59186
  submitOnEnter: false,
57637
59187
  onConfirm: () => dispatch({ type: "apply" }),
@@ -57639,7 +59189,7 @@ function Screen({
57639
59189
  }, undefined, false, undefined, this),
57640
59190
  /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
57641
59191
  dimColor: true,
57642
- children: preview.ok ? "y to apply · b Back · Esc Cancel" : "b Back · Esc Cancel"
59192
+ children: canApply ? "y to apply · b Back · Esc Cancel" : "b Back · Esc Cancel"
57643
59193
  }, undefined, false, undefined, this)
57644
59194
  ]
57645
59195
  }, undefined, true, undefined, this);
@@ -57658,7 +59208,7 @@ function Screen({
57658
59208
 
57659
59209
  // packages/workit-core/src/core/logger.ts
57660
59210
  import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "node:fs";
57661
- import os9 from "node:os";
59211
+ import os10 from "node:os";
57662
59212
  import path17 from "node:path";
57663
59213
  var REDACTED = "[REDACTED]";
57664
59214
  var DEFAULT_MAX_RATE = 20;
@@ -57713,10 +59263,10 @@ var resolveStateDir = () => {
57713
59263
  const override = process.env.WORKFLOW_TOOLKIT_STATE;
57714
59264
  if (override)
57715
59265
  return override;
57716
- const home = os9.homedir();
59266
+ const home = os10.homedir();
57717
59267
  if (process.env.XDG_STATE_HOME)
57718
59268
  return path17.join(process.env.XDG_STATE_HOME, "workit");
57719
- switch (os9.platform()) {
59269
+ switch (os10.platform()) {
57720
59270
  case "darwin":
57721
59271
  return path17.join(home, "Library", "Application Support", "workit");
57722
59272
  case "win32":
@@ -57725,7 +59275,7 @@ var resolveStateDir = () => {
57725
59275
  return path17.join(process.env.HOME ?? home, ".local", "state", "workit");
57726
59276
  }
57727
59277
  };
57728
- var homePattern = new RegExp(`${escapeRegExp2(os9.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
59278
+ var homePattern = new RegExp(`${escapeRegExp2(os10.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
57729
59279
  var applyPatterns = (value) => {
57730
59280
  let out = value;
57731
59281
  out = out.replace(SECRET_VALUE, REDACTED);
@@ -58115,11 +59665,11 @@ async function runCutoverCommand(argv, deps = {}) {
58115
59665
  }
58116
59666
 
58117
59667
  // packages/workit-cli/src/task.ts
58118
- import { readFileSync as readFileSync19 } from "node:fs";
59668
+ import { readFileSync as readFileSync20 } from "node:fs";
58119
59669
  import { createInterface as createInterface2 } from "node:readline/promises";
58120
59670
 
58121
59671
  // packages/workit-core/src/core.ts
58122
- import { spawnSync as spawnSync5 } from "node:child_process";
59672
+ import { spawnSync as spawnSync6 } from "node:child_process";
58123
59673
  import { existsSync as existsSync17, realpathSync as realpathSync10 } from "node:fs";
58124
59674
  import path27 from "node:path";
58125
59675
 
@@ -59792,7 +61342,7 @@ function assertProductWriteAllowed(input) {
59792
61342
  // packages/workit-core/src/core/task-evaluation.ts
59793
61343
  import { createHash as createHash10 } from "node:crypto";
59794
61344
  import * as fs10 from "node:fs";
59795
- import { spawnSync as spawnSync4 } from "node:child_process";
61345
+ import { spawnSync as spawnSync5 } from "node:child_process";
59796
61346
  import path25 from "node:path";
59797
61347
 
59798
61348
  // packages/workit-core/src/core/authority.ts
@@ -59802,11 +61352,11 @@ import path24 from "node:path";
59802
61352
 
59803
61353
  // packages/workit-core/src/core/auto-approval.ts
59804
61354
  import { createHash as createHash8 } from "node:crypto";
59805
- import { readFileSync as readFileSync15, realpathSync as realpathSync5 } from "node:fs";
61355
+ import { readFileSync as readFileSync16, realpathSync as realpathSync5 } from "node:fs";
59806
61356
  import path23 from "node:path";
59807
61357
 
59808
61358
  // packages/workit-core/src/core/branch.ts
59809
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
61359
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
59810
61360
  import { execFileSync as execFileSync3 } from "node:child_process";
59811
61361
  import { createHash as createHash7 } from "node:crypto";
59812
61362
  import path22 from "node:path";
@@ -59911,11 +61461,11 @@ var gitContext = (workspaceRoot, paths = []) => {
59911
61461
  // packages/workit-core/src/core/vcs-config.ts
59912
61462
  import fs8 from "node:fs";
59913
61463
  import path21 from "node:path";
59914
- import { spawnSync as spawnSync3 } from "node:child_process";
61464
+ import { spawnSync as spawnSync4 } from "node:child_process";
59915
61465
  var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path21.join(configDir(), "vcs.json");
59916
61466
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
59917
61467
  var remoteProvider = (cwd) => {
59918
- const r = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
61468
+ const r = spawnSync4("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
59919
61469
  if (r.status !== 0)
59920
61470
  return null;
59921
61471
  const url = (r.stdout ?? "").trim();
@@ -59952,7 +61502,7 @@ var remoteEndpoint = (remote) => {
59952
61502
  var configuredSshEndpoint = (host) => {
59953
61503
  if (host.startsWith("-"))
59954
61504
  return null;
59955
- const resolved = spawnSync3("ssh", ["-G", host], {
61505
+ const resolved = spawnSync4("ssh", ["-G", host], {
59956
61506
  encoding: "utf8",
59957
61507
  timeout: 5000,
59958
61508
  maxBuffer: 1024 * 1024,
@@ -60031,8 +61581,8 @@ function vcsConfig(mode, cwd) {
60031
61581
  const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
60032
61582
  const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
60033
61583
  const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
60034
- const linkIssues = provider === "gitlab" && typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
60035
- const youtrackBaseUrl = provider === "gitlab" && typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
61584
+ const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
61585
+ const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
60036
61586
  let issuesProvider = null;
60037
61587
  let linkOnPr = null;
60038
61588
  if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
@@ -60103,7 +61653,7 @@ function vcsCliIdentity(cwd) {
60103
61653
  PATH: process.env.PATH ?? "",
60104
61654
  ...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
60105
61655
  };
60106
- const result = spawnSync3(bin, ["api", "user"], {
61656
+ const result = spawnSync4(bin, ["api", "user"], {
60107
61657
  cwd: root,
60108
61658
  encoding: "utf8",
60109
61659
  env
@@ -60145,7 +61695,7 @@ function mergedPrStyle(limit = 6, cwd) {
60145
61695
  `).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
60146
61696
  descriptionPreview: desc.slice(0, 600)
60147
61697
  });
60148
- const remoteResult = spawnSync3("git", ["remote", "get-url", "--push", "--all", "origin"], {
61698
+ const remoteResult = spawnSync4("git", ["remote", "get-url", "--push", "--all", "origin"], {
60149
61699
  cwd,
60150
61700
  encoding: "utf8"
60151
61701
  });
@@ -60161,7 +61711,7 @@ function mergedPrStyle(limit = 6, cwd) {
60161
61711
  if (!hostingApiHostMatches(remote, host))
60162
61712
  return { ok: false, error: "PR style target does not match the provider host" };
60163
61713
  if (provider === "gitlab") {
60164
- const run = (args) => spawnSync3("glab", ["api", ...args], {
61714
+ const run = (args) => spawnSync4("glab", ["api", ...args], {
60165
61715
  cwd,
60166
61716
  encoding: "utf8",
60167
61717
  env
@@ -60181,7 +61731,7 @@ function mergedPrStyle(limit = 6, cwd) {
60181
61731
  });
60182
61732
  }
60183
61733
  } else if (provider === "github") {
60184
- const r = spawnSync3("gh", [
61734
+ const r = spawnSync4("gh", [
60185
61735
  "pr",
60186
61736
  "list",
60187
61737
  "--repo",
@@ -60454,7 +62004,7 @@ var branchSetup = ({
60454
62004
  };
60455
62005
  const readManifest = () => {
60456
62006
  try {
60457
- return JSON.parse(readFileSync14(manifestPath, "utf8"));
62007
+ return JSON.parse(readFileSync15(manifestPath, "utf8"));
60458
62008
  } catch {
60459
62009
  return {};
60460
62010
  }
@@ -60650,7 +62200,7 @@ var OPERATION_CLASS = {
60650
62200
  var operationAutoClass = (operation) => typeof operation === "string" ? OPERATION_CLASS[operation] ?? null : null;
60651
62201
  var digestOf = (configDirPath) => {
60652
62202
  try {
60653
- const raw = readFileSync15(`${configDirPath}/workspaces.json`, "utf8");
62203
+ const raw = readFileSync16(`${configDirPath}/workspaces.json`, "utf8");
60654
62204
  return createHash8("sha256").update(raw).digest("hex");
60655
62205
  } catch {
60656
62206
  return null;
@@ -61458,7 +63008,7 @@ var compareCodeUnits2 = (left, right) => {
61458
63008
  return left.length - right.length;
61459
63009
  };
61460
63010
  var gitPaths = (root) => {
61461
- const result = spawnSync4("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
63011
+ const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
61462
63012
  cwd: root,
61463
63013
  encoding: "buffer"
61464
63014
  });
@@ -61471,7 +63021,7 @@ var gitPaths = (root) => {
61471
63021
  };
61472
63022
  }
61473
63023
  const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path25.sep).join("/")) : [];
61474
- const stagedDeleted = spawnSync4("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
63024
+ const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
61475
63025
  if (stagedDeleted.status !== 0) {
61476
63026
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
61477
63027
  if (!/not a git repository/i.test(stderr))
@@ -61602,7 +63152,7 @@ function captureCandidate(root, scope, environment = []) {
61602
63152
  const values = supplied.sort((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
61603
63153
  if (values.some(({ value }) => value === null))
61604
63154
  completeness = "uncertain";
61605
- const headResult = spawnSync4("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
63155
+ const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
61606
63156
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
61607
63157
  completeness = "uncertain";
61608
63158
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -65090,7 +66640,7 @@ function resolveInside(root, candidate) {
65090
66640
  }
65091
66641
  function run2(root, executable, args, env = {}) {
65092
66642
  const cwd = realpathSync10(root);
65093
- const result = spawnSync5(executable, args, {
66643
+ const result = spawnSync6(executable, args, {
65094
66644
  cwd,
65095
66645
  encoding: "utf8",
65096
66646
  env: { ...process.env, ...env }
@@ -65106,7 +66656,7 @@ function run2(root, executable, args, env = {}) {
65106
66656
  // packages/workit-core/src/core/pr-create.ts
65107
66657
  import fs11 from "node:fs";
65108
66658
  import path28 from "node:path";
65109
- import { spawnSync as spawnSync6 } from "node:child_process";
66659
+ import { spawnSync as spawnSync7 } from "node:child_process";
65110
66660
  import { pathToFileURL } from "node:url";
65111
66661
  var remoteRepoPath = (remote) => {
65112
66662
  const raw = remote.trim();
@@ -65208,11 +66758,11 @@ function whichOnPath(tool) {
65208
66758
  }
65209
66759
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
65210
66760
  function repoRoot2(cwd) {
65211
- const result = spawnSync6("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
66761
+ const result = spawnSync7("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
65212
66762
  return result.status === 0 ? (result.stdout ?? "").trim() : cwd;
65213
66763
  }
65214
66764
  var pushRemote = (cwd) => {
65215
- const result = spawnSync6("git", ["remote", "get-url", "--push", "--all", "origin"], {
66765
+ const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
65216
66766
  cwd,
65217
66767
  encoding: "utf8"
65218
66768
  });
@@ -65272,7 +66822,7 @@ var pushTargetIsStable = (cwd, remote) => {
65272
66822
  const target = safePushUrl(remote);
65273
66823
  if (!target)
65274
66824
  return false;
65275
- const result = spawnSync6("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
66825
+ const result = spawnSync7("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
65276
66826
  if (result.status !== 0 && result.status !== 1)
65277
66827
  return false;
65278
66828
  const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
@@ -65320,12 +66870,12 @@ function prCreate(env, cwd) {
65320
66870
  const resolvedDefault = String(cfg.defaultTargetBranch ?? policy.defaultTargetBranch ?? "develop");
65321
66871
  const target = targetOverride || resolvedDefault;
65322
66872
  const title = String(env.WF_PR_TITLE ?? "");
65323
- const br = spawnSync6("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
66873
+ const br = spawnSync7("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
65324
66874
  cwd: root,
65325
66875
  encoding: "utf8"
65326
66876
  });
65327
66877
  const branch = br.status === 0 ? (br.stdout ?? "").trim() : "";
65328
- const sourceResult = branch ? spawnSync6("git", ["rev-parse", "--verify", `refs/heads/${branch}`], {
66878
+ const sourceResult = branch ? spawnSync7("git", ["rev-parse", "--verify", `refs/heads/${branch}`], {
65329
66879
  cwd: root,
65330
66880
  encoding: "utf8"
65331
66881
  }) : null;
@@ -65334,7 +66884,7 @@ function prCreate(env, cwd) {
65334
66884
  return { error: "approved PR source branch and commit are incomplete" };
65335
66885
  if (env.WORKIT_EXPECTED_SOURCE_BRANCH && env.WORKIT_EXPECTED_SOURCE_BRANCH !== branch || env.WORKIT_EXPECTED_SOURCE_COMMIT && env.WORKIT_EXPECTED_SOURCE_COMMIT !== sourceCommit)
65336
66886
  return { error: "approved PR source branch or commit changed before execution" };
65337
- const mergeBaseResult = policy.integration === "merge" ? spawnSync6("git", ["rev-parse", "--verify", `refs/heads/${target}`], {
66887
+ const mergeBaseResult = policy.integration === "merge" ? spawnSync7("git", ["rev-parse", "--verify", `refs/heads/${target}`], {
65338
66888
  cwd: root,
65339
66889
  encoding: "utf8"
65340
66890
  }) : null;
@@ -65350,7 +66900,7 @@ function prCreate(env, cwd) {
65350
66900
  targetBranch: target,
65351
66901
  stderr: "source or target branch commit could not be resolved"
65352
66902
  };
65353
- const merge = spawnSync6("git", ["merge", "--no-ff", sourceCommit, "-m", title], {
66903
+ const merge = spawnSync7("git", ["merge", "--no-ff", sourceCommit, "-m", title], {
65354
66904
  cwd: root,
65355
66905
  encoding: "utf8"
65356
66906
  });
@@ -65361,16 +66911,16 @@ function prCreate(env, cwd) {
65361
66911
  targetBranch: target,
65362
66912
  stderr: (merge.stderr ?? "").slice(0, 800)
65363
66913
  };
65364
- const mergedCommit = spawnSync6("git", ["rev-parse", "HEAD"], {
66914
+ const mergedCommit = spawnSync7("git", ["rev-parse", "HEAD"], {
65365
66915
  cwd: root,
65366
66916
  encoding: "utf8"
65367
66917
  });
65368
66918
  if (mergedCommit.status !== 0)
65369
66919
  return { error: "merge commit could not be resolved", mode: "merge" };
65370
- const updateTarget = spawnSync6("git", ["update-ref", `refs/heads/${target}`, mergedCommit.stdout.trim(), mergeBaseCommit], { cwd: root, encoding: "utf8" });
66920
+ const updateTarget = spawnSync7("git", ["update-ref", `refs/heads/${target}`, mergedCommit.stdout.trim(), mergeBaseCommit], { cwd: root, encoding: "utf8" });
65371
66921
  if (updateTarget.status !== 0)
65372
66922
  return { error: "merge target branch changed before update", mode: "merge" };
65373
- const push = spawnSync6("git", ["push", targetUrl, `${mergedCommit.stdout.trim()}:refs/heads/${target}`], { cwd: root, encoding: "utf8", env: { ...process.env, PATH: process.env.PATH ?? "" } });
66923
+ const push = spawnSync7("git", ["push", targetUrl, `${mergedCommit.stdout.trim()}:refs/heads/${target}`], { cwd: root, encoding: "utf8", env: { ...process.env, PATH: process.env.PATH ?? "" } });
65374
66924
  if (push.status !== 0)
65375
66925
  return {
65376
66926
  error: "push failed",
@@ -65387,7 +66937,7 @@ function prCreate(env, cwd) {
65387
66937
  output: (push.stdout ?? "").trim()
65388
66938
  };
65389
66939
  };
65390
- const co = spawnSync6("git", ["checkout", "--detach", mergeBaseCommit], {
66940
+ const co = spawnSync7("git", ["checkout", "--detach", mergeBaseCommit], {
65391
66941
  cwd: root,
65392
66942
  encoding: "utf8"
65393
66943
  });
@@ -65399,7 +66949,7 @@ function prCreate(env, cwd) {
65399
66949
  stderr: (co.stderr ?? "").slice(0, 800)
65400
66950
  };
65401
66951
  const result = finish();
65402
- spawnSync6("git", ["checkout", branch], { cwd: root, encoding: "utf8" });
66952
+ spawnSync7("git", ["checkout", branch], { cwd: root, encoding: "utf8" });
65403
66953
  return result;
65404
66954
  }
65405
66955
  const pr = cfg.pr ?? {};
@@ -65425,7 +66975,7 @@ function prCreate(env, cwd) {
65425
66975
  const push = pr.pushBranch !== false;
65426
66976
  const skipConfirm = pr.confirmSkip !== false;
65427
66977
  const remoteBranchTip = () => {
65428
- const read = spawnSync6("git", ["ls-remote", targetUrl, `refs/heads/${branch}`], {
66978
+ const read = spawnSync7("git", ["ls-remote", targetUrl, `refs/heads/${branch}`], {
65429
66979
  cwd: root,
65430
66980
  encoding: "utf8",
65431
66981
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -65441,7 +66991,7 @@ function prCreate(env, cwd) {
65441
66991
  stderr: branch ? "source branch commit could not be resolved" : "empty current branch (detached HEAD or unborn HEAD)"
65442
66992
  };
65443
66993
  if (push) {
65444
- const pushed = spawnSync6("git", ["push", "-u", targetUrl, `${sourceCommit}:refs/heads/${branch}`], { cwd: root, encoding: "utf8", env: { ...process.env, PATH: process.env.PATH ?? "" } });
66994
+ const pushed = spawnSync7("git", ["push", "-u", targetUrl, `${sourceCommit}:refs/heads/${branch}`], { cwd: root, encoding: "utf8", env: { ...process.env, PATH: process.env.PATH ?? "" } });
65445
66995
  if (pushed.status !== 0)
65446
66996
  return { error: "push failed", provider, stderr: (pushed.stderr ?? "").slice(0, 800) };
65447
66997
  }
@@ -65486,12 +67036,12 @@ function prCreate(env, cwd) {
65486
67036
  cmd.push("--draft");
65487
67037
  cmdEnv = { ...process.env, PATH: process.env.PATH ?? "", GH_HOST: identity.host };
65488
67038
  }
65489
- const result = spawnSync6(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
67039
+ const result = spawnSync7(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
65490
67040
  if (result.status !== 0) {
65491
67041
  const err = (result.stderr ?? result.stdout ?? "").trim();
65492
67042
  let hint = null;
65493
67043
  if (provider === "gitlab" && (err.includes("409") || err.toLowerCase().includes("already exists"))) {
65494
- const list = spawnSync6("glab", ["mr", "list", `--source-branch=${branch}`, "--output=json"], {
67044
+ const list = spawnSync7("glab", ["mr", "list", `--source-branch=${branch}`, "--output=json"], {
65495
67045
  cwd: root,
65496
67046
  encoding: "utf8",
65497
67047
  env: cmdEnv
@@ -65550,7 +67100,7 @@ function mergePr(cwd, opts) {
65550
67100
  return { error: "approved hosting account changed before execution" };
65551
67101
  if (!/^[a-f0-9]{40,64}$/i.test(opts.sourceCommit))
65552
67102
  return { error: "approved merge source commit is invalid" };
65553
- const currentCommit = spawnSync6("git", ["rev-parse", "--verify", `refs/heads/${opts.source}`], {
67103
+ const currentCommit = spawnSync7("git", ["rev-parse", "--verify", `refs/heads/${opts.source}`], {
65554
67104
  cwd: root,
65555
67105
  encoding: "utf8"
65556
67106
  });
@@ -65564,7 +67114,7 @@ function mergePr(cwd, opts) {
65564
67114
  return { error: `workflow CLI missing (required for ${provider})` };
65565
67115
  const owner = repoPath.split("/")[0];
65566
67116
  const endpoint = provider === "github" ? `repos/${repository.replace(/^[^/]+\//u, "")}/pulls?state=open&head=${encodeURIComponent(`${owner}:${opts.source}`)}&base=${encodeURIComponent(opts.target)}&per_page=100` : `projects/${encodeURIComponent(repoPath)}/merge_requests?state=opened&source_branch=${encodeURIComponent(opts.source)}&target_branch=${encodeURIComponent(opts.target)}&per_page=100`;
65567
- const listOpen = () => spawnSync6(provider === "github" ? "gh" : "glab", ["api", endpoint], {
67117
+ const listOpen = () => spawnSync7(provider === "github" ? "gh" : "glab", ["api", endpoint], {
65568
67118
  cwd: root,
65569
67119
  encoding: "utf8",
65570
67120
  env: {
@@ -65624,7 +67174,7 @@ function mergePr(cwd, opts) {
65624
67174
  cmd.push("--delete-branch");
65625
67175
  cmdEnv = { ...process.env, PATH: process.env.PATH ?? "", GH_HOST: identity.host };
65626
67176
  }
65627
- const result = spawnSync6(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
67177
+ const result = spawnSync7(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
65628
67178
  if (result.status !== 0)
65629
67179
  return {
65630
67180
  error: "merge failed",
@@ -65641,8 +67191,8 @@ function mergePr(cwd, opts) {
65641
67191
  }
65642
67192
 
65643
67193
  // packages/workit-core/src/core/repo-context.ts
65644
- import { spawnSync as spawnSync7 } from "node:child_process";
65645
- import { existsSync as existsSync18, readFileSync as readFileSync18, readdirSync as readdirSync9 } from "node:fs";
67194
+ import { spawnSync as spawnSync8 } from "node:child_process";
67195
+ import { existsSync as existsSync18, readFileSync as readFileSync19, readdirSync as readdirSync9 } from "node:fs";
65646
67196
  import path29 from "node:path";
65647
67197
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
65648
67198
  try {
@@ -65670,7 +67220,7 @@ var invalidRange = (cwd) => ({
65670
67220
  cwd
65671
67221
  });
65672
67222
  var runGit = (cwd, args) => {
65673
- const result = spawnSync7("git", args, { cwd, encoding: "utf8" });
67223
+ const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
65674
67224
  return {
65675
67225
  stdout: (result.stdout ?? "").trimEnd(),
65676
67226
  stderr: (result.stderr ?? "").trimEnd(),
@@ -65812,7 +67362,7 @@ var packageScripts = (cwd, keys) => {
65812
67362
  if (!existsSync18(pkgPath))
65813
67363
  return [];
65814
67364
  try {
65815
- const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
67365
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
65816
67366
  return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
65817
67367
  } catch {
65818
67368
  return [];
@@ -65846,7 +67396,7 @@ var documentationFiles = (cwd) => {
65846
67396
  var readTrimmed = (file, maxLines) => {
65847
67397
  if (!existsSync18(file))
65848
67398
  return "";
65849
- const lines = readFileSync18(file, "utf8").split(`
67399
+ const lines = readFileSync19(file, "utf8").split(`
65850
67400
  `);
65851
67401
  return lines.slice(0, maxLines).join(`
65852
67402
  `);
@@ -66034,7 +67584,7 @@ function docsRefreshContext(root, range) {
66034
67584
  const pkgPath = path29.join(cwd, "package.json");
66035
67585
  if (existsSync18(pkgPath)) {
66036
67586
  try {
66037
- const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
67587
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
66038
67588
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
66039
67589
  `;
66040
67590
  } catch {}
@@ -66111,7 +67661,7 @@ function releaseNotesContext(root, rangeOrTag) {
66111
67661
  }
66112
67662
 
66113
67663
  // packages/workit-core/src/core/tracker-issues.ts
66114
- import { spawnSync as spawnSync8 } from "node:child_process";
67664
+ import { spawnSync as spawnSync9 } from "node:child_process";
66115
67665
  var repoPathFromRemote = (remote) => {
66116
67666
  let rest = (remote || "").trim().replace(/\/+$/, "");
66117
67667
  if (!rest)
@@ -66132,7 +67682,7 @@ var defaultCli = (bin, available) => {
66132
67682
  if (!available)
66133
67683
  return null;
66134
67684
  return (args, root) => {
66135
- const out = spawnSync8(bin, args, {
67685
+ const out = spawnSync9(bin, args, {
66136
67686
  cwd: root,
66137
67687
  encoding: "utf8",
66138
67688
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -66865,7 +68415,7 @@ async function logTimeUpdate(input, operation = defaultOperations) {
66865
68415
  // packages/workit-core/src/core/external-action-effects.ts
66866
68416
  import fs13 from "node:fs";
66867
68417
  import path31 from "node:path";
66868
- import { spawnSync as spawnSync9 } from "node:child_process";
68418
+ import { spawnSync as spawnSync10 } from "node:child_process";
66869
68419
  var run3 = (root, args) => {
66870
68420
  return run2(root, "git", args);
66871
68421
  };
@@ -67265,7 +68815,7 @@ var hostingProvider = (root, remote) => {
67265
68815
  };
67266
68816
  var remoteBranchTip = (root, provider, host, repo, branch) => {
67267
68817
  const endpoint = provider === "github" ? `repos/${repo}/git/ref/heads/${branch.split("/").map(encodeURIComponent).join("/")}` : `projects/${encodeURIComponent(repo)}/repository/branches/${encodeURIComponent(branch)}`;
67268
- const response = spawnSync9(provider === "github" ? "gh" : "glab", ["api", endpoint], {
68818
+ const response = spawnSync10(provider === "github" ? "gh" : "glab", ["api", endpoint], {
67269
68819
  cwd: root,
67270
68820
  encoding: "utf8",
67271
68821
  env: hostingCliEnv(provider, host)
@@ -67273,7 +68823,7 @@ var remoteBranchTip = (root, provider, host, repo, branch) => {
67273
68823
  if (response.status !== 0) {
67274
68824
  if (/\b404\b/.test(response.stderr ?? "")) {
67275
68825
  const project = provider === "github" ? `repos/${repo}` : `projects/${encodeURIComponent(repo)}`;
67276
- const visible = spawnSync9(provider === "github" ? "gh" : "glab", ["api", project], {
68826
+ const visible = spawnSync10(provider === "github" ? "gh" : "glab", ["api", project], {
67277
68827
  cwd: root,
67278
68828
  encoding: "utf8",
67279
68829
  env: hostingCliEnv(provider, host)
@@ -67329,7 +68879,7 @@ var mergedBranch = (root, branch) => {
67329
68879
  outcome: "not_started"
67330
68880
  });
67331
68881
  const endpoint = provider === "github" ? `repos/${repo}/pulls?state=closed&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&per_page=100` : `projects/${encodeURIComponent(repo)}/merge_requests?state=merged&source_branch=${encodeURIComponent(branch)}&per_page=100`;
67332
- const response = spawnSync9(provider === "github" ? "gh" : "glab", ["api", endpoint], {
68882
+ const response = spawnSync10(provider === "github" ? "gh" : "glab", ["api", endpoint], {
67333
68883
  cwd: root,
67334
68884
  encoding: "utf8",
67335
68885
  env: hostingCliEnv(provider, auth.host)
@@ -67391,7 +68941,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
67391
68941
  return unknownHostingEvidence(operation);
67392
68942
  const endpoint = cfg.provider === "github" ? `repos/${repo}/pulls?state=all&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(target)}&per_page=100&page=1` : `projects/${encodeURIComponent(repo)}/merge_requests?state=all&source_branch=${encodeURIComponent(branch)}&target_branch=${encodeURIComponent(target)}&per_page=100&page=1`;
67393
68943
  const cli = cfg.provider === "github" ? "gh" : "glab";
67394
- const response = spawnSync9(cli, ["api", endpoint], {
68944
+ const response = spawnSync10(cli, ["api", endpoint], {
67395
68945
  cwd: root,
67396
68946
  encoding: "utf8",
67397
68947
  env: hostingCliEnv(cfg.provider, auth.host)
@@ -68478,7 +70028,7 @@ async function payloadValue(raw, deps) {
68478
70028
  return { ok: true, value: {} };
68479
70029
  let source;
68480
70030
  try {
68481
- source = raw === "-" ? await readInput(deps.stdin ?? process.stdin) : raw.startsWith("@") ? decodeUtf8(readFileSync19(raw.slice(1))) : raw;
70031
+ source = raw === "-" ? await readInput(deps.stdin ?? process.stdin) : raw.startsWith("@") ? decodeUtf8(readFileSync20(raw.slice(1))) : raw;
68482
70032
  } catch (error) {
68483
70033
  return {
68484
70034
  ok: false,
@@ -69191,6 +70741,498 @@ async function runActionCommand(argv, deps = {}) {
69191
70741
  return result.ok ? 0 : 1;
69192
70742
  }
69193
70743
 
70744
+ // packages/workit-cli/src/upgrade.ts
70745
+ import { spawnSync as spawnSync11 } from "node:child_process";
70746
+ import {
70747
+ existsSync as existsSync19,
70748
+ readFileSync as readFileSync21,
70749
+ mkdirSync as mkdirSync9,
70750
+ copyFileSync as copyFileSync3,
70751
+ chmodSync,
70752
+ cpSync as cpSync4,
70753
+ lstatSync as lstatSync8
70754
+ } from "node:fs";
70755
+ import path32 from "node:path";
70756
+ import os11 from "node:os";
70757
+ import { createHash as createHash11 } from "node:crypto";
70758
+ var HOSTS2 = ["opencode", "cursor", "codex", "pi"];
70759
+ var upgradePaths = (deps) => {
70760
+ const home = deps.home ?? deps.env?.HOME ?? os11.homedir();
70761
+ const env = {
70762
+ ...process.env,
70763
+ ...deps.home || deps.env?.HOME ? {
70764
+ HOME: home,
70765
+ CODEX_HOME: path32.join(home, ".codex"),
70766
+ PI_CODING_AGENT_DIR: path32.join(home, ".pi/agent"),
70767
+ XDG_CONFIG_HOME: path32.join(home, ".config"),
70768
+ WORKFLOW_TOOLKIT_CONFIG: undefined,
70769
+ WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
70770
+ } : {},
70771
+ ...deps.env,
70772
+ HOME: home
70773
+ };
70774
+ const xdg = env.XDG_CONFIG_HOME || path32.join(home, ".config");
70775
+ return {
70776
+ home,
70777
+ env,
70778
+ configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path32.join(xdg, "workit"),
70779
+ opencodeConfig: path32.join(xdg, "opencode/opencode.json"),
70780
+ piSettings: path32.join(env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi/agent"), "settings.json"),
70781
+ codexConfig: path32.join(env.CODEX_HOME ?? path32.join(home, ".codex"), "config.toml")
70782
+ };
70783
+ };
70784
+ var object2 = (file) => {
70785
+ if (!existsSync19(file))
70786
+ return null;
70787
+ let data;
70788
+ try {
70789
+ data = JSON.parse(readFileSync21(file, "utf8"));
70790
+ } catch {
70791
+ throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
70792
+ }
70793
+ if (!data || typeof data !== "object" || Array.isArray(data))
70794
+ throw new Error(`${file} is not a configuration object`);
70795
+ return data;
70796
+ };
70797
+ var digest2 = (file) => existsSync19(file) ? createHash11("sha256").update(readFileSync21(file)).digest("hex") : "missing";
70798
+ var runner = (deps) => deps.run ?? ((command, args) => {
70799
+ const { home, env } = upgradePaths(deps);
70800
+ let step;
70801
+ try {
70802
+ step = hostCommand(command, args, { home, env });
70803
+ } catch {
70804
+ return { status: 1, stdout: "" };
70805
+ }
70806
+ const result = runHostCommand(step, { home, env });
70807
+ return { status: result.exitCode, stdout: result.stdout };
70808
+ });
70809
+ var classify = (host, selector) => ({
70810
+ host,
70811
+ selector,
70812
+ kind: selector === `@brainervirus/workit-${host}` || selector === `@brainervirus/workit-${host}@latest` || selector === `npm:@brainervirus/workit-${host}` || selector === `npm:@brainervirus/workit-${host}@latest` ? "floating" : new RegExp(`^(?:npm:)?@brainervirus/workit-${host}@[^/]+$`).test(selector) ? "pinned" : "local"
70813
+ });
70814
+ var sources = (paths, run, hosts) => {
70815
+ const { home, opencodeConfig, piSettings } = paths;
70816
+ const result = [];
70817
+ if (hosts.includes("opencode") && existsSync19(opencodeConfig + "c"))
70818
+ throw new Error("OpenCode JSONC config is present; inspect effective native configuration before updating");
70819
+ const oc = hosts.includes("opencode") ? object2(opencodeConfig) : null;
70820
+ if (oc && [oc.plugins, oc.plugin].some((value) => value !== undefined && !Array.isArray(value)))
70821
+ throw new Error("OpenCode plugin registrations must be arrays");
70822
+ const plugins = [...oc?.plugins ?? [], ...oc?.plugin ?? []];
70823
+ for (const pin of plugins)
70824
+ if (typeof pin === "string" && isWorkitPlugin(pin))
70825
+ result.push(classify("opencode", pin));
70826
+ const cursor = hosts.includes("cursor") ? object2(path32.join(home, ".cursor/plugins/local/workit/package.json")) : null;
70827
+ if (cursor?.name === "@brainervirus/workit-cursor") {
70828
+ const mcp = object2(path32.join(home, ".cursor/mcp.json"));
70829
+ const args = mcp?.mcpServers?.workit?.args ?? [];
70830
+ const selector = args.find((arg) => typeof arg === "string" && arg.startsWith("--package="));
70831
+ result.push(classify("cursor", typeof selector === "string" ? selector.slice(10) : "local"));
70832
+ }
70833
+ const pi = hosts.includes("pi") ? object2(piSettings) : null;
70834
+ if (pi?.packages !== undefined && !Array.isArray(pi.packages))
70835
+ throw new Error("Pi package registrations must be an array");
70836
+ for (const entry of pi?.packages ?? []) {
70837
+ const source = typeof entry === "string" ? entry : entry?.source;
70838
+ if (typeof source !== "string")
70839
+ continue;
70840
+ const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
70841
+ const local = path32.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path32.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
70842
+ if (npm || local)
70843
+ result.push(classify("pi", source));
70844
+ }
70845
+ const listing = hosts.includes("codex") ? run("codex", ["plugin", "list", "--json"]) : { status: 1, stdout: "" };
70846
+ if (hosts.includes("codex") && listing.status !== 0) {
70847
+ let available = false;
70848
+ try {
70849
+ hostCommand("codex", [], { home, env: paths.env });
70850
+ available = true;
70851
+ } catch {}
70852
+ if (available || existsSync19(paths.codexConfig))
70853
+ throw new Error("Codex plugin inventory failed; inspect the native host before upgrading");
70854
+ }
70855
+ if (listing.status === 0) {
70856
+ let installed;
70857
+ try {
70858
+ installed = JSON.parse(listing.stdout).installed ?? [];
70859
+ } catch {
70860
+ throw new Error("Codex plugin inventory could not be parsed");
70861
+ }
70862
+ if (!Array.isArray(installed))
70863
+ throw new Error("Codex plugin inventory is malformed");
70864
+ for (const plugin of installed) {
70865
+ if (plugin.name !== "workit")
70866
+ continue;
70867
+ const source = plugin.source;
70868
+ const npm = plugin.marketplaceName === "workflow-toolkit" && source?.source === "npm" && source?.package === "@brainervirus/workit-codex";
70869
+ result.push({
70870
+ ...classify("codex", npm ? `${source.package}${source.version ? `@${source.version}` : ""}` : "local"),
70871
+ installedVersion: plugin.version
70872
+ });
70873
+ }
70874
+ }
70875
+ for (const source of result) {
70876
+ if (source.host === "cursor")
70877
+ source.installedVersion = cursor?.version;
70878
+ if (source.host === "pi" && source.kind === "floating") {
70879
+ const inventory = run("pi", ["list"]);
70880
+ if (inventory.status === 0)
70881
+ source.installedVersion = piInstalledVersion(inventory.stdout, source.selector);
70882
+ }
70883
+ }
70884
+ return result;
70885
+ };
70886
+ var piInstalledVersion = (listing, selector) => {
70887
+ const lines = listing.split(/\r?\n/).map((line) => line.trim());
70888
+ const index = lines.indexOf(selector);
70889
+ if (index < 0 || !lines[index + 1] || !path32.isAbsolute(lines[index + 1]))
70890
+ return;
70891
+ const pkg = object2(path32.join(lines[index + 1], "package.json"));
70892
+ return pkg?.name === "@brainervirus/workit-pi" ? pkg.version : undefined;
70893
+ };
70894
+ var versionAtLeast = (installed, latest) => {
70895
+ if (!/^\d+\.\d+\.\d+$/.test(installed))
70896
+ return false;
70897
+ const current = installed.split(".").map(Number);
70898
+ const target = latest.split(".").map(Number);
70899
+ for (let index = 0;index < 3; index++) {
70900
+ if (current[index] !== target[index])
70901
+ return current[index] > target[index];
70902
+ }
70903
+ return true;
70904
+ };
70905
+ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70906
+ const paths = upgradePaths(deps);
70907
+ const { home, configDir } = paths;
70908
+ const run = runner(deps);
70909
+ const state = readSetupState(configDir);
70910
+ const plan = {
70911
+ ok: true,
70912
+ blocked: [],
70913
+ skipped: [],
70914
+ entries: [],
70915
+ configDigests: {},
70916
+ migrations: []
70917
+ };
70918
+ for (const file of [state.config, state.workspaces, state.youtrack, state.vcs]) {
70919
+ if (file.status === "malformed")
70920
+ plan.blocked.push(file.error ?? file.file);
70921
+ plan.configDigests[file.file] = digest2(file.file);
70922
+ }
70923
+ for (const file of [
70924
+ paths.opencodeConfig,
70925
+ paths.opencodeConfig + "c",
70926
+ path32.join(home, ".cursor/settings.json"),
70927
+ path32.join(home, ".cursor/mcp.json"),
70928
+ path32.join(home, ".cursor/plugins/local/workit/package.json"),
70929
+ paths.codexConfig,
70930
+ paths.piSettings
70931
+ ])
70932
+ plan.configDigests[file] = digest2(file);
70933
+ try {
70934
+ const config = object2(state.config.file);
70935
+ if (config && Object.hasOwn(config, "trustedPaths"))
70936
+ plan.migrations.push({
70937
+ id: "2.0-remove-ignored-trusted-paths",
70938
+ file: state.config.file,
70939
+ description: "Remove ignored Workit trustedPaths; native host permissions remain unchanged"
70940
+ });
70941
+ const installedSources = sources(paths, run, hosts);
70942
+ for (const host of ["opencode", "pi"]) {
70943
+ if (new Set(installedSources.filter((source) => source.host === host).map((source) => source.selector)).size > 1)
70944
+ plan.blocked.push(`${host}: conflicting Workit registrations; reconcile them in the native host before upgrading`);
70945
+ }
70946
+ for (const source of installedSources.filter((source, index, list) => list.findIndex((entry) => entry.host === source.host && entry.selector === source.selector) === index)) {
70947
+ if (source.kind !== "floating") {
70948
+ plan.skipped.push({
70949
+ host: source.host,
70950
+ reason: `${source.kind} source preserved; change its source explicitly to upgrade`
70951
+ });
70952
+ continue;
70953
+ }
70954
+ if (source.host === "opencode") {
70955
+ plan.skipped.push({
70956
+ host: source.host,
70957
+ reason: "OpenCode server plugin preserved: scoped native updates are unavailable in qualified 2.0.21; Workit never updates unrelated plugins"
70958
+ });
70959
+ continue;
70960
+ }
70961
+ const latest = run("npm", ["view", `@brainervirus/workit-${source.host}`, "version"]);
70962
+ if (latest.status !== 0 || !/^\d+\.\d+\.\d+$/.test(latest.stdout.trim())) {
70963
+ plan.blocked.push(`${source.host}: registry version unavailable; existing installation preserved`);
70964
+ continue;
70965
+ }
70966
+ if (source.installedVersion && versionAtLeast(source.installedVersion, latest.stdout.trim())) {
70967
+ plan.skipped.push({
70968
+ host: source.host,
70969
+ reason: `already current (${source.installedVersion})`
70970
+ });
70971
+ continue;
70972
+ }
70973
+ const commands = source.host === "pi" ? [{ command: "pi", args: ["update", "--extension", source.selector] }] : source.host === "codex" ? [{ command: "codex", args: ["plugin", "add", "workit@workflow-toolkit", "--json"] }] : planHostInstall("cursor", {
70974
+ home,
70975
+ env: deps.env,
70976
+ cwd: home,
70977
+ packageVersion: latest.stdout.trim(),
70978
+ targetDir: path32.join(home, ".cursor/plugins/local/workit")
70979
+ });
70980
+ plan.entries.push({
70981
+ host: source.host,
70982
+ selector: source.selector,
70983
+ latest: latest.stdout.trim(),
70984
+ commands
70985
+ });
70986
+ }
70987
+ } catch (error) {
70988
+ plan.blocked.push(error instanceof Error ? error.message : "installed source could not be read");
70989
+ }
70990
+ plan.ok = plan.blocked.length === 0;
70991
+ return plan;
70992
+ }
70993
+ function applyUpgrade(plan, deps = {}) {
70994
+ if (!plan.ok)
70995
+ return { ok: false, error: plan.blocked.join("; ") };
70996
+ if (!plan.entries.length && !plan.migrations.length && !plan.cli)
70997
+ return { ok: true };
70998
+ for (const [file, expected] of Object.entries(plan.configDigests))
70999
+ if (digest2(file) !== expected)
71000
+ return { ok: false, error: `configuration changed; preview again: ${file}` };
71001
+ const { home, env, configDir } = upgradePaths(deps);
71002
+ const active = deps.activeHosts?.() ?? runningHosts(deps);
71003
+ if (plan.entries.some((entry) => active.includes(entry.host)))
71004
+ return {
71005
+ ok: false,
71006
+ error: "Stop the selected running hosts before upgrading their loaded plugins"
71007
+ };
71008
+ const backup = path32.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
71009
+ mkdirSync9(backup, { recursive: true, mode: 448 });
71010
+ for (const file of Object.keys(plan.configDigests))
71011
+ if (existsSync19(file)) {
71012
+ const target = path32.join(backup, createHash11("sha256").update(file).digest("hex").slice(0, 12) + "-" + path32.basename(file));
71013
+ copyFileSync3(file, target);
71014
+ chmodSync(target, 384);
71015
+ }
71016
+ for (const migration of plan.migrations) {
71017
+ const value = object2(migration.file);
71018
+ if (migration.id !== "2.0-remove-ignored-trusted-paths" || !value)
71019
+ return {
71020
+ ok: false,
71021
+ backup,
71022
+ error: "Unknown configuration migration; upgrade the CLI before applying"
71023
+ };
71024
+ delete value.trustedPaths;
71025
+ writeFileAtomic(migration.file, JSON.stringify(value, null, 2) + `
71026
+ `, 384);
71027
+ }
71028
+ const run = runner(deps);
71029
+ const cursorRoot = path32.join(home, ".cursor/plugins/local/workit");
71030
+ if (plan.entries.some((entry) => entry.host === "cursor") && existsSync19(cursorRoot))
71031
+ cpSync4(cursorRoot, path32.join(backup, "cursor-package"), { recursive: true });
71032
+ for (const entry of plan.entries) {
71033
+ if (entry.host === "opencode")
71034
+ return {
71035
+ ok: false,
71036
+ backup,
71037
+ error: "Scoped OpenCode server-plugin updates are unsupported; registration preserved"
71038
+ };
71039
+ if (entry.host === "cursor") {
71040
+ const preview = {
71041
+ ok: true,
71042
+ blocked: [],
71043
+ preserved: [],
71044
+ overrides: [],
71045
+ platforms: ["cursor"],
71046
+ state: readSetupState(configDir),
71047
+ mutations: [
71048
+ {
71049
+ type: "install-host",
71050
+ platform: "cursor",
71051
+ path: path32.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
71052
+ commands: entry.commands.map((command) => ({
71053
+ ...command,
71054
+ purpose: command.purpose ?? "Upgrade Cursor"
71055
+ }))
71056
+ },
71057
+ {
71058
+ type: "install-adapter",
71059
+ platform: "cursor",
71060
+ path: path32.join(home, ".cursor/plugins/local/workit")
71061
+ }
71062
+ ]
71063
+ };
71064
+ const result = applySetupPreview(preview, {
71065
+ home,
71066
+ env,
71067
+ cwd: home,
71068
+ preferInstalledAdapter: true,
71069
+ runHostCommand: (command) => {
71070
+ const outcome = run(command.command, command.args);
71071
+ return { exitCode: outcome.status ?? 1, stdout: outcome.stdout, stderr: "" };
71072
+ }
71073
+ });
71074
+ const pkg = object2(path32.join(home, ".cursor/plugins/local/workit/package.json"));
71075
+ if (!result.ok || pkg?.version !== entry.latest)
71076
+ return {
71077
+ ok: false,
71078
+ backup,
71079
+ error: "Cursor update did not verify the requested package version; inspect the installer before retrying"
71080
+ };
71081
+ continue;
71082
+ }
71083
+ for (const command of entry.commands)
71084
+ if (run(command.command, command.args).status !== 0)
71085
+ return {
71086
+ ok: false,
71087
+ backup,
71088
+ error: `${entry.host}: update failed; inspect the native host before retrying`
71089
+ };
71090
+ const verify = entry.host === "codex" ? run("codex", ["plugin", "list", "--json"]) : run("pi", ["list"]);
71091
+ if (verify.status !== 0)
71092
+ return { ok: false, backup, error: `${entry.host}: update verification failed` };
71093
+ if (entry.host === "codex") {
71094
+ let installed = [];
71095
+ try {
71096
+ installed = JSON.parse(verify.stdout).installed ?? [];
71097
+ } catch {}
71098
+ if (!installed.some((plugin) => plugin.name === "workit" && plugin.marketplaceName === "workflow-toolkit" && plugin.version === entry.latest))
71099
+ return { ok: false, backup, error: "Codex did not report the requested Workit version" };
71100
+ }
71101
+ if (entry.host === "pi" && piInstalledVersion(verify.stdout, entry.selector) !== entry.latest)
71102
+ return { ok: false, backup, error: "Pi did not report the requested Workit package version" };
71103
+ }
71104
+ if (plan.cli) {
71105
+ const currentRoot = run("npm", ["root", "--global"]);
71106
+ if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== plan.cli.root)
71107
+ return { ok: false, backup, error: "Global npm location changed; preview again" };
71108
+ const cliBackup = path32.join(backup, "cli-package");
71109
+ cpSync4(path32.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
71110
+ if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${plan.cli.latest}`]).status !== 0)
71111
+ return {
71112
+ ok: false,
71113
+ backup,
71114
+ error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path32.join(cliBackup, "dist/index.js")}`
71115
+ };
71116
+ const installed = object2(path32.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
71117
+ if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== plan.cli.latest)
71118
+ return {
71119
+ ok: false,
71120
+ backup,
71121
+ error: `CLI update did not verify the requested version; preserved package at ${cliBackup}`
71122
+ };
71123
+ }
71124
+ return { ok: true, backup };
71125
+ }
71126
+ async function runUpgradeCommand(argv, deps = {}) {
71127
+ const out = deps.out ?? process.stdout;
71128
+ const flags = new Set(argv);
71129
+ const unknown = argv.filter((arg) => !["--apply", "--confirm", "--json", "--cli"].includes(arg) && !arg.startsWith("--hosts="));
71130
+ const hostFlag = argv.find((arg) => arg.startsWith("--hosts="))?.slice(8);
71131
+ const raw = hostFlag === "none" ? [] : hostFlag?.split(",") ?? HOSTS2;
71132
+ if (unknown.length || raw.some((host) => !HOSTS2.includes(host))) {
71133
+ out.write(`Usage: workit upgrade [--hosts=opencode,cursor,codex,pi|none] [--cli] [--apply --confirm] [--json]
71134
+ `);
71135
+ return 2;
71136
+ }
71137
+ const plan = previewUpgrade(raw, deps);
71138
+ if (flags.has("--cli")) {
71139
+ const run = runner(deps);
71140
+ const root = run("npm", ["root", "--global"]);
71141
+ if (root.status !== 0 || !path32.isAbsolute(root.stdout.trim())) {
71142
+ plan.blocked.push("Global npm location could not be verified");
71143
+ } else {
71144
+ const packageFile = path32.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
71145
+ const pkg = object2(packageFile);
71146
+ if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync8(path32.dirname(packageFile)).isSymbolicLink()) {
71147
+ plan.skipped.push({
71148
+ host: "cli",
71149
+ reason: "No managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
71150
+ });
71151
+ } else {
71152
+ const latest = run("npm", ["view", "@brainervirus/workit-cli", "version"]);
71153
+ if (latest.status !== 0 || !/^\d+\.\d+\.\d+$/.test(latest.stdout.trim()))
71154
+ plan.blocked.push("CLI registry version unavailable; existing installation preserved");
71155
+ else if (!versionAtLeast(pkg.version, latest.stdout.trim())) {
71156
+ plan.cli = { latest: latest.stdout.trim(), root: root.stdout.trim() };
71157
+ plan.configDigests[packageFile] = digest2(packageFile);
71158
+ } else
71159
+ plan.skipped.push({ host: "cli", reason: `already current (${pkg.version})` });
71160
+ }
71161
+ }
71162
+ plan.ok = plan.blocked.length === 0;
71163
+ }
71164
+ if (!flags.has("--apply")) {
71165
+ out.write(JSON.stringify(plan, null, 2) + `
71166
+ `);
71167
+ return plan.ok ? 0 : 1;
71168
+ }
71169
+ if (!flags.has("--confirm")) {
71170
+ out.write(`Apply requires --confirm after reviewing the upgrade preview.
71171
+ `);
71172
+ return 2;
71173
+ }
71174
+ const result = applyUpgrade(plan, deps);
71175
+ out.write(JSON.stringify(result, null, 2) + `
71176
+ `);
71177
+ return result.ok ? 0 : 1;
71178
+ }
71179
+ async function runLaunchCommand(argv, deps = {}) {
71180
+ const [host, ...args] = argv;
71181
+ if (!HOSTS2.includes(host)) {
71182
+ (deps.out ?? process.stdout).write(`Usage: workit launch <opencode|cursor|codex|pi> [--auto-upgrade] [-- host arguments]
71183
+ `);
71184
+ return 2;
71185
+ }
71186
+ const auto = args[0] === "--auto-upgrade";
71187
+ const rest = auto ? args.slice(1) : args;
71188
+ const hostArgs = rest[0] === "--" ? rest.slice(1) : rest;
71189
+ const { home, env } = upgradePaths(deps);
71190
+ let launch;
71191
+ if (!deps.run) {
71192
+ try {
71193
+ launch = hostCommand(host, hostArgs, { home, env });
71194
+ } catch (error) {
71195
+ (deps.out ?? process.stderr).write(`${error instanceof Error ? error.message : "Host executable unavailable"}
71196
+ `);
71197
+ return 1;
71198
+ }
71199
+ }
71200
+ if (auto) {
71201
+ const plan = previewUpgrade([host], deps);
71202
+ if (plan.ok) {
71203
+ for (const skipped of plan.skipped)
71204
+ (deps.out ?? process.stderr).write(`${skipped.host}: ${skipped.reason}
71205
+ `);
71206
+ const result = applyUpgrade(plan, deps);
71207
+ if (!result.ok) {
71208
+ (deps.out ?? process.stdout).write(`${result.error}
71209
+ `);
71210
+ return 1;
71211
+ }
71212
+ } else if (plan.blocked.every((reason) => reason.includes("registry version unavailable"))) {
71213
+ (deps.out ?? process.stderr).write(`Upgrade unavailable: ${plan.blocked.join("; ")}. Starting the unchanged host.
71214
+ `);
71215
+ } else {
71216
+ (deps.out ?? process.stderr).write(`Upgrade blocked: ${plan.blocked.join("; ")}
71217
+ `);
71218
+ return 1;
71219
+ }
71220
+ }
71221
+ if (deps.run)
71222
+ return deps.run(host, hostArgs).status ?? 1;
71223
+ return spawnSync11(launch.command, launch.args, {
71224
+ stdio: "inherit",
71225
+ env
71226
+ }).status ?? 1;
71227
+ }
71228
+ var runningHosts = (deps) => {
71229
+ const run = runner(deps);
71230
+ const list = process.platform === "win32" ? run("tasklist", ["/fo", "csv", "/nh"]) : run("ps", ["-eo", "comm="]);
71231
+ if (list.status !== 0)
71232
+ return HOSTS2;
71233
+ return HOSTS2.filter((host) => new RegExp(`(?:^|[\\/",\\s])${host}(?:\\.exe)?(?:[",\\s]|$)`, "im").test(list.stdout));
71234
+ };
71235
+
69194
71236
  // packages/workit-cli/src/index.tsx
69195
71237
  var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
69196
71238
  var logger = createLogger({
@@ -69215,6 +71257,8 @@ var HELP = `workit — workflow rails for agentic coding
69215
71257
 
69216
71258
  Usage:
69217
71259
  workit init Run the interactive setup wizard
71260
+ workit upgrade Preview upgrades (--apply --confirm; --hosts=a,b; --cli for the CLI)
71261
+ workit launch <host> [--auto-upgrade] [-- args] Upgrade before host startup
69218
71262
  workit doctor Verify the offline installation health (add --json for a machine-readable report)
69219
71263
  workit uninstall Remove workit host registrations interactively (~/.config/workit is kept)
69220
71264
  workit cutover Preview or apply an explicit v1 cutover (apply requires --confirm)
@@ -69223,7 +71267,7 @@ ${COMMAND_DESCRIPTIONS.map(([cmd, desc]) => ` ${cmd.padEnd(helpColumn)}${desc}`
69223
71267
  action payloads: ${externalActionHelp}
69224
71268
  workit Show this help
69225
71269
 
69226
- Run \`npx workit init\` to configure platforms, YouTrack, VCS and project hygiene.
71270
+ Run \`npx @brainervirus/workit-cli init\` to configure platforms, YouTrack, VCS and project hygiene.
69227
71271
  `;
69228
71272
  function printApplySummary(result) {
69229
71273
  for (const entry of result.entries) {
@@ -69437,7 +71481,11 @@ if (__require.main == __require.module) {
69437
71481
  logger.error(EVENT.uncaughtFailure, { phase: "uncaughtException", ...errorDetail(err) });
69438
71482
  process.exit(1);
69439
71483
  });
69440
- if (subcommand === "init") {
71484
+ if (subcommand === "upgrade") {
71485
+ process.exit(await runUpgradeCommand(args.slice(1)));
71486
+ } else if (subcommand === "launch") {
71487
+ process.exit(await runLaunchCommand(args.slice(1)));
71488
+ } else if (subcommand === "init") {
69441
71489
  await runInit();
69442
71490
  } else if (subcommand === "doctor") {
69443
71491
  runDoctorCommand(args);