@brainervirus/workit-cli 8.8.0 → 8.9.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 (2) hide show
  1. package/dist/index.js +1145 -601
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@brainervirus/workit-cli",
68
- version: "8.8.0",
68
+ version: "8.9.1",
69
69
  private: false,
70
70
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
71
71
  keywords: [
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
103
103
  },
104
104
  dependencies: {
105
105
  "@babel/parser": "7.29.9",
106
- "@brainervirus/workit-core": "^8.8.0",
107
- "@brainervirus/workit-cursor": "^8.8.0",
108
- "@brainervirus/workit-opencode": "^8.8.0",
106
+ "@brainervirus/workit-core": "^8.9.1",
107
+ "@brainervirus/workit-cursor": "^8.9.1",
108
+ "@brainervirus/workit-opencode": "^8.9.1",
109
109
  "@inkjs/ui": "2.0.0",
110
110
  ink: "7.1.1",
111
111
  react: "19.2.8"
@@ -11079,6 +11079,66 @@ class TaskStore {
11079
11079
  return bound;
11080
11080
  return success(null, null, { key: key.data, bound: bound.data });
11081
11081
  }
11082
+ taskForWorker(workerId) {
11083
+ if (typeof workerId !== "string" || !validId(workerId))
11084
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
11085
+ const listed = this.listTaskIndex();
11086
+ if (!listed.ok)
11087
+ return listed;
11088
+ for (const entry of listed.data) {
11089
+ if (entry.status === "closed")
11090
+ continue;
11091
+ const loaded = this.load(entry.id);
11092
+ if (!loaded.ok)
11093
+ return loaded;
11094
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
11095
+ return success(null, null, entry.id);
11096
+ }
11097
+ return success(null, null, null);
11098
+ }
11099
+ originTaskEntry() {
11100
+ const key = this.currentKey();
11101
+ const location = this.location();
11102
+ const branch = key.ok ? key.data.branch : null;
11103
+ if (!branch || !location.ok)
11104
+ return success(null, null, null);
11105
+ const common = path8.dirname(location.data.dir);
11106
+ const heads = path8.join(common, "logs", "refs", "heads");
11107
+ const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
11108
+ const signatures = [
11109
+ path8.join(heads, branch),
11110
+ headLog,
11111
+ heads,
11112
+ path8.join(common, "packed-refs")
11113
+ ].map((file) => fileSignature(file) ?? "-");
11114
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
11115
+ let resolved = origins.get(slot);
11116
+ if (resolved?.signature !== signatures.join("|")) {
11117
+ const renames = branchRenames(location.data);
11118
+ const aliases = [branch];
11119
+ for (const rename of renames.toReversed())
11120
+ if (rename.to === aliases.at(-1))
11121
+ aliases.push(rename.from);
11122
+ const start = branchStart(location.data, branch, aliases);
11123
+ const slash = start?.indexOf("/") ?? -1;
11124
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path8.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
11125
+ const name = start && (remote ? start.slice(slash + 1) : start);
11126
+ resolved = {
11127
+ signature: signatures.join("|"),
11128
+ name: name && !aliases.includes(name) ? name : null,
11129
+ renames
11130
+ };
11131
+ origins.set(slot, resolved);
11132
+ }
11133
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
11134
+ if (entry && !entry.ok)
11135
+ return entry;
11136
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
11137
+ }
11138
+ readStoreTask(taskId) {
11139
+ const loaded = this.load(taskId);
11140
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
11141
+ }
11082
11142
  create(input, provenance, expectedWorkspaceRevision) {
11083
11143
  const value = "intent" in input ? input : {
11084
11144
  intent: input,
@@ -11748,12 +11808,12 @@ class TaskStore {
11748
11808
  return 0;
11749
11809
  }
11750
11810
  }
11751
- openTasksForKey(key) {
11811
+ openTasksForKey(key, known) {
11752
11812
  const all = this.listStoreIndex();
11753
11813
  if (!all.ok)
11754
11814
  return all;
11755
- const location = this.location();
11756
- const renames = location.ok ? branchRenames(location.data) : [];
11815
+ const location = known ? null : this.location();
11816
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
11757
11817
  return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
11758
11818
  }
11759
11819
  openTaskForKey(key) {
@@ -12440,7 +12500,34 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
12440
12500
  return left > right;
12441
12501
  }
12442
12502
  return false;
12443
- }, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value), sameDirectoryIdentity = (left, right) => {
12503
+ }, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value), readText = (file) => {
12504
+ try {
12505
+ return fs8.readFileSync(file, "utf8");
12506
+ } catch {
12507
+ return "";
12508
+ }
12509
+ }, REFLOG_LINE, branchStart = (location, branch, aliases) => {
12510
+ const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
12511
+ `, 1)[0];
12512
+ const created = REFLOG_LINE.exec(first);
12513
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
12514
+ if (!created || !start)
12515
+ return null;
12516
+ if (start !== "HEAD")
12517
+ return start;
12518
+ if (!location.git)
12519
+ return null;
12520
+ for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
12521
+ `)) {
12522
+ const entry = REFLOG_LINE.exec(line);
12523
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
12524
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
12525
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
12526
+ return moved[1];
12527
+ }
12528
+ return null;
12529
+ }, refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
12530
+ `), origins, sameDirectoryIdentity = (left, right) => {
12444
12531
  if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
12445
12532
  return false;
12446
12533
  const normalizedLeft = path8.resolve(left);
@@ -12488,6 +12575,8 @@ var init_task_store = __esm(() => {
12488
12575
  AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
12489
12576
  TOMBSTONE = Symbol("tombstone");
12490
12577
  STALE_TEMPORARY_MS = 60 * 60000;
12578
+ REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
12579
+ origins = new Map;
12491
12580
  heldInProcess = new Map;
12492
12581
  });
12493
12582
 
@@ -16442,38 +16531,29 @@ function reconcileResume(view, observations = []) {
16442
16531
  reassessmentRequired: view.task.policy?.policyVersion !== POLICY_VERSION
16443
16532
  });
16444
16533
  }
16445
- function compactTaskContext(view) {
16446
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
16447
- const leftAt = compactTimestamp(left.recordedAt);
16448
- const rightAt = compactTimestamp(right.recordedAt);
16449
- if (leftAt !== rightAt)
16450
- return leftAt > rightAt ? -1 : 1;
16451
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
16452
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
16453
- id: entry.id,
16454
- purpose: entry.data.purpose,
16455
- status: entry.data.response,
16456
- digest: entry.data.digest,
16457
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
16458
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
16459
- }));
16460
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
16534
+ function compactTaskContext(view, inherited = null) {
16535
+ const decisions = compactDecisions([
16536
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
16537
+ inherited
16538
+ ]);
16539
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
16461
16540
  id: method.id,
16462
16541
  assurance: method.assurance,
16463
16542
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
16464
16543
  })) : [];
16544
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
16465
16545
  const gaps = Array.from(new Set([
16466
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
16467
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
16468
- ...view.task.progress.blockers.map((entry) => entry.reason)
16546
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
16547
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
16548
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
16469
16549
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
16470
16550
  const context = {
16471
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
16472
- status: view.task.status,
16551
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
16552
+ status: view?.task.status ?? null,
16473
16553
  decisions,
16474
16554
  methods,
16475
16555
  gaps,
16476
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
16556
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
16477
16557
  };
16478
16558
  let encoded = JSON.stringify(context);
16479
16559
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -16516,7 +16596,21 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
16516
16596
  id: ref.id
16517
16597
  };
16518
16598
  return { kind: ref.kind };
16519
- }, exportDigest = (bundle) => sha256({
16599
+ }, newestDecisionFirst = (left, right) => {
16600
+ const leftAt = compactTimestamp(left.recordedAt);
16601
+ const rightAt = compactTimestamp(right.recordedAt);
16602
+ if (leftAt !== rightAt)
16603
+ return leftAt > rightAt ? -1 : 1;
16604
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
16605
+ }, compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
16606
+ id: entry.id,
16607
+ purpose: entry.data.purpose,
16608
+ status: entry.data.response,
16609
+ digest: entry.data.digest,
16610
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
16611
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
16612
+ origin: source.origin ?? undefined
16613
+ })) : []).slice(0, COMPACT_MAX_ITEMS), exportDigest = (bundle) => sha256({
16520
16614
  schemaVersion: bundle.schemaVersion,
16521
16615
  exportedAt: bundle.exportedAt,
16522
16616
  sourceWorkspaceId: bundle.sourceWorkspaceId,
@@ -17256,6 +17350,17 @@ class WorkitCore {
17256
17350
  const root = this.contextRootError();
17257
17351
  if (!root.ok)
17258
17352
  return root;
17353
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
17354
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
17355
+ if (workerId !== null) {
17356
+ const owner = this.store.taskForWorker(workerId);
17357
+ if (!owner.ok)
17358
+ return owner;
17359
+ if (owner.data)
17360
+ return success(null, null, { ...request, taskId: owner.data });
17361
+ if (addressed)
17362
+ return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
17363
+ }
17259
17364
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
17260
17365
  const found = this.store.implicitTask({
17261
17366
  provenance: provenance(this.context),
@@ -17962,7 +18067,9 @@ class WorkitCore {
17962
18067
  if (!resolved.ok)
17963
18068
  return resolved;
17964
18069
  const value = resolved.data;
17965
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
18070
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
18071
+ const { action, taskId } = value ?? {};
18072
+ return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
17966
18073
  }
17967
18074
  workerOnce(request) {
17968
18075
  const root = this.contextRootError();
@@ -18167,10 +18274,18 @@ class WorkitCore {
18167
18274
  requirements
18168
18275
  });
18169
18276
  }
18170
- compactContext(taskId, freshness) {
18277
+ compactContext(taskId, freshness, origin = null) {
18171
18278
  const root = this.contextRootError();
18172
18279
  if (!root.ok)
18173
18280
  return root;
18281
+ const base = origin && this.store.readStoreTask(origin.taskId);
18282
+ const inherited = base?.ok && base.data ? {
18283
+ origin: origin.origin,
18284
+ taskId: base.data.id,
18285
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
18286
+ } : null;
18287
+ if (taskId === null)
18288
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
18174
18289
  const task = this.store.readTask(taskId);
18175
18290
  if (!task.ok)
18176
18291
  return task;
@@ -18180,7 +18295,7 @@ class WorkitCore {
18180
18295
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
18181
18296
  if (!view.ok)
18182
18297
  return view;
18183
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
18298
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
18184
18299
  }
18185
18300
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
18186
18301
  const workspace = this.store.readWorkspace();
@@ -18591,7 +18706,12 @@ var MAX_JUDGED_REFS = 5, workspaceVerification = (root) => {
18591
18706
  return changed;
18592
18707
  const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
18593
18708
  return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
18594
- }, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, CREATING_ACTIONS, REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
18709
+ }, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, WORKER_ADDRESSED_ACTIONS, workerCommand = (taskId, request) => {
18710
+ const { action, workerId, reason, report } = request;
18711
+ const rest = JSON.stringify({ reason, report });
18712
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
18713
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
18714
+ }, CREATING_ACTIONS, REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
18595
18715
  const value = typeof request === "object" && request !== null ? request : {};
18596
18716
  return {
18597
18717
  task: value.expectedRevision === undefined,
@@ -18813,6 +18933,7 @@ var init_task_engine = __esm(() => {
18813
18933
  "worker.cancel",
18814
18934
  "state.export"
18815
18935
  ]);
18936
+ WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
18816
18937
  CREATING_ACTIONS = new Set([
18817
18938
  "task.revise",
18818
18939
  "task.progress",
@@ -19533,9 +19654,10 @@ async function payloadValue(raw, deps) {
19533
19654
  }
19534
19655
  }
19535
19656
  function inject(request, key, value) {
19657
+ const given = key === "workerId" ? "positional worker id" : "explicit flag";
19536
19658
  if (key in request && !same(request[key], value))
19537
- return failure2("invalid_input", `payload ${key} conflicts with the explicit flag`, {
19538
- fields: [{ path: key, reason: "explicit flag conflicts with payload" }]
19659
+ return failure2("invalid_input", `payload ${key} conflicts with the ${given}`, {
19660
+ fields: [{ path: key, reason: `${given} conflicts with payload` }]
19539
19661
  });
19540
19662
  request[key] = value;
19541
19663
  return success(null, null, null);
@@ -19558,12 +19680,18 @@ async function parseTaskArgs(argv, deps) {
19558
19680
  let workspaceRevision;
19559
19681
  let view;
19560
19682
  let actor;
19683
+ let workerId;
19684
+ const takesWorkerId = family === "worker" && (action === "cancel" || action === "report");
19561
19685
  let json = jsonRequested;
19562
19686
  const judge = [];
19563
19687
  const seen = new Set;
19564
19688
  for (let i = 2;i < argv.length; i += 1) {
19565
19689
  if (argv[i] === "--") {
19566
19690
  const extra = argv.slice(i + 1);
19691
+ if (takesWorkerId && workerId === undefined && extra.length === 1) {
19692
+ workerId = extra[0];
19693
+ break;
19694
+ }
19567
19695
  if (extra.length)
19568
19696
  return parseUsage(`${family} ${actionName} takes no positional arguments (got: ${extra.join(" ")})`, json);
19569
19697
  break;
@@ -19593,6 +19721,10 @@ async function parseTaskArgs(argv, deps) {
19593
19721
  json = true;
19594
19722
  continue;
19595
19723
  }
19724
+ if (takesWorkerId && workerId === undefined && !argv[i].startsWith("-")) {
19725
+ workerId = argv[i];
19726
+ continue;
19727
+ }
19596
19728
  if (!["--payload", "--task", "--revision", "--workspace-revision", "--view", "--actor"].includes(token))
19597
19729
  return parseUsage(`unknown argument: ${argv[i]}`, json);
19598
19730
  const value = inline ?? argv[++i];
@@ -19628,6 +19760,7 @@ async function parseTaskArgs(argv, deps) {
19628
19760
  for (const [key, value] of [
19629
19761
  ["action", action],
19630
19762
  ...taskId === undefined ? [] : [["taskId", taskId]],
19763
+ ...workerId === undefined ? [] : [["workerId", workerId]],
19631
19764
  ...revision === undefined ? [] : [["expectedRevision", revision]],
19632
19765
  ...workspaceRevision === undefined ? [] : [["expectedWorkspaceRevision", workspaceRevision]],
19633
19766
  ...view === undefined ? [] : [["view", view]]
@@ -63938,30 +64071,353 @@ var init_skill_manifests = __esm(() => {
63938
64071
  if (false) {}
63939
64072
  });
63940
64073
 
63941
- // packages/workit-cli/src/admin/doctor.ts
64074
+ // packages/workit-core/src/git/session-hook.ts
63942
64075
  import { spawnSync as spawnSync13 } from "node:child_process";
63943
- import { createHash as createHash13 } from "node:crypto";
63944
64076
  import {
64077
+ chmodSync,
63945
64078
  existsSync as existsSync13,
63946
64079
  lstatSync as lstatSync4,
63947
64080
  mkdirSync as mkdirSync5,
63948
- mkdtempSync,
63949
- readdirSync as readdirSync7,
63950
64081
  readFileSync as readFileSync13,
63951
- readlinkSync as readlinkSync2,
64082
+ renameSync as renameSync3,
63952
64083
  rmSync as rmSync2,
63953
64084
  statSync as statSync4,
63954
- unlinkSync as unlinkSync3,
63955
64085
  writeFileSync as writeFileSync6
63956
64086
  } from "node:fs";
63957
- import os8 from "node:os";
63958
64087
  import path30 from "node:path";
64088
+ function inspectSessionHook(cwd, env = process.env) {
64089
+ const none = { hooksDir: null, hookPath: null, helperPath: null, helperCurrent: false };
64090
+ if (!existsSync13(cwd))
64091
+ return { state: "not_git", ...none };
64092
+ const top = run2(cwd, ["rev-parse", "--show-toplevel"], env);
64093
+ if (!top.ok || !top.out)
64094
+ return { state: "not_git", ...none };
64095
+ const paths = run2(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir", "--git-path", "hooks"], env);
64096
+ const [commonDir, hooksDir] = paths.out.split(/\r?\n/);
64097
+ if (!paths.ok || !commonDir || !hooksDir)
64098
+ return { state: "not_git", ...none };
64099
+ const hookPath = path30.join(hooksDir, SESSION_HOOK_NAME);
64100
+ const helperPath = path30.join(commonDir, ...HELPER);
64101
+ const helperCurrent = readText2(helperPath) === SESSION_TRAILER_SCRIPT;
64102
+ const leftover = leftoverOf(hooksDir);
64103
+ const base = { hooksDir, hookPath, helperPath, helperCurrent, ...leftover ? { leftover } : {} };
64104
+ const blocked = (reason, owner, text = "") => {
64105
+ const old = `${hookPath}.old`;
64106
+ const displaced = readText2(old)?.includes(SESSION_HOOK_MARKER) ? old : null;
64107
+ const input = { owner, text, hooksDir, hookPath, top: top.out, displaced };
64108
+ const manualTarget = manualTargetOf(input);
64109
+ const owned = text ? { owner } : {};
64110
+ if (manualInPlace(input, manualTarget))
64111
+ return {
64112
+ state: "manual",
64113
+ ...base,
64114
+ ...owned,
64115
+ manualTarget,
64116
+ via: owner === "other" ? "existing hook" : owner
64117
+ };
64118
+ return {
64119
+ state: "blocked",
64120
+ ...base,
64121
+ ...owned,
64122
+ reason: displaced ? `${reason} (Workit's earlier hook was moved to ${old})` : reason,
64123
+ manual: manualFor(input),
64124
+ manualTarget
64125
+ };
64126
+ };
64127
+ if (!inside2(hooksDir, commonDir) && !inside2(hooksDir, top.out))
64128
+ return blocked(`core.hooksPath ${hooksDir} is outside the repository; a hook there runs in every repository that uses it`, "other");
64129
+ const text = exists(hookPath) ? readText2(hookPath) ?? "" : null;
64130
+ if (text !== null) {
64131
+ if (text === SESSION_HOOK_SCRIPT)
64132
+ return { state: helperCurrent ? "installed" : "outdated", ...base };
64133
+ if (text.includes(SESSION_HOOK_MARKER))
64134
+ return { state: "outdated", ...base };
64135
+ const owner = ownerOf(text, hooksDir, top.out);
64136
+ return blocked(`${hookPath} belongs to ${owner === "other" ? "another hook" : owner}; Workit never replaces it`, owner, text);
64137
+ }
64138
+ if (!inside2(hooksDir, commonDir)) {
64139
+ const rel = path30.relative(top.out, hookPath).split(path30.sep).join("/");
64140
+ if (!run2(top.out, ["check-ignore", "-q", "--", rel], env).ok)
64141
+ return blocked(`${hooksDir} is in the working tree and not ignored; the hook would show up as a new file`, "other");
64142
+ }
64143
+ return { state: "missing", ...base };
64144
+ }
64145
+ function installSessionHook(cwd, env = process.env) {
64146
+ const status = inspectSessionHook(cwd, env);
64147
+ if (status.state === "not_git")
64148
+ return {
64149
+ action: "skipped",
64150
+ helperWritten: false,
64151
+ status,
64152
+ detail: "not inside a git work tree"
64153
+ };
64154
+ const helperWritten = !status.helperCurrent;
64155
+ if (helperWritten)
64156
+ writeExecutable(status.helperPath, SESSION_TRAILER_SCRIPT);
64157
+ const helper = helperWritten ? `; wrote ${status.helperPath}` : "";
64158
+ if (status.state === "installed")
64159
+ return {
64160
+ action: "unchanged",
64161
+ helperWritten,
64162
+ status,
64163
+ detail: `hook already at ${status.hookPath}`
64164
+ };
64165
+ if (status.state === "manual")
64166
+ return {
64167
+ action: "unchanged",
64168
+ helperWritten,
64169
+ status: inspectSessionHook(cwd, env),
64170
+ detail: `${status.manualTarget} already runs the helper (via ${status.via})${helper}`
64171
+ };
64172
+ if (status.state === "blocked")
64173
+ return {
64174
+ action: "skipped",
64175
+ helperWritten,
64176
+ status: inspectSessionHook(cwd, env),
64177
+ detail: `${status.reason}${helper}`
64178
+ };
64179
+ const fresh = status.state === "missing";
64180
+ const slotCurrent = readText2(status.hookPath) === SESSION_HOOK_SCRIPT;
64181
+ if (!slotCurrent)
64182
+ writeExecutable(status.hookPath, SESSION_HOOK_SCRIPT);
64183
+ const verb = fresh ? "installed" : "updated";
64184
+ return {
64185
+ action: verb,
64186
+ helperWritten,
64187
+ status: inspectSessionHook(cwd, env),
64188
+ detail: `${verb} the Workit ${SESSION_HOOK_NAME} hook at ${status.hookPath}${helper}`
64189
+ };
64190
+ }
64191
+ var SESSION_HOOK_NAME = "prepare-commit-msg", SESSION_HOOK_MARKER = "# workit-session-trailer-hook", HELPER, LEGACY_CHAINED = "commit-msg.workit-chained", SESSION_TRAILER_SCRIPT = `#!/bin/sh
64192
+ # Workit (https://github.com/BrainerVirus/workit) wrote this file with
64193
+ # \`workit doctor --fix\`. A prepare-commit-msg hook runs it with its own
64194
+ # arguments to add a Workit-Session trailer to commits made in an agent session.
64195
+ # Not a hook itself; delete it together with the line that runs it.
64196
+ # lefthook passes a missing argument as its literal placeholder.
64197
+ case "\${2-}" in "" | "{2}" | template) exit 0 ;; esac
64198
+ gitdir=$(git rev-parse --git-dir) || exit 0
64199
+ if [ -d "$gitdir/rebase-merge" ] || [ -d "$gitdir/rebase-apply" ] || [ -f "$gitdir/CHERRY_PICK_HEAD" ]; then
64200
+ exit 0
64201
+ fi
64202
+ trim() { printf '%s' "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'; }
64203
+ inner=$(trim "\${OPENCODE_SESSION_ID-}"); inner_host=opencode
64204
+ if [ -z "$inner" ]; then inner=$(trim "\${PI_SESSION_ID-}"); inner_host=pi; fi
64205
+ if [ -z "$inner" ]; then inner=$(trim "\${CODEX_THREAD_ID-}"); inner_host=codex; fi
64206
+ [ -n "$inner" ] || inner_host=
64207
+ declared=$(trim "\${WORKIT_HOST-}")
64208
+ family=$(printf '%s' "$declared" | sed 's/_cli$//;s/_desktop$//')
64209
+ session=$inner
64210
+ if [ "\${WORKIT_SESSION_ID+set}" = set ]; then
64211
+ if [ -z "$inner" ] || [ -z "$declared" ] || [ "$family" = "$inner_host" ]; then
64212
+ session=$(trim "$WORKIT_SESSION_ID")
64213
+ fi
64214
+ fi
64215
+ [ -n "$session" ] || exit 0
64216
+ [ \${#session} -le 128 ] || exit 0
64217
+ case "$session" in *[!A-Za-z0-9_.:@/+-]*) exit 0 ;; esac
64218
+ add_trailer() {
64219
+ [ -f "$1" ] && [ -r "$1" ] || return 0
64220
+ body=$(sed '/^# -\\{24\\} >8 -\\{24\\}$/,$d' "$1" | git stripspace --strip-comments)
64221
+ [ -n "$body" ] || return 0
64222
+ # A merge message has no final newline; interpret-trailers would glue the
64223
+ # trailer to the subject line.
64224
+ [ -z "$(tail -c 1 "$1")" ] || printf '\\n' >> "$1"
64225
+ git -c trailer.separators=: -c trailer.where=end interpret-trailers --in-place \\
64226
+ --no-divider --if-exists addIfDifferent --trailer "Workit-Session: $session" "$1" || return 0
64227
+ }
64228
+ # $1 is the message file. lefthook cannot quote a path it pastes in, so its
64229
+ # job passes '' and the file is found here: git hands the hook COMMIT_EDITMSG,
64230
+ # except \`git merge\`, which hands it MERGE_MSG. With source "merge" both are
64231
+ # updated: the one git did not hand over is stale or removed after the commit.
64232
+ case "\${1-}" in "" | "{1}") ;; *) add_trailer "$1"; exit 0 ;; esac
64233
+ add_trailer "$(git rev-parse --git-path COMMIT_EDITMSG)"
64234
+ [ "$2" != merge ] || add_trailer "$(git rev-parse --git-path MERGE_MSG)"
64235
+ exit 0
64236
+ `, manualTrailerLine = () => `h="$(git rev-parse --git-common-dir 2>/dev/null)/${HELPER.join("/")}"; [ ! -f "$h" ] || sh "$h" "$@" || true`, lefthookTrailerRun = () => `set -f; IFS=; h=$(git rev-parse --git-common-dir 2>/dev/null)/${HELPER.join("/")}; [ ! -f $h ] || sh $h '' {2} {3} || true`, HELPER_REF, SESSION_HOOK_SCRIPT, run2 = (cwd, args, env) => {
64237
+ const r = spawnSync13("git", args, {
64238
+ cwd,
64239
+ encoding: "utf8",
64240
+ timeout: GIT_TIMEOUTS.local,
64241
+ windowsHide: true,
64242
+ stdio: ["ignore", "pipe", "pipe"],
64243
+ env: { ...env, GIT_TERMINAL_PROMPT: "0" }
64244
+ });
64245
+ return { ok: r.status === 0, out: (r.stdout ?? "").trim() };
64246
+ }, inside2 = (child, parent) => {
64247
+ const rel = path30.relative(path30.resolve(parent), path30.resolve(child));
64248
+ return rel === "" || !rel.startsWith("..") && !path30.isAbsolute(rel);
64249
+ }, readText2 = (file) => {
64250
+ try {
64251
+ return readFileSync13(file, "utf8");
64252
+ } catch {
64253
+ return null;
64254
+ }
64255
+ }, exists = (file) => {
64256
+ try {
64257
+ lstatSync4(file);
64258
+ return true;
64259
+ } catch {
64260
+ return false;
64261
+ }
64262
+ }, LEFTHOOK_CONFIGS, lefthookConfig = (top) => LEFTHOOK_CONFIGS.map((name) => path30.join(top, name)).find((file) => existsSync13(file)) ?? null, isComment = (line) => /^\s*#/.test(line), COMMAND_SH, DIRECT_CALL, VAR_CALL, runsHelperText = (text) => {
64263
+ const lines = text.split(/\r?\n/).filter((line) => !isComment(line));
64264
+ const assigned = lines.some((line) => /(?:^|[\s;])h=/.test(line) && line.includes(HELPER_REF));
64265
+ return lines.some((line) => DIRECT_CALL.test(line) || assigned && VAR_CALL.test(line));
64266
+ }, LEFTHOOK_YAML, indentOf = (line) => line.length - line.trimStart().length, lefthookRunsHelper = (text) => {
64267
+ const lines = text.split(/\r?\n/);
64268
+ const start = lines.findIndex((line) => /^prepare-commit-msg:\s*(?:#.*)?$/.test(line));
64269
+ if (start < 0)
64270
+ return false;
64271
+ const block = [];
64272
+ for (const line of lines.slice(start + 1)) {
64273
+ if (line.trim() && !isComment(line) && indentOf(line) === 0)
64274
+ break;
64275
+ block.push(line);
64276
+ }
64277
+ return block.some((line, i) => {
64278
+ if (isComment(line))
64279
+ return false;
64280
+ const runKey = /^(\s*)(?:-\s+)?run:\s*(.*)$/.exec(line);
64281
+ if (!runKey)
64282
+ return false;
64283
+ let value = runKey[2] ?? "";
64284
+ if (/^[|>][-+]?\s*$/.test(value)) {
64285
+ const body = [];
64286
+ for (const next of block.slice(i + 1)) {
64287
+ if (next.trim() && indentOf(next) <= indentOf(line))
64288
+ break;
64289
+ body.push(next);
64290
+ }
64291
+ value = body.join(`
64292
+ `);
64293
+ }
64294
+ return runsHelperText(value);
64295
+ });
64296
+ }, isExecutable = (file) => {
64297
+ if (process.platform === "win32")
64298
+ return true;
64299
+ try {
64300
+ return (statSync4(file).mode & 73) !== 0;
64301
+ } catch {
64302
+ return false;
64303
+ }
64304
+ }, manualInPlace = (input, target) => {
64305
+ if (input.owner === "lefthook")
64306
+ return LEFTHOOK_YAML.some((name) => {
64307
+ const text = readText2(path30.join(input.top, name));
64308
+ return text !== null && lefthookRunsHelper(text);
64309
+ });
64310
+ const text = readText2(target);
64311
+ if (text === null || !runsHelperText(text))
64312
+ return false;
64313
+ return input.owner === "husky" && target !== input.hookPath || isExecutable(target);
64314
+ }, isHuskyV9 = (text, hooksDir) => /\$\(dirname (?:-- )?"\$0"\)\/h"/.test(text) && existsSync13(path30.join(hooksDir, "h")), ownerOf = (text, hooksDir, top) => {
64315
+ if (text.includes("husky.sh") || isHuskyV9(text, hooksDir))
64316
+ return "husky";
64317
+ if (/lefthook/i.test(text) || lefthookConfig(top))
64318
+ return "lefthook";
64319
+ return "other";
64320
+ }, manualTargetOf = ({ owner, text, hooksDir, hookPath, top }) => {
64321
+ if (owner === "husky" && isHuskyV9(text, hooksDir))
64322
+ return path30.join(path30.dirname(hooksDir), SESSION_HOOK_NAME);
64323
+ if (owner === "lefthook")
64324
+ return lefthookConfig(top) ?? path30.join(top, "lefthook.yml");
64325
+ return hookPath;
64326
+ }, manualFor = (input) => {
64327
+ const { owner, displaced } = input;
64328
+ const target = manualTargetOf(input);
64329
+ const stale = displaced ? `; delete the stale ${displaced} (nothing runs it)` : "";
64330
+ if (owner === "husky")
64331
+ return `add this line to ${target} (create it if missing; husky runs it)${stale}: ${manualTrailerLine()}`;
64332
+ if (owner === "lefthook")
64333
+ return [
64334
+ `merge this job into ${target} (add it to an existing \`${SESSION_HOOK_NAME}:\` \`jobs:\` list rather than a second \`${SESSION_HOOK_NAME}:\` key), then run \`lefthook install\`${stale}:`,
64335
+ `${SESSION_HOOK_NAME}:`,
64336
+ " jobs:",
64337
+ " - name: workit-session",
64338
+ ` run: ${lefthookTrailerRun()}`
64339
+ ].join(`
64340
+ `);
64341
+ return `add this line to ${target} (a new file needs \`#!/bin/sh\` as its first line and \`chmod +x\`)${stale}: ${manualTrailerLine()}`;
64342
+ }, leftoverOf = (hooksDir) => {
64343
+ const chained = path30.join(hooksDir, LEGACY_CHAINED);
64344
+ const commitMsg = path30.join(hooksDir, "commit-msg");
64345
+ const oldOurs = readText2(commitMsg)?.includes(SESSION_HOOK_MARKER) ?? false;
64346
+ if (exists(chained))
64347
+ return oldOurs ? `${chained} was left by an earlier Workit build: move it back to ${commitMsg} (replacing Workit's old hook there)` : `${chained} was left by an earlier Workit build and nothing runs it: if it is your original commit-msg hook, move it back to ${commitMsg}; otherwise delete it`;
64348
+ if (oldOurs)
64349
+ return `${commitMsg} is a hook from an earlier Workit build; delete it (the trailer now comes from ${SESSION_HOOK_NAME})`;
64350
+ return;
64351
+ }, writeExecutable = (file, content) => {
64352
+ mkdirSync5(path30.dirname(file), { recursive: true });
64353
+ const staged = `${file}.workit-new`;
64354
+ try {
64355
+ writeFileSync6(staged, content, { mode: 493 });
64356
+ chmodSync(staged, 493);
64357
+ renameSync3(staged, file);
64358
+ } finally {
64359
+ rmSync2(staged, { force: true });
64360
+ }
64361
+ };
64362
+ var init_session_hook = __esm(() => {
64363
+ init_rev();
64364
+ HELPER = ["workit", "session-trailer.sh"];
64365
+ HELPER_REF = HELPER.join("/");
64366
+ SESSION_HOOK_SCRIPT = `#!/bin/sh
64367
+ ${SESSION_HOOK_MARKER} v4
64368
+ # Workit (https://github.com/BrainerVirus/workit) installed this hook with
64369
+ # \`workit doctor --fix\`: it runs Workit's session-trailer helper, which adds a
64370
+ # Workit-Session trailer to commits made in an agent session. To remove it,
64371
+ # delete this file.
64372
+ ${manualTrailerLine()}
64373
+ `;
64374
+ LEFTHOOK_CONFIGS = [
64375
+ "lefthook.yml",
64376
+ "lefthook.yaml",
64377
+ ".lefthook.yml",
64378
+ ".lefthook.yaml",
64379
+ "lefthook.json",
64380
+ "lefthook.toml"
64381
+ ];
64382
+ COMMAND_SH = String.raw`(?:^|[;&|(!]|\b(?:then|do|else)\s)\s*sh\s+`;
64383
+ DIRECT_CALL = new RegExp(String.raw`${COMMAND_SH}(?:"[^"]*|\S*)${HELPER_REF.replaceAll(".", String.raw`\.`)}`);
64384
+ VAR_CALL = new RegExp(String.raw`${COMMAND_SH}"?\$h\b`);
64385
+ LEFTHOOK_YAML = [
64386
+ "lefthook.yml",
64387
+ "lefthook.yaml",
64388
+ ".lefthook.yml",
64389
+ ".lefthook.yaml",
64390
+ "lefthook-local.yml",
64391
+ "lefthook-local.yaml",
64392
+ ".lefthook-local.yml",
64393
+ ".lefthook-local.yaml"
64394
+ ];
64395
+ });
64396
+
64397
+ // packages/workit-cli/src/admin/doctor.ts
64398
+ import { spawnSync as spawnSync14 } from "node:child_process";
64399
+ import { createHash as createHash13 } from "node:crypto";
64400
+ import {
64401
+ existsSync as existsSync14,
64402
+ lstatSync as lstatSync5,
64403
+ mkdirSync as mkdirSync6,
64404
+ mkdtempSync,
64405
+ readdirSync as readdirSync7,
64406
+ readFileSync as readFileSync14,
64407
+ readlinkSync as readlinkSync2,
64408
+ rmSync as rmSync3,
64409
+ statSync as statSync5,
64410
+ unlinkSync as unlinkSync3,
64411
+ writeFileSync as writeFileSync7
64412
+ } from "node:fs";
64413
+ import os8 from "node:os";
64414
+ import path31 from "node:path";
63959
64415
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63960
- let dir = path30.resolve(cwd);
64416
+ let dir = path31.resolve(cwd);
63961
64417
  while (true) {
63962
- if (existsSync13(path30.join(dir, "packages", "workit-core", "package.json")))
64418
+ if (existsSync14(path31.join(dir, "packages", "workit-core", "package.json")))
63963
64419
  return dir;
63964
- const parent = path30.dirname(dir);
64420
+ const parent = path31.dirname(dir);
63965
64421
  if (parent === dir)
63966
64422
  return null;
63967
64423
  dir = parent;
@@ -63969,8 +64425,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63969
64425
  }, resolve = (options) => {
63970
64426
  const env = options.env ?? process.env;
63971
64427
  const home = options.home ?? env.HOME ?? os8.homedir();
63972
- const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path30.join(home, ".config", "workit");
63973
- const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path30.join(home, ".local", "state", "workit");
64428
+ const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path31.join(home, ".config", "workit");
64429
+ const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path31.join(home, ".local", "state", "workit");
63974
64430
  const cwd = options.cwd ?? process.cwd();
63975
64431
  const dev = options.dev ?? env.WORKFLOW_TOOLKIT_DEV ?? findDevFromCwd(cwd);
63976
64432
  return {
@@ -63981,11 +64437,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63981
64437
  cwd,
63982
64438
  workspaceRoot: options.workspaceRoot ?? env.WORKFLOW_WORKSPACE_ROOT ?? cwd,
63983
64439
  dev,
63984
- opencodeConfig: options.opencodeConfig ?? path30.join(home, ".config", "opencode", "opencode.json"),
63985
- opencodePackageCacheDir: options.opencodePackageCacheDir ?? path30.join(home, ".cache", "opencode", "packages", "@brainervirus", "workit-opencode@latest"),
63986
- cursorSettings: options.cursorSettings ?? path30.join(home, ".cursor", "settings.json"),
63987
- cursorMcp: options.cursorMcp ?? path30.join(home, ".cursor", "mcp.json"),
63988
- cursorPluginDir: options.cursorPluginDir ?? path30.join(home, ".cursor", "plugins", "local", "workit"),
64440
+ opencodeConfig: options.opencodeConfig ?? path31.join(home, ".config", "opencode", "opencode.json"),
64441
+ opencodePackageCacheDir: options.opencodePackageCacheDir ?? path31.join(home, ".cache", "opencode", "packages", "@brainervirus", "workit-opencode@latest"),
64442
+ cursorSettings: options.cursorSettings ?? path31.join(home, ".cursor", "settings.json"),
64443
+ cursorMcp: options.cursorMcp ?? path31.join(home, ".cursor", "mcp.json"),
64444
+ cursorPluginDir: options.cursorPluginDir ?? path31.join(home, ".cursor", "plugins", "local", "workit"),
63989
64445
  cursorHeartbeatDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
63990
64446
  codexHome: options.codexHome,
63991
64447
  piAgentDir: options.piAgentDir,
@@ -63995,14 +64451,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63995
64451
  };
63996
64452
  }, readJson = (p) => {
63997
64453
  try {
63998
- const parsed = JSON.parse(readFileSync13(p, "utf8"));
64454
+ const parsed = JSON.parse(readFileSync14(p, "utf8"));
63999
64455
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
64000
64456
  } catch {
64001
64457
  return null;
64002
64458
  }
64003
64459
  }, parsesAsConfigObject = (p) => {
64004
64460
  try {
64005
- return isConfigObject(JSON.parse(readFileSync13(p, "utf8")));
64461
+ return isConfigObject(JSON.parse(readFileSync14(p, "utf8")));
64006
64462
  } catch {
64007
64463
  return false;
64008
64464
  }
@@ -64012,14 +64468,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64012
64468
  ...new Set(entries.filter((entry) => typeof entry === "string" && isWorkitPlugin(entry)))
64013
64469
  ];
64014
64470
  }, commandOnPath = (name, env) => findOnPath(process.platform === "win32" ? [name, `${name}.exe`] : [name], env) !== null, pathextNames = (name, env) => process.platform === "win32" ? (env.PATHEXT ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean).map((ext) => `${name}${ext.toLowerCase()}`) : [name], findOnPath = (names, env) => {
64015
- const dirs = (env.PATH ?? process.env.PATH ?? "").split(path30.delimiter);
64471
+ const dirs = (env.PATH ?? process.env.PATH ?? "").split(path31.delimiter);
64016
64472
  for (const dir of dirs) {
64017
64473
  if (!dir)
64018
64474
  continue;
64019
64475
  for (const candidateName of names) {
64020
- const candidate = path30.join(dir, candidateName);
64476
+ const candidate = path31.join(dir, candidateName);
64021
64477
  try {
64022
- const st = statSync4(candidate);
64478
+ const st = statSync5(candidate);
64023
64479
  if (process.platform !== "win32" && (st.mode & 73) === 0)
64024
64480
  continue;
64025
64481
  return candidate;
@@ -64028,7 +64484,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64028
64484
  }
64029
64485
  return null;
64030
64486
  }, versionOf = (bin, env, timeout) => {
64031
- const r = spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout });
64487
+ const r = spawnSync14(bin, ["--version"], { encoding: "utf8", env, timeout });
64032
64488
  if (r.error)
64033
64489
  return null;
64034
64490
  return (r.stdout ?? "").trim();
@@ -64043,14 +64499,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64043
64499
  }
64044
64500
  return true;
64045
64501
  }, resolveBun = (env) => {
64046
- if (env.BUN && existsSync13(env.BUN))
64502
+ if (env.BUN && existsSync14(env.BUN))
64047
64503
  return env.BUN;
64048
64504
  for (const candidate of [
64049
- path30.join(os8.homedir(), ".bun/bin/bun"),
64505
+ path31.join(os8.homedir(), ".bun/bin/bun"),
64050
64506
  "/usr/local/bin/bun",
64051
64507
  "/usr/bin/bun"
64052
64508
  ]) {
64053
- if (existsSync13(candidate))
64509
+ if (existsSync14(candidate))
64054
64510
  return candidate;
64055
64511
  }
64056
64512
  return commandOnPath("bun", env) ? "bun" : null;
@@ -64098,7 +64554,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64098
64554
  detail: "no dev checkout found (WORKFLOW_TOOLKIT_DEV) — skipping version parity"
64099
64555
  };
64100
64556
  }
64101
- const corePkg = readJson(path30.join(res.dev, "packages/workit-core/package.json"));
64557
+ const corePkg = readJson(path31.join(res.dev, "packages/workit-core/package.json"));
64102
64558
  if (!corePkg) {
64103
64559
  return {
64104
64560
  id: "versions",
@@ -64108,7 +64564,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64108
64564
  }
64109
64565
  const refs = new Set;
64110
64566
  for (const name of ["workit-opencode", "workit-cursor", "workit-cli"]) {
64111
- const pkg = readJson(path30.join(res.dev, "packages", name, "package.json"));
64567
+ const pkg = readJson(path31.join(res.dev, "packages", name, "package.json"));
64112
64568
  const dep = pkg?.dependencies?.["@brainervirus/workit-core"];
64113
64569
  if (typeof dep === "string")
64114
64570
  refs.add(dep);
@@ -64124,7 +64580,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64124
64580
  if (refs.size > 1) {
64125
64581
  problems.push(`adapters pin different core versions: ${[...refs].join(", ")}`);
64126
64582
  }
64127
- const opencodePkg = readJson(path30.join(res.dev, "packages/workit-opencode/package.json"));
64583
+ const opencodePkg = readJson(path31.join(res.dev, "packages/workit-opencode/package.json"));
64128
64584
  const sdk = opencodePkg?.dependencies?.["@opencode/plugin"] ?? opencodePkg?.devDependencies?.["@opencode/plugin"];
64129
64585
  const sdkVersion = typeof sdk === "string" ? (sdk.match(/^\d+(?:\.\d+){0,2}/) ?? [])[0] : null;
64130
64586
  if (sdkVersion && !semverAtLeast(sdkVersion, SUPPORT_MATRIX.opencode.minimum)) {
@@ -64151,17 +64607,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64151
64607
  status: "pass",
64152
64608
  detail: "opencode CLI not on PATH — skipping version check"
64153
64609
  };
64154
- const scratch = mkdtempSync(path30.join(os8.tmpdir(), "workit-doctor-opencode-"));
64610
+ const scratch = mkdtempSync(path31.join(os8.tmpdir(), "workit-doctor-opencode-"));
64155
64611
  let raw;
64156
64612
  try {
64157
64613
  raw = versionOf("opencode", {
64158
64614
  ...res.env,
64159
- XDG_DATA_HOME: path30.join(scratch, "data"),
64160
- XDG_STATE_HOME: path30.join(scratch, "state"),
64161
- XDG_CACHE_HOME: path30.join(scratch, "cache")
64615
+ XDG_DATA_HOME: path31.join(scratch, "data"),
64616
+ XDG_STATE_HOME: path31.join(scratch, "state"),
64617
+ XDG_CACHE_HOME: path31.join(scratch, "cache")
64162
64618
  }, 5000);
64163
64619
  } finally {
64164
- rmSync2(scratch, { recursive: true, force: true });
64620
+ rmSync3(scratch, { recursive: true, force: true });
64165
64621
  }
64166
64622
  const installed = raw ? (raw.match(/\d+\.\d+\.\d+/) ?? [])[0] ?? null : null;
64167
64623
  if (installed === null)
@@ -64216,7 +64672,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64216
64672
  const tables = new Map;
64217
64673
  let text;
64218
64674
  try {
64219
- text = readFileSync13(file, "utf8");
64675
+ text = readFileSync14(file, "utf8");
64220
64676
  } catch {
64221
64677
  return tables;
64222
64678
  }
@@ -64254,19 +64710,19 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64254
64710
  current[unquote(pair[1])] = value === "true" ? true : value === "false" ? false : unquote(value);
64255
64711
  }
64256
64712
  return tables;
64257
- }, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path30.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
64713
+ }, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path31.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
64258
64714
  const codexHome = codexHomeOf(res);
64259
- const configFile = path30.join(codexHome, "config.toml");
64715
+ const configFile = path31.join(codexHome, "config.toml");
64260
64716
  const installs = [];
64261
64717
  for (const [key, table] of readTomlTables(configFile)) {
64262
64718
  const [kind, id, ...rest] = key.split("\x00");
64263
64719
  if (kind !== "plugins" || rest.length > 0 || !id?.startsWith("workit@") || table.enabled === false)
64264
64720
  continue;
64265
64721
  const marketplace = id.slice("workit@".length);
64266
- const dir = path30.join(codexHome, "plugins", "cache", marketplace, "workit");
64722
+ const dir = path31.join(codexHome, "plugins", "cache", marketplace, "workit");
64267
64723
  let versions = [];
64268
64724
  try {
64269
- versions = readdirSync7(dir).filter((v) => existsSync13(path30.join(dir, v, ".codex-plugin")));
64725
+ versions = readdirSync7(dir).filter((v) => existsSync14(path31.join(dir, v, ".codex-plugin")));
64270
64726
  } catch {
64271
64727
  versions = [];
64272
64728
  }
@@ -64274,7 +64730,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64274
64730
  installs.push({
64275
64731
  id,
64276
64732
  marketplace,
64277
- root: version ? path30.join(dir, version) : null,
64733
+ root: version ? path31.join(dir, version) : null,
64278
64734
  version,
64279
64735
  configFile
64280
64736
  });
@@ -64296,7 +64752,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64296
64752
  };
64297
64753
  return `sha256:${createHash13("sha256").update(JSON.stringify(body)).digest("hex")}`;
64298
64754
  }, codexHookTrust = (install, tables) => {
64299
- const hooks = install.root ? readJson(path30.join(install.root, "hooks", "hooks.json"))?.hooks : null;
64755
+ const hooks = install.root ? readJson(path31.join(install.root, "hooks", "hooks.json"))?.hooks : null;
64300
64756
  if (!hooks || typeof hooks !== "object")
64301
64757
  return null;
64302
64758
  const result = [];
@@ -64369,28 +64825,28 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64369
64825
  status: "pass",
64370
64826
  detail: "no Workit Codex plugin enabled — skipping"
64371
64827
  };
64372
- const agentsDir = path30.join(codexHomeOf(res), "agents");
64828
+ const agentsDir = path31.join(codexHomeOf(res), "agents");
64373
64829
  const problems = [];
64374
64830
  const fixes = [];
64375
64831
  const installed = [];
64376
64832
  for (const install of installs) {
64377
- const source = install.root ? path30.join(install.root, "agents") : null;
64378
- const files = source && existsSync13(source) ? readdirSync7(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
64833
+ const source = install.root ? path31.join(install.root, "agents") : null;
64834
+ const files = source && existsSync14(source) ? readdirSync7(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
64379
64835
  if (files.length === 0) {
64380
64836
  problems.push(`${install.id} ${install.version ?? "?"} bundles no agents`);
64381
64837
  fixes.push(`codex plugin remove ${install.id} && codex plugin add ${install.id}`);
64382
64838
  continue;
64383
64839
  }
64384
- const install_fix = `node ${JSON.stringify(path30.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
64385
- const pluginVersion = readJson(path30.join(install.root, "package.json"))?.version;
64840
+ const install_fix = `node ${JSON.stringify(path31.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
64841
+ const pluginVersion = readJson(path31.join(install.root, "package.json"))?.version;
64386
64842
  const version = typeof pluginVersion === "string" ? pluginVersion.match(SEMVER)?.[0] ?? null : null;
64387
64843
  let stale = false;
64388
64844
  for (const file of files) {
64389
- const dest = path30.join(agentsDir, file);
64390
- const want = stampCodexAgent(readFileSync13(path30.join(source, file), "utf8"), version);
64845
+ const dest = path31.join(agentsDir, file);
64846
+ const want = stampCodexAgent(readFileSync14(path31.join(source, file), "utf8"), version);
64391
64847
  let link = false;
64392
64848
  try {
64393
- link = lstatSync4(dest).isSymbolicLink();
64849
+ link = lstatSync5(dest).isSymbolicLink();
64394
64850
  } catch {
64395
64851
  link = false;
64396
64852
  }
@@ -64401,7 +64857,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64401
64857
  }
64402
64858
  let have = null;
64403
64859
  try {
64404
- have = readFileSync13(dest, "utf8");
64860
+ have = readFileSync14(dest, "utf8");
64405
64861
  } catch {
64406
64862
  have = null;
64407
64863
  }
@@ -64440,7 +64896,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64440
64896
  const env = res.env.PI_CODING_AGENT_DIR;
64441
64897
  if (env)
64442
64898
  return env === "~" ? res.home : env.replace(/^~(?=\/|\\)/, res.home);
64443
- return path30.join(res.home, ".pi", "agent");
64899
+ return path31.join(res.home, ".pi", "agent");
64444
64900
  }, npmSpecName = (spec) => {
64445
64901
  const at = spec.indexOf("@", spec.startsWith("@") ? 1 : 0);
64446
64902
  return at > 0 ? { name: spec.slice(0, at), pinned: true } : { name: spec, pinned: false };
@@ -64448,10 +64904,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64448
64904
  const scopes = [
64449
64905
  {
64450
64906
  scope: "project",
64451
- settingsFile: path30.join(res.cwd, ".pi", "settings.json"),
64452
- base: path30.join(res.cwd, ".pi")
64907
+ settingsFile: path31.join(res.cwd, ".pi", "settings.json"),
64908
+ base: path31.join(res.cwd, ".pi")
64453
64909
  },
64454
- { scope: "user", settingsFile: path30.join(agentDir, "settings.json"), base: agentDir }
64910
+ { scope: "user", settingsFile: path31.join(agentDir, "settings.json"), base: agentDir }
64455
64911
  ];
64456
64912
  for (const { scope, settingsFile, base } of scopes) {
64457
64913
  const packages = readJson(settingsFile)?.packages;
@@ -64466,13 +64922,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64466
64922
  const spec = npmSpecName(source.slice(4).trim());
64467
64923
  if (spec.name !== PI_PACKAGE)
64468
64924
  continue;
64469
- const root = path30.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
64925
+ const root = path31.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
64470
64926
  return { scope, settingsFile, source, filter, root, npm: { pinned: spec.pinned } };
64471
64927
  }
64472
64928
  if (/^(git:|https?:|ssh:|git@)/.test(source))
64473
64929
  continue;
64474
- const local = path30.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
64475
- if (readJson(path30.join(local, "package.json"))?.name === PI_PACKAGE)
64930
+ const local = path31.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
64931
+ if (readJson(path31.join(local, "package.json"))?.name === PI_PACKAGE)
64476
64932
  return { scope, settingsFile, source, filter, root: local, npm: null };
64477
64933
  }
64478
64934
  }
@@ -64483,7 +64939,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64483
64939
  const list = patterns.filter((p) => typeof p === "string");
64484
64940
  if (list.length === 0)
64485
64941
  return false;
64486
- const name = path30.posix.basename(file);
64942
+ const name = path31.posix.basename(file);
64487
64943
  const matches = (p) => {
64488
64944
  const re = globToRegExp3(p);
64489
64945
  return re.test(file) || re.test(name);
@@ -64500,7 +64956,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64500
64956
  return kept;
64501
64957
  }, checkPiExtension = (res) => {
64502
64958
  const agentDir = piAgentDirOf(res);
64503
- if (!existsSync13(agentDir) && !commandOnPath("pi", res.env))
64959
+ if (!existsSync14(agentDir) && !commandOnPath("pi", res.env))
64504
64960
  return { id: "pi_extension", status: "pass", detail: "Pi not installed — skipping" };
64505
64961
  const installCmd = (scope, spec = `npm:${PI_PACKAGE}`) => `pi install ${spec}${scope === "project" ? " -l" : ""}`;
64506
64962
  const entry = findPiEntry(res, agentDir);
@@ -64508,12 +64964,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64508
64964
  return {
64509
64965
  id: "pi_extension",
64510
64966
  status: "warn",
64511
- detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path30.join(agentDir, "settings.json")} or ${path30.join(res.cwd, ".pi", "settings.json")}`,
64967
+ detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path31.join(agentDir, "settings.json")} or ${path31.join(res.cwd, ".pi", "settings.json")}`,
64512
64968
  fix: installCmd("user")
64513
64969
  };
64514
64970
  const where = `${entry.source} (${entry.scope}, ${entry.settingsFile})`;
64515
64971
  const rebuild = `cd ${JSON.stringify(entry.root)} && bun run build`;
64516
- const pkg = readJson(path30.join(entry.root, "package.json"));
64972
+ const pkg = readJson(path31.join(entry.root, "package.json"));
64517
64973
  if (!pkg)
64518
64974
  return {
64519
64975
  id: "pi_extension",
@@ -64522,7 +64978,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64522
64978
  fix: entry.npm ? installCmd(entry.scope, entry.source) : `restore ${entry.root}, then ${rebuild}`
64523
64979
  };
64524
64980
  const declared = Array.isArray(pkg.pi?.extensions) ? pkg.pi.extensions.some((e) => typeof e === "string" && e.replace(/^\.\//, "") === PI_EXTENSION) : false;
64525
- if (!declared || !existsSync13(path30.join(entry.root, PI_EXTENSION)))
64981
+ if (!declared || !existsSync14(path31.join(entry.root, PI_EXTENSION)))
64526
64982
  return {
64527
64983
  id: "pi_extension",
64528
64984
  status: "warn",
@@ -64575,25 +65031,25 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64575
65031
  detail: `Workit Pi extension ${version ?? "?"} from ${where} loads ${PI_EXTENSION}`
64576
65032
  };
64577
65033
  }, assetPathsFor = (host, dev) => {
64578
- const pkg = path30.join(dev, "packages", `workit-${host}`);
65034
+ const pkg = path31.join(dev, "packages", `workit-${host}`);
64579
65035
  switch (host) {
64580
65036
  case "opencode": {
64581
- const packaged = path30.join(pkg, "assets", "skills");
64582
- const root = existsSync13(packaged) ? packaged : path30.join(dev, "packages", "workit-core", "skills");
64583
- return WORKIT_METHOD_SKILLS.map((skill) => path30.join(root, skill, "SKILL.md"));
65037
+ const packaged = path31.join(pkg, "assets", "skills");
65038
+ const root = existsSync14(packaged) ? packaged : path31.join(dev, "packages", "workit-core", "skills");
65039
+ return WORKIT_METHOD_SKILLS.map((skill) => path31.join(root, skill, "SKILL.md"));
64584
65040
  }
64585
65041
  case "cursor":
64586
65042
  return [
64587
- path30.join(pkg, "assets", "templates", "workit-contract.md"),
64588
- path30.join(pkg, "mcp.json"),
64589
- path30.join(pkg, ".cursor-plugin")
65043
+ path31.join(pkg, "assets", "templates", "workit-contract.md"),
65044
+ path31.join(pkg, "mcp.json"),
65045
+ path31.join(pkg, ".cursor-plugin")
64590
65046
  ];
64591
65047
  case "cli":
64592
- return [path30.join(pkg, "assets", "templates", "spec-template.md")];
65048
+ return [path31.join(pkg, "assets", "templates", "spec-template.md")];
64593
65049
  }
64594
65050
  }, hostsFor = (host) => host === "cli" ? ["opencode", "cursor", "cli"] : [host], checkAssets = (res) => {
64595
65051
  const dev = res.dev;
64596
- const missing = dev ? hostsFor(res.host).flatMap((h) => assetPathsFor(h, dev).filter((p) => !existsSync13(p)).map((p) => `${h}: ${p}`)) : [];
65052
+ const missing = dev ? hostsFor(res.host).flatMap((h) => assetPathsFor(h, dev).filter((p) => !existsSync14(p)).map((p) => `${h}: ${p}`)) : [];
64597
65053
  if (res.host === "cursor" || res.host === "cli") {
64598
65054
  const cursorError = validateCursorSkills(res.cursorPluginDir, WORKIT_METHOD_SKILLS);
64599
65055
  if (cursorError)
@@ -64620,31 +65076,31 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64620
65076
  fix: "Reinstall or rebuild the workit package (missing assets under packages/workit-<host>)"
64621
65077
  };
64622
65078
  }, launcherSlotsFor = (host, dev) => {
64623
- const pkg = path30.join(dev, "packages", `workit-${host}`);
65079
+ const pkg = path31.join(dev, "packages", `workit-${host}`);
64624
65080
  switch (host) {
64625
65081
  case "opencode":
64626
- return [[path30.join(pkg, "src", "plugin.ts"), path30.join(pkg, "dist", "plugin.js")]];
65082
+ return [[path31.join(pkg, "src", "plugin.ts"), path31.join(pkg, "dist", "plugin.js")]];
64627
65083
  case "cursor":
64628
65084
  return [
64629
- [path30.join(pkg, "dist", "mcp-server.js")],
64630
- [path30.join(pkg, "dist", "cursor-session-start.js")]
65085
+ [path31.join(pkg, "dist", "mcp-server.js")],
65086
+ [path31.join(pkg, "dist", "cursor-session-start.js")]
64631
65087
  ];
64632
65088
  case "cli":
64633
- return [[path30.join(pkg, "src", "index.tsx"), path30.join(pkg, "dist", "index.js")]];
65089
+ return [[path31.join(pkg, "src", "index.tsx"), path31.join(pkg, "dist", "index.js")]];
64634
65090
  }
64635
65091
  }, validNodeEntry = (entry, runtime, env) => {
64636
65092
  try {
64637
- const stat = statSync4(entry);
64638
- if (!stat.isFile() || stat.size === 0 || !readFileSync13(entry, "utf8").startsWith(`#!/usr/bin/env node
65093
+ const stat = statSync5(entry);
65094
+ if (!stat.isFile() || stat.size === 0 || !readFileSync14(entry, "utf8").startsWith(`#!/usr/bin/env node
64639
65095
  `)) {
64640
65096
  return false;
64641
65097
  }
64642
- return spawnSync13(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
65098
+ return spawnSync14(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
64643
65099
  } catch {
64644
65100
  return false;
64645
65101
  }
64646
65102
  }, registeredCursorLauncher = (res) => {
64647
- if (!existsSync13(res.cursorMcp))
65103
+ if (!existsSync14(res.cursorMcp))
64648
65104
  return "invalid";
64649
65105
  const config = readJson(res.cursorMcp);
64650
65106
  if (!config)
@@ -64657,7 +65113,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64657
65113
  if (typeof command !== "string" || !Array.isArray(args) || typeof args[0] !== "string") {
64658
65114
  return "invalid";
64659
65115
  }
64660
- const executable = path30.basename(command).toLowerCase();
65116
+ const executable = path31.basename(command).toLowerCase();
64661
65117
  const canonical = cursorMcpServerEntry("").args;
64662
65118
  if (executable === "npx" || executable === "npx.exe" || executable === "npx.cmd") {
64663
65119
  if (args.length !== canonical.length || args.some((a, i) => a !== canonical[i]))
@@ -64669,11 +65125,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64669
65125
  return {
64670
65126
  kind: "node",
64671
65127
  runtime: command,
64672
- entry: path30.isAbsolute(args[0]) ? args[0] : path30.resolve(path30.dirname(res.cursorMcp), args[0])
65128
+ entry: path31.isAbsolute(args[0]) ? args[0] : path31.resolve(path31.dirname(res.cursorMcp), args[0])
64673
65129
  };
64674
65130
  }, canonicalCursorHook, isCanonicalCursorHook = (command, res) => command === canonicalCursorHook || command === cursorHooksEntry(res.cursorPluginDir).command, registeredCursorHook = (res) => {
64675
- const hooksFile = path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
64676
- if (!existsSync13(hooksFile))
65131
+ const hooksFile = path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65132
+ if (!existsSync14(hooksFile))
64677
65133
  return "invalid";
64678
65134
  const config = readJson(hooksFile);
64679
65135
  if (!config)
@@ -64690,9 +65146,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64690
65146
  if (!m)
64691
65147
  return false;
64692
65148
  const entry = m[1].trim();
64693
- if (!path30.isAbsolute(entry))
65149
+ if (!path31.isAbsolute(entry))
64694
65150
  return false;
64695
- if (path30.resolve(entry) !== path30.resolve(path30.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
65151
+ if (path31.resolve(entry) !== path31.resolve(path31.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
64696
65152
  return false;
64697
65153
  }
64698
65154
  return validNodeEntry(entry, "node", res.env);
@@ -64701,7 +65157,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64701
65157
  const hosts = hostsFor(res.host);
64702
65158
  const registered = hosts.includes("cursor") ? registeredCursorLauncher(res) : null;
64703
65159
  const runtime = registered && registered !== "invalid" && registered.kind === "node" ? registered.runtime : "node";
64704
- const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) => path30.join(res.cursorPluginDir, rel)).filter((p) => !validNodeEntry(p, runtime, res.env)).map((p) => `cursor: ${p}`) : [];
65160
+ const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) => path31.join(res.cursorPluginDir, rel)).filter((p) => !validNodeEntry(p, runtime, res.env)).map((p) => `cursor: ${p}`) : [];
64705
65161
  if (hosts.includes("cursor")) {
64706
65162
  if (registered === "invalid") {
64707
65163
  missing.push(`cursor: canonical workit MCP launcher in ${res.cursorMcp}`);
@@ -64710,13 +65166,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64710
65166
  }
64711
65167
  const hook = registeredCursorHook(res);
64712
65168
  if (hook === "invalid") {
64713
- missing.push(`cursor: canonical session-start hook in ${path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
65169
+ missing.push(`cursor: canonical session-start hook in ${path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
64714
65170
  } else if (hook !== null && !isCanonicalCursorHook(hook, res) && !validLocalDistHook(hook, res)) {
64715
- missing.push(`cursor: canonical session-start hook in ${path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
65171
+ missing.push(`cursor: canonical session-start hook in ${path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
64716
65172
  }
64717
65173
  }
64718
65174
  if (dev) {
64719
- missing.push(...hosts.filter((h) => h !== "cursor").flatMap((h) => launcherSlotsFor(h, dev).filter((slot) => !slot.some((p) => existsSync13(p))).map((slot) => `${h}: ${slot.join(" or ")}`)));
65175
+ missing.push(...hosts.filter((h) => h !== "cursor").flatMap((h) => launcherSlotsFor(h, dev).filter((slot) => !slot.some((p) => existsSync14(p))).map((slot) => `${h}: ${slot.join(" or ")}`)));
64720
65176
  }
64721
65177
  if (missing.length > 0) {
64722
65178
  return {
@@ -64741,21 +65197,21 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64741
65197
  }, CURSOR_HOOK_FIX = "Run `workit init` and select Cursor to install the bundled hook", checkCursorHook = (res) => {
64742
65198
  if (!hostsFor(res.host).includes("cursor"))
64743
65199
  return { id: "cursor_hook", status: "pass", detail: "cursor hook not inspected on this host" };
64744
- if (!existsSync13(res.cursorPluginDir))
65200
+ if (!existsSync14(res.cursorPluginDir))
64745
65201
  return {
64746
65202
  id: "cursor_hook",
64747
65203
  status: "pass",
64748
65204
  detail: "no Cursor plugin install — hook launcher not inspected"
64749
65205
  };
64750
- const launcher = path30.join(res.cursorPluginDir, "hooks", "launch.mjs");
64751
- if (!existsSync13(launcher))
65206
+ const launcher = path31.join(res.cursorPluginDir, "hooks", "launch.mjs");
65207
+ if (!existsSync14(launcher))
64752
65208
  return {
64753
65209
  id: "cursor_hook",
64754
65210
  status: "warn",
64755
65211
  detail: `cursor hook launcher mode: missing — ${launcher} is absent (an install from before the pinned launcher)`,
64756
65212
  fix: CURSOR_HOOK_FIX
64757
65213
  };
64758
- const hooksFile = path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65214
+ const hooksFile = path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
64759
65215
  const registered = readJson(hooksFile);
64760
65216
  const sessionCommand = registered?.hooks?.sessionStart?.[0]?.command;
64761
65217
  const offLauncher = [
@@ -64770,7 +65226,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64770
65226
  fix: "Re-run install-cursor-plugin.sh (or `workit init` with Cursor) to rewrite the hook entries"
64771
65227
  };
64772
65228
  const started = performance.now();
64773
- const probe = spawnSync13("node", [launcher, "--probe", "workit-cursor-hook"], {
65229
+ const probe = spawnSync14("node", [launcher, "--probe", "workit-cursor-hook"], {
64774
65230
  encoding: "utf8",
64775
65231
  env: { ...res.env, WORKIT_CURSOR_HOOK_NPX_OFFLINE: "1" },
64776
65232
  timeout: 40000,
@@ -64817,15 +65273,15 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64817
65273
  return { id: "cursor_hook", status: "pass", detail: `${label}; probe ${ms} ms` };
64818
65274
  }, SILENT_HOOK_GRACE_MS, mtimeMs = (file) => {
64819
65275
  try {
64820
- return statSync4(file).mtimeMs;
65276
+ return statSync5(file).mtimeMs;
64821
65277
  } catch {
64822
65278
  return null;
64823
65279
  }
64824
65280
  }, silentCursorHooks = (res) => {
64825
- const session = mtimeMs(path30.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
65281
+ const session = mtimeMs(path31.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
64826
65282
  if (session === null || Date.now() - session < SILENT_HOOK_GRACE_MS)
64827
65283
  return null;
64828
- const hook = mtimeMs(path30.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
65284
+ const hook = mtimeMs(path31.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
64829
65285
  if (hook !== null && hook >= session)
64830
65286
  return null;
64831
65287
  return `Cursor started the Workit MCP server at ${new Date(session).toISOString()} but no Workit hook has run since${hook === null ? " (none ever)" : ""} — if the agent ran in that window, Cursor is not running the plugin hooks and Workit enforcement is off`;
@@ -64866,7 +65322,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64866
65322
  return "fragile-cache";
64867
65323
  if (entry.startsWith("file:")) {
64868
65324
  const target = entry.replace(/^file:\/\//, "").replace(/^file:/, "");
64869
- return existsSync13(target) ? "ok" : "missing-file";
65325
+ return existsSync14(target) ? "ok" : "missing-file";
64870
65326
  }
64871
65327
  return "ok";
64872
65328
  }, checkStalePin = (res) => {
@@ -64877,7 +65333,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64877
65333
  detail: "opencode pin not inspected on the cursor host"
64878
65334
  };
64879
65335
  }
64880
- if (!existsSync13(res.opencodeConfig)) {
65336
+ if (!existsSync14(res.opencodeConfig)) {
64881
65337
  return {
64882
65338
  id: "stale_pin",
64883
65339
  status: "pass",
@@ -64921,8 +65377,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64921
65377
  }
64922
65378
  return { id: "stale_pin", status: "pass", detail: "opencode pin resolves" };
64923
65379
  }, pluginMcpSelectors = (res) => {
64924
- const p = path30.join(res.cursorPluginDir, "mcp.json");
64925
- if (!existsSync13(p))
65380
+ const p = path31.join(res.cursorPluginDir, "mcp.json");
65381
+ if (!existsSync14(p))
64926
65382
  return null;
64927
65383
  const cfg = readJson(p);
64928
65384
  const server = cfg?.mcpServers?.workit;
@@ -64944,7 +65400,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64944
65400
  if (!cmd)
64945
65401
  return null;
64946
65402
  try {
64947
- const r = spawnSync13(cmd, ["view", pkgName, "version"], {
65403
+ const r = spawnSync14(cmd, ["view", pkgName, "version"], {
64948
65404
  encoding: "utf8",
64949
65405
  timeout: 20000,
64950
65406
  env: res.env
@@ -64955,13 +65411,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64955
65411
  return null;
64956
65412
  }
64957
65413
  }, installedPluginVersion = (res) => {
64958
- const pkg = readJson(path30.join(res.cursorPluginDir, "package.json"));
65414
+ const pkg = readJson(path31.join(res.cursorPluginDir, "package.json"));
64959
65415
  return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
64960
65416
  }, installedOpenCodeCacheVersion = (res) => {
64961
- const pkg = readJson(path30.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
65417
+ const pkg = readJson(path31.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
64962
65418
  return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
64963
65419
  }, checkOpenCodePackageCache = (res) => {
64964
- if (!existsSync13(res.opencodeConfig)) {
65420
+ if (!existsSync14(res.opencodeConfig)) {
64965
65421
  return {
64966
65422
  id: "stale_install",
64967
65423
  status: "pass",
@@ -65035,7 +65491,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65035
65491
  detail: "cursor plugin not inspected on this host"
65036
65492
  };
65037
65493
  }
65038
- if (existsSync13(res.cursorPluginDir) && lstatSync4(res.cursorPluginDir).isSymbolicLink()) {
65494
+ if (existsSync14(res.cursorPluginDir) && lstatSync5(res.cursorPluginDir).isSymbolicLink()) {
65039
65495
  let target = "";
65040
65496
  try {
65041
65497
  target = readlinkSync2(res.cursorPluginDir);
@@ -65061,7 +65517,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65061
65517
  fix: "Re-run install-cursor-plugin.sh — it rewrites the workit MCP entry to the canonical @latest selector"
65062
65518
  };
65063
65519
  }
65064
- const hooksFile = path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65520
+ const hooksFile = path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65065
65521
  const hookCmd = readJson(hooksFile)?.hooks?.sessionStart?.[0]?.command ?? null;
65066
65522
  const canonicalHook = canonicalCursorHook;
65067
65523
  const staleHook = hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && !hookCmd.startsWith("node ") && hookCmd.includes("@brainervirus/workit-cursor@");
@@ -65073,7 +65529,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65073
65529
  fix: "Re-run install-cursor-plugin.sh — it rewrites the sessionStart hook to the canonical pinned launcher"
65074
65530
  };
65075
65531
  }
65076
- const hookDrift = existsSync13(hooksFile) ? cursorHookDrift(readJson(hooksFile), res.cursorPluginDir) : [];
65532
+ const hookDrift = existsSync14(hooksFile) ? cursorHookDrift(readJson(hooksFile), res.cursorPluginDir) : [];
65077
65533
  if (hookDrift.length > 0) {
65078
65534
  return {
65079
65535
  id: "stale_install",
@@ -65083,7 +65539,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65083
65539
  };
65084
65540
  }
65085
65541
  const installed = installedPluginVersion(res);
65086
- const source = res.dev ? readJson(path30.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path30.join(packageRoot(), "package.json"))?.version ?? null;
65542
+ const source = res.dev ? readJson(path31.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path31.join(packageRoot(), "package.json"))?.version ?? null;
65087
65543
  const localDist = mcpSelectors === null && hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && hookCmd.startsWith("node ");
65088
65544
  if (localDist && installed !== null && source !== null && !semverAtLeast(installed, source)) {
65089
65545
  return {
@@ -65095,7 +65551,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65095
65551
  }
65096
65552
  if (localDist && installed !== null && source !== null && res.dev) {
65097
65553
  const entry = /^node\s+(.+)$/.exec((hookCmd ?? "").trim())?.[1]?.trim() ?? "";
65098
- const sourceBundle = entry && path30.isAbsolute(entry) ? path30.join(res.dev, "packages", "workit-cursor", "dist", path30.basename(entry)) : "";
65554
+ const sourceBundle = entry && path31.isAbsolute(entry) ? path31.join(res.dev, "packages", "workit-cursor", "dist", path31.basename(entry)) : "";
65099
65555
  if (entry && sourceBundle) {
65100
65556
  const installedHash = bundleHashOfFile(entry);
65101
65557
  const sourceHash = bundleHashOfFile(sourceBundle);
@@ -65103,7 +65559,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65103
65559
  return {
65104
65560
  id: "stale_install",
65105
65561
  status: "fail",
65106
- detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path30.basename(entry)} hash mismatch)`,
65562
+ detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path31.basename(entry)} hash mismatch)`,
65107
65563
  fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory with the current build"
65108
65564
  };
65109
65565
  }
@@ -65157,13 +65613,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65157
65613
  const problems = [];
65158
65614
  const opencodeHost = res.host !== "cursor";
65159
65615
  const cursorHost = res.host !== "opencode";
65160
- if (opencodeHost && existsSync13(res.opencodeConfig)) {
65616
+ if (opencodeHost && existsSync14(res.opencodeConfig)) {
65161
65617
  const cfg = readJson(res.opencodeConfig);
65162
65618
  const entries = pluginEntries(cfg);
65163
65619
  if (entries.length > 1)
65164
65620
  problems.push(`opencode registers ${entries.length} workit plugin entries`);
65165
65621
  }
65166
- if (cursorHost && existsSync13(res.cursorSettings)) {
65622
+ if (cursorHost && existsSync14(res.cursorSettings)) {
65167
65623
  const settings = readJson(res.cursorSettings);
65168
65624
  const enabled = settings?.enabled_plugins;
65169
65625
  if (enabled && typeof enabled === "object") {
@@ -65178,7 +65634,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65178
65634
  if (dirs.length > 1)
65179
65635
  problems.push(`cursor plugin_dirs has ${dirs.length} workit entries`);
65180
65636
  }
65181
- if (cursorHost && existsSync13(res.cursorMcp)) {
65637
+ if (cursorHost && existsSync14(res.cursorMcp)) {
65182
65638
  const mcp = readJson(res.cursorMcp);
65183
65639
  const servers = mcp?.mcpServers ?? {};
65184
65640
  if (servers && typeof servers === "object") {
@@ -65203,17 +65659,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65203
65659
  }, checkMalformedConfig = (res) => {
65204
65660
  const files = [];
65205
65661
  for (const name of ["config.json", "youtrack.json", "vcs.json", "workspaces.json"]) {
65206
- const p = path30.join(res.configDir, name);
65207
- if (existsSync13(p))
65662
+ const p = path31.join(res.configDir, name);
65663
+ if (existsSync14(p))
65208
65664
  files.push(p);
65209
65665
  }
65210
65666
  const opencodeHost = res.host !== "cursor";
65211
65667
  const cursorHost = res.host !== "opencode";
65212
- if (opencodeHost && existsSync13(res.opencodeConfig))
65668
+ if (opencodeHost && existsSync14(res.opencodeConfig))
65213
65669
  files.push(res.opencodeConfig);
65214
- if (cursorHost && existsSync13(res.cursorSettings))
65670
+ if (cursorHost && existsSync14(res.cursorSettings))
65215
65671
  files.push(res.cursorSettings);
65216
- if (cursorHost && existsSync13(res.cursorMcp))
65672
+ if (cursorHost && existsSync14(res.cursorMcp))
65217
65673
  files.push(res.cursorMcp);
65218
65674
  const bad = files.filter((p) => !parsesAsConfigObject(p));
65219
65675
  if (bad.length === 0)
@@ -65292,19 +65748,90 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65292
65748
  detail: `current directory ${res.cwd} does not match any configured workspace`,
65293
65749
  fix: `Add a workspace glob matching this directory to ${file} or run /wk-init`
65294
65750
  };
65751
+ }, matchedWorkspace = (res) => {
65752
+ try {
65753
+ return resolveWorkspaceFrom(res.cwd, res.configDir, res.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
65754
+ } catch {
65755
+ return null;
65756
+ }
65757
+ }, SESSION_HOOK_FIX = "workit doctor --fix", sessionHookFinding = (hook) => {
65758
+ const slot = `${SESSION_HOOK_NAME} hook`;
65759
+ switch (hook.state) {
65760
+ case "not_git":
65761
+ return { status: "pass", detail: `not inside a git work tree — skipping the ${slot}` };
65762
+ case "installed":
65763
+ return {
65764
+ status: "pass",
65765
+ detail: `${slot} ${hook.hookPath} adds the Workit-Session trailer to plain git commits`
65766
+ };
65767
+ case "manual":
65768
+ return hook.helperCurrent ? {
65769
+ status: "pass",
65770
+ detail: `${hook.manualTarget} adds the Workit-Session trailer to plain git commits (via ${hook.via})`
65771
+ } : {
65772
+ status: "warn",
65773
+ detail: `${hook.manualTarget} runs the Workit helper (via ${hook.via}), but ${hook.helperPath} is missing or an older version`,
65774
+ fix: SESSION_HOOK_FIX
65775
+ };
65776
+ case "missing":
65777
+ return {
65778
+ status: "warn",
65779
+ detail: `no ${slot} at ${hook.hookPath}: plain git commits from an agent session get no Workit-Session trailer`,
65780
+ fix: SESSION_HOOK_FIX
65781
+ };
65782
+ case "outdated":
65783
+ return {
65784
+ status: "warn",
65785
+ detail: `the Workit ${slot} at ${hook.hookPath} or its helper ${hook.helperPath} is missing or an older version`,
65786
+ fix: SESSION_HOOK_FIX
65787
+ };
65788
+ case "blocked":
65789
+ return {
65790
+ status: "warn",
65791
+ detail: `plain git commits get no Workit-Session trailer and --fix will not install the ${slot}: ${hook.reason}`,
65792
+ fix: hook.helperCurrent ? hook.manual : `${SESSION_HOOK_FIX} (writes ${hook.helperPath}), then ${hook.manual}`
65793
+ };
65794
+ }
65795
+ }, checkSessionHook = (res) => {
65796
+ if (!matchedWorkspace(res))
65797
+ return {
65798
+ id: "session_hook",
65799
+ status: "pass",
65800
+ detail: `not in a Workit workspace — skipping the ${SESSION_HOOK_NAME} session hook`
65801
+ };
65802
+ const hook = inspectSessionHook(res.cwd, res.env);
65803
+ const finding = sessionHookFinding(hook);
65804
+ if (!hook.leftover)
65805
+ return { id: "session_hook", ...finding };
65806
+ return {
65807
+ id: "session_hook",
65808
+ status: "warn",
65809
+ detail: `${finding.detail}; ${hook.leftover}`,
65810
+ fix: finding.fix ? `${finding.fix}; ${hook.leftover}` : hook.leftover
65811
+ };
65812
+ }, fixSessionHook = (options = {}) => {
65813
+ const res = resolve(options);
65814
+ const workspace = matchedWorkspace(res);
65815
+ if (!workspace)
65816
+ return {
65817
+ action: "skipped",
65818
+ detail: `${res.cwd} is not in a Workit workspace; no hook installed`,
65819
+ workspace: null
65820
+ };
65821
+ return { ...installSessionHook(res.cwd, res.env), workspace: workspace.name };
65295
65822
  }, isPlaceholder = (p) => {
65296
65823
  try {
65297
- return readFileSync13(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
65824
+ return readFileSync14(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
65298
65825
  } catch {
65299
65826
  return false;
65300
65827
  }
65301
65828
  }, checkCredentialMetadata = (res) => {
65302
65829
  const tokenPaths = [];
65303
- const youtrackJson = readJson(path30.join(res.configDir, "youtrack.json"));
65830
+ const youtrackJson = readJson(path31.join(res.configDir, "youtrack.json"));
65304
65831
  if (youtrackJson) {
65305
- tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path30.join(res.configDir, "youtrack.token"));
65306
- } else if (existsSync13(path30.join(res.configDir, "youtrack.token"))) {
65307
- tokenPaths.push(path30.join(res.configDir, "youtrack.token"));
65832
+ tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path31.join(res.configDir, "youtrack.token"));
65833
+ } else if (existsSync14(path31.join(res.configDir, "youtrack.token"))) {
65834
+ tokenPaths.push(path31.join(res.configDir, "youtrack.token"));
65308
65835
  }
65309
65836
  if (tokenPaths.length === 0) {
65310
65837
  return {
@@ -65315,13 +65842,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65315
65842
  }
65316
65843
  const problems = [];
65317
65844
  for (const raw of tokenPaths) {
65318
- const p = path30.isAbsolute(raw) ? raw : path30.resolve(res.configDir, raw);
65319
- if (!existsSync13(p)) {
65845
+ const p = path31.isAbsolute(raw) ? raw : path31.resolve(res.configDir, raw);
65846
+ if (!existsSync14(p)) {
65320
65847
  problems.push(`${p} is missing`);
65321
65848
  continue;
65322
65849
  }
65323
65850
  if (process.platform !== "win32") {
65324
- const mode = statSync4(p).mode & 511;
65851
+ const mode = statSync5(p).mode & 511;
65325
65852
  if (mode !== 384)
65326
65853
  problems.push(`${p} must be mode 0600 (found ${mode.toString(8)})`);
65327
65854
  }
@@ -65383,7 +65910,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65383
65910
  }, defaultIdentityProbes = (env) => ({
65384
65911
  origin: (cwd) => {
65385
65912
  try {
65386
- const out = spawnSync13("git", ["remote", "get-url", "origin"], {
65913
+ const out = spawnSync14("git", ["remote", "get-url", "origin"], {
65387
65914
  cwd,
65388
65915
  encoding: "utf8",
65389
65916
  env
@@ -65395,7 +65922,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65395
65922
  },
65396
65923
  ghLogin: (e) => {
65397
65924
  try {
65398
- const out = spawnSync13("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
65925
+ const out = spawnSync14("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
65399
65926
  const login = (out.stdout ?? "").trim();
65400
65927
  return out.status === 0 && login ? login : null;
65401
65928
  } catch {
@@ -65404,7 +65931,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65404
65931
  },
65405
65932
  glabLogin: (e) => {
65406
65933
  try {
65407
- const out = spawnSync13("glab", ["api", "user", "--jq", ".username"], {
65934
+ const out = spawnSync14("glab", ["api", "user", "--jq", ".username"], {
65408
65935
  encoding: "utf8",
65409
65936
  env: e
65410
65937
  });
@@ -65416,7 +65943,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65416
65943
  },
65417
65944
  sshLogin: (host) => {
65418
65945
  try {
65419
- const out = spawnSync13("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
65946
+ const out = spawnSync14("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
65420
65947
  encoding: "utf8",
65421
65948
  env
65422
65949
  });
@@ -65431,7 +65958,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65431
65958
  const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
65432
65959
  if (!remoteHost)
65433
65960
  return { id: "github_identity", status: "pass", detail: "no GitHub remote configured" };
65434
- const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path30.join(res.configDir, "vcs.json"));
65961
+ const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path31.join(res.configDir, "vcs.json"));
65435
65962
  const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
65436
65963
  if (workspace.error)
65437
65964
  return {
@@ -65459,7 +65986,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65459
65986
  const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
65460
65987
  if (!remoteHost)
65461
65988
  return { id: "gitlab_identity", status: "pass", detail: "no GitLab remote configured" };
65462
- const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path30.join(res.configDir, "vcs.json"));
65989
+ const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path31.join(res.configDir, "vcs.json"));
65463
65990
  const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
65464
65991
  if (workspace.error)
65465
65992
  return {
@@ -65485,11 +66012,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65485
66012
  { surface: "SSH", login: probes.sshLogin(remoteHost) }
65486
66013
  ]);
65487
66014
  }, checkLogWritable = (res) => {
65488
- const logsDir = path30.join(res.stateDir, "logs");
65489
- const probe = path30.join(logsDir, "doctor-probe.tmp");
66015
+ const logsDir = path31.join(res.stateDir, "logs");
66016
+ const probe = path31.join(logsDir, "doctor-probe.tmp");
65490
66017
  try {
65491
- mkdirSync5(logsDir, { recursive: true, mode: 448 });
65492
- writeFileSync6(probe, `{"probe":true}
66018
+ mkdirSync6(logsDir, { recursive: true, mode: 448 });
66019
+ writeFileSync7(probe, `{"probe":true}
65493
66020
  `, { mode: 384 });
65494
66021
  return {
65495
66022
  id: "log_writable",
@@ -65576,7 +66103,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65576
66103
  }, CLI_PACKAGE = "@brainervirus/workit-cli", SEMVER, WORKIT_PROBE_TIMEOUT_MS = 1e4, workitVersions, workitVersionAt = (bin, env) => {
65577
66104
  let key;
65578
66105
  try {
65579
- const st = statSync4(bin);
66106
+ const st = statSync5(bin);
65580
66107
  key = `${bin}:${st.ino}:${st.size}:${st.mtimeMs}`;
65581
66108
  } catch {
65582
66109
  return { version: null, timedOut: false };
@@ -65585,12 +66112,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65585
66112
  if (cached)
65586
66113
  return cached;
65587
66114
  const viaShell = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(bin);
65588
- const r = viaShell ? spawnSync13(`"${bin}" --version`, {
66115
+ const r = viaShell ? spawnSync14(`"${bin}" --version`, {
65589
66116
  encoding: "utf8",
65590
66117
  env,
65591
66118
  timeout: WORKIT_PROBE_TIMEOUT_MS,
65592
66119
  shell: true
65593
- }) : spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
66120
+ }) : spawnSync14(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
65594
66121
  const timedOut = r.error !== undefined && "code" in r.error && r.error.code === "ETIMEDOUT";
65595
66122
  const version = r.status === 0 ? (r.stdout ?? "").match(SEMVER)?.[0] ?? null : null;
65596
66123
  const probe = { version: timedOut ? null : version, timedOut };
@@ -65680,6 +66207,7 @@ var init_doctor = __esm(() => {
65680
66207
  init_workspaces();
65681
66208
  init_host_install();
65682
66209
  init_skill_manifests();
66210
+ init_session_hook();
65683
66211
  OPENCODE_V1_FIX = `OpenCode < ${SUPPORT_MATRIX.opencode.minimum} cannot load Workit 3 (V2 plugin API only): upgrade OpenCode to ${SUPPORT_MATRIX.opencode.minimum}+, or stay on Workit 2.x by pinning "@brainervirus/workit-opencode@2" in the opencode.json "plugin" array`;
65684
66212
  AGENT_MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
65685
66213
  canonicalCursorHook = cursorHooksEntry().command;
@@ -65706,6 +66234,7 @@ var init_doctor = __esm(() => {
65706
66234
  checkMalformedConfig,
65707
66235
  checkWorkspaceMismatch,
65708
66236
  checkWorkspaceLock,
66237
+ checkSessionHook,
65709
66238
  checkCredentialMetadata,
65710
66239
  checkGithubIdentity,
65711
66240
  checkGitLabIdentity,
@@ -65727,20 +66256,20 @@ var init_doctor = __esm(() => {
65727
66256
  import {
65728
66257
  copyFileSync as copyFileSync4,
65729
66258
  cpSync as cpSync4,
65730
- existsSync as existsSync14,
65731
- lstatSync as lstatSync5,
65732
- mkdirSync as mkdirSync6,
66259
+ existsSync as existsSync15,
66260
+ lstatSync as lstatSync6,
66261
+ mkdirSync as mkdirSync7,
65733
66262
  mkdtempSync as mkdtempSync2,
65734
- readFileSync as readFileSync14,
66263
+ readFileSync as readFileSync15,
65735
66264
  readdirSync as readdirSync8,
65736
66265
  realpathSync as realpathSync7,
65737
- renameSync as renameSync3,
65738
- rmSync as rmSync3,
65739
- statSync as statSync5,
65740
- writeFileSync as writeFileSync7
66266
+ renameSync as renameSync4,
66267
+ rmSync as rmSync4,
66268
+ statSync as statSync6,
66269
+ writeFileSync as writeFileSync8
65741
66270
  } from "node:fs";
65742
66271
  import os9 from "node:os";
65743
- import path31 from "node:path";
66272
+ import path32 from "node:path";
65744
66273
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
65745
66274
  function parseList(raw) {
65746
66275
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -65784,7 +66313,7 @@ function buildSetupPreview(values, opts = {}) {
65784
66313
  const current = readConfigFromDir(state.configDir);
65785
66314
  mutations.push({
65786
66315
  type: "merge-json",
65787
- path: path31.join(state.configDir, "config.json"),
66316
+ path: path32.join(state.configDir, "config.json"),
65788
66317
  value: {
65789
66318
  locale: values.locale,
65790
66319
  localeOptions: current.localeOptions,
@@ -65810,17 +66339,17 @@ function buildSetupPreview(values, opts = {}) {
65810
66339
  if (blocked.length === 0) {
65811
66340
  mutations.push({
65812
66341
  type: "update-workspaces",
65813
- path: path31.join(state.configDir, "workspaces.json"),
66342
+ path: path32.join(state.configDir, "workspaces.json"),
65814
66343
  entries: values.workspaces,
65815
66344
  expectedRevision: diskWorkspaceResult.revision
65816
66345
  });
65817
66346
  }
65818
66347
  }
65819
66348
  if (values.baseUrl.trim()) {
65820
- const ytPath = path31.join(state.configDir, "youtrack.json");
66349
+ const ytPath = path32.join(state.configDir, "youtrack.json");
65821
66350
  const ytExisting = readConfigRecord(ytPath);
65822
66351
  const ytConfigured = configuredTokenPath(ytExisting, "tokenFile");
65823
- const tokenPath = resolveTokenPath(ytConfigured, path31.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
66352
+ const tokenPath = resolveTokenPath(ytConfigured, path32.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
65824
66353
  const draft = youtrackDraft(values, tokenPath);
65825
66354
  if (ytConfigured !== null && tokenPath !== ytConfigured) {
65826
66355
  delete draft.tokenFile;
@@ -65836,7 +66365,7 @@ function buildSetupPreview(values, opts = {}) {
65836
66365
  path: ytPath,
65837
66366
  value: draft
65838
66367
  });
65839
- if (existsSync14(tokenPath))
66368
+ if (existsSync15(tokenPath))
65840
66369
  preserved.push(tokenPath);
65841
66370
  else
65842
66371
  mutations.push({
@@ -65848,7 +66377,7 @@ function buildSetupPreview(values, opts = {}) {
65848
66377
  });
65849
66378
  }
65850
66379
  if (values.vcsProvider !== "skip") {
65851
- const vcsPath = path31.join(state.configDir, "vcs.json");
66380
+ const vcsPath = path32.join(state.configDir, "vcs.json");
65852
66381
  mutations.push({
65853
66382
  type: "merge-json",
65854
66383
  path: vcsPath,
@@ -65877,7 +66406,7 @@ function buildSetupPreview(values, opts = {}) {
65877
66406
  mutations.push({
65878
66407
  type: "install-host",
65879
66408
  platform: "cursor",
65880
- path: path31.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
66409
+ path: path32.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
65881
66410
  commands
65882
66411
  });
65883
66412
  }
@@ -65890,7 +66419,7 @@ function buildSetupPreview(values, opts = {}) {
65890
66419
  mutations.push({
65891
66420
  type: "install-host",
65892
66421
  platform,
65893
- path: platform === "claude-code" ? claudeInstallRecord(paths.home, env) : platform === "codex" ? path31.join(env.CODEX_HOME ?? path31.join(paths.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : path31.join(env.PI_CODING_AGENT_DIR ?? path31.join(paths.home, ".pi", "agent"), "settings.json"),
66422
+ path: platform === "claude-code" ? claudeInstallRecord(paths.home, env) : platform === "codex" ? path32.join(env.CODEX_HOME ?? path32.join(paths.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : path32.join(env.PI_CODING_AGENT_DIR ?? path32.join(paths.home, ".pi", "agent"), "settings.json"),
65894
66423
  commands
65895
66424
  });
65896
66425
  }
@@ -65898,9 +66427,9 @@ function buildSetupPreview(values, opts = {}) {
65898
66427
  }
65899
66428
  }
65900
66429
  if (values.applyProject) {
65901
- const root = path31.resolve(opts.cwd ?? process.cwd());
65902
- const gitignorePath = path31.join(root, ".gitignore");
65903
- const existing = existsSync14(gitignorePath) ? readFileSync14(gitignorePath, "utf8") : "";
66430
+ const root = path32.resolve(opts.cwd ?? process.cwd());
66431
+ const gitignorePath = path32.join(root, ".gitignore");
66432
+ const existing = existsSync15(gitignorePath) ? readFileSync15(gitignorePath, "utf8") : "";
65904
66433
  const existingLines = new Set(existing.split(`
65905
66434
  `).map((l) => l.trim()).filter(Boolean));
65906
66435
  const entries = GITIGNORE_ENTRIES.filter((e) => e.trim() !== "" && !existingLines.has(e.trim()));
@@ -65936,10 +66465,10 @@ function deepMerge(base, patch) {
65936
66465
  return out;
65937
66466
  }
65938
66467
  function applyMutation(m) {
65939
- const dir = path31.dirname(m.path);
66468
+ const dir = path32.dirname(m.path);
65940
66469
  switch (m.type) {
65941
66470
  case "create-file": {
65942
- mkdirSync6(dir, { recursive: true });
66471
+ mkdirSync7(dir, { recursive: true });
65943
66472
  const res = writeFileExclusive(m.path, m.content, m.mode);
65944
66473
  return res === "created" ? { platform: "core", file: m.path, status: "Installed" } : {
65945
66474
  platform: "core",
@@ -65962,8 +66491,8 @@ function applyMutation(m) {
65962
66491
  if (existing.kind === "record" && JSON.stringify(existing.value) === JSON.stringify(merged)) {
65963
66492
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
65964
66493
  }
65965
- mkdirSync6(dir, { recursive: true });
65966
- writeFileSync7(m.path, JSON.stringify(merged, null, 2) + `
66494
+ mkdirSync7(dir, { recursive: true });
66495
+ writeFileSync8(m.path, JSON.stringify(merged, null, 2) + `
65967
66496
  `, "utf8");
65968
66497
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
65969
66498
  }
@@ -66004,12 +66533,12 @@ function applyMutation(m) {
66004
66533
  `;
66005
66534
  if (prev === next)
66006
66535
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
66007
- mkdirSync6(dir, { recursive: true });
66536
+ mkdirSync7(dir, { recursive: true });
66008
66537
  const tmp = `${m.path}.${process.pid}.tmp`;
66009
66538
  try {
66010
- writeFileSync7(tmp, next, "utf8");
66539
+ writeFileSync8(tmp, next, "utf8");
66011
66540
  if (workspacesRevision(readFileSafe(m.path)) !== m.expectedRevision) {
66012
- rmSync3(tmp, { force: true });
66541
+ rmSync4(tmp, { force: true });
66013
66542
  return {
66014
66543
  platform: "core",
66015
66544
  file: m.path,
@@ -66017,9 +66546,9 @@ function applyMutation(m) {
66017
66546
  detail: "workspace config changed while applying the preview; rebuild and review it"
66018
66547
  };
66019
66548
  }
66020
- renameSync3(tmp, m.path);
66549
+ renameSync4(tmp, m.path);
66021
66550
  } catch (error) {
66022
- rmSync3(tmp, { force: true });
66551
+ rmSync4(tmp, { force: true });
66023
66552
  return {
66024
66553
  platform: "core",
66025
66554
  file: m.path,
@@ -66042,11 +66571,11 @@ function applyMutation(m) {
66042
66571
  detail: "all entries already present"
66043
66572
  };
66044
66573
  }
66045
- mkdirSync6(dir, { recursive: true });
66574
+ mkdirSync7(dir, { recursive: true });
66046
66575
  const separator = existing && !existing.endsWith(`
66047
66576
  `) ? `
66048
66577
  ` : "";
66049
- writeFileSync7(m.path, existing + separator + (existing ? `
66578
+ writeFileSync8(m.path, existing + separator + (existing ? `
66050
66579
  ` : "") + add.join(`
66051
66580
  `) + `
66052
66581
  `, "utf8");
@@ -66085,8 +66614,8 @@ function applyMutation(m) {
66085
66614
  detail: "already configured"
66086
66615
  };
66087
66616
  }
66088
- mkdirSync6(path31.dirname(m.path), { recursive: true });
66089
- writeFileSync7(m.path, JSON.stringify(merged, null, 2) + `
66617
+ mkdirSync7(path32.dirname(m.path), { recursive: true });
66618
+ writeFileSync8(m.path, JSON.stringify(merged, null, 2) + `
66090
66619
  `, "utf8");
66091
66620
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
66092
66621
  }
@@ -66094,23 +66623,23 @@ function applyMutation(m) {
66094
66623
  }
66095
66624
  function adapterRoot(platform, res, preferInstalledAdapter = false) {
66096
66625
  const candidates = [];
66097
- const stagedCursor = path31.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
66626
+ const stagedCursor = path32.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
66098
66627
  if (platform === "cursor" && preferInstalledAdapter)
66099
66628
  candidates.push(stagedCursor);
66100
66629
  if (res.dev) {
66101
- candidates.push(path31.join(res.dev, "packages", `workit-${platform}`));
66630
+ candidates.push(path32.join(res.dev, "packages", `workit-${platform}`));
66102
66631
  } else {
66103
- candidates.push(path31.join(packageRoot(), "..", `workit-${platform}`));
66104
- let dir = path31.resolve(res.cwd);
66632
+ candidates.push(path32.join(packageRoot(), "..", `workit-${platform}`));
66633
+ let dir = path32.resolve(res.cwd);
66105
66634
  while (true) {
66106
- candidates.push(path31.join(dir, "packages", `workit-${platform}`));
66107
- const parent = path31.dirname(dir);
66635
+ candidates.push(path32.join(dir, "packages", `workit-${platform}`));
66636
+ const parent = path32.dirname(dir);
66108
66637
  if (parent === dir)
66109
66638
  break;
66110
66639
  dir = parent;
66111
66640
  }
66112
66641
  }
66113
- candidates.push(path31.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
66642
+ candidates.push(path32.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
66114
66643
  if (platform === "cursor")
66115
66644
  candidates.push(stagedCursor);
66116
66645
  for (const candidate of candidates) {
@@ -66129,7 +66658,7 @@ function applyOpenCode(root, res) {
66129
66658
  detail: "workit-opencode package has no loadable plugin entry (src/plugin.ts or dist/plugin.js)"
66130
66659
  };
66131
66660
  }
66132
- mkdirSync6(path31.dirname(res.opencodeConfig), { recursive: true });
66661
+ mkdirSync7(path32.dirname(res.opencodeConfig), { recursive: true });
66133
66662
  const existing = readExisting(res.opencodeConfig);
66134
66663
  if (existing.kind === "malformed") {
66135
66664
  return {
@@ -66158,7 +66687,7 @@ function applyOpenCode(root, res) {
66158
66687
  detail: "already registered"
66159
66688
  };
66160
66689
  }
66161
- writeFileSync7(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
66690
+ writeFileSync8(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
66162
66691
  `, "utf8");
66163
66692
  return existing.kind === "record" ? {
66164
66693
  platform: "opencode",
@@ -66174,54 +66703,54 @@ function applyOpenCode(root, res) {
66174
66703
  }
66175
66704
  function copyPluginDir(src, dest) {
66176
66705
  const realSrc = realpathSync7(src);
66177
- const marker = path31.join(dest, ".workit-root");
66178
- const destIsLink = existsSync14(dest) && lstatSync5(dest).isSymbolicLink();
66706
+ const marker = path32.join(dest, ".workit-root");
66707
+ const destIsLink = existsSync15(dest) && lstatSync6(dest).isSymbolicLink();
66179
66708
  const synced = !destIsLink && readFileSafe(marker)?.trim() === realSrc && samePluginContent(realSrc, dest, dest);
66180
66709
  if (synced)
66181
66710
  return "Skipped";
66182
- const hadDir = existsSync14(dest);
66711
+ const hadDir = existsSync15(dest);
66183
66712
  const rules = preservedCursorRules(realSrc, dest);
66184
- const parent = path31.dirname(dest);
66185
- mkdirSync6(parent, { recursive: true });
66186
- const swap = mkdtempSync2(path31.join(parent, `.${path31.basename(dest)}.swap-`));
66187
- const stage = path31.join(swap, "stage");
66188
- const backup = path31.join(swap, "backup");
66713
+ const parent = path32.dirname(dest);
66714
+ mkdirSync7(parent, { recursive: true });
66715
+ const swap = mkdtempSync2(path32.join(parent, `.${path32.basename(dest)}.swap-`));
66716
+ const stage = path32.join(swap, "stage");
66717
+ const backup = path32.join(swap, "backup");
66189
66718
  try {
66190
66719
  cpSync4(realSrc, stage, {
66191
66720
  recursive: true,
66192
- filter: (entry) => !path31.relative(realSrc, entry).split(path31.sep).includes("node_modules")
66721
+ filter: (entry) => !path32.relative(realSrc, entry).split(path32.sep).includes("node_modules")
66193
66722
  });
66194
- const stagedHooks = path31.join(stage, CURSOR_HOOKS_FILE);
66195
- if (existsSync14(stagedHooks))
66196
- writeFileSync7(stagedHooks, localizedCursorHooks(readFileSync14(stagedHooks), dest));
66723
+ const stagedHooks = path32.join(stage, CURSOR_HOOKS_FILE);
66724
+ if (existsSync15(stagedHooks))
66725
+ writeFileSync8(stagedHooks, localizedCursorHooks(readFileSync15(stagedHooks), dest));
66197
66726
  for (const [name, content] of rules) {
66198
- mkdirSync6(path31.join(stage, "rules"), { recursive: true });
66199
- writeFileSync7(path31.join(stage, "rules", name), content);
66727
+ mkdirSync7(path32.join(stage, "rules"), { recursive: true });
66728
+ writeFileSync8(path32.join(stage, "rules", name), content);
66200
66729
  }
66201
- writeFileSync7(path31.join(stage, ".workit-root"), realSrc + `
66730
+ writeFileSync8(path32.join(stage, ".workit-root"), realSrc + `
66202
66731
  `, "utf8");
66203
66732
  if (!samePluginContent(realSrc, stage, dest))
66204
66733
  throw new Error("staged adapter content is incomplete");
66205
- if (lstatSync5(stage).isSymbolicLink()) {
66734
+ if (lstatSync6(stage).isSymbolicLink()) {
66206
66735
  throw new Error("staged adapter resolved to a symlink — refusing fragile cache install");
66207
66736
  }
66208
66737
  if (hadDir)
66209
- renameSync3(dest, backup);
66738
+ renameSync4(dest, backup);
66210
66739
  try {
66211
- renameSync3(stage, dest);
66740
+ renameSync4(stage, dest);
66212
66741
  } catch (error) {
66213
- if (hadDir && !existsSync14(dest))
66214
- renameSync3(backup, dest);
66742
+ if (hadDir && !existsSync15(dest))
66743
+ renameSync4(backup, dest);
66215
66744
  throw error;
66216
66745
  }
66217
- rmSync3(backup, { recursive: true, force: true });
66746
+ rmSync4(backup, { recursive: true, force: true });
66218
66747
  } finally {
66219
- rmSync3(swap, { recursive: true, force: true });
66748
+ rmSync4(swap, { recursive: true, force: true });
66220
66749
  }
66221
66750
  return hadDir ? "Configured" : "Installed";
66222
66751
  }
66223
66752
  function applyCursorSettings(root, res) {
66224
- mkdirSync6(path31.dirname(res.cursorSettings), { recursive: true });
66753
+ mkdirSync7(path32.dirname(res.cursorSettings), { recursive: true });
66225
66754
  const settingsExisting = readExisting(res.cursorSettings);
66226
66755
  if (settingsExisting.kind === "malformed") {
66227
66756
  return {
@@ -66240,7 +66769,7 @@ function applyCursorSettings(root, res) {
66240
66769
  detail: "already registered"
66241
66770
  };
66242
66771
  }
66243
- writeFileSync7(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
66772
+ writeFileSync8(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
66244
66773
  `, "utf8");
66245
66774
  return {
66246
66775
  platform: "cursor",
@@ -66249,7 +66778,7 @@ function applyCursorSettings(root, res) {
66249
66778
  };
66250
66779
  }
66251
66780
  function applyCursorMcp(root, res) {
66252
- mkdirSync6(path31.dirname(res.cursorMcp), { recursive: true });
66781
+ mkdirSync7(path32.dirname(res.cursorMcp), { recursive: true });
66253
66782
  const mcpExisting = readExisting(res.cursorMcp);
66254
66783
  if (mcpExisting.kind === "malformed") {
66255
66784
  return {
@@ -66279,7 +66808,7 @@ function applyCursorMcp(root, res) {
66279
66808
  detail: "already registered"
66280
66809
  };
66281
66810
  }
66282
- writeFileSync7(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
66811
+ writeFileSync8(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
66283
66812
  `, "utf8");
66284
66813
  return {
66285
66814
  platform: "cursor",
@@ -66288,9 +66817,9 @@ function applyCursorMcp(root, res) {
66288
66817
  };
66289
66818
  }
66290
66819
  function verifyPlatform(platform, res) {
66291
- if (platform === "opencode" && !existsSync14(res.opencodeConfig))
66820
+ if (platform === "opencode" && !existsSync15(res.opencodeConfig))
66292
66821
  return null;
66293
- if (platform === "cursor" && !existsSync14(res.cursorSettings) && !existsSync14(res.cursorMcp)) {
66822
+ if (platform === "cursor" && !existsSync15(res.cursorSettings) && !existsSync15(res.cursorMcp)) {
66294
66823
  return null;
66295
66824
  }
66296
66825
  return runDoctor({
@@ -66355,7 +66884,7 @@ function applySetupPreview(preview, options = {}) {
66355
66884
  let cursorCopyOk = false;
66356
66885
  for (const mutation of preview.mutations) {
66357
66886
  if (mutation.type === "install-host") {
66358
- const expectedPath = mutation.platform === "cursor" ? path31.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap") : mutation.platform === "codex" ? path31.join(res.env.CODEX_HOME ?? path31.join(res.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : mutation.platform === "pi" ? path31.join(res.env.PI_CODING_AGENT_DIR ?? path31.join(res.home, ".pi", "agent"), "settings.json") : mutation.platform === "claude-code" ? claudeInstallRecord(res.home, res.env) : "";
66887
+ const expectedPath = mutation.platform === "cursor" ? path32.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap") : mutation.platform === "codex" ? path32.join(res.env.CODEX_HOME ?? path32.join(res.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : mutation.platform === "pi" ? path32.join(res.env.PI_CODING_AGENT_DIR ?? path32.join(res.home, ".pi", "agent"), "settings.json") : mutation.platform === "claude-code" ? claudeInstallRecord(res.home, res.env) : "";
66359
66888
  if (mutation.path !== expectedPath) {
66360
66889
  entries.push({
66361
66890
  platform: mutation.platform,
@@ -66536,12 +67065,12 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66536
67065
  return configured ?? fallback;
66537
67066
  }, readConfigRecord = (file) => {
66538
67067
  try {
66539
- const value = JSON.parse(readFileSync14(file, "utf8"));
67068
+ const value = JSON.parse(readFileSync15(file, "utf8"));
66540
67069
  return isRecord5(value) ? value : null;
66541
67070
  } catch {
66542
67071
  return null;
66543
67072
  }
66544
- }, claudeInstallRecord = (home, env) => path31.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
67073
+ }, claudeInstallRecord = (home, env) => path32.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
66545
67074
  const env = options.env ?? process.env;
66546
67075
  const home = options.home ?? env.HOME ?? os9.homedir();
66547
67076
  return {
@@ -66549,17 +67078,17 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66549
67078
  configDir: resolvedConfigDir,
66550
67079
  cwd: options.cwd ?? process.cwd(),
66551
67080
  env,
66552
- opencodeConfig: options.opencodeConfig ?? path31.join(home, ".config", "opencode", "opencode.json"),
66553
- cursorSettings: options.cursorSettings ?? path31.join(home, ".cursor", "settings.json"),
66554
- cursorMcp: options.cursorMcp ?? path31.join(home, ".cursor", "mcp.json"),
66555
- cursorPluginDir: options.cursorPluginDir ?? path31.join(home, ".cursor", "plugins", "local", "workit")
67081
+ opencodeConfig: options.opencodeConfig ?? path32.join(home, ".config", "opencode", "opencode.json"),
67082
+ cursorSettings: options.cursorSettings ?? path32.join(home, ".cursor", "settings.json"),
67083
+ cursorMcp: options.cursorMcp ?? path32.join(home, ".cursor", "mcp.json"),
67084
+ cursorPluginDir: options.cursorPluginDir ?? path32.join(home, ".cursor", "plugins", "local", "workit")
66556
67085
  };
66557
67086
  }, isRecord5 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readExisting = (p) => {
66558
67087
  let raw;
66559
67088
  try {
66560
- raw = readFileSync14(p, "utf8");
67089
+ raw = readFileSync15(p, "utf8");
66561
67090
  } catch {
66562
- if (existsSync14(p))
67091
+ if (existsSync15(p))
66563
67092
  return { kind: "malformed", error: `${p} is not readable` };
66564
67093
  return { kind: "missing" };
66565
67094
  }
@@ -66574,7 +67103,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66574
67103
  return { kind: "malformed", error: `${p} is not a JSON object` };
66575
67104
  }, readFileSafe = (p) => {
66576
67105
  try {
66577
- return readFileSync14(p, "utf8");
67106
+ return readFileSync15(p, "utf8");
66578
67107
  } catch {
66579
67108
  return null;
66580
67109
  }
@@ -66589,7 +67118,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66589
67118
  }
66590
67119
  }, isAdapter = (root, platform) => {
66591
67120
  try {
66592
- const pkg = JSON.parse(readFileSync14(path31.join(root, "package.json"), "utf8"));
67121
+ const pkg = JSON.parse(readFileSync15(path32.join(root, "package.json"), "utf8"));
66593
67122
  return pkg.name === `@brainervirus/workit-${platform}`;
66594
67123
  } catch {
66595
67124
  return false;
@@ -66603,8 +67132,8 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66603
67132
  if (!isDevOpenCodeAdapterRoot(root, opts.dev))
66604
67133
  return OPENCODE_NPM_PIN;
66605
67134
  for (const rel of ["src/plugin.ts", "dist/plugin.js"]) {
66606
- const entry = path31.join(root, rel);
66607
- if (existsSync14(entry))
67135
+ const entry = path32.join(root, rel);
67136
+ if (existsSync15(entry))
66608
67137
  return `file://${entry}`;
66609
67138
  }
66610
67139
  return null;
@@ -66621,22 +67150,22 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66621
67150
  for (const entry of readdirSync8(src, { withFileTypes: true })) {
66622
67151
  if (entry.name === "node_modules")
66623
67152
  continue;
66624
- const source = path31.join(src, entry.name);
66625
- const installed = path31.join(dest, entry.name);
66626
- const rel = path31.join(relative, entry.name);
67153
+ const source = path32.join(src, entry.name);
67154
+ const installed = path32.join(dest, entry.name);
67155
+ const rel = path32.join(relative, entry.name);
66627
67156
  if (entry.isDirectory()) {
66628
- if (!statSync5(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
67157
+ if (!statSync6(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
66629
67158
  return false;
66630
67159
  } else {
66631
- const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync14(source), pluginDir) : readFileSync14(source);
66632
- if (!expected.equals(readFileSync14(installed)))
67160
+ const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync15(source), pluginDir) : readFileSync15(source);
67161
+ if (!expected.equals(readFileSync15(installed)))
66633
67162
  return false;
66634
67163
  }
66635
67164
  }
66636
67165
  for (const entry of readdirSync8(dest, { withFileTypes: true })) {
66637
- if (existsSync14(path31.join(src, entry.name)))
67166
+ if (existsSync15(path32.join(src, entry.name)))
66638
67167
  continue;
66639
- const rel = path31.join(relative, entry.name);
67168
+ const rel = path32.join(relative, entry.name);
66640
67169
  if (rel === ".workit-root")
66641
67170
  continue;
66642
67171
  if (relative === "rules" && entry.isFile() && entry.name.endsWith(".mdc"))
@@ -66649,36 +67178,36 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66649
67178
  }
66650
67179
  }, preservedCursorRules = (src, dest) => {
66651
67180
  const preserved = new Map;
66652
- const sourceRules = path31.join(src, "rules");
66653
- const installedRules = path31.join(dest, "rules");
67181
+ const sourceRules = path32.join(src, "rules");
67182
+ const installedRules = path32.join(dest, "rules");
66654
67183
  try {
66655
67184
  for (const entry of readdirSync8(installedRules, { withFileTypes: true })) {
66656
67185
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
66657
67186
  continue;
66658
- if (existsSync14(path31.join(sourceRules, entry.name)))
67187
+ if (existsSync15(path32.join(sourceRules, entry.name)))
66659
67188
  continue;
66660
- preserved.set(entry.name, readFileSync14(path31.join(installedRules, entry.name)));
67189
+ preserved.set(entry.name, readFileSync15(path32.join(installedRules, entry.name)));
66661
67190
  }
66662
67191
  } catch {}
66663
67192
  return preserved;
66664
67193
  }, removeLegacyCursorDir = (res) => {
66665
- const legacy = path31.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
66666
- if (legacy === res.cursorPluginDir || !existsSync14(legacy))
67194
+ const legacy = path32.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
67195
+ if (legacy === res.cursorPluginDir || !existsSync15(legacy))
66667
67196
  return;
66668
- const legacyRules = path31.join(legacy, "rules");
66669
- if (existsSync14(legacyRules)) {
67197
+ const legacyRules = path32.join(legacy, "rules");
67198
+ if (existsSync15(legacyRules)) {
66670
67199
  try {
66671
- mkdirSync6(path31.join(res.cursorPluginDir, "rules"), { recursive: true });
67200
+ mkdirSync7(path32.join(res.cursorPluginDir, "rules"), { recursive: true });
66672
67201
  for (const entry of readdirSync8(legacyRules, { withFileTypes: true })) {
66673
67202
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
66674
67203
  continue;
66675
- const target = path31.join(res.cursorPluginDir, "rules", entry.name);
66676
- if (!existsSync14(target))
66677
- copyFileSync4(path31.join(legacyRules, entry.name), target);
67204
+ const target = path32.join(res.cursorPluginDir, "rules", entry.name);
67205
+ if (!existsSync15(target))
67206
+ copyFileSync4(path32.join(legacyRules, entry.name), target);
66678
67207
  }
66679
67208
  } catch {}
66680
67209
  }
66681
- rmSync3(legacy, { recursive: true, force: true });
67210
+ rmSync4(legacy, { recursive: true, force: true });
66682
67211
  }, failedDetail = (report) => report.checks.filter((c) => c.status === "fail").map((c) => `${c.id}: ${c.detail}`).join("; "), setupCompletionGuidance = () => [
66683
67212
  "Run `workit doctor` to verify your installation.",
66684
67213
  "Run /wk-status in OpenCode to review token setup steps."
@@ -66708,16 +67237,16 @@ var init_setup = __esm(() => {
66708
67237
  { envKey: "WORKFLOW_GITHUB_HOST", affects: "vcs.json github.host" }
66709
67238
  ];
66710
67239
  SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
66711
- CURSOR_HOOKS_FILE = path31.join("hooks", "hooks-cursor.json");
67240
+ CURSOR_HOOKS_FILE = path32.join("hooks", "hooks-cursor.json");
66712
67241
  });
66713
67242
 
66714
67243
  // packages/workit-core/src/core/workspace-branch-policy.ts
66715
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
66716
- import path32 from "node:path";
67244
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
67245
+ import path33 from "node:path";
66717
67246
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
66718
67247
  function applyWorkspaceBranchPolicy(opts) {
66719
67248
  const { workspace_root, env = process.env } = opts;
66720
- const dir = path32.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
67249
+ const dir = path33.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
66721
67250
  const {
66722
67251
  status,
66723
67252
  path: wsPath,
@@ -66729,7 +67258,7 @@ function applyWorkspaceBranchPolicy(opts) {
66729
67258
  if (status === "malformed" || status === "invalid")
66730
67259
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
66731
67260
  const detection = detectBranchPolicy(workspace_root);
66732
- const name = String(env.WORKFLOW_BP_NAME ?? path32.basename(workspace_root));
67261
+ const name = String(env.WORKFLOW_BP_NAME ?? path33.basename(workspace_root));
66733
67262
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
66734
67263
  const policy = {
66735
67264
  preset: detection.preset,
@@ -66778,22 +67307,22 @@ function applyWorkspaceBranchPolicy(opts) {
66778
67307
  ok: false,
66779
67308
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
66780
67309
  };
66781
- mkdirSync7(path32.dirname(wsPath), { recursive: true });
67310
+ mkdirSync8(path33.dirname(wsPath), { recursive: true });
66782
67311
  const tmp = `${wsPath}.${process.pid}.tmp`;
66783
67312
  try {
66784
- writeFileSync8(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
67313
+ writeFileSync9(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
66785
67314
  `, "utf8");
66786
67315
  if (workspacesRevision(readFileSafe2(wsPath)) !== revision) {
66787
- rmSync4(tmp, { force: true });
67316
+ rmSync5(tmp, { force: true });
66788
67317
  return {
66789
67318
  ok: false,
66790
67319
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
66791
67320
  };
66792
67321
  }
66793
- renameSync4(tmp, wsPath);
67322
+ renameSync5(tmp, wsPath);
66794
67323
  } catch (error) {
66795
67324
  try {
66796
- rmSync4(tmp, { force: true });
67325
+ rmSync5(tmp, { force: true });
66797
67326
  } catch {}
66798
67327
  return {
66799
67328
  ok: false,
@@ -66810,7 +67339,7 @@ function applyWorkspaceBranchPolicy(opts) {
66810
67339
  }
66811
67340
  var readFileSafe2 = (p) => {
66812
67341
  try {
66813
- return readFileSync15(p, "utf8");
67342
+ return readFileSync16(p, "utf8");
66814
67343
  } catch {
66815
67344
  return null;
66816
67345
  }
@@ -67062,8 +67591,8 @@ var init_logic = __esm(() => {
67062
67591
  });
67063
67592
 
67064
67593
  // packages/workit-cli/src/wizard-state.ts
67065
- import { existsSync as existsSync15, statSync as statSync6 } from "node:fs";
67066
- import path33 from "node:path";
67594
+ import { existsSync as existsSync16, statSync as statSync7 } from "node:fs";
67595
+ import path34 from "node:path";
67067
67596
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
67068
67597
  function resolveBasePath(values, env = process.env) {
67069
67598
  return env.WORKFLOW_WORKSPACE_ROOT?.trim() || values.basePath.trim();
@@ -67582,7 +68111,7 @@ function workspaceDraftText(draft, field, value) {
67582
68111
  }
67583
68112
  var NEXT, PREV, skipsCustomBranch = (screen, preset) => (screen === "branchAllowed" || screen === "branchProtected") && preset !== "custom", skipsYoutrack = (screen, tracker) => screen === "youtrack" && tracker !== "youtrack", skipsBasePath = (screen, basePath) => screen === "basePath" && basePath !== "", isDirectory = (p) => {
67584
68113
  try {
67585
- return statSync6(p).isDirectory();
68114
+ return statSync7(p).isDirectory();
67586
68115
  } catch {
67587
68116
  return false;
67588
68117
  }
@@ -67590,8 +68119,8 @@ var NEXT, PREV, skipsCustomBranch = (screen, preset) => (screen === "branchAllow
67590
68119
  const p = value.trim();
67591
68120
  if (!p)
67592
68121
  return "workspace root path is required";
67593
- return !path33.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
67594
- }, skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync15(path33.join(basePath, ".git")), decodeBranchPreset = (value, fallback) => value === "gitflow" || value === "github-flow" || value === "trunk-based" || value === "custom" ? value : fallback, decodeVcsProvider = (value, fallback) => value === "gitlab" || value === "github" || value === "skip" ? value : fallback, decodeIssueTracker = (value, fallback) => value === "youtrack" || value === "github" || value === "gitlab" || value === "none" ? value : fallback, GITHUB_ISSUES, withGithubIssues = (draft, tracker) => tracker === "github" ? { ...draft, issues: { ...GITHUB_ISSUES } } : draft;
68122
+ return !path34.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
68123
+ }, skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync16(path34.join(basePath, ".git")), decodeBranchPreset = (value, fallback) => value === "gitflow" || value === "github-flow" || value === "trunk-based" || value === "custom" ? value : fallback, decodeVcsProvider = (value, fallback) => value === "gitlab" || value === "github" || value === "skip" ? value : fallback, decodeIssueTracker = (value, fallback) => value === "youtrack" || value === "github" || value === "gitlab" || value === "none" ? value : fallback, GITHUB_ISSUES, withGithubIssues = (draft, tracker) => tracker === "github" ? { ...draft, issues: { ...GITHUB_ISSUES } } : draft;
67595
68124
  var init_wizard_state = __esm(() => {
67596
68125
  init_config();
67597
68126
  init_workspaces();
@@ -67650,9 +68179,9 @@ var init_wizard_state = __esm(() => {
67650
68179
  });
67651
68180
 
67652
68181
  // packages/workit-cli/src/admin/uninstall.ts
67653
- import { existsSync as existsSync16, readFileSync as readFileSync16, realpathSync as realpathSync8, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
68182
+ import { existsSync as existsSync17, readFileSync as readFileSync17, realpathSync as realpathSync8, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "node:fs";
67654
68183
  import os10 from "node:os";
67655
- import path34 from "node:path";
68184
+ import path35 from "node:path";
67656
68185
  function cleanCursorSettings(settings, pluginDir) {
67657
68186
  const next = { ...settings };
67658
68187
  let changed = false;
@@ -67752,7 +68281,7 @@ function planUninstall(paths = {}) {
67752
68281
  const mcpExisting = readJsonRecord(res.cursorMcp);
67753
68282
  const settingsDirty = settingsExisting.kind === "malformed" || settingsExisting.kind === "record" && cleanCursorSettings(settingsExisting.value, res.cursorPluginDir).changed;
67754
68283
  const mcpDirty = mcpExisting.kind === "malformed" || mcpExisting.kind === "record" && cleanCursorMcp(mcpExisting.value).changed;
67755
- const dirExists = existsSync16(res.cursorPluginDir);
68284
+ const dirExists = existsSync17(res.cursorPluginDir);
67756
68285
  const actions = [];
67757
68286
  if (settingsDirty) {
67758
68287
  actions.push({
@@ -67780,7 +68309,7 @@ function planUninstall(paths = {}) {
67780
68309
  installed: settingsDirty || mcpDirty || dirExists,
67781
68310
  actions
67782
68311
  };
67783
- const codexInstalled = existsSync16(res.codexPluginDir);
68312
+ const codexInstalled = existsSync17(res.codexPluginDir);
67784
68313
  const codex = {
67785
68314
  host: "codex",
67786
68315
  installed: codexInstalled,
@@ -67827,17 +68356,17 @@ function applyUninstall(plan, paths = {}) {
67827
68356
  status = outcome.status;
67828
68357
  detail = outcome.detail;
67829
68358
  } else if (action.kind === "remove-dir") {
67830
- const resolved = path34.resolve(action.path);
67831
- const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path34.resolve(res.codexPluginDir) && path34.basename(resolved) === "workit" && path34.basename(path34.dirname(resolved)) === "plugins";
68359
+ const resolved = path35.resolve(action.path);
68360
+ const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path35.resolve(res.codexPluginDir) && path35.basename(resolved) === "workit" && path35.basename(path35.dirname(resolved)) === "plugins";
67832
68361
  if (!allowed) {
67833
68362
  status = "failed";
67834
68363
  detail = `refusing to remove non-canonical plugin directory: ${resolved}`;
67835
- } else if (!existsSync16(resolved)) {
68364
+ } else if (!existsSync17(resolved)) {
67836
68365
  status = "skipped";
67837
68366
  detail = "directory already absent";
67838
68367
  } else {
67839
68368
  try {
67840
- rmSync5(resolved, { recursive: true, force: true });
68369
+ rmSync6(resolved, { recursive: true, force: true });
67841
68370
  status = "removed";
67842
68371
  } catch (error) {
67843
68372
  status = "failed";
@@ -67863,19 +68392,19 @@ function applyUninstall(plan, paths = {}) {
67863
68392
  var resolveUninstallPaths = (options = {}) => {
67864
68393
  const home = options.home ?? options.env?.HOME ?? os10.homedir();
67865
68394
  return {
67866
- opencodeConfig: options.opencodeConfig ?? path34.join(home, ".config", "opencode", "opencode.json"),
67867
- cursorSettings: options.cursorSettings ?? path34.join(home, ".cursor", "settings.json"),
67868
- cursorMcp: options.cursorMcp ?? path34.join(home, ".cursor", "mcp.json"),
67869
- cursorPluginDir: options.cursorPluginDir ?? path34.join(home, ".cursor", "plugins", "local", "workit"),
67870
- codexPluginDir: path34.join(home, ".codex", "plugins", "workit"),
67871
- piConfig: path34.join(home, ".pi", "config.json")
68395
+ opencodeConfig: options.opencodeConfig ?? path35.join(home, ".config", "opencode", "opencode.json"),
68396
+ cursorSettings: options.cursorSettings ?? path35.join(home, ".cursor", "settings.json"),
68397
+ cursorMcp: options.cursorMcp ?? path35.join(home, ".cursor", "mcp.json"),
68398
+ cursorPluginDir: options.cursorPluginDir ?? path35.join(home, ".cursor", "plugins", "local", "workit"),
68399
+ codexPluginDir: path35.join(home, ".codex", "plugins", "workit"),
68400
+ piConfig: path35.join(home, ".pi", "config.json")
67872
68401
  };
67873
68402
  }, isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readJsonRecord = (file) => {
67874
68403
  let raw;
67875
68404
  try {
67876
- raw = readFileSync16(file, "utf8");
68405
+ raw = readFileSync17(file, "utf8");
67877
68406
  } catch {
67878
- if (existsSync16(file))
68407
+ if (existsSync17(file))
67879
68408
  return { kind: "malformed", error: `${file} is not readable` };
67880
68409
  return { kind: "missing" };
67881
68410
  }
@@ -67886,8 +68415,8 @@ var resolveUninstallPaths = (options = {}) => {
67886
68415
  return { kind: "malformed", error: `${file} is not valid JSON` };
67887
68416
  }
67888
68417
  }, stripTrailingSep = (p) => {
67889
- const j = path34.join(p);
67890
- return path34.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
68418
+ const j = path35.join(p);
68419
+ return path35.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
67891
68420
  }, CURSOR_LEGACY_IDENTITIES, jsonCleanerFor = (target, res) => {
67892
68421
  if (target === res.opencodeConfig)
67893
68422
  return cleanOpenCodeConfig;
@@ -67899,8 +68428,8 @@ var resolveUninstallPaths = (options = {}) => {
67899
68428
  return cleanPiConfig;
67900
68429
  return null;
67901
68430
  }, canonicalRemoveDirAllowed = (actionPath, res) => {
67902
- const expected = path34.resolve(res.cursorPluginDir);
67903
- if (!(actionPath === expected && path34.basename(expected) === "workit" && path34.basename(path34.dirname(expected)) === "local" && path34.basename(path34.dirname(path34.dirname(expected))) === "plugins")) {
68431
+ const expected = path35.resolve(res.cursorPluginDir);
68432
+ if (!(actionPath === expected && path35.basename(expected) === "workit" && path35.basename(path35.dirname(expected)) === "local" && path35.basename(path35.dirname(path35.dirname(expected))) === "plugins")) {
67904
68433
  return false;
67905
68434
  }
67906
68435
  try {
@@ -67923,7 +68452,7 @@ var resolveUninstallPaths = (options = {}) => {
67923
68452
  `;
67924
68453
  let current;
67925
68454
  try {
67926
- current = readFileSync16(target, "utf8");
68455
+ current = readFileSync17(target, "utf8");
67927
68456
  } catch (error) {
67928
68457
  return {
67929
68458
  status: "failed",
@@ -67934,7 +68463,7 @@ var resolveUninstallPaths = (options = {}) => {
67934
68463
  return { status: "skipped", detail: "already clean" };
67935
68464
  }
67936
68465
  try {
67937
- writeFileSync9(target, serialized, "utf8");
68466
+ writeFileSync10(target, serialized, "utf8");
67938
68467
  } catch (error) {
67939
68468
  return {
67940
68469
  status: "failed",
@@ -67952,7 +68481,7 @@ var resolveUninstallPaths = (options = {}) => {
67952
68481
  };
67953
68482
  const home = paths.home ?? paths.env?.HOME ?? os10.homedir();
67954
68483
  const env = paths.env ?? process.env;
67955
- const recorded = claudeWorkitInstalls(home, env, action.cwd).some((install) => install.id === id && install.scope === scope && (install.projectPath === null ? action.cwd === null : path34.resolve(install.projectPath) === path34.resolve(action.cwd ?? "")));
68484
+ const recorded = claudeWorkitInstalls(home, env, action.cwd).some((install) => install.id === id && install.scope === scope && (install.projectPath === null ? action.cwd === null : path35.resolve(install.projectPath) === path35.resolve(action.cwd ?? "")));
67956
68485
  if (!recorded)
67957
68486
  return { status: "skipped", detail: `${id} (${scope} scope) is no longer installed` };
67958
68487
  const executable = findHostExecutable("claude", { home, env });
@@ -70431,9 +70960,9 @@ var init_src2 = __esm(async () => {
70431
70960
  // packages/workit-cli/src/verbs/init.ts
70432
70961
  var exports_init = {};
70433
70962
  __export(exports_init, {
70434
- run: () => run2
70963
+ run: () => run3
70435
70964
  });
70436
- async function run2(_argv, io) {
70965
+ async function run3(_argv, io) {
70437
70966
  if (io.json)
70438
70967
  return emit(io, fail("invalid_input", "workit init is an interactive wizard and has no --json output", {
70439
70968
  unblock: "run `workit init` in a terminal without --json"
@@ -70447,19 +70976,19 @@ var init_init = __esm(() => {
70447
70976
  });
70448
70977
 
70449
70978
  // packages/workit-cli/src/upgrade.ts
70450
- import { spawnSync as spawnSync14 } from "node:child_process";
70979
+ import { spawnSync as spawnSync15 } from "node:child_process";
70451
70980
  import {
70452
- existsSync as existsSync17,
70981
+ existsSync as existsSync18,
70453
70982
  mkdtempSync as mkdtempSync3,
70454
- rmSync as rmSync6,
70455
- readFileSync as readFileSync17,
70456
- mkdirSync as mkdirSync8,
70983
+ rmSync as rmSync7,
70984
+ readFileSync as readFileSync18,
70985
+ mkdirSync as mkdirSync9,
70457
70986
  copyFileSync as copyFileSync5,
70458
- chmodSync,
70987
+ chmodSync as chmodSync2,
70459
70988
  cpSync as cpSync5,
70460
- lstatSync as lstatSync6
70989
+ lstatSync as lstatSync7
70461
70990
  } from "node:fs";
70462
- import path35 from "node:path";
70991
+ import path36 from "node:path";
70463
70992
  import os12 from "node:os";
70464
70993
  import { createHash as createHash14 } from "node:crypto";
70465
70994
  function previewUpgrade(hosts = HOSTS2, deps = {}) {
@@ -70483,9 +71012,9 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70483
71012
  for (const file of [
70484
71013
  paths.opencodeConfig,
70485
71014
  paths.opencodeConfig + "c",
70486
- path35.join(home, ".cursor/settings.json"),
70487
- path35.join(home, ".cursor/mcp.json"),
70488
- path35.join(home, ".cursor/plugins/local/workit/package.json"),
71015
+ path36.join(home, ".cursor/settings.json"),
71016
+ path36.join(home, ".cursor/mcp.json"),
71017
+ path36.join(home, ".cursor/plugins/local/workit/package.json"),
70489
71018
  paths.codexConfig,
70490
71019
  paths.piSettings
70491
71020
  ])
@@ -70500,20 +71029,20 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70500
71029
  });
70501
71030
  const installedSources = sources(paths, run, hosts);
70502
71031
  if (installedSources.some((source) => source.host === "opencode")) {
70503
- const scratch = mkdtempSync3(path35.join(os12.tmpdir(), "workit-upgrade-opencode-"));
71032
+ const scratch = mkdtempSync3(path36.join(os12.tmpdir(), "workit-upgrade-opencode-"));
70504
71033
  let probe;
70505
71034
  try {
70506
71035
  probe = runner({
70507
71036
  ...deps,
70508
71037
  env: {
70509
71038
  ...deps.env,
70510
- XDG_DATA_HOME: path35.join(scratch, "data"),
70511
- XDG_STATE_HOME: path35.join(scratch, "state"),
70512
- XDG_CACHE_HOME: path35.join(scratch, "cache")
71039
+ XDG_DATA_HOME: path36.join(scratch, "data"),
71040
+ XDG_STATE_HOME: path36.join(scratch, "state"),
71041
+ XDG_CACHE_HOME: path36.join(scratch, "cache")
70513
71042
  }
70514
71043
  })("opencode", ["--version"]);
70515
71044
  } finally {
70516
- rmSync6(scratch, { recursive: true, force: true });
71045
+ rmSync7(scratch, { recursive: true, force: true });
70517
71046
  }
70518
71047
  const installed = probe.status === 0 ? probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] : undefined;
70519
71048
  if (installed && !versionAtLeast(installed, SUPPORT_MATRIX.opencode.minimum))
@@ -70555,7 +71084,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70555
71084
  env: deps.env,
70556
71085
  cwd: home,
70557
71086
  packageVersion: latest.stdout.trim(),
70558
- targetDir: path35.join(home, ".cursor/plugins/local/workit")
71087
+ targetDir: path36.join(home, ".cursor/plugins/local/workit")
70559
71088
  });
70560
71089
  plan.entries.push({
70561
71090
  host: source.host,
@@ -70585,13 +71114,13 @@ function applyUpgrade(plan, deps = {}) {
70585
71114
  ok: false,
70586
71115
  error: "Stop the selected running hosts before upgrading their loaded plugins"
70587
71116
  };
70588
- const backup = path35.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
70589
- mkdirSync8(backup, { recursive: true, mode: 448 });
71117
+ const backup = path36.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
71118
+ mkdirSync9(backup, { recursive: true, mode: 448 });
70590
71119
  for (const file of Object.keys(plan.configDigests))
70591
- if (existsSync17(file)) {
70592
- const target = path35.join(backup, createHash14("sha256").update(file).digest("hex").slice(0, 12) + "-" + path35.basename(file));
71120
+ if (existsSync18(file)) {
71121
+ const target = path36.join(backup, createHash14("sha256").update(file).digest("hex").slice(0, 12) + "-" + path36.basename(file));
70593
71122
  copyFileSync5(file, target);
70594
- chmodSync(target, 384);
71123
+ chmodSync2(target, 384);
70595
71124
  }
70596
71125
  for (const migration of plan.migrations) {
70597
71126
  const value = object2(migration.file);
@@ -70606,9 +71135,9 @@ function applyUpgrade(plan, deps = {}) {
70606
71135
  `, 384);
70607
71136
  }
70608
71137
  const run = runner(deps);
70609
- const cursorRoot = path35.join(home, ".cursor/plugins/local/workit");
70610
- if (plan.entries.some((entry) => entry.host === "cursor") && existsSync17(cursorRoot))
70611
- cpSync5(cursorRoot, path35.join(backup, "cursor-package"), { recursive: true });
71138
+ const cursorRoot = path36.join(home, ".cursor/plugins/local/workit");
71139
+ if (plan.entries.some((entry) => entry.host === "cursor") && existsSync18(cursorRoot))
71140
+ cpSync5(cursorRoot, path36.join(backup, "cursor-package"), { recursive: true });
70612
71141
  for (const entry of plan.entries) {
70613
71142
  if (entry.host === "opencode")
70614
71143
  return {
@@ -70628,7 +71157,7 @@ function applyUpgrade(plan, deps = {}) {
70628
71157
  {
70629
71158
  type: "install-host",
70630
71159
  platform: "cursor",
70631
- path: path35.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
71160
+ path: path36.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
70632
71161
  commands: entry.commands.map((command) => ({
70633
71162
  ...command,
70634
71163
  purpose: command.purpose ?? "Upgrade Cursor"
@@ -70637,7 +71166,7 @@ function applyUpgrade(plan, deps = {}) {
70637
71166
  {
70638
71167
  type: "install-adapter",
70639
71168
  platform: "cursor",
70640
- path: path35.join(home, ".cursor/plugins/local/workit")
71169
+ path: path36.join(home, ".cursor/plugins/local/workit")
70641
71170
  }
70642
71171
  ]
70643
71172
  };
@@ -70651,7 +71180,7 @@ function applyUpgrade(plan, deps = {}) {
70651
71180
  return { exitCode: outcome.status ?? 1, stdout: outcome.stdout, stderr: "" };
70652
71181
  }
70653
71182
  });
70654
- const pkg = object2(path35.join(home, ".cursor/plugins/local/workit/package.json"));
71183
+ const pkg = object2(path36.join(home, ".cursor/plugins/local/workit/package.json"));
70655
71184
  if (!result.ok || pkg?.version !== entry.latest)
70656
71185
  return {
70657
71186
  ok: false,
@@ -70685,15 +71214,15 @@ function applyUpgrade(plan, deps = {}) {
70685
71214
  const currentRoot = run("npm", ["root", "--global"]);
70686
71215
  if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== plan.cli.root)
70687
71216
  return { ok: false, backup, error: "Global npm location changed; preview again" };
70688
- const cliBackup = path35.join(backup, "cli-package");
70689
- cpSync5(path35.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
71217
+ const cliBackup = path36.join(backup, "cli-package");
71218
+ cpSync5(path36.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
70690
71219
  if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${plan.cli.latest}`]).status !== 0)
70691
71220
  return {
70692
71221
  ok: false,
70693
71222
  backup,
70694
- error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path35.join(cliBackup, "dist/index.js")}`
71223
+ error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path36.join(cliBackup, "dist/index.js")}`
70695
71224
  };
70696
- const installed = object2(path35.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
71225
+ const installed = object2(path36.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
70697
71226
  if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== plan.cli.latest)
70698
71227
  return {
70699
71228
  ok: false,
@@ -70718,12 +71247,12 @@ async function runUpgradeCommand(argv, deps = {}) {
70718
71247
  if (flags.has("--cli")) {
70719
71248
  const run = runner(deps);
70720
71249
  const root = run("npm", ["root", "--global"]);
70721
- if (root.status !== 0 || !path35.isAbsolute(root.stdout.trim())) {
71250
+ if (root.status !== 0 || !path36.isAbsolute(root.stdout.trim())) {
70722
71251
  plan.blocked.push("Global npm location could not be verified");
70723
71252
  } else {
70724
- const packageFile = path35.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
71253
+ const packageFile = path36.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
70725
71254
  const pkg = object2(packageFile);
70726
- if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync6(path35.dirname(packageFile)).isSymbolicLink()) {
71255
+ if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(path36.dirname(packageFile)).isSymbolicLink()) {
70727
71256
  plan.skipped.push({
70728
71257
  host: "cli",
70729
71258
  reason: "No managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
@@ -70803,7 +71332,7 @@ async function runLaunchCommand(argv, deps = {}) {
70803
71332
  }
70804
71333
  if (deps.run)
70805
71334
  return deps.run(host, hostArgs).status ?? 1;
70806
- return spawnSync14(launch.command, launch.args, {
71335
+ return spawnSync15(launch.command, launch.args, {
70807
71336
  stdio: "inherit",
70808
71337
  env
70809
71338
  }).status ?? 1;
@@ -70814,37 +71343,37 @@ var HOSTS2, upgradePaths = (deps) => {
70814
71343
  ...process.env,
70815
71344
  ...deps.home || deps.env?.HOME ? {
70816
71345
  HOME: home,
70817
- CODEX_HOME: path35.join(home, ".codex"),
70818
- PI_CODING_AGENT_DIR: path35.join(home, ".pi/agent"),
70819
- XDG_CONFIG_HOME: path35.join(home, ".config"),
71346
+ CODEX_HOME: path36.join(home, ".codex"),
71347
+ PI_CODING_AGENT_DIR: path36.join(home, ".pi/agent"),
71348
+ XDG_CONFIG_HOME: path36.join(home, ".config"),
70820
71349
  WORKFLOW_TOOLKIT_CONFIG: undefined,
70821
71350
  WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
70822
71351
  } : {},
70823
71352
  ...deps.env,
70824
71353
  HOME: home
70825
71354
  };
70826
- const xdg = env.XDG_CONFIG_HOME || path35.join(home, ".config");
71355
+ const xdg = env.XDG_CONFIG_HOME || path36.join(home, ".config");
70827
71356
  return {
70828
71357
  home,
70829
71358
  env,
70830
- configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path35.join(xdg, "workit"),
70831
- opencodeConfig: path35.join(xdg, "opencode/opencode.json"),
70832
- piSettings: path35.join(env.PI_CODING_AGENT_DIR ?? path35.join(home, ".pi/agent"), "settings.json"),
70833
- codexConfig: path35.join(env.CODEX_HOME ?? path35.join(home, ".codex"), "config.toml")
71359
+ configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path36.join(xdg, "workit"),
71360
+ opencodeConfig: path36.join(xdg, "opencode/opencode.json"),
71361
+ piSettings: path36.join(env.PI_CODING_AGENT_DIR ?? path36.join(home, ".pi/agent"), "settings.json"),
71362
+ codexConfig: path36.join(env.CODEX_HOME ?? path36.join(home, ".codex"), "config.toml")
70834
71363
  };
70835
71364
  }, object2 = (file) => {
70836
- if (!existsSync17(file))
71365
+ if (!existsSync18(file))
70837
71366
  return null;
70838
71367
  let data;
70839
71368
  try {
70840
- data = JSON.parse(readFileSync17(file, "utf8"));
71369
+ data = JSON.parse(readFileSync18(file, "utf8"));
70841
71370
  } catch {
70842
71371
  throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
70843
71372
  }
70844
71373
  if (!data || typeof data !== "object" || Array.isArray(data))
70845
71374
  throw new Error(`${file} is not a configuration object`);
70846
71375
  return data;
70847
- }, digest2 = (file) => existsSync17(file) ? createHash14("sha256").update(readFileSync17(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
71376
+ }, digest2 = (file) => existsSync18(file) ? createHash14("sha256").update(readFileSync18(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
70848
71377
  const { home, env } = upgradePaths(deps);
70849
71378
  let step;
70850
71379
  try {
@@ -70861,7 +71390,7 @@ var HOSTS2, upgradePaths = (deps) => {
70861
71390
  }), sources = (paths, run, hosts) => {
70862
71391
  const { home, opencodeConfig, piSettings } = paths;
70863
71392
  const result = [];
70864
- if (hosts.includes("opencode") && existsSync17(opencodeConfig + "c"))
71393
+ if (hosts.includes("opencode") && existsSync18(opencodeConfig + "c"))
70865
71394
  throw new Error("OpenCode JSONC config is present; inspect effective native configuration before updating");
70866
71395
  const oc = hosts.includes("opencode") ? object2(opencodeConfig) : null;
70867
71396
  if (oc && [oc.plugins, oc.plugin].some((value) => value !== undefined && !Array.isArray(value)))
@@ -70870,9 +71399,9 @@ var HOSTS2, upgradePaths = (deps) => {
70870
71399
  for (const pin of plugins)
70871
71400
  if (typeof pin === "string" && isWorkitPlugin(pin))
70872
71401
  result.push(classify("opencode", pin));
70873
- const cursor = hosts.includes("cursor") ? object2(path35.join(home, ".cursor/plugins/local/workit/package.json")) : null;
71402
+ const cursor = hosts.includes("cursor") ? object2(path36.join(home, ".cursor/plugins/local/workit/package.json")) : null;
70874
71403
  if (cursor?.name === "@brainervirus/workit-cursor") {
70875
- const mcp = object2(path35.join(home, ".cursor/mcp.json"));
71404
+ const mcp = object2(path36.join(home, ".cursor/mcp.json"));
70876
71405
  const args = mcp?.mcpServers?.workit?.args ?? [];
70877
71406
  const selector = args.find((arg) => typeof arg === "string" && arg.startsWith("--package="));
70878
71407
  result.push(classify("cursor", typeof selector === "string" ? selector.slice(10) : "local"));
@@ -70885,7 +71414,7 @@ var HOSTS2, upgradePaths = (deps) => {
70885
71414
  if (typeof source !== "string")
70886
71415
  continue;
70887
71416
  const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
70888
- const local = path35.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path35.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
71417
+ const local = path36.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path36.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
70889
71418
  if (npm || local)
70890
71419
  result.push(classify("pi", source));
70891
71420
  }
@@ -70896,7 +71425,7 @@ var HOSTS2, upgradePaths = (deps) => {
70896
71425
  hostCommand("codex", [], { home, env: paths.env });
70897
71426
  available = true;
70898
71427
  } catch {}
70899
- if (available || existsSync17(paths.codexConfig))
71428
+ if (available || existsSync18(paths.codexConfig))
70900
71429
  throw new Error("Codex plugin inventory failed; inspect the native host before upgrading");
70901
71430
  }
70902
71431
  if (listing.status === 0) {
@@ -70932,9 +71461,9 @@ var HOSTS2, upgradePaths = (deps) => {
70932
71461
  }, piInstalledVersion = (listing, selector) => {
70933
71462
  const lines = listing.split(/\r?\n/).map((line) => line.trim());
70934
71463
  const index = lines.indexOf(selector);
70935
- if (index < 0 || !lines[index + 1] || !path35.isAbsolute(lines[index + 1]))
71464
+ if (index < 0 || !lines[index + 1] || !path36.isAbsolute(lines[index + 1]))
70936
71465
  return;
70937
- const pkg = object2(path35.join(lines[index + 1], "package.json"));
71466
+ const pkg = object2(path36.join(lines[index + 1], "package.json"));
70938
71467
  return pkg?.name === "@brainervirus/workit-pi" ? pkg.version : undefined;
70939
71468
  }, versionAtLeast = (installed, latest) => {
70940
71469
  if (!/^\d+\.\d+\.\d+$/.test(installed))
@@ -70967,9 +71496,9 @@ var init_upgrade = __esm(() => {
70967
71496
  // packages/workit-cli/src/verbs/upgrade.ts
70968
71497
  var exports_upgrade = {};
70969
71498
  __export(exports_upgrade, {
70970
- run: () => run3
71499
+ run: () => run4
70971
71500
  });
70972
- var run3 = (argv) => runUpgradeCommand(argv);
71501
+ var run4 = (argv) => runUpgradeCommand(argv);
70973
71502
  var init_upgrade2 = __esm(() => {
70974
71503
  init_upgrade();
70975
71504
  });
@@ -70977,17 +71506,17 @@ var init_upgrade2 = __esm(() => {
70977
71506
  // packages/workit-cli/src/verbs/launch.ts
70978
71507
  var exports_launch = {};
70979
71508
  __export(exports_launch, {
70980
- run: () => run4
71509
+ run: () => run5
70981
71510
  });
70982
- var run4 = (argv) => runLaunchCommand(argv);
71511
+ var run5 = (argv) => runLaunchCommand(argv);
70983
71512
  var init_launch = __esm(() => {
70984
71513
  init_upgrade();
70985
71514
  });
70986
71515
 
70987
71516
  // packages/workit-core/src/knowledge.ts
70988
- import { spawnSync as spawnSync15 } from "node:child_process";
71517
+ import { spawnSync as spawnSync16 } from "node:child_process";
70989
71518
  import fs17 from "node:fs";
70990
- import path36 from "node:path";
71519
+ import path37 from "node:path";
70991
71520
  function lintKnowledge(root) {
70992
71521
  const seen = new Set;
70993
71522
  const unreadable = [];
@@ -71091,7 +71620,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71091
71620
  const end = lines.indexOf("---", 1);
71092
71621
  return end === -1 ? lines : [...lines.slice(0, end + 1).map(() => ""), ...lines.slice(end + 1)];
71093
71622
  }, read = (root, file) => {
71094
- const full = path36.join(root, file);
71623
+ const full = path37.join(root, file);
71095
71624
  try {
71096
71625
  fs17.lstatSync(full);
71097
71626
  } catch {
@@ -71106,7 +71635,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71106
71635
  return "unreadable";
71107
71636
  }
71108
71637
  }, trackedTopLevel = (root) => {
71109
- const run = spawnSync15("git", ["ls-files", "-z", "--cached"], {
71638
+ const run = spawnSync16("git", ["ls-files", "-z", "--cached"], {
71110
71639
  cwd: root,
71111
71640
  encoding: "utf8",
71112
71641
  maxBuffer: 256 * 1024 * 1024,
@@ -71116,7 +71645,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71116
71645
  if (run.status !== 0)
71117
71646
  return null;
71118
71647
  return new Set(run.stdout.split("\x00").filter(Boolean).map((file) => file.split("/")[0]));
71119
- }, exists = (target) => {
71648
+ }, exists2 = (target) => {
71120
71649
  try {
71121
71650
  fs17.statSync(target);
71122
71651
  return true;
@@ -71132,7 +71661,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71132
71661
  }
71133
71662
  }, brokenLinks = (root, doc, tracked) => {
71134
71663
  const findings = [];
71135
- const base = path36.dirname(path36.join(root, doc.file));
71664
+ const base = path37.dirname(path37.join(root, doc.file));
71136
71665
  const missing = (target, line, how) => findings.push({
71137
71666
  rule: "broken-link",
71138
71667
  file: doc.file,
@@ -71151,8 +71680,8 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71151
71680
  const local = localTarget(target);
71152
71681
  if (!local)
71153
71682
  continue;
71154
- const resolved = local.startsWith("/") ? path36.join(root, local) : path36.resolve(base, local);
71155
- if (!exists(resolved))
71683
+ const resolved = local.startsWith("/") ? path37.join(root, local) : path37.resolve(base, local);
71684
+ if (!exists2(resolved))
71156
71685
  missing(target, index + 1, "link");
71157
71686
  }
71158
71687
  for (const [, span] of text.matchAll(CODE_SPAN)) {
@@ -71161,10 +71690,10 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71161
71690
  continue;
71162
71691
  const relative = /^\.\.?\//.test(candidate);
71163
71692
  const first = candidate.split("/")[0];
71164
- if (!relative && !(tracked ? tracked.has(first) : exists(path36.join(root, first))))
71693
+ if (!relative && !(tracked ? tracked.has(first) : exists2(path37.join(root, first))))
71165
71694
  continue;
71166
- const resolved = relative ? path36.resolve(base, candidate) : path36.join(root, candidate);
71167
- if (!exists(resolved))
71695
+ const resolved = relative ? path37.resolve(base, candidate) : path37.join(root, candidate);
71696
+ if (!exists2(resolved))
71168
71697
  missing(span, index + 1, "pointer");
71169
71698
  }
71170
71699
  }
@@ -71289,7 +71818,7 @@ var init_prompt = () => {};
71289
71818
  // packages/workit-cli/src/verbs/doctor.ts
71290
71819
  var exports_doctor = {};
71291
71820
  __export(exports_doctor, {
71292
- run: () => run5
71821
+ run: () => run6
71293
71822
  });
71294
71823
  async function confirmForcedLockClear(root, args, io) {
71295
71824
  const lock = inspectMetadataLock(root);
@@ -71312,7 +71841,7 @@ async function confirmForcedLockClear(root, args, io) {
71312
71841
  return "cancelled";
71313
71842
  return /^y(es)?$/i.test(answer.trim());
71314
71843
  }
71315
- async function run5(argv, io) {
71844
+ async function run6(argv, io) {
71316
71845
  const root = workspaceRootFor();
71317
71846
  const force = argv.includes("--force");
71318
71847
  let fixLock = null;
@@ -71330,6 +71859,18 @@ async function run5(argv, io) {
71330
71859
  }
71331
71860
  fixLock = clearStaleMetadataLock(root, { force });
71332
71861
  }
71862
+ let sessionHook = null;
71863
+ if (argv.includes("--fix")) {
71864
+ try {
71865
+ const fixed = fixSessionHook({ host: "cli", cwd: io.cwd, workspaceRoot: root });
71866
+ sessionHook = { action: fixed.action, detail: fixed.detail };
71867
+ } catch (error) {
71868
+ sessionHook = {
71869
+ action: "failed",
71870
+ detail: error instanceof Error ? error.message : String(error)
71871
+ };
71872
+ }
71873
+ }
71333
71874
  const report = runDoctor({
71334
71875
  host: "cli",
71335
71876
  cwd: io.cwd,
@@ -71352,7 +71893,7 @@ async function run5(argv, io) {
71352
71893
  budget: knowledge.budget,
71353
71894
  findings: knowledge.findings.length
71354
71895
  } : { unavailable: true };
71355
- io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, knowledge: summary }, null, 2)}
71896
+ io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, ...sessionHook ? { sessionHook } : {}, knowledge: summary }, null, 2)}
71356
71897
  `);
71357
71898
  return report.exitCode;
71358
71899
  }
@@ -71361,6 +71902,9 @@ async function run5(argv, io) {
71361
71902
  io.stdout(`fix-lock: ${what}${fixLock.guardCleared ? "; removed abandoned reclaim guard" : ""}
71362
71903
  `);
71363
71904
  }
71905
+ if (sessionHook)
71906
+ io.stdout(`fix: session hook ${sessionHook.action}: ${sessionHook.detail}
71907
+ `);
71364
71908
  io.stdout(`workit doctor — ${report.ok ? "healthy" : "problems found"} (${report.offline ? "offline" : "online"})
71365
71909
  `);
71366
71910
  for (const check of report.checks) {
@@ -71493,11 +72037,11 @@ var init_redact = __esm(() => {
71493
72037
  });
71494
72038
 
71495
72039
  // packages/workit-core/src/checks.ts
71496
- import { spawn, spawnSync as spawnSync16 } from "node:child_process";
72040
+ import { spawn, spawnSync as spawnSync17 } from "node:child_process";
71497
72041
  import { createHash as createHash15 } from "node:crypto";
71498
72042
  import fs18 from "node:fs";
71499
72043
  import os13 from "node:os";
71500
- import path37 from "node:path";
72044
+ import path38 from "node:path";
71501
72045
  import { StringDecoder } from "node:string_decoder";
71502
72046
  function tailLines(text, lines = TAIL_LINES) {
71503
72047
  const all = text.replace(/\r\n?/gu, `
@@ -71617,7 +72161,7 @@ function planSpawn(argv, options) {
71617
72161
  return { file: argv[0], args: [], verbatim: false, shell: true };
71618
72162
  if (options.platform !== "win32")
71619
72163
  return { file: argv[0], args: argv.slice(1), verbatim: false, shell: false };
71620
- const win = path37.win32;
72164
+ const win = path38.win32;
71621
72165
  const isFile = options.isFile ?? ((file) => {
71622
72166
  try {
71623
72167
  return fs18.statSync(file).isFile();
@@ -71672,7 +72216,7 @@ function runCheckCommand(argv, options) {
71672
72216
  if (!child?.pid)
71673
72217
  return;
71674
72218
  if (windows)
71675
- spawnSync16("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
72219
+ spawnSync17("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
71676
72220
  stdio: "ignore",
71677
72221
  windowsHide: true
71678
72222
  });
@@ -71774,10 +72318,10 @@ function runCheckCommand(argv, options) {
71774
72318
  function storeLog(storeRoot, log) {
71775
72319
  const digest = logDigest(log);
71776
72320
  const ref = `blobs/logs/${digest.slice("sha256:".length)}.log`;
71777
- const file = path37.join(storeRoot, ref);
72321
+ const file = path38.join(storeRoot, ref);
71778
72322
  try {
71779
72323
  if (!fs18.existsSync(file)) {
71780
- fs18.mkdirSync(path37.dirname(file), { recursive: true });
72324
+ fs18.mkdirSync(path38.dirname(file), { recursive: true });
71781
72325
  const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
71782
72326
  fs18.writeFileSync(temp, log, { mode: 384 });
71783
72327
  fs18.renameSync(temp, file);
@@ -71790,7 +72334,7 @@ function storeLog(storeRoot, log) {
71790
72334
  function pruneCheckLogs(storeRoot, options = {}) {
71791
72335
  const limits = { ...LOG_RETENTION, ...options.retention };
71792
72336
  const now = options.now ?? Date.now();
71793
- const dir = path37.join(storeRoot, "blobs", "logs");
72337
+ const dir = path38.join(storeRoot, "blobs", "logs");
71794
72338
  const report = { removed: 0, removedBytes: 0, kept: 0 };
71795
72339
  let names;
71796
72340
  try {
@@ -71800,7 +72344,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
71800
72344
  }
71801
72345
  const files = names.flatMap((name) => {
71802
72346
  try {
71803
- const stat = fs18.statSync(path37.join(dir, name));
72347
+ const stat = fs18.statSync(path38.join(dir, name));
71804
72348
  return stat.isFile() ? [{ name, size: stat.size, mtimeMs: stat.mtimeMs }] : [];
71805
72349
  } catch {
71806
72350
  return [];
@@ -71819,7 +72363,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
71819
72363
  continue;
71820
72364
  if (!options.dryRun)
71821
72365
  try {
71822
- fs18.rmSync(path37.join(dir, file.name), { force: true });
72366
+ fs18.rmSync(path38.join(dir, file.name), { force: true });
71823
72367
  } catch {
71824
72368
  continue;
71825
72369
  }
@@ -71850,9 +72394,9 @@ var init_checks3 = __esm(() => {
71850
72394
  // packages/workit-cli/src/verbs/gc.ts
71851
72395
  var exports_gc = {};
71852
72396
  __export(exports_gc, {
71853
- run: () => run6
72397
+ run: () => run7
71854
72398
  });
71855
- async function run6(argv, io) {
72399
+ async function run7(argv, io) {
71856
72400
  const dryRun = argv.includes("--dry-run");
71857
72401
  const pruneRecovery = argv.includes("--prune-recovery");
71858
72402
  if (pruneRecovery && !dryRun && !argv.includes("--yes"))
@@ -71896,9 +72440,9 @@ var init_gc = __esm(() => {
71896
72440
  // packages/workit-cli/src/verbs/uninstall.ts
71897
72441
  var exports_uninstall = {};
71898
72442
  __export(exports_uninstall, {
71899
- run: () => run7
72443
+ run: () => run8
71900
72444
  });
71901
- async function run7(_argv, io) {
72445
+ async function run8(_argv, io) {
71902
72446
  if (io.json)
71903
72447
  return emit(io, fail("invalid_input", "workit uninstall is interactive and has no --json output", {
71904
72448
  unblock: "run `workit uninstall` in a terminal without --json"
@@ -71914,7 +72458,7 @@ var init_uninstall2 = __esm(() => {
71914
72458
  // packages/workit-cli/src/verbs/grant.ts
71915
72459
  var exports_grant = {};
71916
72460
  __export(exports_grant, {
71917
- run: () => run8,
72461
+ run: () => run9,
71918
72462
  runGrant: () => runGrant
71919
72463
  });
71920
72464
  function show(argv, io) {
@@ -72035,7 +72579,7 @@ var USAGE2 = "workit grant show [<workspace>] [--all] | grant set <workspace> <k
72035
72579
  ...endpoint.endpointIssue ? [` note: ${endpoint.endpointIssue}`] : [],
72036
72580
  ` verification: ${verification} (${verification === "independent" ? "a normal-risk behavior change needs a non-author verdict" : "a normal-risk behavior change needs a passing check and the author's --self verdict, shown as self-reviewed"})`
72037
72581
  ], usage2 = (io, message) => emit(io, fail("invalid_input", `${message}
72038
- usage: ${USAGE2}`)), run8 = (argv, io) => runGrant(argv, io);
72582
+ usage: ${USAGE2}`)), run9 = (argv, io) => runGrant(argv, io);
72039
72583
  var init_grant = __esm(() => {
72040
72584
  init_autonomy();
72041
72585
  init_release_tracks();
@@ -72064,9 +72608,9 @@ var init_grant = __esm(() => {
72064
72608
  // packages/workit-cli/src/verbs/check.ts
72065
72609
  var exports_check = {};
72066
72610
  __export(exports_check, {
72067
- run: () => run9
72611
+ run: () => run10
72068
72612
  });
72069
- import path38 from "node:path";
72613
+ import path39 from "node:path";
72070
72614
  function parse5(argv) {
72071
72615
  const split = argv.indexOf("--");
72072
72616
  const head = split >= 0 ? argv.slice(0, split) : [...argv];
@@ -72215,7 +72759,7 @@ function boundedArgv(argv) {
72215
72759
  const truncated = cut.length !== argv.length || cut.some((item, index) => item !== argv[index]);
72216
72760
  return { argv: cut, truncated };
72217
72761
  }
72218
- async function run9(argv, io) {
72762
+ async function run10(argv, io) {
72219
72763
  const options = parse5(argv);
72220
72764
  if (options instanceof Error)
72221
72765
  return emit(io, fail("invalid_input", options.message, { unblock: USAGE3 }));
@@ -72249,7 +72793,7 @@ async function run9(argv, io) {
72249
72793
  if (options.shell && command.length !== 1)
72250
72794
  return emit(io, fail("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
72251
72795
  }
72252
- const relCwd = posix(path38.relative(top, runCwd));
72796
+ const relCwd = posix(path39.relative(top, runCwd));
72253
72797
  const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
72254
72798
  const actor = actorFromEnv(io.env);
72255
72799
  const branchKey = codeKey(io.cwd, { base: options.base });
@@ -72373,7 +72917,7 @@ async function run9(argv, io) {
72373
72917
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
72374
72918
  return 0;
72375
72919
  }
72376
- var USAGE3 = "workit check <name> | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--json] -- <cmd…>", RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path38.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS;
72920
+ var USAGE3 = "workit check <name> | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--json] -- <cmd…>", RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path39.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS;
72377
72921
  var init_check = __esm(() => {
72378
72922
  init_check_config();
72379
72923
  init_checks3();
@@ -72395,9 +72939,9 @@ var init_check = __esm(() => {
72395
72939
  var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
72396
72940
 
72397
72941
  // packages/workit-core/src/git/ops.ts
72398
- import { spawnSync as spawnSync17 } from "node:child_process";
72399
- import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
72400
- import path39 from "node:path";
72942
+ import { spawnSync as spawnSync18 } from "node:child_process";
72943
+ import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
72944
+ import path40 from "node:path";
72401
72945
  function operationInProgress(cwd) {
72402
72946
  const markers = [
72403
72947
  ["rebase-merge", "rebase"],
@@ -72410,18 +72954,18 @@ function operationInProgress(cwd) {
72410
72954
  if (!run.ok)
72411
72955
  return null;
72412
72956
  const found = run.stdout.split(`
72413
- `).map((line) => path39.resolve(cwd, line.trim()));
72957
+ `).map((line) => path40.resolve(cwd, line.trim()));
72414
72958
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
72415
72959
  for (const [index, [, operation]] of markers.entries()) {
72416
72960
  const marker = found[index];
72417
- if (!marker || !existsSync18(marker))
72961
+ if (!marker || !existsSync19(marker))
72418
72962
  continue;
72419
- if (index === 1 && existsSync18(path39.join(marker, "applying")))
72963
+ if (index === 1 && existsSync19(path40.join(marker, "applying")))
72420
72964
  return { operation: "am", commitAllowed: false, branch: null };
72421
- if (index === 0 && existsSync18(path39.join(marker, "amend")) && !conflicted()) {
72965
+ if (index === 0 && existsSync19(path40.join(marker, "amend")) && !conflicted()) {
72422
72966
  let headName = "";
72423
72967
  try {
72424
- headName = readFileSync18(path39.join(marker, "head-name"), "utf8").trim();
72968
+ headName = readFileSync19(path40.join(marker, "head-name"), "utf8").trim();
72425
72969
  } catch {}
72426
72970
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
72427
72971
  return { operation, commitAllowed: branch !== null, branch };
@@ -72671,7 +73215,7 @@ function gitCommit(cwd, input) {
72671
73215
  if (paths.some((value) => value.startsWith("-") || !value.trim()))
72672
73216
  return failure3("invalid_input", "paths must not be empty or start with -");
72673
73217
  const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
72674
- const addable = paths.filter((value) => existsSync18(path39.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
73218
+ const addable = paths.filter((value) => existsSync19(path40.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
72675
73219
  if (addable.length) {
72676
73220
  const added = git5(cwd, ["add", "-A", "--", ...addable]);
72677
73221
  if (!added.ok)
@@ -72950,7 +73494,7 @@ ${run.stdout}`;
72950
73494
  } : { ok: false, lease: false, error: gitError(run) };
72951
73495
  }
72952
73496
  var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
72953
- const result = spawnSync17("git", args, {
73497
+ const result = spawnSync18("git", args, {
72954
73498
  cwd,
72955
73499
  encoding: "utf8",
72956
73500
  env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
@@ -73004,10 +73548,10 @@ var init_ops = __esm(() => {
73004
73548
 
73005
73549
  // packages/workit-core/src/forge/reruns.ts
73006
73550
  import fs19 from "node:fs";
73007
- import path40 from "node:path";
73551
+ import path41 from "node:path";
73008
73552
  function rerunLogPath(cwd) {
73009
73553
  const common = gitCommonDir(cwd);
73010
- return common ? path40.join(common, "workit", "ci-reruns.jsonl") : null;
73554
+ return common ? path41.join(common, "workit", "ci-reruns.jsonl") : null;
73011
73555
  }
73012
73556
  function rerunCounts(cwd, key) {
73013
73557
  const counts = new Map;
@@ -73042,7 +73586,7 @@ function recordReruns(cwd, rows) {
73042
73586
  if (!file)
73043
73587
  return false;
73044
73588
  try {
73045
- fs19.mkdirSync(path40.dirname(file), { recursive: true });
73589
+ fs19.mkdirSync(path41.dirname(file), { recursive: true });
73046
73590
  fs19.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
73047
73591
  `).join(""));
73048
73592
  return true;
@@ -73055,7 +73599,7 @@ function withRerunLock(cwd, fn) {
73055
73599
  if (!file)
73056
73600
  return fn();
73057
73601
  const lock = `${file.slice(0, -".jsonl".length)}.lock`;
73058
- fs19.mkdirSync(path40.dirname(lock), { recursive: true });
73602
+ fs19.mkdirSync(path41.dirname(lock), { recursive: true });
73059
73603
  const until = Date.now() + LOCK_WAIT_MS2;
73060
73604
  for (;; ) {
73061
73605
  try {
@@ -73478,7 +74022,7 @@ var init_report = __esm(() => {
73478
74022
  });
73479
74023
 
73480
74024
  // packages/workit-core/src/forge/pr-ops.ts
73481
- import { spawnSync as spawnSync18 } from "node:child_process";
74025
+ import { spawnSync as spawnSync19 } from "node:child_process";
73482
74026
  function fillFromCommits(cwd, resolved, base) {
73483
74027
  const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
73484
74028
  if (!base.startsWith("-"))
@@ -73486,7 +74030,7 @@ function fillFromCommits(cwd, resolved, base) {
73486
74030
  const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
73487
74031
  if (!from)
73488
74032
  return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
73489
- const log = spawnSync18("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
74033
+ const log = spawnSync19("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
73490
74034
  cwd,
73491
74035
  encoding: "utf8",
73492
74036
  timeout: GIT_TIMEOUTS.local,
@@ -73842,10 +74386,10 @@ var init_pr_ops = __esm(() => {
73842
74386
  });
73843
74387
 
73844
74388
  // packages/workit-core/src/stack.ts
73845
- import { spawnSync as spawnSync19 } from "node:child_process";
74389
+ import { spawnSync as spawnSync20 } from "node:child_process";
73846
74390
  import { createHash as createHash16, randomBytes as randomBytes3 } from "node:crypto";
73847
74391
  import fs20 from "node:fs";
73848
- import path41 from "node:path";
74392
+ import path42 from "node:path";
73849
74393
  function worktreeOf(cwd, branch) {
73850
74394
  const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
73851
74395
  if (!list.ok)
@@ -73889,7 +74433,7 @@ function gitRestack(cwd, input) {
73889
74433
  if (!scratch)
73890
74434
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
73891
74435
  fs20.mkdirSync(scratch, { recursive: true });
73892
- dir = path41.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
74436
+ dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
73893
74437
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
73894
74438
  if (!added.ok)
73895
74439
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -73930,7 +74474,7 @@ function pruneScratch(cwd) {
73930
74474
  if (!scratch || !fs20.existsSync(scratch))
73931
74475
  return;
73932
74476
  for (const name of fs20.readdirSync(scratch)) {
73933
- const dir = path41.join(scratch, name);
74477
+ const dir = path42.join(scratch, name);
73934
74478
  if (!rebaseInProgress(dir) && !trackedDirt(dir))
73935
74479
  gitRun3(cwd, ["worktree", "remove", "--force", dir]);
73936
74480
  }
@@ -73940,7 +74484,7 @@ function stacksDir(cwd) {
73940
74484
  const root = storeRoot(cwd);
73941
74485
  if (!root.ok)
73942
74486
  return stackFail(root.code, root.error, root.unblock);
73943
- return { ok: true, data: path41.join(root.value.root, "stacks") };
74487
+ return { ok: true, data: path42.join(root.value.root, "stacks") };
73944
74488
  }
73945
74489
  function parseStack(raw) {
73946
74490
  if (!isRecord7(raw) || !Array.isArray(raw.branches))
@@ -73992,7 +74536,7 @@ function readStack(cwd, name) {
73992
74536
  const dir = stacksDir(cwd);
73993
74537
  if (!dir.ok)
73994
74538
  return dir;
73995
- const file = path41.join(dir.data, fileFor(name));
74539
+ const file = path42.join(dir.data, fileFor(name));
73996
74540
  let text;
73997
74541
  try {
73998
74542
  text = fs20.readFileSync(file, "utf8");
@@ -74013,7 +74557,7 @@ function writeStack(cwd, stack, now = new Date) {
74013
74557
  const dir = stacksDir(cwd);
74014
74558
  if (!dir.ok)
74015
74559
  return dir;
74016
- const file = path41.join(dir.data, fileFor(stack.name));
74560
+ const file = path42.join(dir.data, fileFor(stack.name));
74017
74561
  const temp = `${file}.${process.pid}.tmp`;
74018
74562
  try {
74019
74563
  fs20.mkdirSync(dir.data, { recursive: true });
@@ -74041,7 +74585,7 @@ function listStacks(cwd) {
74041
74585
  for (const name of names) {
74042
74586
  let parsed = null;
74043
74587
  try {
74044
- parsed = parseStack(JSON.parse(fs20.readFileSync(path41.join(dir.data, name), "utf8")));
74588
+ parsed = parseStack(JSON.parse(fs20.readFileSync(path42.join(dir.data, name), "utf8")));
74045
74589
  } catch {
74046
74590
  parsed = null;
74047
74591
  }
@@ -74100,7 +74644,7 @@ function takeLock(lock) {
74100
74644
  host: localLockHost(),
74101
74645
  nonce: randomBytes3(8).toString("hex")
74102
74646
  };
74103
- fs20.writeFileSync(path41.join(lock, OWNER), JSON.stringify(owner));
74647
+ fs20.writeFileSync(path42.join(lock, OWNER), JSON.stringify(owner));
74104
74648
  return owner;
74105
74649
  }
74106
74650
  function reapLock(lock, observed) {
@@ -74137,7 +74681,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
74137
74681
  return dir;
74138
74682
  if (!validName(name))
74139
74683
  return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
74140
- const lock = path41.join(dir.data, `${fileFor(name)}.lock`);
74684
+ const lock = path42.join(dir.data, `${fileFor(name)}.lock`);
74141
74685
  try {
74142
74686
  fs20.mkdirSync(dir.data, { recursive: true });
74143
74687
  } catch (error) {
@@ -74167,7 +74711,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
74167
74711
  const touch = () => {
74168
74712
  try {
74169
74713
  const now = new Date;
74170
- fs20.utimesSync(path41.join(lock, OWNER), now, now);
74714
+ fs20.utimesSync(path42.join(lock, OWNER), now, now);
74171
74715
  } catch {}
74172
74716
  };
74173
74717
  const beat = setInterval(touch, HEARTBEAT_MS);
@@ -74355,7 +74899,7 @@ function commitKeys(cwd, range) {
74355
74899
  const patches = new Map;
74356
74900
  const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
74357
74901
  if (diff.ok && diff.stdout.trim()) {
74358
- const ids = spawnSync19("git", ["patch-id", "--stable"], {
74902
+ const ids = spawnSync20("git", ["patch-id", "--stable"], {
74359
74903
  cwd,
74360
74904
  input: diff.stdout,
74361
74905
  encoding: "utf8",
@@ -74979,7 +75523,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
74979
75523
  ...unblock ? { unblock } : {},
74980
75524
  ...data ? { data } : {}
74981
75525
  }), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
74982
- const run = spawnSync19("git", args, {
75526
+ const run = spawnSync20("git", args, {
74983
75527
  cwd,
74984
75528
  encoding: "utf8",
74985
75529
  timeout: timeoutMs,
@@ -75007,7 +75551,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
75007
75551
  return !status.ok || status.stdout.trim() !== "";
75008
75552
  }, scratchDir = (cwd) => {
75009
75553
  const dir = stacksDir(cwd);
75010
- return dir.ok ? path41.join(dir.data, "worktrees") : null;
75554
+ return dir.ok ? path42.join(dir.data, "worktrees") : null;
75011
75555
  }, firstLine = (run) => (`${run.stderr}
75012
75556
  ${run.stdout}`.split(`
75013
75557
  `).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash16("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
@@ -75022,12 +75566,12 @@ ${run.stdout}`.split(`
75022
75566
  }, isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str3 = (value) => typeof value === "string" && value ? value : null, LOCK_WAIT_MS3 = 2000, HEARTBEAT_MS = 20000, HEARTBEAT_STALE_MS, OWNERLESS_STALE_MS = 30000, OWNER = "owner", readOwner = (lock) => {
75023
75567
  let raw = null;
75024
75568
  try {
75025
- raw = fs20.readFileSync(path41.join(lock, OWNER), "utf8");
75569
+ raw = fs20.readFileSync(path42.join(lock, OWNER), "utf8");
75026
75570
  } catch {
75027
75571
  raw = null;
75028
75572
  }
75029
75573
  let age = null;
75030
- for (const target of [path41.join(lock, OWNER), lock])
75574
+ for (const target of [path42.join(lock, OWNER), lock])
75031
75575
  try {
75032
75576
  age = Date.now() - fs20.statSync(target).mtimeMs;
75033
75577
  break;
@@ -75060,7 +75604,7 @@ var init_stack = __esm(() => {
75060
75604
  });
75061
75605
 
75062
75606
  // packages/workit-core/src/forge/pr-edit.ts
75063
- import { spawnSync as spawnSync20 } from "node:child_process";
75607
+ import { spawnSync as spawnSync21 } from "node:child_process";
75064
75608
  function openPr(cwd, resolved, pr) {
75065
75609
  const grant = requireGrant(cwd, "pr");
75066
75610
  if (!grant.allowed)
@@ -75225,7 +75769,7 @@ function replyToThread(cwd, resolved, input) {
75225
75769
  });
75226
75770
  }
75227
75771
  var label = (resolved, number) => `${resolved.forge.kind === "github" ? "PR #" : "MR !"}${number}`, git6 = (cwd, args) => {
75228
- const run = spawnSync20("git", args, {
75772
+ const run = spawnSync21("git", args, {
75229
75773
  cwd,
75230
75774
  encoding: "utf8",
75231
75775
  timeout: GIT_TIMEOUTS.local,
@@ -75245,15 +75789,15 @@ var init_pr_edit = __esm(() => {
75245
75789
  });
75246
75790
 
75247
75791
  // packages/workit-core/src/forge/exec.ts
75248
- import { spawnSync as spawnSync21 } from "node:child_process";
75792
+ import { spawnSync as spawnSync22 } from "node:child_process";
75249
75793
  import fs21 from "node:fs";
75250
- import path42 from "node:path";
75794
+ import path43 from "node:path";
75251
75795
  function findExecutable(bin, env) {
75252
- const dirs = (env.PATH ?? env.Path ?? "").split(path42.delimiter).filter(Boolean);
75796
+ const dirs = (env.PATH ?? env.Path ?? "").split(path43.delimiter).filter(Boolean);
75253
75797
  const exts = process.platform === "win32" ? [".exe"] : [""];
75254
75798
  for (const dir of dirs)
75255
75799
  for (const ext of exts) {
75256
- const candidate = path42.join(dir, `${bin}${ext}`);
75800
+ const candidate = path43.join(dir, `${bin}${ext}`);
75257
75801
  try {
75258
75802
  const stat = fs21.statSync(candidate);
75259
75803
  if (stat.isFile())
@@ -75280,7 +75824,7 @@ function systemRunner(kind, apiHost, env = process.env) {
75280
75824
  if (!exe)
75281
75825
  return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
75282
75826
  const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
75283
- const result = spawnSync21(exe, [...args], {
75827
+ const result = spawnSync22(exe, [...args], {
75284
75828
  encoding: "utf8",
75285
75829
  env: { ...childEnv, ...token },
75286
75830
  maxBuffer: MAX_BUFFER2,
@@ -76444,7 +76988,7 @@ var init_duration = __esm(() => {
76444
76988
  });
76445
76989
 
76446
76990
  // packages/workit-cli/src/verbs/forge-common.ts
76447
- import { readFileSync as readFileSync19 } from "node:fs";
76991
+ import { readFileSync as readFileSync20 } from "node:fs";
76448
76992
  function parseFlags(argv, spec) {
76449
76993
  const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
76450
76994
  for (let index = 0;index < argv.length; index += 1) {
@@ -76588,7 +77132,7 @@ var init_forge_common = __esm(() => {
76588
77132
  forgeDeps = {
76589
77133
  runner: undefined,
76590
77134
  npm: undefined,
76591
- readStdin: () => readFileSync19(0, "utf8"),
77135
+ readStdin: () => readFileSync20(0, "utf8"),
76592
77136
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
76593
77137
  now: () => Date.now()
76594
77138
  };
@@ -76597,14 +77141,14 @@ var init_forge_common = __esm(() => {
76597
77141
  // packages/workit-cli/src/verbs/pr.ts
76598
77142
  var exports_pr = {};
76599
77143
  __export(exports_pr, {
76600
- run: () => run10
77144
+ run: () => run11
76601
77145
  });
76602
- import { readFileSync as readFileSync20 } from "node:fs";
76603
- import path43 from "node:path";
77146
+ import { readFileSync as readFileSync21 } from "node:fs";
77147
+ import path44 from "node:path";
76604
77148
  function readBodyFile(io, file) {
76605
77149
  try {
76606
77150
  return {
76607
- body: file === "-" ? forgeDeps.readStdin() : readFileSync20(path43.resolve(io.cwd, file), "utf8")
77151
+ body: file === "-" ? forgeDeps.readStdin() : readFileSync21(path44.resolve(io.cwd, file), "utf8")
76608
77152
  };
76609
77153
  } catch {
76610
77154
  return `cannot read --body-file ${file}`;
@@ -76931,7 +77475,7 @@ async function reply(argv, io) {
76931
77475
  `${[data.replied ? "replied to" : null, data.resolved ? "resolved" : null].filter(Boolean).join(" and ")} thread ${data.thread} on ${prLabel(kind, data.number)}${data.replyUrl ? ` ${data.replyUrl}` : ""}`
76932
77476
  ]);
76933
77477
  }
76934
- async function run10(argv, io) {
77478
+ async function run11(argv, io) {
76935
77479
  const [sub, ...rest] = argv;
76936
77480
  if (sub === "status")
76937
77481
  return status(rest, io);
@@ -76966,7 +77510,7 @@ var init_pr = __esm(() => {
76966
77510
  // packages/workit-cli/src/verbs/ci.ts
76967
77511
  var exports_ci = {};
76968
77512
  __export(exports_ci, {
76969
- run: () => run11
77513
+ run: () => run12
76970
77514
  });
76971
77515
  async function wait(argv, io) {
76972
77516
  const flags = parseFlags(argv, {
@@ -77135,7 +77679,7 @@ async function rerun(argv, io) {
77135
77679
  "next: workit ci wait"
77136
77680
  ]);
77137
77681
  }
77138
- async function run11(argv, io) {
77682
+ async function run12(argv, io) {
77139
77683
  const [sub, ...rest] = argv;
77140
77684
  if (sub === "wait")
77141
77685
  return wait(rest, io);
@@ -77183,10 +77727,10 @@ var init_implicit_task = __esm(() => {
77183
77727
  // packages/workit-cli/src/verbs/git.ts
77184
77728
  var exports_git = {};
77185
77729
  __export(exports_git, {
77186
- run: () => run12
77730
+ run: () => run13
77187
77731
  });
77188
- import { readFileSync as readFileSync21 } from "node:fs";
77189
- import path44 from "node:path";
77732
+ import { readFileSync as readFileSync22 } from "node:fs";
77733
+ import path45 from "node:path";
77190
77734
  async function branch(argv, io) {
77191
77735
  const flags = parseFlags(argv, {
77192
77736
  base: "value",
@@ -77277,7 +77821,7 @@ function commitMessage(parsed, io) {
77277
77821
 
77278
77822
  `);
77279
77823
  try {
77280
- return file === "-" ? readFileSync21(0, "utf8") : readFileSync21(path44.resolve(io.cwd, file), "utf8");
77824
+ return file === "-" ? readFileSync22(0, "utf8") : readFileSync22(path45.resolve(io.cwd, file), "utf8");
77281
77825
  } catch (error) {
77282
77826
  return {
77283
77827
  error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
@@ -77380,7 +77924,7 @@ async function push(argv, io) {
77380
77924
  ...data.upstream ? [`upstream: ${data.remote}/${data.branch}`] : []
77381
77925
  ]);
77382
77926
  }
77383
- async function run12(argv, io) {
77927
+ async function run13(argv, io) {
77384
77928
  const [sub, ...rest] = argv;
77385
77929
  if (sub === "branch")
77386
77930
  return branch(rest, io);
@@ -77408,7 +77952,7 @@ var init_git2 = __esm(() => {
77408
77952
  });
77409
77953
 
77410
77954
  // packages/workit-core/src/forge/verify.ts
77411
- import { spawnSync as spawnSync22 } from "node:child_process";
77955
+ import { spawnSync as spawnSync23 } from "node:child_process";
77412
77956
  function finish(cwd, report, actor) {
77413
77957
  const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
77414
77958
  const out = { ...report, delivered };
@@ -77621,7 +78165,7 @@ var NPM_NAME, SEMVER2, SAFE_ARG, systemNpm = (args) => {
77621
78165
  };
77622
78166
  const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
77623
78167
  const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
77624
- const result = process.platform === "win32" ? spawnSync22(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync22("npm", [...args], options);
78168
+ const result = process.platform === "win32" ? spawnSync23(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync23("npm", [...args], options);
77625
78169
  return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
77626
78170
  }, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
77627
78171
  try {
@@ -77646,9 +78190,9 @@ var init_verify = __esm(() => {
77646
78190
  // packages/workit-cli/src/verbs/verify-delivery.ts
77647
78191
  var exports_verify_delivery = {};
77648
78192
  __export(exports_verify_delivery, {
77649
- run: () => run13
78193
+ run: () => run14
77650
78194
  });
77651
- async function run13(argv, io) {
78195
+ async function run14(argv, io) {
77652
78196
  const flags = parseFlags(argv, {
77653
78197
  expect: "value",
77654
78198
  branch: "value",
@@ -77730,7 +78274,7 @@ var init_verify_delivery = __esm(() => {
77730
78274
  // packages/workit-cli/src/verbs/stack.ts
77731
78275
  var exports_stack = {};
77732
78276
  __export(exports_stack, {
77733
- run: () => run14
78277
+ run: () => run15
77734
78278
  });
77735
78279
  async function plan(argv, io) {
77736
78280
  const flags = parseFlags(argv, { name: "value", trunk: "value", track: "value" });
@@ -77912,7 +78456,7 @@ async function land(argv, io) {
77912
78456
  unblock: ${data.stoppedAt.unblock}` : data.complete ? "stack complete" : ""
77913
78457
  ].filter(Boolean));
77914
78458
  }
77915
- async function run14(argv, io) {
78459
+ async function run15(argv, io) {
77916
78460
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
77917
78461
  if (sub === "plan")
77918
78462
  return plan(rest, io);
@@ -77942,10 +78486,10 @@ var init_stack2 = __esm(() => {
77942
78486
  });
77943
78487
 
77944
78488
  // packages/workit-core/src/fanout.ts
77945
- import { spawnSync as spawnSync23 } from "node:child_process";
78489
+ import { spawnSync as spawnSync24 } from "node:child_process";
77946
78490
  import { createHash as createHash17 } from "node:crypto";
77947
78491
  import fs23 from "node:fs";
77948
- import path45 from "node:path";
78492
+ import path46 from "node:path";
77949
78493
  function expandBraces(pattern, limit = 64) {
77950
78494
  for (let index = 0;index < pattern.length; index += 1) {
77951
78495
  if (pattern[index] === "\\") {
@@ -78179,7 +78723,7 @@ function parsePlan(raw, defaults, name) {
78179
78723
  id,
78180
78724
  branch,
78181
78725
  base: optional("base") ?? "",
78182
- worktree: path45.resolve(defaults.root, optional("worktree") ?? path45.join(defaults.worktreeRoot, id)),
78726
+ worktree: path46.resolve(defaults.root, optional("worktree") ?? path46.join(defaults.worktreeRoot, id)),
78183
78727
  tier,
78184
78728
  dependsOn: [...new Set(dependsOn ?? [])],
78185
78729
  scope,
@@ -78408,11 +78952,11 @@ function defaultTrunk(cwd) {
78408
78952
  }
78409
78953
  function mainCheckout(cwd) {
78410
78954
  const common = gitCommonDir(cwd);
78411
- return common && path45.basename(common) === ".git" ? path45.dirname(common) : path45.resolve(cwd);
78955
+ return common && path46.basename(common) === ".git" ? path46.dirname(common) : path46.resolve(cwd);
78412
78956
  }
78413
78957
  function worktreeRoot(cwd) {
78414
78958
  const main = mainCheckout(cwd);
78415
- return path45.join(path45.dirname(main), `${path45.basename(main)}-wt`);
78959
+ return path46.join(path46.dirname(main), `${path46.basename(main)}-wt`);
78416
78960
  }
78417
78961
  function sliceHash(slice) {
78418
78962
  const {
@@ -78430,7 +78974,7 @@ function fanoutsDir(cwd) {
78430
78974
  const root = storeRoot(cwd);
78431
78975
  if (!root.ok)
78432
78976
  return fanoutFail(root.code, root.error, root.unblock);
78433
- return { ok: true, data: path45.join(root.value.root, "fanouts") };
78977
+ return { ok: true, data: path46.join(root.value.root, "fanouts") };
78434
78978
  }
78435
78979
  function parseStored(raw) {
78436
78980
  if (!isRecord8(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -78496,7 +79040,7 @@ function readFanout(cwd, name) {
78496
79040
  const dir = fanoutsDir(cwd);
78497
79041
  if (!dir.ok)
78498
79042
  return dir;
78499
- const file = path45.join(dir.data, stackFileName(name));
79043
+ const file = path46.join(dir.data, stackFileName(name));
78500
79044
  let parsed = null;
78501
79045
  try {
78502
79046
  parsed = parseStored(JSON.parse(fs23.readFileSync(file, "utf8")));
@@ -78521,7 +79065,7 @@ function listFanouts(cwd) {
78521
79065
  for (const entry of names) {
78522
79066
  let parsed = null;
78523
79067
  try {
78524
- parsed = parseStored(JSON.parse(fs23.readFileSync(path45.join(dir.data, entry), "utf8")));
79068
+ parsed = parseStored(JSON.parse(fs23.readFileSync(path46.join(dir.data, entry), "utf8")));
78525
79069
  } catch {
78526
79070
  parsed = null;
78527
79071
  }
@@ -78551,7 +79095,7 @@ function writeFanout(cwd, plan) {
78551
79095
  const dir = fanoutsDir(cwd);
78552
79096
  if (!dir.ok)
78553
79097
  return dir;
78554
- const file = path45.join(dir.data, stackFileName(plan.name));
79098
+ const file = path46.join(dir.data, stackFileName(plan.name));
78555
79099
  const temp = `${file}.${process.pid}.tmp`;
78556
79100
  try {
78557
79101
  fs23.mkdirSync(dir.data, { recursive: true });
@@ -78715,7 +79259,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
78715
79259
  text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
78716
79260
  };
78717
79261
  }, gitRun4 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
78718
- const run = spawnSync23("git", args, {
79262
+ const run = spawnSync24("git", args, {
78719
79263
  cwd,
78720
79264
  encoding: "utf8",
78721
79265
  timeout: timeoutMs,
@@ -78755,7 +79299,7 @@ var init_fanout = __esm(() => {
78755
79299
  // packages/workit-core/src/fanout-worktree.ts
78756
79300
  import { randomBytes as randomBytes4 } from "node:crypto";
78757
79301
  import fs24 from "node:fs";
78758
- import path46 from "node:path";
79302
+ import path47 from "node:path";
78759
79303
  function rowSample(lines) {
78760
79304
  const out = [];
78761
79305
  let bytes = 0;
@@ -78815,7 +79359,7 @@ function adminDir(worktree) {
78815
79359
  const file = dir.stdout.trim();
78816
79360
  let token = null;
78817
79361
  try {
78818
- token = fs24.readFileSync(path46.join(file, TOKEN_FILE), "utf8").trim() || null;
79362
+ token = fs24.readFileSync(path47.join(file, TOKEN_FILE), "utf8").trim() || null;
78819
79363
  } catch {
78820
79364
  token = null;
78821
79365
  }
@@ -78823,7 +79367,7 @@ function adminDir(worktree) {
78823
79367
  const stat = worktreeDeps.stat(file);
78824
79368
  return {
78825
79369
  path: file,
78826
- id: path46.basename(file),
79370
+ id: path47.basename(file),
78827
79371
  ino: stat.ino.toString(),
78828
79372
  token,
78829
79373
  birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
@@ -78839,7 +79383,7 @@ function markAdminDir(worktree) {
78839
79383
  return null;
78840
79384
  const token = randomBytes4(16).toString("hex");
78841
79385
  try {
78842
- fs24.writeFileSync(path46.join(admin.path, TOKEN_FILE), `${token}
79386
+ fs24.writeFileSync(path47.join(admin.path, TOKEN_FILE), `${token}
78843
79387
  `);
78844
79388
  } catch {
78845
79389
  return null;
@@ -78854,7 +79398,7 @@ function excludeScratch(cwd) {
78854
79398
  const common = gitCommonDir(cwd);
78855
79399
  if (!common)
78856
79400
  return;
78857
- const file = path46.join(common, "info", "exclude");
79401
+ const file = path47.join(common, "info", "exclude");
78858
79402
  let text = "";
78859
79403
  try {
78860
79404
  text = fs24.readFileSync(file, "utf8");
@@ -78863,7 +79407,7 @@ function excludeScratch(cwd) {
78863
79407
  }
78864
79408
  if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
78865
79409
  return;
78866
- fs24.mkdirSync(path46.dirname(file), { recursive: true });
79410
+ fs24.mkdirSync(path47.dirname(file), { recursive: true });
78867
79411
  fs24.appendFileSync(file, `${text && !text.endsWith(`
78868
79412
  `) ? `
78869
79413
  ` : ""}# workit fanout: per-worker scratch dirs
@@ -78871,7 +79415,7 @@ ${EXCLUDE_LINE}
78871
79415
  `);
78872
79416
  }
78873
79417
  function ensureScratch(worktree) {
78874
- const scratch = path46.join(worktree, SCRATCH_DIR);
79418
+ const scratch = path47.join(worktree, SCRATCH_DIR);
78875
79419
  fs24.mkdirSync(scratch, { recursive: true });
78876
79420
  return scratch;
78877
79421
  }
@@ -79007,7 +79551,7 @@ function releaseSliceWorktree(cwd, plan, input) {
79007
79551
  const entry = worktrees[index];
79008
79552
  if (index === 0)
79009
79553
  return fanoutFail("blocked", `${target} is the main checkout; it is never released`);
79010
- if (inside2(cwd, entry.path))
79554
+ if (inside3(cwd, entry.path))
79011
79555
  return fanoutFail("blocked", `this command runs inside ${entry.path}`, `run it from the main checkout: workit --cwd <main checkout> fanout worktree release ${slice.id}`);
79012
79556
  if (entry.branch !== null && entry.branch !== `refs/heads/${slice.branch}`)
79013
79557
  return fanoutFail("blocked", `${entry.path} is on ${entry.branch.replace(/^refs\/heads\//u, "")}, not slice ${slice.id}'s ${slice.branch}; nothing was removed`);
@@ -79089,7 +79633,7 @@ function unpushedCount(cwd, branch) {
79089
79633
  return count.ok ? Number(count.stdout.trim()) : null;
79090
79634
  }
79091
79635
  function removeScratch(worktree) {
79092
- const scratch = path46.join(worktree, SCRATCH_DIR);
79636
+ const scratch = path47.join(worktree, SCRATCH_DIR);
79093
79637
  let stat;
79094
79638
  try {
79095
79639
  stat = fs24.lstatSync(scratch);
@@ -79105,16 +79649,16 @@ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000,
79105
79649
  try {
79106
79650
  return fs24.realpathSync.native(target);
79107
79651
  } catch {
79108
- return path46.resolve(target);
79652
+ return path47.resolve(target);
79109
79653
  }
79110
79654
  }, samePath = (a, b) => {
79111
79655
  const left = canonical2(a);
79112
79656
  const right = canonical2(b);
79113
79657
  return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
79114
- }, inside2 = (child, parent) => {
79115
- const relative = path46.relative(canonical2(parent), canonical2(child));
79116
- return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative);
79117
- }, slicePath = (cwd, slice) => path46.isAbsolute(slice.worktree) ? slice.worktree : path46.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, TOKEN_FILE = "workit-fanout-token", worktreeDeps, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
79658
+ }, inside3 = (child, parent) => {
79659
+ const relative = path47.relative(canonical2(parent), canonical2(child));
79660
+ return relative === "" || !relative.startsWith("..") && !path47.isAbsolute(relative);
79661
+ }, slicePath = (cwd, slice) => path47.isAbsolute(slice.worktree) ? slice.worktree : path47.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, TOKEN_FILE = "workit-fanout-token", worktreeDeps, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
79118
79662
  `)[0] || "unknown error"}`);
79119
79663
  var init_fanout_worktree = __esm(() => {
79120
79664
  init_ops();
@@ -79235,7 +79779,7 @@ var init_fanout_brief = __esm(() => {
79235
79779
  });
79236
79780
 
79237
79781
  // packages/workit-core/src/fanout-landed.ts
79238
- import { spawnSync as spawnSync24 } from "node:child_process";
79782
+ import { spawnSync as spawnSync25 } from "node:child_process";
79239
79783
  function forgeBreaker(resolved) {
79240
79784
  if (!resolved)
79241
79785
  return { forge: null, down: () => null };
@@ -79326,7 +79870,7 @@ function trunkPatchIds(ctx, fork) {
79326
79870
  "--"
79327
79871
  ]);
79328
79872
  if (log.ok && log.stdout.trim()) {
79329
- const run = spawnSync24("git", ["patch-id", "--stable"], {
79873
+ const run = spawnSync25("git", ["patch-id", "--stable"], {
79330
79874
  cwd: ctx.cwd,
79331
79875
  input: log.stdout,
79332
79876
  encoding: "utf8",
@@ -79887,10 +80431,10 @@ var init_fanout_status = __esm(() => {
79887
80431
  // packages/workit-cli/src/verbs/fanout.ts
79888
80432
  var exports_fanout = {};
79889
80433
  __export(exports_fanout, {
79890
- run: () => run15
80434
+ run: () => run16
79891
80435
  });
79892
80436
  import fs25 from "node:fs";
79893
- import path47 from "node:path";
80437
+ import path48 from "node:path";
79894
80438
  function forgeFor(io, offline) {
79895
80439
  if (offline)
79896
80440
  return { forge: null, error: "--offline" };
@@ -79951,7 +80495,7 @@ function plan2(argv, io) {
79951
80495
  return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
79952
80496
  if (flags.positionals.length !== 1)
79953
80497
  return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
79954
- const file = path47.resolve(io.cwd, flags.positionals[0]);
80498
+ const file = path48.resolve(io.cwd, flags.positionals[0]);
79955
80499
  let raw;
79956
80500
  try {
79957
80501
  raw = JSON.parse(fs25.readFileSync(file, "utf8"));
@@ -80107,7 +80651,7 @@ function worktree(argv, io) {
80107
80651
  ...data.notes.map((note) => `note: ${note}`)
80108
80652
  ]);
80109
80653
  }
80110
- async function run15(argv, io) {
80654
+ async function run16(argv, io) {
80111
80655
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
80112
80656
  if (sub === "plan")
80113
80657
  return plan2(rest, io);
@@ -80173,7 +80717,7 @@ var init_fanout2 = __esm(() => {
80173
80717
  // packages/workit-cli/src/verbs/ledger.ts
80174
80718
  var exports_ledger = {};
80175
80719
  __export(exports_ledger, {
80176
- run: () => run16
80720
+ run: () => run17
80177
80721
  });
80178
80722
  import { randomBytes as randomBytes5 } from "node:crypto";
80179
80723
  import { parseArgs } from "node:util";
@@ -80197,7 +80741,7 @@ function targetBranch(io, rows, branch, pr) {
80197
80741
  }
80198
80742
  return { ok: true, value: branch ?? null };
80199
80743
  }
80200
- async function run16(argv, io) {
80744
+ async function run17(argv, io) {
80201
80745
  let parsed;
80202
80746
  try {
80203
80747
  parsed = parseArgs({ args: argv, options: OPTIONS, allowPositionals: true, strict: true });
@@ -95434,7 +95978,7 @@ var init_ast = __esm(() => {
95434
95978
 
95435
95979
  // packages/workit-cli/src/test-audit/rules.ts
95436
95980
  import { builtinModules } from "node:module";
95437
- import path48 from "node:path";
95981
+ import path49 from "node:path";
95438
95982
  function testCallee(call) {
95439
95983
  let callee = call.callee;
95440
95984
  if (callee.type === "CallExpression")
@@ -95927,12 +96471,12 @@ function checkConstantsOnly(ctx, emit) {
95927
96471
  }
95928
96472
  function checkOverMocking(ctx, emit) {
95929
96473
  const { program } = ctx.parsed;
95930
- const unit = path48.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
96474
+ const unit = path49.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
95931
96475
  const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
95932
96476
  const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
95933
96477
  for (const call of mocked) {
95934
96478
  const spec = call.arguments[0].value;
95935
- const base = path48.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
96479
+ const base = path49.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
95936
96480
  if (base === unit)
95937
96481
  emit({
95938
96482
  rule: "over-mocking",
@@ -96320,11 +96864,11 @@ var init_rules = __esm(() => {
96320
96864
  });
96321
96865
 
96322
96866
  // packages/workit-cli/src/test-audit/audit.ts
96323
- import { spawnSync as spawnSync25 } from "node:child_process";
96324
- import { existsSync as existsSync19, readdirSync as readdirSync9, readFileSync as readFileSync22, statSync as statSync7 } from "node:fs";
96325
- import path49 from "node:path";
96867
+ import { spawnSync as spawnSync26 } from "node:child_process";
96868
+ import { existsSync as existsSync20, readdirSync as readdirSync9, readFileSync as readFileSync23, statSync as statSync8 } from "node:fs";
96869
+ import path50 from "node:path";
96326
96870
  function git7(cwd, args) {
96327
- const run = spawnSync25("git", ["-c", "core.quotepath=off", ...args], {
96871
+ const run = spawnSync26("git", ["-c", "core.quotepath=off", ...args], {
96328
96872
  cwd,
96329
96873
  encoding: "utf8",
96330
96874
  maxBuffer: 64 * 1024 * 1024
@@ -96335,7 +96879,7 @@ function walkTests(dir, out) {
96335
96879
  for (const entry of readdirSync9(dir, { withFileTypes: true })) {
96336
96880
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
96337
96881
  continue;
96338
- const full = path49.join(dir, entry.name);
96882
+ const full = path50.join(dir, entry.name);
96339
96883
  if (entry.isDirectory()) {
96340
96884
  if (!SKIP_DIRS.has(entry.name))
96341
96885
  walkTests(full, out);
@@ -96347,10 +96891,10 @@ function expandPaths(cwd, inputs) {
96347
96891
  const files = [];
96348
96892
  const missing = [];
96349
96893
  for (const input of inputs.length > 0 ? inputs : ["."]) {
96350
- const full = path49.resolve(cwd, input);
96351
- if (!existsSync19(full))
96894
+ const full = path50.resolve(cwd, input);
96895
+ if (!existsSync20(full))
96352
96896
  missing.push(input);
96353
- else if (statSync7(full).isDirectory())
96897
+ else if (statSync8(full).isDirectory())
96354
96898
  walkTests(full, files);
96355
96899
  else
96356
96900
  files.push(full);
@@ -96384,11 +96928,11 @@ function changedLines(cwd, base) {
96384
96928
  for (let offset = 0;offset < count; offset += 1)
96385
96929
  lines.add(start + offset);
96386
96930
  }
96387
- changed.set(path49.join(root, file), lines);
96931
+ changed.set(path50.join(root, file), lines);
96388
96932
  }
96389
96933
  const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
96390
96934
  for (const file of untracked.stdout.split("\x00").filter(Boolean))
96391
- changed.set(path49.join(root, file), "all");
96935
+ changed.set(path50.join(root, file), "all");
96392
96936
  return changed;
96393
96937
  }
96394
96938
  function auditFiles(files, display) {
@@ -96401,7 +96945,7 @@ function auditFiles(files, display) {
96401
96945
  const shown = display(file);
96402
96946
  let parsed;
96403
96947
  try {
96404
- parsed = parseSource(readFileSync22(file, "utf8"), file);
96948
+ parsed = parseSource(readFileSync23(file, "utf8"), file);
96405
96949
  } catch (error) {
96406
96950
  parseErrors.push({
96407
96951
  file: shown,
@@ -96450,7 +96994,7 @@ function auditFiles(files, display) {
96450
96994
  durationMs: Math.round(performance.now() - started)
96451
96995
  };
96452
96996
  }
96453
- var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path49.sep).join("/"), SEVERITY_ORDER, ruleIds;
96997
+ var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path50.sep).join("/"), SEVERITY_ORDER, ruleIds;
96454
96998
  var init_audit = __esm(() => {
96455
96999
  init_ast();
96456
97000
  init_rules();
@@ -96464,23 +97008,23 @@ var init_audit = __esm(() => {
96464
97008
  // packages/workit-cli/src/test-audit/mutate.ts
96465
97009
  import { spawn as spawn2 } from "node:child_process";
96466
97010
  import {
96467
- existsSync as existsSync20,
96468
- lstatSync as lstatSync7,
96469
- mkdirSync as mkdirSync9,
97011
+ existsSync as existsSync21,
97012
+ lstatSync as lstatSync8,
97013
+ mkdirSync as mkdirSync10,
96470
97014
  mkdtempSync as mkdtempSync4,
96471
97015
  readdirSync as readdirSync10,
96472
- readFileSync as readFileSync23,
97016
+ readFileSync as readFileSync24,
96473
97017
  readlinkSync as readlinkSync3,
96474
97018
  realpathSync as realpathSync9,
96475
- renameSync as renameSync5,
96476
- rmSync as rmSync7,
96477
- statSync as statSync8,
97019
+ renameSync as renameSync6,
97020
+ rmSync as rmSync8,
97021
+ statSync as statSync9,
96478
97022
  symlinkSync,
96479
- writeFileSync as writeFileSync10,
97023
+ writeFileSync as writeFileSync11,
96480
97024
  copyFileSync as copyFileSync6
96481
97025
  } from "node:fs";
96482
97026
  import os14 from "node:os";
96483
- import path50 from "node:path";
97027
+ import path51 from "node:path";
96484
97028
  function mutantsFor(source, file, lines) {
96485
97029
  const parsed = parseSource(source, file);
96486
97030
  const out = [];
@@ -96553,7 +97097,7 @@ function relatedTests(source, testFiles) {
96553
97097
  for (const match of text.matchAll(SPECIFIER)) {
96554
97098
  const spec = match[1];
96555
97099
  if (spec.startsWith(".")) {
96556
- if (stripExt(path50.posix.join(path50.posix.dirname(file), spec)) === target)
97100
+ if (stripExt(path51.posix.join(path51.posix.dirname(file), spec)) === target)
96557
97101
  return true;
96558
97102
  } else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
96559
97103
  return true;
@@ -96573,12 +97117,12 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
96573
97117
  for (const name of names) {
96574
97118
  if (!name.startsWith(TEMP_PREFIX))
96575
97119
  continue;
96576
- const dir = path50.join(tmp, name);
97120
+ const dir = path51.join(tmp, name);
96577
97121
  try {
96578
- const stat = lstatSync7(dir);
97122
+ const stat = lstatSync8(dir);
96579
97123
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
96580
97124
  continue;
96581
- const pid = Number(readFileSync23(path50.join(dir, PID_FILE), "utf8"));
97125
+ const pid = Number(readFileSync24(path51.join(dir, PID_FILE), "utf8"));
96582
97126
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
96583
97127
  continue;
96584
97128
  } catch (error) {
@@ -96586,23 +97130,23 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
96586
97130
  continue;
96587
97131
  }
96588
97132
  try {
96589
- rmSync7(dir, { recursive: true, force: true });
97133
+ rmSync8(dir, { recursive: true, force: true });
96590
97134
  removed += 1;
96591
97135
  } catch {}
96592
97136
  }
96593
97137
  return removed;
96594
97138
  }
96595
97139
  function linkNodeModules(origin, target, repo, copy) {
96596
- mkdirSync9(target, { recursive: true });
97140
+ mkdirSync10(target, { recursive: true });
96597
97141
  for (const entry of readdirSync10(origin, { withFileTypes: true })) {
96598
- const from = path50.join(origin, entry.name);
96599
- const to = path50.join(target, entry.name);
97142
+ const from = path51.join(origin, entry.name);
97143
+ const to = path51.join(target, entry.name);
96600
97144
  if (entry.name.startsWith("@") && entry.isDirectory()) {
96601
97145
  linkNodeModules(from, to, repo, copy);
96602
97146
  continue;
96603
97147
  }
96604
97148
  if (entry.name.startsWith(".") && entry.name !== ".bin" && entry.isDirectory()) {
96605
- mkdirSync9(to, { recursive: true });
97149
+ mkdirSync10(to, { recursive: true });
96606
97150
  continue;
96607
97151
  }
96608
97152
  if (entry.isSymbolicLink()) {
@@ -96612,9 +97156,9 @@ function linkNodeModules(origin, target, repo, copy) {
96612
97156
  } catch {
96613
97157
  continue;
96614
97158
  }
96615
- const relative = path50.relative(repo, real);
96616
- if (!relative.startsWith("..") && !path50.isAbsolute(relative) && !relative.split(path50.sep).includes("node_modules")) {
96617
- symlinkSync(path50.join(copy, relative), to, "junction");
97159
+ const relative = path51.relative(repo, real);
97160
+ if (!relative.startsWith("..") && !path51.isAbsolute(relative) && !relative.split(path51.sep).includes("node_modules")) {
97161
+ symlinkSync(path51.join(copy, relative), to, "junction");
96618
97162
  continue;
96619
97163
  }
96620
97164
  }
@@ -96629,33 +97173,33 @@ function copySymlink(repo, tree, from, to) {
96629
97173
  real = null;
96630
97174
  }
96631
97175
  const realRepo = realpathSync9(repo);
96632
- if (real && inside3(realRepo, real)) {
96633
- symlinkSync(path50.relative(path50.dirname(to), path50.join(tree, path50.relative(realRepo, real))), to);
97176
+ if (real && inside4(realRepo, real)) {
97177
+ symlinkSync(path51.relative(path51.dirname(to), path51.join(tree, path51.relative(realRepo, real))), to);
96634
97178
  return;
96635
97179
  }
96636
- if (real && statSync8(real).isFile())
97180
+ if (real && statSync9(real).isFile())
96637
97181
  copyFileSync6(real, to);
96638
97182
  else
96639
97183
  symlinkSync(readlinkSync3(from), to);
96640
97184
  }
96641
97185
  function writeInside(copy, file, content) {
96642
- const target = path50.join(copy, file);
97186
+ const target = path51.join(copy, file);
96643
97187
  try {
96644
- const stat = lstatSync7(target);
96645
- if (!stat.isFile() || !inside3(realpathSync9(copy), realpathSync9(path50.dirname(target))))
97188
+ const stat = lstatSync8(target);
97189
+ if (!stat.isFile() || !inside4(realpathSync9(copy), realpathSync9(path51.dirname(target))))
96646
97190
  return false;
96647
97191
  } catch {
96648
97192
  return false;
96649
97193
  }
96650
97194
  const temp = `${target}.workit-mutant-${process.pid}`;
96651
- writeFileSync10(temp, content);
96652
- renameSync5(temp, target);
97195
+ writeFileSync11(temp, content);
97196
+ renameSync6(temp, target);
96653
97197
  return true;
96654
97198
  }
96655
97199
  function copyWorkingTree(repo) {
96656
- const dir = mkdtempSync4(path50.join(os14.tmpdir(), TEMP_PREFIX));
96657
- writeFileSync10(path50.join(dir, PID_FILE), String(process.pid));
96658
- const tree = path50.join(dir, "tree");
97200
+ const dir = mkdtempSync4(path51.join(os14.tmpdir(), TEMP_PREFIX));
97201
+ writeFileSync11(path51.join(dir, PID_FILE), String(process.pid));
97202
+ const tree = path51.join(dir, "tree");
96659
97203
  const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
96660
97204
  if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
96661
97205
  const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
@@ -96666,35 +97210,35 @@ function copyWorkingTree(repo) {
96666
97210
  git7(tree, ["update-ref", "--no-deref", "HEAD", head]);
96667
97211
  git7(tree, ["reset", "-q"]);
96668
97212
  } else
96669
- mkdirSync9(tree, { recursive: true });
97213
+ mkdirSync10(tree, { recursive: true });
96670
97214
  const listed = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00");
96671
97215
  const dirs = new Set([""]);
96672
97216
  for (const relative of listed) {
96673
97217
  if (!relative)
96674
97218
  continue;
96675
- const from = path50.join(repo, relative);
97219
+ const from = path51.join(repo, relative);
96676
97220
  let stat;
96677
97221
  try {
96678
- stat = lstatSync7(from);
97222
+ stat = lstatSync8(from);
96679
97223
  } catch {
96680
97224
  continue;
96681
97225
  }
96682
- const to = path50.join(tree, relative);
96683
- mkdirSync9(path50.dirname(to), { recursive: true });
97226
+ const to = path51.join(tree, relative);
97227
+ mkdirSync10(path51.dirname(to), { recursive: true });
96684
97228
  if (stat.isSymbolicLink())
96685
97229
  copySymlink(repo, tree, from, to);
96686
97230
  else if (stat.isFile())
96687
97231
  copyFileSync6(from, to);
96688
- let parent = path50.dirname(relative);
97232
+ let parent = path51.dirname(relative);
96689
97233
  while (parent !== "." && !dirs.has(parent)) {
96690
97234
  dirs.add(parent);
96691
- parent = path50.dirname(parent);
97235
+ parent = path51.dirname(parent);
96692
97236
  }
96693
97237
  }
96694
97238
  for (const parent of dirs) {
96695
- const modules = path50.join(repo, parent, "node_modules");
96696
- if (existsSync20(modules) && !existsSync20(path50.join(tree, parent, "node_modules")))
96697
- linkNodeModules(modules, path50.join(tree, parent, "node_modules"), repo, tree);
97239
+ const modules = path51.join(repo, parent, "node_modules");
97240
+ if (existsSync21(modules) && !existsSync21(path51.join(tree, parent, "node_modules")))
97241
+ linkNodeModules(modules, path51.join(tree, parent, "node_modules"), repo, tree);
96698
97242
  }
96699
97243
  return { dir, tree };
96700
97244
  }
@@ -96733,20 +97277,20 @@ async function runMutation(options) {
96733
97277
  const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
96734
97278
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
96735
97279
  try {
96736
- return { file, text: readFileSync23(path50.join(repo, file), "utf8") };
97280
+ return { file, text: readFileSync24(path51.join(repo, file), "utf8") };
96737
97281
  } catch {
96738
97282
  return { file, text: "" };
96739
97283
  }
96740
97284
  });
96741
97285
  const candidates = [];
96742
97286
  for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
96743
- const relative = toPosix(path50.relative(repo, absolute));
97287
+ const relative = toPosix(path51.relative(repo, absolute));
96744
97288
  if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
96745
97289
  continue;
96746
- if (NOT_SOURCE.test(relative) || !existsSync20(absolute))
97290
+ if (NOT_SOURCE.test(relative) || !existsSync21(absolute))
96747
97291
  continue;
96748
97292
  try {
96749
- candidates.push(...mutantsFor(readFileSync23(absolute, "utf8"), relative, lines));
97293
+ candidates.push(...mutantsFor(readFileSync24(absolute, "utf8"), relative, lines));
96750
97294
  } catch {}
96751
97295
  }
96752
97296
  const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
@@ -96754,9 +97298,9 @@ async function runMutation(options) {
96754
97298
  for (const mutant of mutants)
96755
97299
  if (!related.has(mutant.file))
96756
97300
  related.set(mutant.file, relatedTests(mutant.file, testFiles));
96757
- const cwdRelative = path50.relative(repo, options.cwd);
97301
+ const cwdRelative = path51.relative(repo, options.cwd);
96758
97302
  const asArg = (file) => {
96759
- const relative = toPosix(path50.relative(cwdRelative, file));
97303
+ const relative = toPosix(path51.relative(cwdRelative, file));
96760
97304
  return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
96761
97305
  };
96762
97306
  const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
@@ -96791,7 +97335,7 @@ async function runMutation(options) {
96791
97335
  return finish2(options, 0, results, counts, started);
96792
97336
  removeStaleCopies();
96793
97337
  const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
96794
- const copyCwd = path50.join(copy, cwdRelative);
97338
+ const copyCwd = path51.join(copy, cwdRelative);
96795
97339
  let current = null;
96796
97340
  const restore = () => {
96797
97341
  if (current)
@@ -96802,7 +97346,7 @@ async function runMutation(options) {
96802
97346
  for (const child of active)
96803
97347
  killTree(child);
96804
97348
  restore();
96805
- rmSync7(copyDir, { recursive: true, force: true });
97349
+ rmSync8(copyDir, { recursive: true, force: true });
96806
97350
  };
96807
97351
  const onSignal = (signal) => {
96808
97352
  cleanup();
@@ -96826,7 +97370,7 @@ async function runMutation(options) {
96826
97370
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
96827
97371
  continue;
96828
97372
  }
96829
- const original = readFileSync23(path50.join(copy, mutant.file), "utf8");
97373
+ const original = readFileSync24(path51.join(copy, mutant.file), "utf8");
96830
97374
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
96831
97375
  current = { file: mutant.file, original };
96832
97376
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -96882,9 +97426,9 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
96882
97426
  } catch (error) {
96883
97427
  return error.code === "EPERM";
96884
97428
  }
96885
- }, inside3 = (root, file) => {
96886
- const relative = path50.relative(root, file);
96887
- return relative === "" || !relative.startsWith("..") && !path50.isAbsolute(relative);
97429
+ }, inside4 = (root, file) => {
97430
+ const relative = path51.relative(root, file);
97431
+ return relative === "" || !relative.startsWith("..") && !path51.isAbsolute(relative);
96888
97432
  }, active, killTree = (child) => {
96889
97433
  try {
96890
97434
  if (child.pid && process.platform !== "win32")
@@ -96930,10 +97474,10 @@ var init_mutate = __esm(() => {
96930
97474
  // packages/workit-cli/src/verbs/test-audit.ts
96931
97475
  var exports_test_audit = {};
96932
97476
  __export(exports_test_audit, {
96933
- run: () => run17
97477
+ run: () => run18
96934
97478
  });
96935
- import { existsSync as existsSync21 } from "node:fs";
96936
- import path51 from "node:path";
97479
+ import { existsSync as existsSync22 } from "node:fs";
97480
+ import path52 from "node:path";
96937
97481
  function parse7(argv) {
96938
97482
  const options = {
96939
97483
  paths: [],
@@ -97031,14 +97575,14 @@ function human(data) {
97031
97575
  }
97032
97576
  return lines;
97033
97577
  }
97034
- async function run17(argv, io) {
97578
+ async function run18(argv, io) {
97035
97579
  const options = parse7(argv);
97036
97580
  if (typeof options === "string")
97037
97581
  return emit(io, fail("invalid_input", `${options}
97038
97582
  usage: ${USAGE10}`));
97039
97583
  const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
97040
- const repo = top.ok ? path51.resolve(top.stdout.trim()) : null;
97041
- const display = (file) => toPosix(path51.relative(io.cwd, file) || file);
97584
+ const repo = top.ok ? path52.resolve(top.stdout.trim()) : null;
97585
+ const display = (file) => toPosix(path52.relative(io.cwd, file) || file);
97042
97586
  let base = null;
97043
97587
  let changed = null;
97044
97588
  if (options.diff || options.mutate) {
@@ -97055,7 +97599,7 @@ usage: ${USAGE10}`));
97055
97599
  }
97056
97600
  let files;
97057
97601
  if (options.diff && changed)
97058
- files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync21(file)).toSorted();
97602
+ files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync22(file)).toSorted();
97059
97603
  else {
97060
97604
  const expanded = expandPaths(io.cwd, options.paths);
97061
97605
  if (expanded.missing.length > 0)
@@ -97142,9 +97686,9 @@ var init_test_audit = __esm(() => {
97142
97686
  // packages/workit-cli/src/verbs/knowledge.ts
97143
97687
  var exports_knowledge = {};
97144
97688
  __export(exports_knowledge, {
97145
- run: () => run18
97689
+ run: () => run19
97146
97690
  });
97147
- async function run18(argv, io) {
97691
+ async function run19(argv, io) {
97148
97692
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
97149
97693
  if (sub !== "lint" || rest.length > 0)
97150
97694
  return emit(io, fail("invalid_input", `usage: ${USAGE11}`));
@@ -97278,11 +97822,11 @@ var init_youtrack_writes = __esm(() => {
97278
97822
  // packages/workit-cli/src/verbs/youtrack.ts
97279
97823
  var exports_youtrack = {};
97280
97824
  __export(exports_youtrack, {
97281
- run: () => run19
97825
+ run: () => run20
97282
97826
  });
97283
- import { readFileSync as readFileSync24 } from "node:fs";
97284
- import path52 from "node:path";
97285
- async function run19(argv, io) {
97827
+ import { readFileSync as readFileSync25 } from "node:fs";
97828
+ import path53 from "node:path";
97829
+ async function run20(argv, io) {
97286
97830
  const [sub, ...rest] = argv;
97287
97831
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
97288
97832
  return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE12);
@@ -97327,7 +97871,7 @@ async function run19(argv, io) {
97327
97871
  let markdown = flags.values.markdown ?? "";
97328
97872
  if (flags.values.file)
97329
97873
  try {
97330
- markdown = readFileSync24(path52.resolve(io.cwd, flags.values.file), "utf8");
97874
+ markdown = readFileSync25(path53.resolve(io.cwd, flags.values.file), "utf8");
97331
97875
  } catch (error) {
97332
97876
  return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE12);
97333
97877
  }
@@ -97393,11 +97937,11 @@ var init_youtrack2 = __esm(() => {
97393
97937
  // packages/workit-cli/src/verbs/changelog.ts
97394
97938
  var exports_changelog = {};
97395
97939
  __export(exports_changelog, {
97396
- run: () => run20
97940
+ run: () => run21
97397
97941
  });
97398
- import { readFileSync as readFileSync25 } from "node:fs";
97399
- import path53 from "node:path";
97400
- async function run20(argv, io) {
97942
+ import { readFileSync as readFileSync26 } from "node:fs";
97943
+ import path54 from "node:path";
97944
+ async function run21(argv, io) {
97401
97945
  const [sub, ...rest] = argv;
97402
97946
  if (sub !== "apply")
97403
97947
  return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE13);
@@ -97442,7 +97986,7 @@ async function run20(argv, io) {
97442
97986
  ]);
97443
97987
  }
97444
97988
  var USAGE13 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
97445
- const text = raw === "-" ? readFileSync25(0, "utf8") : raw.startsWith("@") ? readFileSync25(path53.resolve(cwd, raw.slice(1)), "utf8") : raw;
97989
+ const text = raw === "-" ? readFileSync26(0, "utf8") : raw.startsWith("@") ? readFileSync26(path54.resolve(cwd, raw.slice(1)), "utf8") : raw;
97446
97990
  return JSON.parse(text);
97447
97991
  };
97448
97992
  var init_changelog2 = __esm(() => {
@@ -97454,7 +97998,7 @@ var init_changelog2 = __esm(() => {
97454
97998
  // packages/workit-cli/src/verbs/handoff.ts
97455
97999
  var exports_handoff = {};
97456
98000
  __export(exports_handoff, {
97457
- run: () => run21
98001
+ run: () => run22
97458
98002
  });
97459
98003
  import { parseArgs as parseArgs2 } from "node:util";
97460
98004
  function human2(brief) {
@@ -97524,7 +98068,7 @@ async function resumeBrief(argv, io) {
97524
98068
  }
97525
98069
  return emit(io, ok({ ...data, recorded }), human2);
97526
98070
  }
97527
- async function run21(argv, io) {
98071
+ async function run22(argv, io) {
97528
98072
  if (!argv.some((arg) => arg === "--task" || arg.startsWith("--task=")))
97529
98073
  return resumeBrief(argv, io);
97530
98074
  await Promise.resolve().then(() => init_task());
@@ -97538,7 +98082,7 @@ var init_handoff = __esm(() => {
97538
98082
 
97539
98083
  // packages/workit-cli/src/main.ts
97540
98084
  init_package();
97541
- import path54 from "node:path";
98085
+ import path55 from "node:path";
97542
98086
 
97543
98087
  // packages/workit-cli/src/help.ts
97544
98088
  var HELP_FLAGS = new Set(["--help", "-h"]);
@@ -97700,7 +98244,7 @@ var familySubcommands = (name) => {
97700
98244
  const implicit = name === "task" ? TASK_IMPLICIT : [];
97701
98245
  const grammar = Object.entries(FAMILY_ACTIONS[name]).filter(([action]) => !implicit.some((entry) => entry.name === action)).map(([action, summary]) => ({
97702
98246
  name: action,
97703
- usage: name === "policy" && action !== "explain" ? `workit policy ${action} ${POLICY_JUDGE} ${FAMILY_OPTIONS}` : `workit ${name} ${action} ${FAMILY_OPTIONS}`,
98247
+ usage: name === "policy" && action !== "explain" ? `workit policy ${action} ${POLICY_JUDGE} ${FAMILY_OPTIONS}` : name === "worker" && action !== "assign" ? `workit worker ${action} [<workerId>] ${FAMILY_OPTIONS}` : `workit ${name} ${action} ${FAMILY_OPTIONS}`,
97704
98248
  summary
97705
98249
  }));
97706
98250
  return [...implicit, ...grammar];
@@ -97757,8 +98301,8 @@ var VERBS = [
97757
98301
  {
97758
98302
  name: "doctor",
97759
98303
  group: "setup",
97760
- usage: "workit doctor [--fix-lock [--force [--yes]]]",
97761
- summary: "Verify the offline installation health (--fix-lock clears a stale workit store lock)",
98304
+ usage: "workit doctor [--fix] [--fix-lock [--force [--yes]]]",
98305
+ summary: "Verify the offline installation health (--fix installs the session trailer hook; --fix-lock clears a stale workit store lock)",
97762
98306
  load: () => Promise.resolve().then(() => (init_doctor2(), exports_doctor))
97763
98307
  },
97764
98308
  {
@@ -98184,7 +98728,7 @@ var defaultIo = (json) => ({
98184
98728
  });
98185
98729
  async function main(argv, overrides = {}, options = {}) {
98186
98730
  const parsed = parseGlobals(argv);
98187
- const cwd = path54.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
98731
+ const cwd = path55.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
98188
98732
  const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
98189
98733
  if (parsed.error)
98190
98734
  return emit(io, fail("invalid_input", parsed.error));