@brainervirus/workit-cli 8.7.0 → 8.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1023 -570
  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.7.0",
68
+ version: "8.9.0",
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.7.0",
107
- "@brainervirus/workit-cursor": "^8.7.0",
108
- "@brainervirus/workit-opencode": "^8.7.0",
106
+ "@brainervirus/workit-core": "^8.9.0",
107
+ "@brainervirus/workit-cursor": "^8.9.0",
108
+ "@brainervirus/workit-opencode": "^8.9.0",
109
109
  "@inkjs/ui": "2.0.0",
110
110
  ink: "7.1.1",
111
111
  react: "19.2.8"
@@ -15136,6 +15136,9 @@ function summarizeRow(row) {
15136
15136
  case "pr.created":
15137
15137
  summary = `#${row.pr ?? "?"} -> ${text("base")} at ${(row.head ?? "?").slice(0, 12)}${row.created === false ? " (existing)" : ""}`;
15138
15138
  break;
15139
+ case "pr.status":
15140
+ summary = `#${row.pr ?? "?"} ${text("state") && text("state") !== "open" ? `${text("state")} ` : ""}checks ${text("checks") || "?"} at ${(row.head ?? "?").slice(0, 12)}`;
15141
+ break;
15139
15142
  case "pr.merged":
15140
15143
  summary = `#${row.pr ?? "?"} ${text("method")} at ${(row.head ?? "?").slice(0, 12)}${text("mergeSha") ? ` -> ${text("mergeSha").slice(0, 12)}` : ""}`;
15141
15144
  break;
@@ -63192,7 +63195,7 @@ function cursorHookDrift(installed, pluginDir) {
63192
63195
  if (pluginDir)
63193
63196
  commands.push(canonicalHookEntry(event, pluginDir).command);
63194
63197
  const entry = Array.isArray(list) ? list[0] : undefined;
63195
- if (!isRecord4(entry) || !commands.includes(entry.command) || entry.failClosed === true || event === "preToolUse" && entry.matcher !== canonical.matcher)
63198
+ if (!isRecord4(entry) || !commands.includes(entry.command) || entry.failClosed === true || event === "stop" && entry.loop_limit !== canonical.loop_limit || event === "preToolUse" && entry.matcher !== canonical.matcher)
63196
63199
  drift.push(event);
63197
63200
  }
63198
63201
  return drift;
@@ -63215,7 +63218,11 @@ function cursorHooksEntry(pluginDir) {
63215
63218
  }
63216
63219
  var OPENCODE_NPM_PIN = "@brainervirus/workit-opencode", isRecord4 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), named = (s, name) => s === name || s.startsWith(`${name}@`), CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@latest", cursorHookLauncher = (pluginDir) => pluginDir ? `node "${shellQuotedPath(path26.join(pluginDir, "hooks", "launch.mjs"))}"` : 'node "${CURSOR_PLUGIN_ROOT}/hooks/launch.mjs"', shellQuotedPath = (file) => path26.sep === "\\" ? file : file.replace(/([$`"\\])/g, "\\$1"), CURSOR_HOOK_RUN_COMMAND, CURSOR_PRETOOLUSE_MATCHER = "Write|Edit|Delete|Shell|Remove|ApplyPatch|Apply_Patch|Patch|Rename|Mkdir|Mv|Cp|Touch", CURSOR_HOOK_EVENTS, canonicalHookEntry = (event, pluginDir) => {
63217
63220
  const command = `${cursorHookLauncher(pluginDir)} workit-cursor-hook`;
63218
- return event === "preToolUse" ? { command, matcher: CURSOR_PRETOOLUSE_MATCHER, failClosed: false } : { command, failClosed: false };
63221
+ if (event === "preToolUse")
63222
+ return { command, matcher: CURSOR_PRETOOLUSE_MATCHER, failClosed: false };
63223
+ if (event === "stop")
63224
+ return { command, loop_limit: 1, failClosed: false };
63225
+ return { command, failClosed: false };
63219
63226
  };
63220
63227
  var init_registration = __esm(() => {
63221
63228
  CURSOR_HOOK_RUN_COMMAND = `${cursorHookLauncher()} workit-cursor-hook`;
@@ -63224,6 +63231,7 @@ var init_registration = __esm(() => {
63224
63231
  "beforeShellExecution",
63225
63232
  "subagentStart",
63226
63233
  "subagentStop",
63234
+ "stop",
63227
63235
  "preCompact"
63228
63236
  ];
63229
63237
  });
@@ -63930,30 +63938,353 @@ var init_skill_manifests = __esm(() => {
63930
63938
  if (false) {}
63931
63939
  });
63932
63940
 
63933
- // packages/workit-cli/src/admin/doctor.ts
63941
+ // packages/workit-core/src/git/session-hook.ts
63934
63942
  import { spawnSync as spawnSync13 } from "node:child_process";
63935
- import { createHash as createHash13 } from "node:crypto";
63936
63943
  import {
63944
+ chmodSync,
63937
63945
  existsSync as existsSync13,
63938
63946
  lstatSync as lstatSync4,
63939
63947
  mkdirSync as mkdirSync5,
63940
- mkdtempSync,
63941
- readdirSync as readdirSync7,
63942
63948
  readFileSync as readFileSync13,
63943
- readlinkSync as readlinkSync2,
63949
+ renameSync as renameSync3,
63944
63950
  rmSync as rmSync2,
63945
63951
  statSync as statSync4,
63946
- unlinkSync as unlinkSync3,
63947
63952
  writeFileSync as writeFileSync6
63948
63953
  } from "node:fs";
63949
- import os8 from "node:os";
63950
63954
  import path30 from "node:path";
63955
+ function inspectSessionHook(cwd, env = process.env) {
63956
+ const none = { hooksDir: null, hookPath: null, helperPath: null, helperCurrent: false };
63957
+ if (!existsSync13(cwd))
63958
+ return { state: "not_git", ...none };
63959
+ const top = run2(cwd, ["rev-parse", "--show-toplevel"], env);
63960
+ if (!top.ok || !top.out)
63961
+ return { state: "not_git", ...none };
63962
+ const paths = run2(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir", "--git-path", "hooks"], env);
63963
+ const [commonDir, hooksDir] = paths.out.split(/\r?\n/);
63964
+ if (!paths.ok || !commonDir || !hooksDir)
63965
+ return { state: "not_git", ...none };
63966
+ const hookPath = path30.join(hooksDir, SESSION_HOOK_NAME);
63967
+ const helperPath = path30.join(commonDir, ...HELPER);
63968
+ const helperCurrent = readText(helperPath) === SESSION_TRAILER_SCRIPT;
63969
+ const leftover = leftoverOf(hooksDir);
63970
+ const base = { hooksDir, hookPath, helperPath, helperCurrent, ...leftover ? { leftover } : {} };
63971
+ const blocked = (reason, owner, text = "") => {
63972
+ const old = `${hookPath}.old`;
63973
+ const displaced = readText(old)?.includes(SESSION_HOOK_MARKER) ? old : null;
63974
+ const input = { owner, text, hooksDir, hookPath, top: top.out, displaced };
63975
+ const manualTarget = manualTargetOf(input);
63976
+ const owned = text ? { owner } : {};
63977
+ if (manualInPlace(input, manualTarget))
63978
+ return {
63979
+ state: "manual",
63980
+ ...base,
63981
+ ...owned,
63982
+ manualTarget,
63983
+ via: owner === "other" ? "existing hook" : owner
63984
+ };
63985
+ return {
63986
+ state: "blocked",
63987
+ ...base,
63988
+ ...owned,
63989
+ reason: displaced ? `${reason} (Workit's earlier hook was moved to ${old})` : reason,
63990
+ manual: manualFor(input),
63991
+ manualTarget
63992
+ };
63993
+ };
63994
+ if (!inside2(hooksDir, commonDir) && !inside2(hooksDir, top.out))
63995
+ return blocked(`core.hooksPath ${hooksDir} is outside the repository; a hook there runs in every repository that uses it`, "other");
63996
+ const text = exists(hookPath) ? readText(hookPath) ?? "" : null;
63997
+ if (text !== null) {
63998
+ if (text === SESSION_HOOK_SCRIPT)
63999
+ return { state: helperCurrent ? "installed" : "outdated", ...base };
64000
+ if (text.includes(SESSION_HOOK_MARKER))
64001
+ return { state: "outdated", ...base };
64002
+ const owner = ownerOf(text, hooksDir, top.out);
64003
+ return blocked(`${hookPath} belongs to ${owner === "other" ? "another hook" : owner}; Workit never replaces it`, owner, text);
64004
+ }
64005
+ if (!inside2(hooksDir, commonDir)) {
64006
+ const rel = path30.relative(top.out, hookPath).split(path30.sep).join("/");
64007
+ if (!run2(top.out, ["check-ignore", "-q", "--", rel], env).ok)
64008
+ return blocked(`${hooksDir} is in the working tree and not ignored; the hook would show up as a new file`, "other");
64009
+ }
64010
+ return { state: "missing", ...base };
64011
+ }
64012
+ function installSessionHook(cwd, env = process.env) {
64013
+ const status = inspectSessionHook(cwd, env);
64014
+ if (status.state === "not_git")
64015
+ return {
64016
+ action: "skipped",
64017
+ helperWritten: false,
64018
+ status,
64019
+ detail: "not inside a git work tree"
64020
+ };
64021
+ const helperWritten = !status.helperCurrent;
64022
+ if (helperWritten)
64023
+ writeExecutable(status.helperPath, SESSION_TRAILER_SCRIPT);
64024
+ const helper = helperWritten ? `; wrote ${status.helperPath}` : "";
64025
+ if (status.state === "installed")
64026
+ return {
64027
+ action: "unchanged",
64028
+ helperWritten,
64029
+ status,
64030
+ detail: `hook already at ${status.hookPath}`
64031
+ };
64032
+ if (status.state === "manual")
64033
+ return {
64034
+ action: "unchanged",
64035
+ helperWritten,
64036
+ status: inspectSessionHook(cwd, env),
64037
+ detail: `${status.manualTarget} already runs the helper (via ${status.via})${helper}`
64038
+ };
64039
+ if (status.state === "blocked")
64040
+ return {
64041
+ action: "skipped",
64042
+ helperWritten,
64043
+ status: inspectSessionHook(cwd, env),
64044
+ detail: `${status.reason}${helper}`
64045
+ };
64046
+ const fresh = status.state === "missing";
64047
+ const slotCurrent = readText(status.hookPath) === SESSION_HOOK_SCRIPT;
64048
+ if (!slotCurrent)
64049
+ writeExecutable(status.hookPath, SESSION_HOOK_SCRIPT);
64050
+ const verb = fresh ? "installed" : "updated";
64051
+ return {
64052
+ action: verb,
64053
+ helperWritten,
64054
+ status: inspectSessionHook(cwd, env),
64055
+ detail: `${verb} the Workit ${SESSION_HOOK_NAME} hook at ${status.hookPath}${helper}`
64056
+ };
64057
+ }
64058
+ 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
64059
+ # Workit (https://github.com/BrainerVirus/workit) wrote this file with
64060
+ # \`workit doctor --fix\`. A prepare-commit-msg hook runs it with its own
64061
+ # arguments to add a Workit-Session trailer to commits made in an agent session.
64062
+ # Not a hook itself; delete it together with the line that runs it.
64063
+ # lefthook passes a missing argument as its literal placeholder.
64064
+ case "\${2-}" in "" | "{2}" | template) exit 0 ;; esac
64065
+ gitdir=$(git rev-parse --git-dir) || exit 0
64066
+ if [ -d "$gitdir/rebase-merge" ] || [ -d "$gitdir/rebase-apply" ] || [ -f "$gitdir/CHERRY_PICK_HEAD" ]; then
64067
+ exit 0
64068
+ fi
64069
+ trim() { printf '%s' "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'; }
64070
+ inner=$(trim "\${OPENCODE_SESSION_ID-}"); inner_host=opencode
64071
+ if [ -z "$inner" ]; then inner=$(trim "\${PI_SESSION_ID-}"); inner_host=pi; fi
64072
+ if [ -z "$inner" ]; then inner=$(trim "\${CODEX_THREAD_ID-}"); inner_host=codex; fi
64073
+ [ -n "$inner" ] || inner_host=
64074
+ declared=$(trim "\${WORKIT_HOST-}")
64075
+ family=$(printf '%s' "$declared" | sed 's/_cli$//;s/_desktop$//')
64076
+ session=$inner
64077
+ if [ "\${WORKIT_SESSION_ID+set}" = set ]; then
64078
+ if [ -z "$inner" ] || [ -z "$declared" ] || [ "$family" = "$inner_host" ]; then
64079
+ session=$(trim "$WORKIT_SESSION_ID")
64080
+ fi
64081
+ fi
64082
+ [ -n "$session" ] || exit 0
64083
+ [ \${#session} -le 128 ] || exit 0
64084
+ case "$session" in *[!A-Za-z0-9_.:@/+-]*) exit 0 ;; esac
64085
+ add_trailer() {
64086
+ [ -f "$1" ] && [ -r "$1" ] || return 0
64087
+ body=$(sed '/^# -\\{24\\} >8 -\\{24\\}$/,$d' "$1" | git stripspace --strip-comments)
64088
+ [ -n "$body" ] || return 0
64089
+ # A merge message has no final newline; interpret-trailers would glue the
64090
+ # trailer to the subject line.
64091
+ [ -z "$(tail -c 1 "$1")" ] || printf '\\n' >> "$1"
64092
+ git -c trailer.separators=: -c trailer.where=end interpret-trailers --in-place \\
64093
+ --no-divider --if-exists addIfDifferent --trailer "Workit-Session: $session" "$1" || return 0
64094
+ }
64095
+ # $1 is the message file. lefthook cannot quote a path it pastes in, so its
64096
+ # job passes '' and the file is found here: git hands the hook COMMIT_EDITMSG,
64097
+ # except \`git merge\`, which hands it MERGE_MSG. With source "merge" both are
64098
+ # updated: the one git did not hand over is stale or removed after the commit.
64099
+ case "\${1-}" in "" | "{1}") ;; *) add_trailer "$1"; exit 0 ;; esac
64100
+ add_trailer "$(git rev-parse --git-path COMMIT_EDITMSG)"
64101
+ [ "$2" != merge ] || add_trailer "$(git rev-parse --git-path MERGE_MSG)"
64102
+ exit 0
64103
+ `, 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) => {
64104
+ const r = spawnSync13("git", args, {
64105
+ cwd,
64106
+ encoding: "utf8",
64107
+ timeout: GIT_TIMEOUTS.local,
64108
+ windowsHide: true,
64109
+ stdio: ["ignore", "pipe", "pipe"],
64110
+ env: { ...env, GIT_TERMINAL_PROMPT: "0" }
64111
+ });
64112
+ return { ok: r.status === 0, out: (r.stdout ?? "").trim() };
64113
+ }, inside2 = (child, parent) => {
64114
+ const rel = path30.relative(path30.resolve(parent), path30.resolve(child));
64115
+ return rel === "" || !rel.startsWith("..") && !path30.isAbsolute(rel);
64116
+ }, readText = (file) => {
64117
+ try {
64118
+ return readFileSync13(file, "utf8");
64119
+ } catch {
64120
+ return null;
64121
+ }
64122
+ }, exists = (file) => {
64123
+ try {
64124
+ lstatSync4(file);
64125
+ return true;
64126
+ } catch {
64127
+ return false;
64128
+ }
64129
+ }, 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) => {
64130
+ const lines = text.split(/\r?\n/).filter((line) => !isComment(line));
64131
+ const assigned = lines.some((line) => /(?:^|[\s;])h=/.test(line) && line.includes(HELPER_REF));
64132
+ return lines.some((line) => DIRECT_CALL.test(line) || assigned && VAR_CALL.test(line));
64133
+ }, LEFTHOOK_YAML, indentOf = (line) => line.length - line.trimStart().length, lefthookRunsHelper = (text) => {
64134
+ const lines = text.split(/\r?\n/);
64135
+ const start = lines.findIndex((line) => /^prepare-commit-msg:\s*(?:#.*)?$/.test(line));
64136
+ if (start < 0)
64137
+ return false;
64138
+ const block = [];
64139
+ for (const line of lines.slice(start + 1)) {
64140
+ if (line.trim() && !isComment(line) && indentOf(line) === 0)
64141
+ break;
64142
+ block.push(line);
64143
+ }
64144
+ return block.some((line, i) => {
64145
+ if (isComment(line))
64146
+ return false;
64147
+ const runKey = /^(\s*)(?:-\s+)?run:\s*(.*)$/.exec(line);
64148
+ if (!runKey)
64149
+ return false;
64150
+ let value = runKey[2] ?? "";
64151
+ if (/^[|>][-+]?\s*$/.test(value)) {
64152
+ const body = [];
64153
+ for (const next of block.slice(i + 1)) {
64154
+ if (next.trim() && indentOf(next) <= indentOf(line))
64155
+ break;
64156
+ body.push(next);
64157
+ }
64158
+ value = body.join(`
64159
+ `);
64160
+ }
64161
+ return runsHelperText(value);
64162
+ });
64163
+ }, isExecutable = (file) => {
64164
+ if (process.platform === "win32")
64165
+ return true;
64166
+ try {
64167
+ return (statSync4(file).mode & 73) !== 0;
64168
+ } catch {
64169
+ return false;
64170
+ }
64171
+ }, manualInPlace = (input, target) => {
64172
+ if (input.owner === "lefthook")
64173
+ return LEFTHOOK_YAML.some((name) => {
64174
+ const text = readText(path30.join(input.top, name));
64175
+ return text !== null && lefthookRunsHelper(text);
64176
+ });
64177
+ const text = readText(target);
64178
+ if (text === null || !runsHelperText(text))
64179
+ return false;
64180
+ return input.owner === "husky" && target !== input.hookPath || isExecutable(target);
64181
+ }, isHuskyV9 = (text, hooksDir) => /\$\(dirname (?:-- )?"\$0"\)\/h"/.test(text) && existsSync13(path30.join(hooksDir, "h")), ownerOf = (text, hooksDir, top) => {
64182
+ if (text.includes("husky.sh") || isHuskyV9(text, hooksDir))
64183
+ return "husky";
64184
+ if (/lefthook/i.test(text) || lefthookConfig(top))
64185
+ return "lefthook";
64186
+ return "other";
64187
+ }, manualTargetOf = ({ owner, text, hooksDir, hookPath, top }) => {
64188
+ if (owner === "husky" && isHuskyV9(text, hooksDir))
64189
+ return path30.join(path30.dirname(hooksDir), SESSION_HOOK_NAME);
64190
+ if (owner === "lefthook")
64191
+ return lefthookConfig(top) ?? path30.join(top, "lefthook.yml");
64192
+ return hookPath;
64193
+ }, manualFor = (input) => {
64194
+ const { owner, displaced } = input;
64195
+ const target = manualTargetOf(input);
64196
+ const stale = displaced ? `; delete the stale ${displaced} (nothing runs it)` : "";
64197
+ if (owner === "husky")
64198
+ return `add this line to ${target} (create it if missing; husky runs it)${stale}: ${manualTrailerLine()}`;
64199
+ if (owner === "lefthook")
64200
+ return [
64201
+ `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}:`,
64202
+ `${SESSION_HOOK_NAME}:`,
64203
+ " jobs:",
64204
+ " - name: workit-session",
64205
+ ` run: ${lefthookTrailerRun()}`
64206
+ ].join(`
64207
+ `);
64208
+ return `add this line to ${target} (a new file needs \`#!/bin/sh\` as its first line and \`chmod +x\`)${stale}: ${manualTrailerLine()}`;
64209
+ }, leftoverOf = (hooksDir) => {
64210
+ const chained = path30.join(hooksDir, LEGACY_CHAINED);
64211
+ const commitMsg = path30.join(hooksDir, "commit-msg");
64212
+ const oldOurs = readText(commitMsg)?.includes(SESSION_HOOK_MARKER) ?? false;
64213
+ if (exists(chained))
64214
+ 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`;
64215
+ if (oldOurs)
64216
+ return `${commitMsg} is a hook from an earlier Workit build; delete it (the trailer now comes from ${SESSION_HOOK_NAME})`;
64217
+ return;
64218
+ }, writeExecutable = (file, content) => {
64219
+ mkdirSync5(path30.dirname(file), { recursive: true });
64220
+ const staged = `${file}.workit-new`;
64221
+ try {
64222
+ writeFileSync6(staged, content, { mode: 493 });
64223
+ chmodSync(staged, 493);
64224
+ renameSync3(staged, file);
64225
+ } finally {
64226
+ rmSync2(staged, { force: true });
64227
+ }
64228
+ };
64229
+ var init_session_hook = __esm(() => {
64230
+ init_rev();
64231
+ HELPER = ["workit", "session-trailer.sh"];
64232
+ HELPER_REF = HELPER.join("/");
64233
+ SESSION_HOOK_SCRIPT = `#!/bin/sh
64234
+ ${SESSION_HOOK_MARKER} v4
64235
+ # Workit (https://github.com/BrainerVirus/workit) installed this hook with
64236
+ # \`workit doctor --fix\`: it runs Workit's session-trailer helper, which adds a
64237
+ # Workit-Session trailer to commits made in an agent session. To remove it,
64238
+ # delete this file.
64239
+ ${manualTrailerLine()}
64240
+ `;
64241
+ LEFTHOOK_CONFIGS = [
64242
+ "lefthook.yml",
64243
+ "lefthook.yaml",
64244
+ ".lefthook.yml",
64245
+ ".lefthook.yaml",
64246
+ "lefthook.json",
64247
+ "lefthook.toml"
64248
+ ];
64249
+ COMMAND_SH = String.raw`(?:^|[;&|(!]|\b(?:then|do|else)\s)\s*sh\s+`;
64250
+ DIRECT_CALL = new RegExp(String.raw`${COMMAND_SH}(?:"[^"]*|\S*)${HELPER_REF.replaceAll(".", String.raw`\.`)}`);
64251
+ VAR_CALL = new RegExp(String.raw`${COMMAND_SH}"?\$h\b`);
64252
+ LEFTHOOK_YAML = [
64253
+ "lefthook.yml",
64254
+ "lefthook.yaml",
64255
+ ".lefthook.yml",
64256
+ ".lefthook.yaml",
64257
+ "lefthook-local.yml",
64258
+ "lefthook-local.yaml",
64259
+ ".lefthook-local.yml",
64260
+ ".lefthook-local.yaml"
64261
+ ];
64262
+ });
64263
+
64264
+ // packages/workit-cli/src/admin/doctor.ts
64265
+ import { spawnSync as spawnSync14 } from "node:child_process";
64266
+ import { createHash as createHash13 } from "node:crypto";
64267
+ import {
64268
+ existsSync as existsSync14,
64269
+ lstatSync as lstatSync5,
64270
+ mkdirSync as mkdirSync6,
64271
+ mkdtempSync,
64272
+ readdirSync as readdirSync7,
64273
+ readFileSync as readFileSync14,
64274
+ readlinkSync as readlinkSync2,
64275
+ rmSync as rmSync3,
64276
+ statSync as statSync5,
64277
+ unlinkSync as unlinkSync3,
64278
+ writeFileSync as writeFileSync7
64279
+ } from "node:fs";
64280
+ import os8 from "node:os";
64281
+ import path31 from "node:path";
63951
64282
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63952
- let dir = path30.resolve(cwd);
64283
+ let dir = path31.resolve(cwd);
63953
64284
  while (true) {
63954
- if (existsSync13(path30.join(dir, "packages", "workit-core", "package.json")))
64285
+ if (existsSync14(path31.join(dir, "packages", "workit-core", "package.json")))
63955
64286
  return dir;
63956
- const parent = path30.dirname(dir);
64287
+ const parent = path31.dirname(dir);
63957
64288
  if (parent === dir)
63958
64289
  return null;
63959
64290
  dir = parent;
@@ -63961,8 +64292,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63961
64292
  }, resolve = (options) => {
63962
64293
  const env = options.env ?? process.env;
63963
64294
  const home = options.home ?? env.HOME ?? os8.homedir();
63964
- const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path30.join(home, ".config", "workit");
63965
- const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path30.join(home, ".local", "state", "workit");
64295
+ const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path31.join(home, ".config", "workit");
64296
+ const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path31.join(home, ".local", "state", "workit");
63966
64297
  const cwd = options.cwd ?? process.cwd();
63967
64298
  const dev = options.dev ?? env.WORKFLOW_TOOLKIT_DEV ?? findDevFromCwd(cwd);
63968
64299
  return {
@@ -63973,11 +64304,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63973
64304
  cwd,
63974
64305
  workspaceRoot: options.workspaceRoot ?? env.WORKFLOW_WORKSPACE_ROOT ?? cwd,
63975
64306
  dev,
63976
- opencodeConfig: options.opencodeConfig ?? path30.join(home, ".config", "opencode", "opencode.json"),
63977
- opencodePackageCacheDir: options.opencodePackageCacheDir ?? path30.join(home, ".cache", "opencode", "packages", "@brainervirus", "workit-opencode@latest"),
63978
- cursorSettings: options.cursorSettings ?? path30.join(home, ".cursor", "settings.json"),
63979
- cursorMcp: options.cursorMcp ?? path30.join(home, ".cursor", "mcp.json"),
63980
- cursorPluginDir: options.cursorPluginDir ?? path30.join(home, ".cursor", "plugins", "local", "workit"),
64307
+ opencodeConfig: options.opencodeConfig ?? path31.join(home, ".config", "opencode", "opencode.json"),
64308
+ opencodePackageCacheDir: options.opencodePackageCacheDir ?? path31.join(home, ".cache", "opencode", "packages", "@brainervirus", "workit-opencode@latest"),
64309
+ cursorSettings: options.cursorSettings ?? path31.join(home, ".cursor", "settings.json"),
64310
+ cursorMcp: options.cursorMcp ?? path31.join(home, ".cursor", "mcp.json"),
64311
+ cursorPluginDir: options.cursorPluginDir ?? path31.join(home, ".cursor", "plugins", "local", "workit"),
63981
64312
  cursorHeartbeatDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
63982
64313
  codexHome: options.codexHome,
63983
64314
  piAgentDir: options.piAgentDir,
@@ -63987,14 +64318,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
63987
64318
  };
63988
64319
  }, readJson = (p) => {
63989
64320
  try {
63990
- const parsed = JSON.parse(readFileSync13(p, "utf8"));
64321
+ const parsed = JSON.parse(readFileSync14(p, "utf8"));
63991
64322
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
63992
64323
  } catch {
63993
64324
  return null;
63994
64325
  }
63995
64326
  }, parsesAsConfigObject = (p) => {
63996
64327
  try {
63997
- return isConfigObject(JSON.parse(readFileSync13(p, "utf8")));
64328
+ return isConfigObject(JSON.parse(readFileSync14(p, "utf8")));
63998
64329
  } catch {
63999
64330
  return false;
64000
64331
  }
@@ -64004,14 +64335,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64004
64335
  ...new Set(entries.filter((entry) => typeof entry === "string" && isWorkitPlugin(entry)))
64005
64336
  ];
64006
64337
  }, 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) => {
64007
- const dirs = (env.PATH ?? process.env.PATH ?? "").split(path30.delimiter);
64338
+ const dirs = (env.PATH ?? process.env.PATH ?? "").split(path31.delimiter);
64008
64339
  for (const dir of dirs) {
64009
64340
  if (!dir)
64010
64341
  continue;
64011
64342
  for (const candidateName of names) {
64012
- const candidate = path30.join(dir, candidateName);
64343
+ const candidate = path31.join(dir, candidateName);
64013
64344
  try {
64014
- const st = statSync4(candidate);
64345
+ const st = statSync5(candidate);
64015
64346
  if (process.platform !== "win32" && (st.mode & 73) === 0)
64016
64347
  continue;
64017
64348
  return candidate;
@@ -64020,7 +64351,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64020
64351
  }
64021
64352
  return null;
64022
64353
  }, versionOf = (bin, env, timeout) => {
64023
- const r = spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout });
64354
+ const r = spawnSync14(bin, ["--version"], { encoding: "utf8", env, timeout });
64024
64355
  if (r.error)
64025
64356
  return null;
64026
64357
  return (r.stdout ?? "").trim();
@@ -64035,14 +64366,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64035
64366
  }
64036
64367
  return true;
64037
64368
  }, resolveBun = (env) => {
64038
- if (env.BUN && existsSync13(env.BUN))
64369
+ if (env.BUN && existsSync14(env.BUN))
64039
64370
  return env.BUN;
64040
64371
  for (const candidate of [
64041
- path30.join(os8.homedir(), ".bun/bin/bun"),
64372
+ path31.join(os8.homedir(), ".bun/bin/bun"),
64042
64373
  "/usr/local/bin/bun",
64043
64374
  "/usr/bin/bun"
64044
64375
  ]) {
64045
- if (existsSync13(candidate))
64376
+ if (existsSync14(candidate))
64046
64377
  return candidate;
64047
64378
  }
64048
64379
  return commandOnPath("bun", env) ? "bun" : null;
@@ -64090,7 +64421,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64090
64421
  detail: "no dev checkout found (WORKFLOW_TOOLKIT_DEV) — skipping version parity"
64091
64422
  };
64092
64423
  }
64093
- const corePkg = readJson(path30.join(res.dev, "packages/workit-core/package.json"));
64424
+ const corePkg = readJson(path31.join(res.dev, "packages/workit-core/package.json"));
64094
64425
  if (!corePkg) {
64095
64426
  return {
64096
64427
  id: "versions",
@@ -64100,7 +64431,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64100
64431
  }
64101
64432
  const refs = new Set;
64102
64433
  for (const name of ["workit-opencode", "workit-cursor", "workit-cli"]) {
64103
- const pkg = readJson(path30.join(res.dev, "packages", name, "package.json"));
64434
+ const pkg = readJson(path31.join(res.dev, "packages", name, "package.json"));
64104
64435
  const dep = pkg?.dependencies?.["@brainervirus/workit-core"];
64105
64436
  if (typeof dep === "string")
64106
64437
  refs.add(dep);
@@ -64116,7 +64447,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64116
64447
  if (refs.size > 1) {
64117
64448
  problems.push(`adapters pin different core versions: ${[...refs].join(", ")}`);
64118
64449
  }
64119
- const opencodePkg = readJson(path30.join(res.dev, "packages/workit-opencode/package.json"));
64450
+ const opencodePkg = readJson(path31.join(res.dev, "packages/workit-opencode/package.json"));
64120
64451
  const sdk = opencodePkg?.dependencies?.["@opencode/plugin"] ?? opencodePkg?.devDependencies?.["@opencode/plugin"];
64121
64452
  const sdkVersion = typeof sdk === "string" ? (sdk.match(/^\d+(?:\.\d+){0,2}/) ?? [])[0] : null;
64122
64453
  if (sdkVersion && !semverAtLeast(sdkVersion, SUPPORT_MATRIX.opencode.minimum)) {
@@ -64143,17 +64474,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64143
64474
  status: "pass",
64144
64475
  detail: "opencode CLI not on PATH — skipping version check"
64145
64476
  };
64146
- const scratch = mkdtempSync(path30.join(os8.tmpdir(), "workit-doctor-opencode-"));
64477
+ const scratch = mkdtempSync(path31.join(os8.tmpdir(), "workit-doctor-opencode-"));
64147
64478
  let raw;
64148
64479
  try {
64149
64480
  raw = versionOf("opencode", {
64150
64481
  ...res.env,
64151
- XDG_DATA_HOME: path30.join(scratch, "data"),
64152
- XDG_STATE_HOME: path30.join(scratch, "state"),
64153
- XDG_CACHE_HOME: path30.join(scratch, "cache")
64482
+ XDG_DATA_HOME: path31.join(scratch, "data"),
64483
+ XDG_STATE_HOME: path31.join(scratch, "state"),
64484
+ XDG_CACHE_HOME: path31.join(scratch, "cache")
64154
64485
  }, 5000);
64155
64486
  } finally {
64156
- rmSync2(scratch, { recursive: true, force: true });
64487
+ rmSync3(scratch, { recursive: true, force: true });
64157
64488
  }
64158
64489
  const installed = raw ? (raw.match(/\d+\.\d+\.\d+/) ?? [])[0] ?? null : null;
64159
64490
  if (installed === null)
@@ -64208,7 +64539,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64208
64539
  const tables = new Map;
64209
64540
  let text;
64210
64541
  try {
64211
- text = readFileSync13(file, "utf8");
64542
+ text = readFileSync14(file, "utf8");
64212
64543
  } catch {
64213
64544
  return tables;
64214
64545
  }
@@ -64246,19 +64577,19 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64246
64577
  current[unquote(pair[1])] = value === "true" ? true : value === "false" ? false : unquote(value);
64247
64578
  }
64248
64579
  return tables;
64249
- }, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path30.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
64580
+ }, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path31.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
64250
64581
  const codexHome = codexHomeOf(res);
64251
- const configFile = path30.join(codexHome, "config.toml");
64582
+ const configFile = path31.join(codexHome, "config.toml");
64252
64583
  const installs = [];
64253
64584
  for (const [key, table] of readTomlTables(configFile)) {
64254
64585
  const [kind, id, ...rest] = key.split("\x00");
64255
64586
  if (kind !== "plugins" || rest.length > 0 || !id?.startsWith("workit@") || table.enabled === false)
64256
64587
  continue;
64257
64588
  const marketplace = id.slice("workit@".length);
64258
- const dir = path30.join(codexHome, "plugins", "cache", marketplace, "workit");
64589
+ const dir = path31.join(codexHome, "plugins", "cache", marketplace, "workit");
64259
64590
  let versions = [];
64260
64591
  try {
64261
- versions = readdirSync7(dir).filter((v) => existsSync13(path30.join(dir, v, ".codex-plugin")));
64592
+ versions = readdirSync7(dir).filter((v) => existsSync14(path31.join(dir, v, ".codex-plugin")));
64262
64593
  } catch {
64263
64594
  versions = [];
64264
64595
  }
@@ -64266,7 +64597,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64266
64597
  installs.push({
64267
64598
  id,
64268
64599
  marketplace,
64269
- root: version ? path30.join(dir, version) : null,
64600
+ root: version ? path31.join(dir, version) : null,
64270
64601
  version,
64271
64602
  configFile
64272
64603
  });
@@ -64288,7 +64619,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64288
64619
  };
64289
64620
  return `sha256:${createHash13("sha256").update(JSON.stringify(body)).digest("hex")}`;
64290
64621
  }, codexHookTrust = (install, tables) => {
64291
- const hooks = install.root ? readJson(path30.join(install.root, "hooks", "hooks.json"))?.hooks : null;
64622
+ const hooks = install.root ? readJson(path31.join(install.root, "hooks", "hooks.json"))?.hooks : null;
64292
64623
  if (!hooks || typeof hooks !== "object")
64293
64624
  return null;
64294
64625
  const result = [];
@@ -64361,28 +64692,28 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64361
64692
  status: "pass",
64362
64693
  detail: "no Workit Codex plugin enabled — skipping"
64363
64694
  };
64364
- const agentsDir = path30.join(codexHomeOf(res), "agents");
64695
+ const agentsDir = path31.join(codexHomeOf(res), "agents");
64365
64696
  const problems = [];
64366
64697
  const fixes = [];
64367
64698
  const installed = [];
64368
64699
  for (const install of installs) {
64369
- const source = install.root ? path30.join(install.root, "agents") : null;
64370
- const files = source && existsSync13(source) ? readdirSync7(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
64700
+ const source = install.root ? path31.join(install.root, "agents") : null;
64701
+ const files = source && existsSync14(source) ? readdirSync7(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
64371
64702
  if (files.length === 0) {
64372
64703
  problems.push(`${install.id} ${install.version ?? "?"} bundles no agents`);
64373
64704
  fixes.push(`codex plugin remove ${install.id} && codex plugin add ${install.id}`);
64374
64705
  continue;
64375
64706
  }
64376
- const install_fix = `node ${JSON.stringify(path30.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
64377
- const pluginVersion = readJson(path30.join(install.root, "package.json"))?.version;
64707
+ const install_fix = `node ${JSON.stringify(path31.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
64708
+ const pluginVersion = readJson(path31.join(install.root, "package.json"))?.version;
64378
64709
  const version = typeof pluginVersion === "string" ? pluginVersion.match(SEMVER)?.[0] ?? null : null;
64379
64710
  let stale = false;
64380
64711
  for (const file of files) {
64381
- const dest = path30.join(agentsDir, file);
64382
- const want = stampCodexAgent(readFileSync13(path30.join(source, file), "utf8"), version);
64712
+ const dest = path31.join(agentsDir, file);
64713
+ const want = stampCodexAgent(readFileSync14(path31.join(source, file), "utf8"), version);
64383
64714
  let link = false;
64384
64715
  try {
64385
- link = lstatSync4(dest).isSymbolicLink();
64716
+ link = lstatSync5(dest).isSymbolicLink();
64386
64717
  } catch {
64387
64718
  link = false;
64388
64719
  }
@@ -64393,7 +64724,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64393
64724
  }
64394
64725
  let have = null;
64395
64726
  try {
64396
- have = readFileSync13(dest, "utf8");
64727
+ have = readFileSync14(dest, "utf8");
64397
64728
  } catch {
64398
64729
  have = null;
64399
64730
  }
@@ -64432,7 +64763,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64432
64763
  const env = res.env.PI_CODING_AGENT_DIR;
64433
64764
  if (env)
64434
64765
  return env === "~" ? res.home : env.replace(/^~(?=\/|\\)/, res.home);
64435
- return path30.join(res.home, ".pi", "agent");
64766
+ return path31.join(res.home, ".pi", "agent");
64436
64767
  }, npmSpecName = (spec) => {
64437
64768
  const at = spec.indexOf("@", spec.startsWith("@") ? 1 : 0);
64438
64769
  return at > 0 ? { name: spec.slice(0, at), pinned: true } : { name: spec, pinned: false };
@@ -64440,10 +64771,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64440
64771
  const scopes = [
64441
64772
  {
64442
64773
  scope: "project",
64443
- settingsFile: path30.join(res.cwd, ".pi", "settings.json"),
64444
- base: path30.join(res.cwd, ".pi")
64774
+ settingsFile: path31.join(res.cwd, ".pi", "settings.json"),
64775
+ base: path31.join(res.cwd, ".pi")
64445
64776
  },
64446
- { scope: "user", settingsFile: path30.join(agentDir, "settings.json"), base: agentDir }
64777
+ { scope: "user", settingsFile: path31.join(agentDir, "settings.json"), base: agentDir }
64447
64778
  ];
64448
64779
  for (const { scope, settingsFile, base } of scopes) {
64449
64780
  const packages = readJson(settingsFile)?.packages;
@@ -64458,13 +64789,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64458
64789
  const spec = npmSpecName(source.slice(4).trim());
64459
64790
  if (spec.name !== PI_PACKAGE)
64460
64791
  continue;
64461
- const root = path30.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
64792
+ const root = path31.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
64462
64793
  return { scope, settingsFile, source, filter, root, npm: { pinned: spec.pinned } };
64463
64794
  }
64464
64795
  if (/^(git:|https?:|ssh:|git@)/.test(source))
64465
64796
  continue;
64466
- const local = path30.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
64467
- if (readJson(path30.join(local, "package.json"))?.name === PI_PACKAGE)
64797
+ const local = path31.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
64798
+ if (readJson(path31.join(local, "package.json"))?.name === PI_PACKAGE)
64468
64799
  return { scope, settingsFile, source, filter, root: local, npm: null };
64469
64800
  }
64470
64801
  }
@@ -64475,7 +64806,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64475
64806
  const list = patterns.filter((p) => typeof p === "string");
64476
64807
  if (list.length === 0)
64477
64808
  return false;
64478
- const name = path30.posix.basename(file);
64809
+ const name = path31.posix.basename(file);
64479
64810
  const matches = (p) => {
64480
64811
  const re = globToRegExp3(p);
64481
64812
  return re.test(file) || re.test(name);
@@ -64492,7 +64823,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64492
64823
  return kept;
64493
64824
  }, checkPiExtension = (res) => {
64494
64825
  const agentDir = piAgentDirOf(res);
64495
- if (!existsSync13(agentDir) && !commandOnPath("pi", res.env))
64826
+ if (!existsSync14(agentDir) && !commandOnPath("pi", res.env))
64496
64827
  return { id: "pi_extension", status: "pass", detail: "Pi not installed — skipping" };
64497
64828
  const installCmd = (scope, spec = `npm:${PI_PACKAGE}`) => `pi install ${spec}${scope === "project" ? " -l" : ""}`;
64498
64829
  const entry = findPiEntry(res, agentDir);
@@ -64500,12 +64831,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64500
64831
  return {
64501
64832
  id: "pi_extension",
64502
64833
  status: "warn",
64503
- detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path30.join(agentDir, "settings.json")} or ${path30.join(res.cwd, ".pi", "settings.json")}`,
64834
+ detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path31.join(agentDir, "settings.json")} or ${path31.join(res.cwd, ".pi", "settings.json")}`,
64504
64835
  fix: installCmd("user")
64505
64836
  };
64506
64837
  const where = `${entry.source} (${entry.scope}, ${entry.settingsFile})`;
64507
64838
  const rebuild = `cd ${JSON.stringify(entry.root)} && bun run build`;
64508
- const pkg = readJson(path30.join(entry.root, "package.json"));
64839
+ const pkg = readJson(path31.join(entry.root, "package.json"));
64509
64840
  if (!pkg)
64510
64841
  return {
64511
64842
  id: "pi_extension",
@@ -64514,7 +64845,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64514
64845
  fix: entry.npm ? installCmd(entry.scope, entry.source) : `restore ${entry.root}, then ${rebuild}`
64515
64846
  };
64516
64847
  const declared = Array.isArray(pkg.pi?.extensions) ? pkg.pi.extensions.some((e) => typeof e === "string" && e.replace(/^\.\//, "") === PI_EXTENSION) : false;
64517
- if (!declared || !existsSync13(path30.join(entry.root, PI_EXTENSION)))
64848
+ if (!declared || !existsSync14(path31.join(entry.root, PI_EXTENSION)))
64518
64849
  return {
64519
64850
  id: "pi_extension",
64520
64851
  status: "warn",
@@ -64567,25 +64898,25 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64567
64898
  detail: `Workit Pi extension ${version ?? "?"} from ${where} loads ${PI_EXTENSION}`
64568
64899
  };
64569
64900
  }, assetPathsFor = (host, dev) => {
64570
- const pkg = path30.join(dev, "packages", `workit-${host}`);
64901
+ const pkg = path31.join(dev, "packages", `workit-${host}`);
64571
64902
  switch (host) {
64572
64903
  case "opencode": {
64573
- const packaged = path30.join(pkg, "assets", "skills");
64574
- const root = existsSync13(packaged) ? packaged : path30.join(dev, "packages", "workit-core", "skills");
64575
- return WORKIT_METHOD_SKILLS.map((skill) => path30.join(root, skill, "SKILL.md"));
64904
+ const packaged = path31.join(pkg, "assets", "skills");
64905
+ const root = existsSync14(packaged) ? packaged : path31.join(dev, "packages", "workit-core", "skills");
64906
+ return WORKIT_METHOD_SKILLS.map((skill) => path31.join(root, skill, "SKILL.md"));
64576
64907
  }
64577
64908
  case "cursor":
64578
64909
  return [
64579
- path30.join(pkg, "assets", "templates", "workit-contract.md"),
64580
- path30.join(pkg, "mcp.json"),
64581
- path30.join(pkg, ".cursor-plugin")
64910
+ path31.join(pkg, "assets", "templates", "workit-contract.md"),
64911
+ path31.join(pkg, "mcp.json"),
64912
+ path31.join(pkg, ".cursor-plugin")
64582
64913
  ];
64583
64914
  case "cli":
64584
- return [path30.join(pkg, "assets", "templates", "spec-template.md")];
64915
+ return [path31.join(pkg, "assets", "templates", "spec-template.md")];
64585
64916
  }
64586
64917
  }, hostsFor = (host) => host === "cli" ? ["opencode", "cursor", "cli"] : [host], checkAssets = (res) => {
64587
64918
  const dev = res.dev;
64588
- const missing = dev ? hostsFor(res.host).flatMap((h) => assetPathsFor(h, dev).filter((p) => !existsSync13(p)).map((p) => `${h}: ${p}`)) : [];
64919
+ const missing = dev ? hostsFor(res.host).flatMap((h) => assetPathsFor(h, dev).filter((p) => !existsSync14(p)).map((p) => `${h}: ${p}`)) : [];
64589
64920
  if (res.host === "cursor" || res.host === "cli") {
64590
64921
  const cursorError = validateCursorSkills(res.cursorPluginDir, WORKIT_METHOD_SKILLS);
64591
64922
  if (cursorError)
@@ -64612,31 +64943,31 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64612
64943
  fix: "Reinstall or rebuild the workit package (missing assets under packages/workit-<host>)"
64613
64944
  };
64614
64945
  }, launcherSlotsFor = (host, dev) => {
64615
- const pkg = path30.join(dev, "packages", `workit-${host}`);
64946
+ const pkg = path31.join(dev, "packages", `workit-${host}`);
64616
64947
  switch (host) {
64617
64948
  case "opencode":
64618
- return [[path30.join(pkg, "src", "plugin.ts"), path30.join(pkg, "dist", "plugin.js")]];
64949
+ return [[path31.join(pkg, "src", "plugin.ts"), path31.join(pkg, "dist", "plugin.js")]];
64619
64950
  case "cursor":
64620
64951
  return [
64621
- [path30.join(pkg, "dist", "mcp-server.js")],
64622
- [path30.join(pkg, "dist", "cursor-session-start.js")]
64952
+ [path31.join(pkg, "dist", "mcp-server.js")],
64953
+ [path31.join(pkg, "dist", "cursor-session-start.js")]
64623
64954
  ];
64624
64955
  case "cli":
64625
- return [[path30.join(pkg, "src", "index.tsx"), path30.join(pkg, "dist", "index.js")]];
64956
+ return [[path31.join(pkg, "src", "index.tsx"), path31.join(pkg, "dist", "index.js")]];
64626
64957
  }
64627
64958
  }, validNodeEntry = (entry, runtime, env) => {
64628
64959
  try {
64629
- const stat = statSync4(entry);
64630
- if (!stat.isFile() || stat.size === 0 || !readFileSync13(entry, "utf8").startsWith(`#!/usr/bin/env node
64960
+ const stat = statSync5(entry);
64961
+ if (!stat.isFile() || stat.size === 0 || !readFileSync14(entry, "utf8").startsWith(`#!/usr/bin/env node
64631
64962
  `)) {
64632
64963
  return false;
64633
64964
  }
64634
- return spawnSync13(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
64965
+ return spawnSync14(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
64635
64966
  } catch {
64636
64967
  return false;
64637
64968
  }
64638
64969
  }, registeredCursorLauncher = (res) => {
64639
- if (!existsSync13(res.cursorMcp))
64970
+ if (!existsSync14(res.cursorMcp))
64640
64971
  return "invalid";
64641
64972
  const config = readJson(res.cursorMcp);
64642
64973
  if (!config)
@@ -64649,7 +64980,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64649
64980
  if (typeof command !== "string" || !Array.isArray(args) || typeof args[0] !== "string") {
64650
64981
  return "invalid";
64651
64982
  }
64652
- const executable = path30.basename(command).toLowerCase();
64983
+ const executable = path31.basename(command).toLowerCase();
64653
64984
  const canonical = cursorMcpServerEntry("").args;
64654
64985
  if (executable === "npx" || executable === "npx.exe" || executable === "npx.cmd") {
64655
64986
  if (args.length !== canonical.length || args.some((a, i) => a !== canonical[i]))
@@ -64661,11 +64992,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64661
64992
  return {
64662
64993
  kind: "node",
64663
64994
  runtime: command,
64664
- entry: path30.isAbsolute(args[0]) ? args[0] : path30.resolve(path30.dirname(res.cursorMcp), args[0])
64995
+ entry: path31.isAbsolute(args[0]) ? args[0] : path31.resolve(path31.dirname(res.cursorMcp), args[0])
64665
64996
  };
64666
64997
  }, canonicalCursorHook, isCanonicalCursorHook = (command, res) => command === canonicalCursorHook || command === cursorHooksEntry(res.cursorPluginDir).command, registeredCursorHook = (res) => {
64667
- const hooksFile = path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
64668
- if (!existsSync13(hooksFile))
64998
+ const hooksFile = path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
64999
+ if (!existsSync14(hooksFile))
64669
65000
  return "invalid";
64670
65001
  const config = readJson(hooksFile);
64671
65002
  if (!config)
@@ -64682,9 +65013,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64682
65013
  if (!m)
64683
65014
  return false;
64684
65015
  const entry = m[1].trim();
64685
- if (!path30.isAbsolute(entry))
65016
+ if (!path31.isAbsolute(entry))
64686
65017
  return false;
64687
- if (path30.resolve(entry) !== path30.resolve(path30.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
65018
+ if (path31.resolve(entry) !== path31.resolve(path31.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
64688
65019
  return false;
64689
65020
  }
64690
65021
  return validNodeEntry(entry, "node", res.env);
@@ -64693,7 +65024,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64693
65024
  const hosts = hostsFor(res.host);
64694
65025
  const registered = hosts.includes("cursor") ? registeredCursorLauncher(res) : null;
64695
65026
  const runtime = registered && registered !== "invalid" && registered.kind === "node" ? registered.runtime : "node";
64696
- 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}`) : [];
65027
+ 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}`) : [];
64697
65028
  if (hosts.includes("cursor")) {
64698
65029
  if (registered === "invalid") {
64699
65030
  missing.push(`cursor: canonical workit MCP launcher in ${res.cursorMcp}`);
@@ -64702,13 +65033,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64702
65033
  }
64703
65034
  const hook = registeredCursorHook(res);
64704
65035
  if (hook === "invalid") {
64705
- missing.push(`cursor: canonical session-start hook in ${path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
65036
+ missing.push(`cursor: canonical session-start hook in ${path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
64706
65037
  } else if (hook !== null && !isCanonicalCursorHook(hook, res) && !validLocalDistHook(hook, res)) {
64707
- missing.push(`cursor: canonical session-start hook in ${path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
65038
+ missing.push(`cursor: canonical session-start hook in ${path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
64708
65039
  }
64709
65040
  }
64710
65041
  if (dev) {
64711
- 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 ")}`)));
65042
+ 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 ")}`)));
64712
65043
  }
64713
65044
  if (missing.length > 0) {
64714
65045
  return {
@@ -64733,21 +65064,21 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64733
65064
  }, CURSOR_HOOK_FIX = "Run `workit init` and select Cursor to install the bundled hook", checkCursorHook = (res) => {
64734
65065
  if (!hostsFor(res.host).includes("cursor"))
64735
65066
  return { id: "cursor_hook", status: "pass", detail: "cursor hook not inspected on this host" };
64736
- if (!existsSync13(res.cursorPluginDir))
65067
+ if (!existsSync14(res.cursorPluginDir))
64737
65068
  return {
64738
65069
  id: "cursor_hook",
64739
65070
  status: "pass",
64740
65071
  detail: "no Cursor plugin install — hook launcher not inspected"
64741
65072
  };
64742
- const launcher = path30.join(res.cursorPluginDir, "hooks", "launch.mjs");
64743
- if (!existsSync13(launcher))
65073
+ const launcher = path31.join(res.cursorPluginDir, "hooks", "launch.mjs");
65074
+ if (!existsSync14(launcher))
64744
65075
  return {
64745
65076
  id: "cursor_hook",
64746
65077
  status: "warn",
64747
65078
  detail: `cursor hook launcher mode: missing — ${launcher} is absent (an install from before the pinned launcher)`,
64748
65079
  fix: CURSOR_HOOK_FIX
64749
65080
  };
64750
- const hooksFile = path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65081
+ const hooksFile = path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
64751
65082
  const registered = readJson(hooksFile);
64752
65083
  const sessionCommand = registered?.hooks?.sessionStart?.[0]?.command;
64753
65084
  const offLauncher = [
@@ -64762,7 +65093,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64762
65093
  fix: "Re-run install-cursor-plugin.sh (or `workit init` with Cursor) to rewrite the hook entries"
64763
65094
  };
64764
65095
  const started = performance.now();
64765
- const probe = spawnSync13("node", [launcher, "--probe", "workit-cursor-hook"], {
65096
+ const probe = spawnSync14("node", [launcher, "--probe", "workit-cursor-hook"], {
64766
65097
  encoding: "utf8",
64767
65098
  env: { ...res.env, WORKIT_CURSOR_HOOK_NPX_OFFLINE: "1" },
64768
65099
  timeout: 40000,
@@ -64809,15 +65140,15 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64809
65140
  return { id: "cursor_hook", status: "pass", detail: `${label}; probe ${ms} ms` };
64810
65141
  }, SILENT_HOOK_GRACE_MS, mtimeMs = (file) => {
64811
65142
  try {
64812
- return statSync4(file).mtimeMs;
65143
+ return statSync5(file).mtimeMs;
64813
65144
  } catch {
64814
65145
  return null;
64815
65146
  }
64816
65147
  }, silentCursorHooks = (res) => {
64817
- const session = mtimeMs(path30.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
65148
+ const session = mtimeMs(path31.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
64818
65149
  if (session === null || Date.now() - session < SILENT_HOOK_GRACE_MS)
64819
65150
  return null;
64820
- const hook = mtimeMs(path30.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
65151
+ const hook = mtimeMs(path31.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
64821
65152
  if (hook !== null && hook >= session)
64822
65153
  return null;
64823
65154
  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`;
@@ -64858,7 +65189,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64858
65189
  return "fragile-cache";
64859
65190
  if (entry.startsWith("file:")) {
64860
65191
  const target = entry.replace(/^file:\/\//, "").replace(/^file:/, "");
64861
- return existsSync13(target) ? "ok" : "missing-file";
65192
+ return existsSync14(target) ? "ok" : "missing-file";
64862
65193
  }
64863
65194
  return "ok";
64864
65195
  }, checkStalePin = (res) => {
@@ -64869,7 +65200,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64869
65200
  detail: "opencode pin not inspected on the cursor host"
64870
65201
  };
64871
65202
  }
64872
- if (!existsSync13(res.opencodeConfig)) {
65203
+ if (!existsSync14(res.opencodeConfig)) {
64873
65204
  return {
64874
65205
  id: "stale_pin",
64875
65206
  status: "pass",
@@ -64913,8 +65244,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64913
65244
  }
64914
65245
  return { id: "stale_pin", status: "pass", detail: "opencode pin resolves" };
64915
65246
  }, pluginMcpSelectors = (res) => {
64916
- const p = path30.join(res.cursorPluginDir, "mcp.json");
64917
- if (!existsSync13(p))
65247
+ const p = path31.join(res.cursorPluginDir, "mcp.json");
65248
+ if (!existsSync14(p))
64918
65249
  return null;
64919
65250
  const cfg = readJson(p);
64920
65251
  const server = cfg?.mcpServers?.workit;
@@ -64936,7 +65267,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64936
65267
  if (!cmd)
64937
65268
  return null;
64938
65269
  try {
64939
- const r = spawnSync13(cmd, ["view", pkgName, "version"], {
65270
+ const r = spawnSync14(cmd, ["view", pkgName, "version"], {
64940
65271
  encoding: "utf8",
64941
65272
  timeout: 20000,
64942
65273
  env: res.env
@@ -64947,13 +65278,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
64947
65278
  return null;
64948
65279
  }
64949
65280
  }, installedPluginVersion = (res) => {
64950
- const pkg = readJson(path30.join(res.cursorPluginDir, "package.json"));
65281
+ const pkg = readJson(path31.join(res.cursorPluginDir, "package.json"));
64951
65282
  return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
64952
65283
  }, installedOpenCodeCacheVersion = (res) => {
64953
- const pkg = readJson(path30.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
65284
+ const pkg = readJson(path31.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
64954
65285
  return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
64955
65286
  }, checkOpenCodePackageCache = (res) => {
64956
- if (!existsSync13(res.opencodeConfig)) {
65287
+ if (!existsSync14(res.opencodeConfig)) {
64957
65288
  return {
64958
65289
  id: "stale_install",
64959
65290
  status: "pass",
@@ -65027,7 +65358,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65027
65358
  detail: "cursor plugin not inspected on this host"
65028
65359
  };
65029
65360
  }
65030
- if (existsSync13(res.cursorPluginDir) && lstatSync4(res.cursorPluginDir).isSymbolicLink()) {
65361
+ if (existsSync14(res.cursorPluginDir) && lstatSync5(res.cursorPluginDir).isSymbolicLink()) {
65031
65362
  let target = "";
65032
65363
  try {
65033
65364
  target = readlinkSync2(res.cursorPluginDir);
@@ -65053,7 +65384,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65053
65384
  fix: "Re-run install-cursor-plugin.sh — it rewrites the workit MCP entry to the canonical @latest selector"
65054
65385
  };
65055
65386
  }
65056
- const hooksFile = path30.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65387
+ const hooksFile = path31.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
65057
65388
  const hookCmd = readJson(hooksFile)?.hooks?.sessionStart?.[0]?.command ?? null;
65058
65389
  const canonicalHook = canonicalCursorHook;
65059
65390
  const staleHook = hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && !hookCmd.startsWith("node ") && hookCmd.includes("@brainervirus/workit-cursor@");
@@ -65065,7 +65396,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65065
65396
  fix: "Re-run install-cursor-plugin.sh — it rewrites the sessionStart hook to the canonical pinned launcher"
65066
65397
  };
65067
65398
  }
65068
- const hookDrift = existsSync13(hooksFile) ? cursorHookDrift(readJson(hooksFile), res.cursorPluginDir) : [];
65399
+ const hookDrift = existsSync14(hooksFile) ? cursorHookDrift(readJson(hooksFile), res.cursorPluginDir) : [];
65069
65400
  if (hookDrift.length > 0) {
65070
65401
  return {
65071
65402
  id: "stale_install",
@@ -65075,7 +65406,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65075
65406
  };
65076
65407
  }
65077
65408
  const installed = installedPluginVersion(res);
65078
- const source = res.dev ? readJson(path30.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path30.join(packageRoot(), "package.json"))?.version ?? null;
65409
+ const source = res.dev ? readJson(path31.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path31.join(packageRoot(), "package.json"))?.version ?? null;
65079
65410
  const localDist = mcpSelectors === null && hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && hookCmd.startsWith("node ");
65080
65411
  if (localDist && installed !== null && source !== null && !semverAtLeast(installed, source)) {
65081
65412
  return {
@@ -65087,7 +65418,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65087
65418
  }
65088
65419
  if (localDist && installed !== null && source !== null && res.dev) {
65089
65420
  const entry = /^node\s+(.+)$/.exec((hookCmd ?? "").trim())?.[1]?.trim() ?? "";
65090
- const sourceBundle = entry && path30.isAbsolute(entry) ? path30.join(res.dev, "packages", "workit-cursor", "dist", path30.basename(entry)) : "";
65421
+ const sourceBundle = entry && path31.isAbsolute(entry) ? path31.join(res.dev, "packages", "workit-cursor", "dist", path31.basename(entry)) : "";
65091
65422
  if (entry && sourceBundle) {
65092
65423
  const installedHash = bundleHashOfFile(entry);
65093
65424
  const sourceHash = bundleHashOfFile(sourceBundle);
@@ -65095,7 +65426,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65095
65426
  return {
65096
65427
  id: "stale_install",
65097
65428
  status: "fail",
65098
- detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path30.basename(entry)} hash mismatch)`,
65429
+ detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path31.basename(entry)} hash mismatch)`,
65099
65430
  fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory with the current build"
65100
65431
  };
65101
65432
  }
@@ -65149,13 +65480,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65149
65480
  const problems = [];
65150
65481
  const opencodeHost = res.host !== "cursor";
65151
65482
  const cursorHost = res.host !== "opencode";
65152
- if (opencodeHost && existsSync13(res.opencodeConfig)) {
65483
+ if (opencodeHost && existsSync14(res.opencodeConfig)) {
65153
65484
  const cfg = readJson(res.opencodeConfig);
65154
65485
  const entries = pluginEntries(cfg);
65155
65486
  if (entries.length > 1)
65156
65487
  problems.push(`opencode registers ${entries.length} workit plugin entries`);
65157
65488
  }
65158
- if (cursorHost && existsSync13(res.cursorSettings)) {
65489
+ if (cursorHost && existsSync14(res.cursorSettings)) {
65159
65490
  const settings = readJson(res.cursorSettings);
65160
65491
  const enabled = settings?.enabled_plugins;
65161
65492
  if (enabled && typeof enabled === "object") {
@@ -65170,7 +65501,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65170
65501
  if (dirs.length > 1)
65171
65502
  problems.push(`cursor plugin_dirs has ${dirs.length} workit entries`);
65172
65503
  }
65173
- if (cursorHost && existsSync13(res.cursorMcp)) {
65504
+ if (cursorHost && existsSync14(res.cursorMcp)) {
65174
65505
  const mcp = readJson(res.cursorMcp);
65175
65506
  const servers = mcp?.mcpServers ?? {};
65176
65507
  if (servers && typeof servers === "object") {
@@ -65195,17 +65526,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65195
65526
  }, checkMalformedConfig = (res) => {
65196
65527
  const files = [];
65197
65528
  for (const name of ["config.json", "youtrack.json", "vcs.json", "workspaces.json"]) {
65198
- const p = path30.join(res.configDir, name);
65199
- if (existsSync13(p))
65529
+ const p = path31.join(res.configDir, name);
65530
+ if (existsSync14(p))
65200
65531
  files.push(p);
65201
65532
  }
65202
65533
  const opencodeHost = res.host !== "cursor";
65203
65534
  const cursorHost = res.host !== "opencode";
65204
- if (opencodeHost && existsSync13(res.opencodeConfig))
65535
+ if (opencodeHost && existsSync14(res.opencodeConfig))
65205
65536
  files.push(res.opencodeConfig);
65206
- if (cursorHost && existsSync13(res.cursorSettings))
65537
+ if (cursorHost && existsSync14(res.cursorSettings))
65207
65538
  files.push(res.cursorSettings);
65208
- if (cursorHost && existsSync13(res.cursorMcp))
65539
+ if (cursorHost && existsSync14(res.cursorMcp))
65209
65540
  files.push(res.cursorMcp);
65210
65541
  const bad = files.filter((p) => !parsesAsConfigObject(p));
65211
65542
  if (bad.length === 0)
@@ -65284,19 +65615,90 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65284
65615
  detail: `current directory ${res.cwd} does not match any configured workspace`,
65285
65616
  fix: `Add a workspace glob matching this directory to ${file} or run /wk-init`
65286
65617
  };
65618
+ }, matchedWorkspace = (res) => {
65619
+ try {
65620
+ return resolveWorkspaceFrom(res.cwd, res.configDir, res.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
65621
+ } catch {
65622
+ return null;
65623
+ }
65624
+ }, SESSION_HOOK_FIX = "workit doctor --fix", sessionHookFinding = (hook) => {
65625
+ const slot = `${SESSION_HOOK_NAME} hook`;
65626
+ switch (hook.state) {
65627
+ case "not_git":
65628
+ return { status: "pass", detail: `not inside a git work tree — skipping the ${slot}` };
65629
+ case "installed":
65630
+ return {
65631
+ status: "pass",
65632
+ detail: `${slot} ${hook.hookPath} adds the Workit-Session trailer to plain git commits`
65633
+ };
65634
+ case "manual":
65635
+ return hook.helperCurrent ? {
65636
+ status: "pass",
65637
+ detail: `${hook.manualTarget} adds the Workit-Session trailer to plain git commits (via ${hook.via})`
65638
+ } : {
65639
+ status: "warn",
65640
+ detail: `${hook.manualTarget} runs the Workit helper (via ${hook.via}), but ${hook.helperPath} is missing or an older version`,
65641
+ fix: SESSION_HOOK_FIX
65642
+ };
65643
+ case "missing":
65644
+ return {
65645
+ status: "warn",
65646
+ detail: `no ${slot} at ${hook.hookPath}: plain git commits from an agent session get no Workit-Session trailer`,
65647
+ fix: SESSION_HOOK_FIX
65648
+ };
65649
+ case "outdated":
65650
+ return {
65651
+ status: "warn",
65652
+ detail: `the Workit ${slot} at ${hook.hookPath} or its helper ${hook.helperPath} is missing or an older version`,
65653
+ fix: SESSION_HOOK_FIX
65654
+ };
65655
+ case "blocked":
65656
+ return {
65657
+ status: "warn",
65658
+ detail: `plain git commits get no Workit-Session trailer and --fix will not install the ${slot}: ${hook.reason}`,
65659
+ fix: hook.helperCurrent ? hook.manual : `${SESSION_HOOK_FIX} (writes ${hook.helperPath}), then ${hook.manual}`
65660
+ };
65661
+ }
65662
+ }, checkSessionHook = (res) => {
65663
+ if (!matchedWorkspace(res))
65664
+ return {
65665
+ id: "session_hook",
65666
+ status: "pass",
65667
+ detail: `not in a Workit workspace — skipping the ${SESSION_HOOK_NAME} session hook`
65668
+ };
65669
+ const hook = inspectSessionHook(res.cwd, res.env);
65670
+ const finding = sessionHookFinding(hook);
65671
+ if (!hook.leftover)
65672
+ return { id: "session_hook", ...finding };
65673
+ return {
65674
+ id: "session_hook",
65675
+ status: "warn",
65676
+ detail: `${finding.detail}; ${hook.leftover}`,
65677
+ fix: finding.fix ? `${finding.fix}; ${hook.leftover}` : hook.leftover
65678
+ };
65679
+ }, fixSessionHook = (options = {}) => {
65680
+ const res = resolve(options);
65681
+ const workspace = matchedWorkspace(res);
65682
+ if (!workspace)
65683
+ return {
65684
+ action: "skipped",
65685
+ detail: `${res.cwd} is not in a Workit workspace; no hook installed`,
65686
+ workspace: null
65687
+ };
65688
+ return { ...installSessionHook(res.cwd, res.env), workspace: workspace.name };
65287
65689
  }, isPlaceholder = (p) => {
65288
65690
  try {
65289
- return readFileSync13(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
65691
+ return readFileSync14(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
65290
65692
  } catch {
65291
65693
  return false;
65292
65694
  }
65293
65695
  }, checkCredentialMetadata = (res) => {
65294
65696
  const tokenPaths = [];
65295
- const youtrackJson = readJson(path30.join(res.configDir, "youtrack.json"));
65697
+ const youtrackJson = readJson(path31.join(res.configDir, "youtrack.json"));
65296
65698
  if (youtrackJson) {
65297
- tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path30.join(res.configDir, "youtrack.token"));
65298
- } else if (existsSync13(path30.join(res.configDir, "youtrack.token"))) {
65299
- tokenPaths.push(path30.join(res.configDir, "youtrack.token"));
65699
+ tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path31.join(res.configDir, "youtrack.token"));
65700
+ } else if (existsSync14(path31.join(res.configDir, "youtrack.token"))) {
65701
+ tokenPaths.push(path31.join(res.configDir, "youtrack.token"));
65300
65702
  }
65301
65703
  if (tokenPaths.length === 0) {
65302
65704
  return {
@@ -65307,13 +65709,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65307
65709
  }
65308
65710
  const problems = [];
65309
65711
  for (const raw of tokenPaths) {
65310
- const p = path30.isAbsolute(raw) ? raw : path30.resolve(res.configDir, raw);
65311
- if (!existsSync13(p)) {
65712
+ const p = path31.isAbsolute(raw) ? raw : path31.resolve(res.configDir, raw);
65713
+ if (!existsSync14(p)) {
65312
65714
  problems.push(`${p} is missing`);
65313
65715
  continue;
65314
65716
  }
65315
65717
  if (process.platform !== "win32") {
65316
- const mode = statSync4(p).mode & 511;
65718
+ const mode = statSync5(p).mode & 511;
65317
65719
  if (mode !== 384)
65318
65720
  problems.push(`${p} must be mode 0600 (found ${mode.toString(8)})`);
65319
65721
  }
@@ -65375,7 +65777,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65375
65777
  }, defaultIdentityProbes = (env) => ({
65376
65778
  origin: (cwd) => {
65377
65779
  try {
65378
- const out = spawnSync13("git", ["remote", "get-url", "origin"], {
65780
+ const out = spawnSync14("git", ["remote", "get-url", "origin"], {
65379
65781
  cwd,
65380
65782
  encoding: "utf8",
65381
65783
  env
@@ -65387,7 +65789,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65387
65789
  },
65388
65790
  ghLogin: (e) => {
65389
65791
  try {
65390
- const out = spawnSync13("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
65792
+ const out = spawnSync14("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
65391
65793
  const login = (out.stdout ?? "").trim();
65392
65794
  return out.status === 0 && login ? login : null;
65393
65795
  } catch {
@@ -65396,7 +65798,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65396
65798
  },
65397
65799
  glabLogin: (e) => {
65398
65800
  try {
65399
- const out = spawnSync13("glab", ["api", "user", "--jq", ".username"], {
65801
+ const out = spawnSync14("glab", ["api", "user", "--jq", ".username"], {
65400
65802
  encoding: "utf8",
65401
65803
  env: e
65402
65804
  });
@@ -65408,7 +65810,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65408
65810
  },
65409
65811
  sshLogin: (host) => {
65410
65812
  try {
65411
- const out = spawnSync13("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
65813
+ const out = spawnSync14("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
65412
65814
  encoding: "utf8",
65413
65815
  env
65414
65816
  });
@@ -65423,7 +65825,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65423
65825
  const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
65424
65826
  if (!remoteHost)
65425
65827
  return { id: "github_identity", status: "pass", detail: "no GitHub remote configured" };
65426
- const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path30.join(res.configDir, "vcs.json"));
65828
+ const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path31.join(res.configDir, "vcs.json"));
65427
65829
  const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
65428
65830
  if (workspace.error)
65429
65831
  return {
@@ -65451,7 +65853,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65451
65853
  const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
65452
65854
  if (!remoteHost)
65453
65855
  return { id: "gitlab_identity", status: "pass", detail: "no GitLab remote configured" };
65454
- const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path30.join(res.configDir, "vcs.json"));
65856
+ const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path31.join(res.configDir, "vcs.json"));
65455
65857
  const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
65456
65858
  if (workspace.error)
65457
65859
  return {
@@ -65477,11 +65879,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65477
65879
  { surface: "SSH", login: probes.sshLogin(remoteHost) }
65478
65880
  ]);
65479
65881
  }, checkLogWritable = (res) => {
65480
- const logsDir = path30.join(res.stateDir, "logs");
65481
- const probe = path30.join(logsDir, "doctor-probe.tmp");
65882
+ const logsDir = path31.join(res.stateDir, "logs");
65883
+ const probe = path31.join(logsDir, "doctor-probe.tmp");
65482
65884
  try {
65483
- mkdirSync5(logsDir, { recursive: true, mode: 448 });
65484
- writeFileSync6(probe, `{"probe":true}
65885
+ mkdirSync6(logsDir, { recursive: true, mode: 448 });
65886
+ writeFileSync7(probe, `{"probe":true}
65485
65887
  `, { mode: 384 });
65486
65888
  return {
65487
65889
  id: "log_writable",
@@ -65568,7 +65970,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65568
65970
  }, CLI_PACKAGE = "@brainervirus/workit-cli", SEMVER, WORKIT_PROBE_TIMEOUT_MS = 1e4, workitVersions, workitVersionAt = (bin, env) => {
65569
65971
  let key;
65570
65972
  try {
65571
- const st = statSync4(bin);
65973
+ const st = statSync5(bin);
65572
65974
  key = `${bin}:${st.ino}:${st.size}:${st.mtimeMs}`;
65573
65975
  } catch {
65574
65976
  return { version: null, timedOut: false };
@@ -65577,12 +65979,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
65577
65979
  if (cached)
65578
65980
  return cached;
65579
65981
  const viaShell = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(bin);
65580
- const r = viaShell ? spawnSync13(`"${bin}" --version`, {
65982
+ const r = viaShell ? spawnSync14(`"${bin}" --version`, {
65581
65983
  encoding: "utf8",
65582
65984
  env,
65583
65985
  timeout: WORKIT_PROBE_TIMEOUT_MS,
65584
65986
  shell: true
65585
- }) : spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
65987
+ }) : spawnSync14(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
65586
65988
  const timedOut = r.error !== undefined && "code" in r.error && r.error.code === "ETIMEDOUT";
65587
65989
  const version = r.status === 0 ? (r.stdout ?? "").match(SEMVER)?.[0] ?? null : null;
65588
65990
  const probe = { version: timedOut ? null : version, timedOut };
@@ -65672,6 +66074,7 @@ var init_doctor = __esm(() => {
65672
66074
  init_workspaces();
65673
66075
  init_host_install();
65674
66076
  init_skill_manifests();
66077
+ init_session_hook();
65675
66078
  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`;
65676
66079
  AGENT_MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
65677
66080
  canonicalCursorHook = cursorHooksEntry().command;
@@ -65698,6 +66101,7 @@ var init_doctor = __esm(() => {
65698
66101
  checkMalformedConfig,
65699
66102
  checkWorkspaceMismatch,
65700
66103
  checkWorkspaceLock,
66104
+ checkSessionHook,
65701
66105
  checkCredentialMetadata,
65702
66106
  checkGithubIdentity,
65703
66107
  checkGitLabIdentity,
@@ -65719,20 +66123,20 @@ var init_doctor = __esm(() => {
65719
66123
  import {
65720
66124
  copyFileSync as copyFileSync4,
65721
66125
  cpSync as cpSync4,
65722
- existsSync as existsSync14,
65723
- lstatSync as lstatSync5,
65724
- mkdirSync as mkdirSync6,
66126
+ existsSync as existsSync15,
66127
+ lstatSync as lstatSync6,
66128
+ mkdirSync as mkdirSync7,
65725
66129
  mkdtempSync as mkdtempSync2,
65726
- readFileSync as readFileSync14,
66130
+ readFileSync as readFileSync15,
65727
66131
  readdirSync as readdirSync8,
65728
66132
  realpathSync as realpathSync7,
65729
- renameSync as renameSync3,
65730
- rmSync as rmSync3,
65731
- statSync as statSync5,
65732
- writeFileSync as writeFileSync7
66133
+ renameSync as renameSync4,
66134
+ rmSync as rmSync4,
66135
+ statSync as statSync6,
66136
+ writeFileSync as writeFileSync8
65733
66137
  } from "node:fs";
65734
66138
  import os9 from "node:os";
65735
- import path31 from "node:path";
66139
+ import path32 from "node:path";
65736
66140
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
65737
66141
  function parseList(raw) {
65738
66142
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -65776,7 +66180,7 @@ function buildSetupPreview(values, opts = {}) {
65776
66180
  const current = readConfigFromDir(state.configDir);
65777
66181
  mutations.push({
65778
66182
  type: "merge-json",
65779
- path: path31.join(state.configDir, "config.json"),
66183
+ path: path32.join(state.configDir, "config.json"),
65780
66184
  value: {
65781
66185
  locale: values.locale,
65782
66186
  localeOptions: current.localeOptions,
@@ -65802,17 +66206,17 @@ function buildSetupPreview(values, opts = {}) {
65802
66206
  if (blocked.length === 0) {
65803
66207
  mutations.push({
65804
66208
  type: "update-workspaces",
65805
- path: path31.join(state.configDir, "workspaces.json"),
66209
+ path: path32.join(state.configDir, "workspaces.json"),
65806
66210
  entries: values.workspaces,
65807
66211
  expectedRevision: diskWorkspaceResult.revision
65808
66212
  });
65809
66213
  }
65810
66214
  }
65811
66215
  if (values.baseUrl.trim()) {
65812
- const ytPath = path31.join(state.configDir, "youtrack.json");
66216
+ const ytPath = path32.join(state.configDir, "youtrack.json");
65813
66217
  const ytExisting = readConfigRecord(ytPath);
65814
66218
  const ytConfigured = configuredTokenPath(ytExisting, "tokenFile");
65815
- const tokenPath = resolveTokenPath(ytConfigured, path31.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
66219
+ const tokenPath = resolveTokenPath(ytConfigured, path32.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
65816
66220
  const draft = youtrackDraft(values, tokenPath);
65817
66221
  if (ytConfigured !== null && tokenPath !== ytConfigured) {
65818
66222
  delete draft.tokenFile;
@@ -65828,7 +66232,7 @@ function buildSetupPreview(values, opts = {}) {
65828
66232
  path: ytPath,
65829
66233
  value: draft
65830
66234
  });
65831
- if (existsSync14(tokenPath))
66235
+ if (existsSync15(tokenPath))
65832
66236
  preserved.push(tokenPath);
65833
66237
  else
65834
66238
  mutations.push({
@@ -65840,7 +66244,7 @@ function buildSetupPreview(values, opts = {}) {
65840
66244
  });
65841
66245
  }
65842
66246
  if (values.vcsProvider !== "skip") {
65843
- const vcsPath = path31.join(state.configDir, "vcs.json");
66247
+ const vcsPath = path32.join(state.configDir, "vcs.json");
65844
66248
  mutations.push({
65845
66249
  type: "merge-json",
65846
66250
  path: vcsPath,
@@ -65869,7 +66273,7 @@ function buildSetupPreview(values, opts = {}) {
65869
66273
  mutations.push({
65870
66274
  type: "install-host",
65871
66275
  platform: "cursor",
65872
- path: path31.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
66276
+ path: path32.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
65873
66277
  commands
65874
66278
  });
65875
66279
  }
@@ -65882,7 +66286,7 @@ function buildSetupPreview(values, opts = {}) {
65882
66286
  mutations.push({
65883
66287
  type: "install-host",
65884
66288
  platform,
65885
- 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"),
66289
+ 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"),
65886
66290
  commands
65887
66291
  });
65888
66292
  }
@@ -65890,9 +66294,9 @@ function buildSetupPreview(values, opts = {}) {
65890
66294
  }
65891
66295
  }
65892
66296
  if (values.applyProject) {
65893
- const root = path31.resolve(opts.cwd ?? process.cwd());
65894
- const gitignorePath = path31.join(root, ".gitignore");
65895
- const existing = existsSync14(gitignorePath) ? readFileSync14(gitignorePath, "utf8") : "";
66297
+ const root = path32.resolve(opts.cwd ?? process.cwd());
66298
+ const gitignorePath = path32.join(root, ".gitignore");
66299
+ const existing = existsSync15(gitignorePath) ? readFileSync15(gitignorePath, "utf8") : "";
65896
66300
  const existingLines = new Set(existing.split(`
65897
66301
  `).map((l) => l.trim()).filter(Boolean));
65898
66302
  const entries = GITIGNORE_ENTRIES.filter((e) => e.trim() !== "" && !existingLines.has(e.trim()));
@@ -65928,10 +66332,10 @@ function deepMerge(base, patch) {
65928
66332
  return out;
65929
66333
  }
65930
66334
  function applyMutation(m) {
65931
- const dir = path31.dirname(m.path);
66335
+ const dir = path32.dirname(m.path);
65932
66336
  switch (m.type) {
65933
66337
  case "create-file": {
65934
- mkdirSync6(dir, { recursive: true });
66338
+ mkdirSync7(dir, { recursive: true });
65935
66339
  const res = writeFileExclusive(m.path, m.content, m.mode);
65936
66340
  return res === "created" ? { platform: "core", file: m.path, status: "Installed" } : {
65937
66341
  platform: "core",
@@ -65954,8 +66358,8 @@ function applyMutation(m) {
65954
66358
  if (existing.kind === "record" && JSON.stringify(existing.value) === JSON.stringify(merged)) {
65955
66359
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
65956
66360
  }
65957
- mkdirSync6(dir, { recursive: true });
65958
- writeFileSync7(m.path, JSON.stringify(merged, null, 2) + `
66361
+ mkdirSync7(dir, { recursive: true });
66362
+ writeFileSync8(m.path, JSON.stringify(merged, null, 2) + `
65959
66363
  `, "utf8");
65960
66364
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
65961
66365
  }
@@ -65996,12 +66400,12 @@ function applyMutation(m) {
65996
66400
  `;
65997
66401
  if (prev === next)
65998
66402
  return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
65999
- mkdirSync6(dir, { recursive: true });
66403
+ mkdirSync7(dir, { recursive: true });
66000
66404
  const tmp = `${m.path}.${process.pid}.tmp`;
66001
66405
  try {
66002
- writeFileSync7(tmp, next, "utf8");
66406
+ writeFileSync8(tmp, next, "utf8");
66003
66407
  if (workspacesRevision(readFileSafe(m.path)) !== m.expectedRevision) {
66004
- rmSync3(tmp, { force: true });
66408
+ rmSync4(tmp, { force: true });
66005
66409
  return {
66006
66410
  platform: "core",
66007
66411
  file: m.path,
@@ -66009,9 +66413,9 @@ function applyMutation(m) {
66009
66413
  detail: "workspace config changed while applying the preview; rebuild and review it"
66010
66414
  };
66011
66415
  }
66012
- renameSync3(tmp, m.path);
66416
+ renameSync4(tmp, m.path);
66013
66417
  } catch (error) {
66014
- rmSync3(tmp, { force: true });
66418
+ rmSync4(tmp, { force: true });
66015
66419
  return {
66016
66420
  platform: "core",
66017
66421
  file: m.path,
@@ -66034,11 +66438,11 @@ function applyMutation(m) {
66034
66438
  detail: "all entries already present"
66035
66439
  };
66036
66440
  }
66037
- mkdirSync6(dir, { recursive: true });
66441
+ mkdirSync7(dir, { recursive: true });
66038
66442
  const separator = existing && !existing.endsWith(`
66039
66443
  `) ? `
66040
66444
  ` : "";
66041
- writeFileSync7(m.path, existing + separator + (existing ? `
66445
+ writeFileSync8(m.path, existing + separator + (existing ? `
66042
66446
  ` : "") + add.join(`
66043
66447
  `) + `
66044
66448
  `, "utf8");
@@ -66077,8 +66481,8 @@ function applyMutation(m) {
66077
66481
  detail: "already configured"
66078
66482
  };
66079
66483
  }
66080
- mkdirSync6(path31.dirname(m.path), { recursive: true });
66081
- writeFileSync7(m.path, JSON.stringify(merged, null, 2) + `
66484
+ mkdirSync7(path32.dirname(m.path), { recursive: true });
66485
+ writeFileSync8(m.path, JSON.stringify(merged, null, 2) + `
66082
66486
  `, "utf8");
66083
66487
  return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
66084
66488
  }
@@ -66086,23 +66490,23 @@ function applyMutation(m) {
66086
66490
  }
66087
66491
  function adapterRoot(platform, res, preferInstalledAdapter = false) {
66088
66492
  const candidates = [];
66089
- const stagedCursor = path31.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
66493
+ const stagedCursor = path32.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
66090
66494
  if (platform === "cursor" && preferInstalledAdapter)
66091
66495
  candidates.push(stagedCursor);
66092
66496
  if (res.dev) {
66093
- candidates.push(path31.join(res.dev, "packages", `workit-${platform}`));
66497
+ candidates.push(path32.join(res.dev, "packages", `workit-${platform}`));
66094
66498
  } else {
66095
- candidates.push(path31.join(packageRoot(), "..", `workit-${platform}`));
66096
- let dir = path31.resolve(res.cwd);
66499
+ candidates.push(path32.join(packageRoot(), "..", `workit-${platform}`));
66500
+ let dir = path32.resolve(res.cwd);
66097
66501
  while (true) {
66098
- candidates.push(path31.join(dir, "packages", `workit-${platform}`));
66099
- const parent = path31.dirname(dir);
66502
+ candidates.push(path32.join(dir, "packages", `workit-${platform}`));
66503
+ const parent = path32.dirname(dir);
66100
66504
  if (parent === dir)
66101
66505
  break;
66102
66506
  dir = parent;
66103
66507
  }
66104
66508
  }
66105
- candidates.push(path31.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
66509
+ candidates.push(path32.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
66106
66510
  if (platform === "cursor")
66107
66511
  candidates.push(stagedCursor);
66108
66512
  for (const candidate of candidates) {
@@ -66121,7 +66525,7 @@ function applyOpenCode(root, res) {
66121
66525
  detail: "workit-opencode package has no loadable plugin entry (src/plugin.ts or dist/plugin.js)"
66122
66526
  };
66123
66527
  }
66124
- mkdirSync6(path31.dirname(res.opencodeConfig), { recursive: true });
66528
+ mkdirSync7(path32.dirname(res.opencodeConfig), { recursive: true });
66125
66529
  const existing = readExisting(res.opencodeConfig);
66126
66530
  if (existing.kind === "malformed") {
66127
66531
  return {
@@ -66150,7 +66554,7 @@ function applyOpenCode(root, res) {
66150
66554
  detail: "already registered"
66151
66555
  };
66152
66556
  }
66153
- writeFileSync7(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
66557
+ writeFileSync8(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
66154
66558
  `, "utf8");
66155
66559
  return existing.kind === "record" ? {
66156
66560
  platform: "opencode",
@@ -66166,54 +66570,54 @@ function applyOpenCode(root, res) {
66166
66570
  }
66167
66571
  function copyPluginDir(src, dest) {
66168
66572
  const realSrc = realpathSync7(src);
66169
- const marker = path31.join(dest, ".workit-root");
66170
- const destIsLink = existsSync14(dest) && lstatSync5(dest).isSymbolicLink();
66573
+ const marker = path32.join(dest, ".workit-root");
66574
+ const destIsLink = existsSync15(dest) && lstatSync6(dest).isSymbolicLink();
66171
66575
  const synced = !destIsLink && readFileSafe(marker)?.trim() === realSrc && samePluginContent(realSrc, dest, dest);
66172
66576
  if (synced)
66173
66577
  return "Skipped";
66174
- const hadDir = existsSync14(dest);
66578
+ const hadDir = existsSync15(dest);
66175
66579
  const rules = preservedCursorRules(realSrc, dest);
66176
- const parent = path31.dirname(dest);
66177
- mkdirSync6(parent, { recursive: true });
66178
- const swap = mkdtempSync2(path31.join(parent, `.${path31.basename(dest)}.swap-`));
66179
- const stage = path31.join(swap, "stage");
66180
- const backup = path31.join(swap, "backup");
66580
+ const parent = path32.dirname(dest);
66581
+ mkdirSync7(parent, { recursive: true });
66582
+ const swap = mkdtempSync2(path32.join(parent, `.${path32.basename(dest)}.swap-`));
66583
+ const stage = path32.join(swap, "stage");
66584
+ const backup = path32.join(swap, "backup");
66181
66585
  try {
66182
66586
  cpSync4(realSrc, stage, {
66183
66587
  recursive: true,
66184
- filter: (entry) => !path31.relative(realSrc, entry).split(path31.sep).includes("node_modules")
66588
+ filter: (entry) => !path32.relative(realSrc, entry).split(path32.sep).includes("node_modules")
66185
66589
  });
66186
- const stagedHooks = path31.join(stage, CURSOR_HOOKS_FILE);
66187
- if (existsSync14(stagedHooks))
66188
- writeFileSync7(stagedHooks, localizedCursorHooks(readFileSync14(stagedHooks), dest));
66590
+ const stagedHooks = path32.join(stage, CURSOR_HOOKS_FILE);
66591
+ if (existsSync15(stagedHooks))
66592
+ writeFileSync8(stagedHooks, localizedCursorHooks(readFileSync15(stagedHooks), dest));
66189
66593
  for (const [name, content] of rules) {
66190
- mkdirSync6(path31.join(stage, "rules"), { recursive: true });
66191
- writeFileSync7(path31.join(stage, "rules", name), content);
66594
+ mkdirSync7(path32.join(stage, "rules"), { recursive: true });
66595
+ writeFileSync8(path32.join(stage, "rules", name), content);
66192
66596
  }
66193
- writeFileSync7(path31.join(stage, ".workit-root"), realSrc + `
66597
+ writeFileSync8(path32.join(stage, ".workit-root"), realSrc + `
66194
66598
  `, "utf8");
66195
66599
  if (!samePluginContent(realSrc, stage, dest))
66196
66600
  throw new Error("staged adapter content is incomplete");
66197
- if (lstatSync5(stage).isSymbolicLink()) {
66601
+ if (lstatSync6(stage).isSymbolicLink()) {
66198
66602
  throw new Error("staged adapter resolved to a symlink — refusing fragile cache install");
66199
66603
  }
66200
66604
  if (hadDir)
66201
- renameSync3(dest, backup);
66605
+ renameSync4(dest, backup);
66202
66606
  try {
66203
- renameSync3(stage, dest);
66607
+ renameSync4(stage, dest);
66204
66608
  } catch (error) {
66205
- if (hadDir && !existsSync14(dest))
66206
- renameSync3(backup, dest);
66609
+ if (hadDir && !existsSync15(dest))
66610
+ renameSync4(backup, dest);
66207
66611
  throw error;
66208
66612
  }
66209
- rmSync3(backup, { recursive: true, force: true });
66613
+ rmSync4(backup, { recursive: true, force: true });
66210
66614
  } finally {
66211
- rmSync3(swap, { recursive: true, force: true });
66615
+ rmSync4(swap, { recursive: true, force: true });
66212
66616
  }
66213
66617
  return hadDir ? "Configured" : "Installed";
66214
66618
  }
66215
66619
  function applyCursorSettings(root, res) {
66216
- mkdirSync6(path31.dirname(res.cursorSettings), { recursive: true });
66620
+ mkdirSync7(path32.dirname(res.cursorSettings), { recursive: true });
66217
66621
  const settingsExisting = readExisting(res.cursorSettings);
66218
66622
  if (settingsExisting.kind === "malformed") {
66219
66623
  return {
@@ -66232,7 +66636,7 @@ function applyCursorSettings(root, res) {
66232
66636
  detail: "already registered"
66233
66637
  };
66234
66638
  }
66235
- writeFileSync7(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
66639
+ writeFileSync8(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
66236
66640
  `, "utf8");
66237
66641
  return {
66238
66642
  platform: "cursor",
@@ -66241,7 +66645,7 @@ function applyCursorSettings(root, res) {
66241
66645
  };
66242
66646
  }
66243
66647
  function applyCursorMcp(root, res) {
66244
- mkdirSync6(path31.dirname(res.cursorMcp), { recursive: true });
66648
+ mkdirSync7(path32.dirname(res.cursorMcp), { recursive: true });
66245
66649
  const mcpExisting = readExisting(res.cursorMcp);
66246
66650
  if (mcpExisting.kind === "malformed") {
66247
66651
  return {
@@ -66271,7 +66675,7 @@ function applyCursorMcp(root, res) {
66271
66675
  detail: "already registered"
66272
66676
  };
66273
66677
  }
66274
- writeFileSync7(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
66678
+ writeFileSync8(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
66275
66679
  `, "utf8");
66276
66680
  return {
66277
66681
  platform: "cursor",
@@ -66280,9 +66684,9 @@ function applyCursorMcp(root, res) {
66280
66684
  };
66281
66685
  }
66282
66686
  function verifyPlatform(platform, res) {
66283
- if (platform === "opencode" && !existsSync14(res.opencodeConfig))
66687
+ if (platform === "opencode" && !existsSync15(res.opencodeConfig))
66284
66688
  return null;
66285
- if (platform === "cursor" && !existsSync14(res.cursorSettings) && !existsSync14(res.cursorMcp)) {
66689
+ if (platform === "cursor" && !existsSync15(res.cursorSettings) && !existsSync15(res.cursorMcp)) {
66286
66690
  return null;
66287
66691
  }
66288
66692
  return runDoctor({
@@ -66347,7 +66751,7 @@ function applySetupPreview(preview, options = {}) {
66347
66751
  let cursorCopyOk = false;
66348
66752
  for (const mutation of preview.mutations) {
66349
66753
  if (mutation.type === "install-host") {
66350
- 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) : "";
66754
+ 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) : "";
66351
66755
  if (mutation.path !== expectedPath) {
66352
66756
  entries.push({
66353
66757
  platform: mutation.platform,
@@ -66528,12 +66932,12 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66528
66932
  return configured ?? fallback;
66529
66933
  }, readConfigRecord = (file) => {
66530
66934
  try {
66531
- const value = JSON.parse(readFileSync14(file, "utf8"));
66935
+ const value = JSON.parse(readFileSync15(file, "utf8"));
66532
66936
  return isRecord5(value) ? value : null;
66533
66937
  } catch {
66534
66938
  return null;
66535
66939
  }
66536
- }, claudeInstallRecord = (home, env) => path31.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
66940
+ }, claudeInstallRecord = (home, env) => path32.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
66537
66941
  const env = options.env ?? process.env;
66538
66942
  const home = options.home ?? env.HOME ?? os9.homedir();
66539
66943
  return {
@@ -66541,17 +66945,17 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66541
66945
  configDir: resolvedConfigDir,
66542
66946
  cwd: options.cwd ?? process.cwd(),
66543
66947
  env,
66544
- opencodeConfig: options.opencodeConfig ?? path31.join(home, ".config", "opencode", "opencode.json"),
66545
- cursorSettings: options.cursorSettings ?? path31.join(home, ".cursor", "settings.json"),
66546
- cursorMcp: options.cursorMcp ?? path31.join(home, ".cursor", "mcp.json"),
66547
- cursorPluginDir: options.cursorPluginDir ?? path31.join(home, ".cursor", "plugins", "local", "workit")
66948
+ opencodeConfig: options.opencodeConfig ?? path32.join(home, ".config", "opencode", "opencode.json"),
66949
+ cursorSettings: options.cursorSettings ?? path32.join(home, ".cursor", "settings.json"),
66950
+ cursorMcp: options.cursorMcp ?? path32.join(home, ".cursor", "mcp.json"),
66951
+ cursorPluginDir: options.cursorPluginDir ?? path32.join(home, ".cursor", "plugins", "local", "workit")
66548
66952
  };
66549
66953
  }, isRecord5 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readExisting = (p) => {
66550
66954
  let raw;
66551
66955
  try {
66552
- raw = readFileSync14(p, "utf8");
66956
+ raw = readFileSync15(p, "utf8");
66553
66957
  } catch {
66554
- if (existsSync14(p))
66958
+ if (existsSync15(p))
66555
66959
  return { kind: "malformed", error: `${p} is not readable` };
66556
66960
  return { kind: "missing" };
66557
66961
  }
@@ -66566,7 +66970,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66566
66970
  return { kind: "malformed", error: `${p} is not a JSON object` };
66567
66971
  }, readFileSafe = (p) => {
66568
66972
  try {
66569
- return readFileSync14(p, "utf8");
66973
+ return readFileSync15(p, "utf8");
66570
66974
  } catch {
66571
66975
  return null;
66572
66976
  }
@@ -66581,7 +66985,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66581
66985
  }
66582
66986
  }, isAdapter = (root, platform) => {
66583
66987
  try {
66584
- const pkg = JSON.parse(readFileSync14(path31.join(root, "package.json"), "utf8"));
66988
+ const pkg = JSON.parse(readFileSync15(path32.join(root, "package.json"), "utf8"));
66585
66989
  return pkg.name === `@brainervirus/workit-${platform}`;
66586
66990
  } catch {
66587
66991
  return false;
@@ -66595,8 +66999,8 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66595
66999
  if (!isDevOpenCodeAdapterRoot(root, opts.dev))
66596
67000
  return OPENCODE_NPM_PIN;
66597
67001
  for (const rel of ["src/plugin.ts", "dist/plugin.js"]) {
66598
- const entry = path31.join(root, rel);
66599
- if (existsSync14(entry))
67002
+ const entry = path32.join(root, rel);
67003
+ if (existsSync15(entry))
66600
67004
  return `file://${entry}`;
66601
67005
  }
66602
67006
  return null;
@@ -66613,22 +67017,22 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66613
67017
  for (const entry of readdirSync8(src, { withFileTypes: true })) {
66614
67018
  if (entry.name === "node_modules")
66615
67019
  continue;
66616
- const source = path31.join(src, entry.name);
66617
- const installed = path31.join(dest, entry.name);
66618
- const rel = path31.join(relative, entry.name);
67020
+ const source = path32.join(src, entry.name);
67021
+ const installed = path32.join(dest, entry.name);
67022
+ const rel = path32.join(relative, entry.name);
66619
67023
  if (entry.isDirectory()) {
66620
- if (!statSync5(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
67024
+ if (!statSync6(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
66621
67025
  return false;
66622
67026
  } else {
66623
- const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync14(source), pluginDir) : readFileSync14(source);
66624
- if (!expected.equals(readFileSync14(installed)))
67027
+ const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync15(source), pluginDir) : readFileSync15(source);
67028
+ if (!expected.equals(readFileSync15(installed)))
66625
67029
  return false;
66626
67030
  }
66627
67031
  }
66628
67032
  for (const entry of readdirSync8(dest, { withFileTypes: true })) {
66629
- if (existsSync14(path31.join(src, entry.name)))
67033
+ if (existsSync15(path32.join(src, entry.name)))
66630
67034
  continue;
66631
- const rel = path31.join(relative, entry.name);
67035
+ const rel = path32.join(relative, entry.name);
66632
67036
  if (rel === ".workit-root")
66633
67037
  continue;
66634
67038
  if (relative === "rules" && entry.isFile() && entry.name.endsWith(".mdc"))
@@ -66641,36 +67045,36 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
66641
67045
  }
66642
67046
  }, preservedCursorRules = (src, dest) => {
66643
67047
  const preserved = new Map;
66644
- const sourceRules = path31.join(src, "rules");
66645
- const installedRules = path31.join(dest, "rules");
67048
+ const sourceRules = path32.join(src, "rules");
67049
+ const installedRules = path32.join(dest, "rules");
66646
67050
  try {
66647
67051
  for (const entry of readdirSync8(installedRules, { withFileTypes: true })) {
66648
67052
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
66649
67053
  continue;
66650
- if (existsSync14(path31.join(sourceRules, entry.name)))
67054
+ if (existsSync15(path32.join(sourceRules, entry.name)))
66651
67055
  continue;
66652
- preserved.set(entry.name, readFileSync14(path31.join(installedRules, entry.name)));
67056
+ preserved.set(entry.name, readFileSync15(path32.join(installedRules, entry.name)));
66653
67057
  }
66654
67058
  } catch {}
66655
67059
  return preserved;
66656
67060
  }, removeLegacyCursorDir = (res) => {
66657
- const legacy = path31.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
66658
- if (legacy === res.cursorPluginDir || !existsSync14(legacy))
67061
+ const legacy = path32.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
67062
+ if (legacy === res.cursorPluginDir || !existsSync15(legacy))
66659
67063
  return;
66660
- const legacyRules = path31.join(legacy, "rules");
66661
- if (existsSync14(legacyRules)) {
67064
+ const legacyRules = path32.join(legacy, "rules");
67065
+ if (existsSync15(legacyRules)) {
66662
67066
  try {
66663
- mkdirSync6(path31.join(res.cursorPluginDir, "rules"), { recursive: true });
67067
+ mkdirSync7(path32.join(res.cursorPluginDir, "rules"), { recursive: true });
66664
67068
  for (const entry of readdirSync8(legacyRules, { withFileTypes: true })) {
66665
67069
  if (!entry.isFile() || !entry.name.endsWith(".mdc"))
66666
67070
  continue;
66667
- const target = path31.join(res.cursorPluginDir, "rules", entry.name);
66668
- if (!existsSync14(target))
66669
- copyFileSync4(path31.join(legacyRules, entry.name), target);
67071
+ const target = path32.join(res.cursorPluginDir, "rules", entry.name);
67072
+ if (!existsSync15(target))
67073
+ copyFileSync4(path32.join(legacyRules, entry.name), target);
66670
67074
  }
66671
67075
  } catch {}
66672
67076
  }
66673
- rmSync3(legacy, { recursive: true, force: true });
67077
+ rmSync4(legacy, { recursive: true, force: true });
66674
67078
  }, failedDetail = (report) => report.checks.filter((c) => c.status === "fail").map((c) => `${c.id}: ${c.detail}`).join("; "), setupCompletionGuidance = () => [
66675
67079
  "Run `workit doctor` to verify your installation.",
66676
67080
  "Run /wk-status in OpenCode to review token setup steps."
@@ -66700,16 +67104,16 @@ var init_setup = __esm(() => {
66700
67104
  { envKey: "WORKFLOW_GITHUB_HOST", affects: "vcs.json github.host" }
66701
67105
  ];
66702
67106
  SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
66703
- CURSOR_HOOKS_FILE = path31.join("hooks", "hooks-cursor.json");
67107
+ CURSOR_HOOKS_FILE = path32.join("hooks", "hooks-cursor.json");
66704
67108
  });
66705
67109
 
66706
67110
  // packages/workit-core/src/core/workspace-branch-policy.ts
66707
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
66708
- import path32 from "node:path";
67111
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
67112
+ import path33 from "node:path";
66709
67113
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
66710
67114
  function applyWorkspaceBranchPolicy(opts) {
66711
67115
  const { workspace_root, env = process.env } = opts;
66712
- const dir = path32.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
67116
+ const dir = path33.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
66713
67117
  const {
66714
67118
  status,
66715
67119
  path: wsPath,
@@ -66721,7 +67125,7 @@ function applyWorkspaceBranchPolicy(opts) {
66721
67125
  if (status === "malformed" || status === "invalid")
66722
67126
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
66723
67127
  const detection = detectBranchPolicy(workspace_root);
66724
- const name = String(env.WORKFLOW_BP_NAME ?? path32.basename(workspace_root));
67128
+ const name = String(env.WORKFLOW_BP_NAME ?? path33.basename(workspace_root));
66725
67129
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
66726
67130
  const policy = {
66727
67131
  preset: detection.preset,
@@ -66770,22 +67174,22 @@ function applyWorkspaceBranchPolicy(opts) {
66770
67174
  ok: false,
66771
67175
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
66772
67176
  };
66773
- mkdirSync7(path32.dirname(wsPath), { recursive: true });
67177
+ mkdirSync8(path33.dirname(wsPath), { recursive: true });
66774
67178
  const tmp = `${wsPath}.${process.pid}.tmp`;
66775
67179
  try {
66776
- writeFileSync8(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
67180
+ writeFileSync9(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
66777
67181
  `, "utf8");
66778
67182
  if (workspacesRevision(readFileSafe2(wsPath)) !== revision) {
66779
- rmSync4(tmp, { force: true });
67183
+ rmSync5(tmp, { force: true });
66780
67184
  return {
66781
67185
  ok: false,
66782
67186
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
66783
67187
  };
66784
67188
  }
66785
- renameSync4(tmp, wsPath);
67189
+ renameSync5(tmp, wsPath);
66786
67190
  } catch (error) {
66787
67191
  try {
66788
- rmSync4(tmp, { force: true });
67192
+ rmSync5(tmp, { force: true });
66789
67193
  } catch {}
66790
67194
  return {
66791
67195
  ok: false,
@@ -66802,7 +67206,7 @@ function applyWorkspaceBranchPolicy(opts) {
66802
67206
  }
66803
67207
  var readFileSafe2 = (p) => {
66804
67208
  try {
66805
- return readFileSync15(p, "utf8");
67209
+ return readFileSync16(p, "utf8");
66806
67210
  } catch {
66807
67211
  return null;
66808
67212
  }
@@ -67054,8 +67458,8 @@ var init_logic = __esm(() => {
67054
67458
  });
67055
67459
 
67056
67460
  // packages/workit-cli/src/wizard-state.ts
67057
- import { existsSync as existsSync15, statSync as statSync6 } from "node:fs";
67058
- import path33 from "node:path";
67461
+ import { existsSync as existsSync16, statSync as statSync7 } from "node:fs";
67462
+ import path34 from "node:path";
67059
67463
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
67060
67464
  function resolveBasePath(values, env = process.env) {
67061
67465
  return env.WORKFLOW_WORKSPACE_ROOT?.trim() || values.basePath.trim();
@@ -67574,7 +67978,7 @@ function workspaceDraftText(draft, field, value) {
67574
67978
  }
67575
67979
  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) => {
67576
67980
  try {
67577
- return statSync6(p).isDirectory();
67981
+ return statSync7(p).isDirectory();
67578
67982
  } catch {
67579
67983
  return false;
67580
67984
  }
@@ -67582,8 +67986,8 @@ var NEXT, PREV, skipsCustomBranch = (screen, preset) => (screen === "branchAllow
67582
67986
  const p = value.trim();
67583
67987
  if (!p)
67584
67988
  return "workspace root path is required";
67585
- return !path33.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
67586
- }, 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;
67989
+ return !path34.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
67990
+ }, 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;
67587
67991
  var init_wizard_state = __esm(() => {
67588
67992
  init_config();
67589
67993
  init_workspaces();
@@ -67642,9 +68046,9 @@ var init_wizard_state = __esm(() => {
67642
68046
  });
67643
68047
 
67644
68048
  // packages/workit-cli/src/admin/uninstall.ts
67645
- import { existsSync as existsSync16, readFileSync as readFileSync16, realpathSync as realpathSync8, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
68049
+ import { existsSync as existsSync17, readFileSync as readFileSync17, realpathSync as realpathSync8, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "node:fs";
67646
68050
  import os10 from "node:os";
67647
- import path34 from "node:path";
68051
+ import path35 from "node:path";
67648
68052
  function cleanCursorSettings(settings, pluginDir) {
67649
68053
  const next = { ...settings };
67650
68054
  let changed = false;
@@ -67744,7 +68148,7 @@ function planUninstall(paths = {}) {
67744
68148
  const mcpExisting = readJsonRecord(res.cursorMcp);
67745
68149
  const settingsDirty = settingsExisting.kind === "malformed" || settingsExisting.kind === "record" && cleanCursorSettings(settingsExisting.value, res.cursorPluginDir).changed;
67746
68150
  const mcpDirty = mcpExisting.kind === "malformed" || mcpExisting.kind === "record" && cleanCursorMcp(mcpExisting.value).changed;
67747
- const dirExists = existsSync16(res.cursorPluginDir);
68151
+ const dirExists = existsSync17(res.cursorPluginDir);
67748
68152
  const actions = [];
67749
68153
  if (settingsDirty) {
67750
68154
  actions.push({
@@ -67772,7 +68176,7 @@ function planUninstall(paths = {}) {
67772
68176
  installed: settingsDirty || mcpDirty || dirExists,
67773
68177
  actions
67774
68178
  };
67775
- const codexInstalled = existsSync16(res.codexPluginDir);
68179
+ const codexInstalled = existsSync17(res.codexPluginDir);
67776
68180
  const codex = {
67777
68181
  host: "codex",
67778
68182
  installed: codexInstalled,
@@ -67819,17 +68223,17 @@ function applyUninstall(plan, paths = {}) {
67819
68223
  status = outcome.status;
67820
68224
  detail = outcome.detail;
67821
68225
  } else if (action.kind === "remove-dir") {
67822
- const resolved = path34.resolve(action.path);
67823
- const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path34.resolve(res.codexPluginDir) && path34.basename(resolved) === "workit" && path34.basename(path34.dirname(resolved)) === "plugins";
68226
+ const resolved = path35.resolve(action.path);
68227
+ const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path35.resolve(res.codexPluginDir) && path35.basename(resolved) === "workit" && path35.basename(path35.dirname(resolved)) === "plugins";
67824
68228
  if (!allowed) {
67825
68229
  status = "failed";
67826
68230
  detail = `refusing to remove non-canonical plugin directory: ${resolved}`;
67827
- } else if (!existsSync16(resolved)) {
68231
+ } else if (!existsSync17(resolved)) {
67828
68232
  status = "skipped";
67829
68233
  detail = "directory already absent";
67830
68234
  } else {
67831
68235
  try {
67832
- rmSync5(resolved, { recursive: true, force: true });
68236
+ rmSync6(resolved, { recursive: true, force: true });
67833
68237
  status = "removed";
67834
68238
  } catch (error) {
67835
68239
  status = "failed";
@@ -67855,19 +68259,19 @@ function applyUninstall(plan, paths = {}) {
67855
68259
  var resolveUninstallPaths = (options = {}) => {
67856
68260
  const home = options.home ?? options.env?.HOME ?? os10.homedir();
67857
68261
  return {
67858
- opencodeConfig: options.opencodeConfig ?? path34.join(home, ".config", "opencode", "opencode.json"),
67859
- cursorSettings: options.cursorSettings ?? path34.join(home, ".cursor", "settings.json"),
67860
- cursorMcp: options.cursorMcp ?? path34.join(home, ".cursor", "mcp.json"),
67861
- cursorPluginDir: options.cursorPluginDir ?? path34.join(home, ".cursor", "plugins", "local", "workit"),
67862
- codexPluginDir: path34.join(home, ".codex", "plugins", "workit"),
67863
- piConfig: path34.join(home, ".pi", "config.json")
68262
+ opencodeConfig: options.opencodeConfig ?? path35.join(home, ".config", "opencode", "opencode.json"),
68263
+ cursorSettings: options.cursorSettings ?? path35.join(home, ".cursor", "settings.json"),
68264
+ cursorMcp: options.cursorMcp ?? path35.join(home, ".cursor", "mcp.json"),
68265
+ cursorPluginDir: options.cursorPluginDir ?? path35.join(home, ".cursor", "plugins", "local", "workit"),
68266
+ codexPluginDir: path35.join(home, ".codex", "plugins", "workit"),
68267
+ piConfig: path35.join(home, ".pi", "config.json")
67864
68268
  };
67865
68269
  }, isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readJsonRecord = (file) => {
67866
68270
  let raw;
67867
68271
  try {
67868
- raw = readFileSync16(file, "utf8");
68272
+ raw = readFileSync17(file, "utf8");
67869
68273
  } catch {
67870
- if (existsSync16(file))
68274
+ if (existsSync17(file))
67871
68275
  return { kind: "malformed", error: `${file} is not readable` };
67872
68276
  return { kind: "missing" };
67873
68277
  }
@@ -67878,8 +68282,8 @@ var resolveUninstallPaths = (options = {}) => {
67878
68282
  return { kind: "malformed", error: `${file} is not valid JSON` };
67879
68283
  }
67880
68284
  }, stripTrailingSep = (p) => {
67881
- const j = path34.join(p);
67882
- return path34.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
68285
+ const j = path35.join(p);
68286
+ return path35.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
67883
68287
  }, CURSOR_LEGACY_IDENTITIES, jsonCleanerFor = (target, res) => {
67884
68288
  if (target === res.opencodeConfig)
67885
68289
  return cleanOpenCodeConfig;
@@ -67891,8 +68295,8 @@ var resolveUninstallPaths = (options = {}) => {
67891
68295
  return cleanPiConfig;
67892
68296
  return null;
67893
68297
  }, canonicalRemoveDirAllowed = (actionPath, res) => {
67894
- const expected = path34.resolve(res.cursorPluginDir);
67895
- if (!(actionPath === expected && path34.basename(expected) === "workit" && path34.basename(path34.dirname(expected)) === "local" && path34.basename(path34.dirname(path34.dirname(expected))) === "plugins")) {
68298
+ const expected = path35.resolve(res.cursorPluginDir);
68299
+ if (!(actionPath === expected && path35.basename(expected) === "workit" && path35.basename(path35.dirname(expected)) === "local" && path35.basename(path35.dirname(path35.dirname(expected))) === "plugins")) {
67896
68300
  return false;
67897
68301
  }
67898
68302
  try {
@@ -67915,7 +68319,7 @@ var resolveUninstallPaths = (options = {}) => {
67915
68319
  `;
67916
68320
  let current;
67917
68321
  try {
67918
- current = readFileSync16(target, "utf8");
68322
+ current = readFileSync17(target, "utf8");
67919
68323
  } catch (error) {
67920
68324
  return {
67921
68325
  status: "failed",
@@ -67926,7 +68330,7 @@ var resolveUninstallPaths = (options = {}) => {
67926
68330
  return { status: "skipped", detail: "already clean" };
67927
68331
  }
67928
68332
  try {
67929
- writeFileSync9(target, serialized, "utf8");
68333
+ writeFileSync10(target, serialized, "utf8");
67930
68334
  } catch (error) {
67931
68335
  return {
67932
68336
  status: "failed",
@@ -67944,7 +68348,7 @@ var resolveUninstallPaths = (options = {}) => {
67944
68348
  };
67945
68349
  const home = paths.home ?? paths.env?.HOME ?? os10.homedir();
67946
68350
  const env = paths.env ?? process.env;
67947
- 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 ?? "")));
68351
+ 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 ?? "")));
67948
68352
  if (!recorded)
67949
68353
  return { status: "skipped", detail: `${id} (${scope} scope) is no longer installed` };
67950
68354
  const executable = findHostExecutable("claude", { home, env });
@@ -70423,9 +70827,9 @@ var init_src2 = __esm(async () => {
70423
70827
  // packages/workit-cli/src/verbs/init.ts
70424
70828
  var exports_init = {};
70425
70829
  __export(exports_init, {
70426
- run: () => run2
70830
+ run: () => run3
70427
70831
  });
70428
- async function run2(_argv, io) {
70832
+ async function run3(_argv, io) {
70429
70833
  if (io.json)
70430
70834
  return emit(io, fail("invalid_input", "workit init is an interactive wizard and has no --json output", {
70431
70835
  unblock: "run `workit init` in a terminal without --json"
@@ -70439,19 +70843,19 @@ var init_init = __esm(() => {
70439
70843
  });
70440
70844
 
70441
70845
  // packages/workit-cli/src/upgrade.ts
70442
- import { spawnSync as spawnSync14 } from "node:child_process";
70846
+ import { spawnSync as spawnSync15 } from "node:child_process";
70443
70847
  import {
70444
- existsSync as existsSync17,
70848
+ existsSync as existsSync18,
70445
70849
  mkdtempSync as mkdtempSync3,
70446
- rmSync as rmSync6,
70447
- readFileSync as readFileSync17,
70448
- mkdirSync as mkdirSync8,
70850
+ rmSync as rmSync7,
70851
+ readFileSync as readFileSync18,
70852
+ mkdirSync as mkdirSync9,
70449
70853
  copyFileSync as copyFileSync5,
70450
- chmodSync,
70854
+ chmodSync as chmodSync2,
70451
70855
  cpSync as cpSync5,
70452
- lstatSync as lstatSync6
70856
+ lstatSync as lstatSync7
70453
70857
  } from "node:fs";
70454
- import path35 from "node:path";
70858
+ import path36 from "node:path";
70455
70859
  import os12 from "node:os";
70456
70860
  import { createHash as createHash14 } from "node:crypto";
70457
70861
  function previewUpgrade(hosts = HOSTS2, deps = {}) {
@@ -70475,9 +70879,9 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70475
70879
  for (const file of [
70476
70880
  paths.opencodeConfig,
70477
70881
  paths.opencodeConfig + "c",
70478
- path35.join(home, ".cursor/settings.json"),
70479
- path35.join(home, ".cursor/mcp.json"),
70480
- path35.join(home, ".cursor/plugins/local/workit/package.json"),
70882
+ path36.join(home, ".cursor/settings.json"),
70883
+ path36.join(home, ".cursor/mcp.json"),
70884
+ path36.join(home, ".cursor/plugins/local/workit/package.json"),
70481
70885
  paths.codexConfig,
70482
70886
  paths.piSettings
70483
70887
  ])
@@ -70492,20 +70896,20 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70492
70896
  });
70493
70897
  const installedSources = sources(paths, run, hosts);
70494
70898
  if (installedSources.some((source) => source.host === "opencode")) {
70495
- const scratch = mkdtempSync3(path35.join(os12.tmpdir(), "workit-upgrade-opencode-"));
70899
+ const scratch = mkdtempSync3(path36.join(os12.tmpdir(), "workit-upgrade-opencode-"));
70496
70900
  let probe;
70497
70901
  try {
70498
70902
  probe = runner({
70499
70903
  ...deps,
70500
70904
  env: {
70501
70905
  ...deps.env,
70502
- XDG_DATA_HOME: path35.join(scratch, "data"),
70503
- XDG_STATE_HOME: path35.join(scratch, "state"),
70504
- XDG_CACHE_HOME: path35.join(scratch, "cache")
70906
+ XDG_DATA_HOME: path36.join(scratch, "data"),
70907
+ XDG_STATE_HOME: path36.join(scratch, "state"),
70908
+ XDG_CACHE_HOME: path36.join(scratch, "cache")
70505
70909
  }
70506
70910
  })("opencode", ["--version"]);
70507
70911
  } finally {
70508
- rmSync6(scratch, { recursive: true, force: true });
70912
+ rmSync7(scratch, { recursive: true, force: true });
70509
70913
  }
70510
70914
  const installed = probe.status === 0 ? probe.stdout.match(/\d+\.\d+\.\d+/)?.[0] : undefined;
70511
70915
  if (installed && !versionAtLeast(installed, SUPPORT_MATRIX.opencode.minimum))
@@ -70547,7 +70951,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
70547
70951
  env: deps.env,
70548
70952
  cwd: home,
70549
70953
  packageVersion: latest.stdout.trim(),
70550
- targetDir: path35.join(home, ".cursor/plugins/local/workit")
70954
+ targetDir: path36.join(home, ".cursor/plugins/local/workit")
70551
70955
  });
70552
70956
  plan.entries.push({
70553
70957
  host: source.host,
@@ -70577,13 +70981,13 @@ function applyUpgrade(plan, deps = {}) {
70577
70981
  ok: false,
70578
70982
  error: "Stop the selected running hosts before upgrading their loaded plugins"
70579
70983
  };
70580
- const backup = path35.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
70581
- mkdirSync8(backup, { recursive: true, mode: 448 });
70984
+ const backup = path36.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
70985
+ mkdirSync9(backup, { recursive: true, mode: 448 });
70582
70986
  for (const file of Object.keys(plan.configDigests))
70583
- if (existsSync17(file)) {
70584
- const target = path35.join(backup, createHash14("sha256").update(file).digest("hex").slice(0, 12) + "-" + path35.basename(file));
70987
+ if (existsSync18(file)) {
70988
+ const target = path36.join(backup, createHash14("sha256").update(file).digest("hex").slice(0, 12) + "-" + path36.basename(file));
70585
70989
  copyFileSync5(file, target);
70586
- chmodSync(target, 384);
70990
+ chmodSync2(target, 384);
70587
70991
  }
70588
70992
  for (const migration of plan.migrations) {
70589
70993
  const value = object2(migration.file);
@@ -70598,9 +71002,9 @@ function applyUpgrade(plan, deps = {}) {
70598
71002
  `, 384);
70599
71003
  }
70600
71004
  const run = runner(deps);
70601
- const cursorRoot = path35.join(home, ".cursor/plugins/local/workit");
70602
- if (plan.entries.some((entry) => entry.host === "cursor") && existsSync17(cursorRoot))
70603
- cpSync5(cursorRoot, path35.join(backup, "cursor-package"), { recursive: true });
71005
+ const cursorRoot = path36.join(home, ".cursor/plugins/local/workit");
71006
+ if (plan.entries.some((entry) => entry.host === "cursor") && existsSync18(cursorRoot))
71007
+ cpSync5(cursorRoot, path36.join(backup, "cursor-package"), { recursive: true });
70604
71008
  for (const entry of plan.entries) {
70605
71009
  if (entry.host === "opencode")
70606
71010
  return {
@@ -70620,7 +71024,7 @@ function applyUpgrade(plan, deps = {}) {
70620
71024
  {
70621
71025
  type: "install-host",
70622
71026
  platform: "cursor",
70623
- path: path35.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
71027
+ path: path36.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
70624
71028
  commands: entry.commands.map((command) => ({
70625
71029
  ...command,
70626
71030
  purpose: command.purpose ?? "Upgrade Cursor"
@@ -70629,7 +71033,7 @@ function applyUpgrade(plan, deps = {}) {
70629
71033
  {
70630
71034
  type: "install-adapter",
70631
71035
  platform: "cursor",
70632
- path: path35.join(home, ".cursor/plugins/local/workit")
71036
+ path: path36.join(home, ".cursor/plugins/local/workit")
70633
71037
  }
70634
71038
  ]
70635
71039
  };
@@ -70643,7 +71047,7 @@ function applyUpgrade(plan, deps = {}) {
70643
71047
  return { exitCode: outcome.status ?? 1, stdout: outcome.stdout, stderr: "" };
70644
71048
  }
70645
71049
  });
70646
- const pkg = object2(path35.join(home, ".cursor/plugins/local/workit/package.json"));
71050
+ const pkg = object2(path36.join(home, ".cursor/plugins/local/workit/package.json"));
70647
71051
  if (!result.ok || pkg?.version !== entry.latest)
70648
71052
  return {
70649
71053
  ok: false,
@@ -70677,15 +71081,15 @@ function applyUpgrade(plan, deps = {}) {
70677
71081
  const currentRoot = run("npm", ["root", "--global"]);
70678
71082
  if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== plan.cli.root)
70679
71083
  return { ok: false, backup, error: "Global npm location changed; preview again" };
70680
- const cliBackup = path35.join(backup, "cli-package");
70681
- cpSync5(path35.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
71084
+ const cliBackup = path36.join(backup, "cli-package");
71085
+ cpSync5(path36.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
70682
71086
  if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${plan.cli.latest}`]).status !== 0)
70683
71087
  return {
70684
71088
  ok: false,
70685
71089
  backup,
70686
- error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path35.join(cliBackup, "dist/index.js")}`
71090
+ error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path36.join(cliBackup, "dist/index.js")}`
70687
71091
  };
70688
- const installed = object2(path35.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
71092
+ const installed = object2(path36.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
70689
71093
  if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== plan.cli.latest)
70690
71094
  return {
70691
71095
  ok: false,
@@ -70710,12 +71114,12 @@ async function runUpgradeCommand(argv, deps = {}) {
70710
71114
  if (flags.has("--cli")) {
70711
71115
  const run = runner(deps);
70712
71116
  const root = run("npm", ["root", "--global"]);
70713
- if (root.status !== 0 || !path35.isAbsolute(root.stdout.trim())) {
71117
+ if (root.status !== 0 || !path36.isAbsolute(root.stdout.trim())) {
70714
71118
  plan.blocked.push("Global npm location could not be verified");
70715
71119
  } else {
70716
- const packageFile = path35.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
71120
+ const packageFile = path36.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
70717
71121
  const pkg = object2(packageFile);
70718
- if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync6(path35.dirname(packageFile)).isSymbolicLink()) {
71122
+ if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(path36.dirname(packageFile)).isSymbolicLink()) {
70719
71123
  plan.skipped.push({
70720
71124
  host: "cli",
70721
71125
  reason: "No managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
@@ -70795,7 +71199,7 @@ async function runLaunchCommand(argv, deps = {}) {
70795
71199
  }
70796
71200
  if (deps.run)
70797
71201
  return deps.run(host, hostArgs).status ?? 1;
70798
- return spawnSync14(launch.command, launch.args, {
71202
+ return spawnSync15(launch.command, launch.args, {
70799
71203
  stdio: "inherit",
70800
71204
  env
70801
71205
  }).status ?? 1;
@@ -70806,37 +71210,37 @@ var HOSTS2, upgradePaths = (deps) => {
70806
71210
  ...process.env,
70807
71211
  ...deps.home || deps.env?.HOME ? {
70808
71212
  HOME: home,
70809
- CODEX_HOME: path35.join(home, ".codex"),
70810
- PI_CODING_AGENT_DIR: path35.join(home, ".pi/agent"),
70811
- XDG_CONFIG_HOME: path35.join(home, ".config"),
71213
+ CODEX_HOME: path36.join(home, ".codex"),
71214
+ PI_CODING_AGENT_DIR: path36.join(home, ".pi/agent"),
71215
+ XDG_CONFIG_HOME: path36.join(home, ".config"),
70812
71216
  WORKFLOW_TOOLKIT_CONFIG: undefined,
70813
71217
  WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
70814
71218
  } : {},
70815
71219
  ...deps.env,
70816
71220
  HOME: home
70817
71221
  };
70818
- const xdg = env.XDG_CONFIG_HOME || path35.join(home, ".config");
71222
+ const xdg = env.XDG_CONFIG_HOME || path36.join(home, ".config");
70819
71223
  return {
70820
71224
  home,
70821
71225
  env,
70822
- configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path35.join(xdg, "workit"),
70823
- opencodeConfig: path35.join(xdg, "opencode/opencode.json"),
70824
- piSettings: path35.join(env.PI_CODING_AGENT_DIR ?? path35.join(home, ".pi/agent"), "settings.json"),
70825
- codexConfig: path35.join(env.CODEX_HOME ?? path35.join(home, ".codex"), "config.toml")
71226
+ configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path36.join(xdg, "workit"),
71227
+ opencodeConfig: path36.join(xdg, "opencode/opencode.json"),
71228
+ piSettings: path36.join(env.PI_CODING_AGENT_DIR ?? path36.join(home, ".pi/agent"), "settings.json"),
71229
+ codexConfig: path36.join(env.CODEX_HOME ?? path36.join(home, ".codex"), "config.toml")
70826
71230
  };
70827
71231
  }, object2 = (file) => {
70828
- if (!existsSync17(file))
71232
+ if (!existsSync18(file))
70829
71233
  return null;
70830
71234
  let data;
70831
71235
  try {
70832
- data = JSON.parse(readFileSync17(file, "utf8"));
71236
+ data = JSON.parse(readFileSync18(file, "utf8"));
70833
71237
  } catch {
70834
71238
  throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
70835
71239
  }
70836
71240
  if (!data || typeof data !== "object" || Array.isArray(data))
70837
71241
  throw new Error(`${file} is not a configuration object`);
70838
71242
  return data;
70839
- }, digest2 = (file) => existsSync17(file) ? createHash14("sha256").update(readFileSync17(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
71243
+ }, digest2 = (file) => existsSync18(file) ? createHash14("sha256").update(readFileSync18(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
70840
71244
  const { home, env } = upgradePaths(deps);
70841
71245
  let step;
70842
71246
  try {
@@ -70853,7 +71257,7 @@ var HOSTS2, upgradePaths = (deps) => {
70853
71257
  }), sources = (paths, run, hosts) => {
70854
71258
  const { home, opencodeConfig, piSettings } = paths;
70855
71259
  const result = [];
70856
- if (hosts.includes("opencode") && existsSync17(opencodeConfig + "c"))
71260
+ if (hosts.includes("opencode") && existsSync18(opencodeConfig + "c"))
70857
71261
  throw new Error("OpenCode JSONC config is present; inspect effective native configuration before updating");
70858
71262
  const oc = hosts.includes("opencode") ? object2(opencodeConfig) : null;
70859
71263
  if (oc && [oc.plugins, oc.plugin].some((value) => value !== undefined && !Array.isArray(value)))
@@ -70862,9 +71266,9 @@ var HOSTS2, upgradePaths = (deps) => {
70862
71266
  for (const pin of plugins)
70863
71267
  if (typeof pin === "string" && isWorkitPlugin(pin))
70864
71268
  result.push(classify("opencode", pin));
70865
- const cursor = hosts.includes("cursor") ? object2(path35.join(home, ".cursor/plugins/local/workit/package.json")) : null;
71269
+ const cursor = hosts.includes("cursor") ? object2(path36.join(home, ".cursor/plugins/local/workit/package.json")) : null;
70866
71270
  if (cursor?.name === "@brainervirus/workit-cursor") {
70867
- const mcp = object2(path35.join(home, ".cursor/mcp.json"));
71271
+ const mcp = object2(path36.join(home, ".cursor/mcp.json"));
70868
71272
  const args = mcp?.mcpServers?.workit?.args ?? [];
70869
71273
  const selector = args.find((arg) => typeof arg === "string" && arg.startsWith("--package="));
70870
71274
  result.push(classify("cursor", typeof selector === "string" ? selector.slice(10) : "local"));
@@ -70877,7 +71281,7 @@ var HOSTS2, upgradePaths = (deps) => {
70877
71281
  if (typeof source !== "string")
70878
71282
  continue;
70879
71283
  const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
70880
- const local = path35.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path35.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
71284
+ const local = path36.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path36.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
70881
71285
  if (npm || local)
70882
71286
  result.push(classify("pi", source));
70883
71287
  }
@@ -70888,7 +71292,7 @@ var HOSTS2, upgradePaths = (deps) => {
70888
71292
  hostCommand("codex", [], { home, env: paths.env });
70889
71293
  available = true;
70890
71294
  } catch {}
70891
- if (available || existsSync17(paths.codexConfig))
71295
+ if (available || existsSync18(paths.codexConfig))
70892
71296
  throw new Error("Codex plugin inventory failed; inspect the native host before upgrading");
70893
71297
  }
70894
71298
  if (listing.status === 0) {
@@ -70924,9 +71328,9 @@ var HOSTS2, upgradePaths = (deps) => {
70924
71328
  }, piInstalledVersion = (listing, selector) => {
70925
71329
  const lines = listing.split(/\r?\n/).map((line) => line.trim());
70926
71330
  const index = lines.indexOf(selector);
70927
- if (index < 0 || !lines[index + 1] || !path35.isAbsolute(lines[index + 1]))
71331
+ if (index < 0 || !lines[index + 1] || !path36.isAbsolute(lines[index + 1]))
70928
71332
  return;
70929
- const pkg = object2(path35.join(lines[index + 1], "package.json"));
71333
+ const pkg = object2(path36.join(lines[index + 1], "package.json"));
70930
71334
  return pkg?.name === "@brainervirus/workit-pi" ? pkg.version : undefined;
70931
71335
  }, versionAtLeast = (installed, latest) => {
70932
71336
  if (!/^\d+\.\d+\.\d+$/.test(installed))
@@ -70959,9 +71363,9 @@ var init_upgrade = __esm(() => {
70959
71363
  // packages/workit-cli/src/verbs/upgrade.ts
70960
71364
  var exports_upgrade = {};
70961
71365
  __export(exports_upgrade, {
70962
- run: () => run3
71366
+ run: () => run4
70963
71367
  });
70964
- var run3 = (argv) => runUpgradeCommand(argv);
71368
+ var run4 = (argv) => runUpgradeCommand(argv);
70965
71369
  var init_upgrade2 = __esm(() => {
70966
71370
  init_upgrade();
70967
71371
  });
@@ -70969,17 +71373,17 @@ var init_upgrade2 = __esm(() => {
70969
71373
  // packages/workit-cli/src/verbs/launch.ts
70970
71374
  var exports_launch = {};
70971
71375
  __export(exports_launch, {
70972
- run: () => run4
71376
+ run: () => run5
70973
71377
  });
70974
- var run4 = (argv) => runLaunchCommand(argv);
71378
+ var run5 = (argv) => runLaunchCommand(argv);
70975
71379
  var init_launch = __esm(() => {
70976
71380
  init_upgrade();
70977
71381
  });
70978
71382
 
70979
71383
  // packages/workit-core/src/knowledge.ts
70980
- import { spawnSync as spawnSync15 } from "node:child_process";
71384
+ import { spawnSync as spawnSync16 } from "node:child_process";
70981
71385
  import fs17 from "node:fs";
70982
- import path36 from "node:path";
71386
+ import path37 from "node:path";
70983
71387
  function lintKnowledge(root) {
70984
71388
  const seen = new Set;
70985
71389
  const unreadable = [];
@@ -71083,7 +71487,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71083
71487
  const end = lines.indexOf("---", 1);
71084
71488
  return end === -1 ? lines : [...lines.slice(0, end + 1).map(() => ""), ...lines.slice(end + 1)];
71085
71489
  }, read = (root, file) => {
71086
- const full = path36.join(root, file);
71490
+ const full = path37.join(root, file);
71087
71491
  try {
71088
71492
  fs17.lstatSync(full);
71089
71493
  } catch {
@@ -71098,7 +71502,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71098
71502
  return "unreadable";
71099
71503
  }
71100
71504
  }, trackedTopLevel = (root) => {
71101
- const run = spawnSync15("git", ["ls-files", "-z", "--cached"], {
71505
+ const run = spawnSync16("git", ["ls-files", "-z", "--cached"], {
71102
71506
  cwd: root,
71103
71507
  encoding: "utf8",
71104
71508
  maxBuffer: 256 * 1024 * 1024,
@@ -71108,7 +71512,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71108
71512
  if (run.status !== 0)
71109
71513
  return null;
71110
71514
  return new Set(run.stdout.split("\x00").filter(Boolean).map((file) => file.split("/")[0]));
71111
- }, exists = (target) => {
71515
+ }, exists2 = (target) => {
71112
71516
  try {
71113
71517
  fs17.statSync(target);
71114
71518
  return true;
@@ -71124,7 +71528,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71124
71528
  }
71125
71529
  }, brokenLinks = (root, doc, tracked) => {
71126
71530
  const findings = [];
71127
- const base = path36.dirname(path36.join(root, doc.file));
71531
+ const base = path37.dirname(path37.join(root, doc.file));
71128
71532
  const missing = (target, line, how) => findings.push({
71129
71533
  rule: "broken-link",
71130
71534
  file: doc.file,
@@ -71143,8 +71547,8 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71143
71547
  const local = localTarget(target);
71144
71548
  if (!local)
71145
71549
  continue;
71146
- const resolved = local.startsWith("/") ? path36.join(root, local) : path36.resolve(base, local);
71147
- if (!exists(resolved))
71550
+ const resolved = local.startsWith("/") ? path37.join(root, local) : path37.resolve(base, local);
71551
+ if (!exists2(resolved))
71148
71552
  missing(target, index + 1, "link");
71149
71553
  }
71150
71554
  for (const [, span] of text.matchAll(CODE_SPAN)) {
@@ -71153,10 +71557,10 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
71153
71557
  continue;
71154
71558
  const relative = /^\.\.?\//.test(candidate);
71155
71559
  const first = candidate.split("/")[0];
71156
- if (!relative && !(tracked ? tracked.has(first) : exists(path36.join(root, first))))
71560
+ if (!relative && !(tracked ? tracked.has(first) : exists2(path37.join(root, first))))
71157
71561
  continue;
71158
- const resolved = relative ? path36.resolve(base, candidate) : path36.join(root, candidate);
71159
- if (!exists(resolved))
71562
+ const resolved = relative ? path37.resolve(base, candidate) : path37.join(root, candidate);
71563
+ if (!exists2(resolved))
71160
71564
  missing(span, index + 1, "pointer");
71161
71565
  }
71162
71566
  }
@@ -71281,7 +71685,7 @@ var init_prompt = () => {};
71281
71685
  // packages/workit-cli/src/verbs/doctor.ts
71282
71686
  var exports_doctor = {};
71283
71687
  __export(exports_doctor, {
71284
- run: () => run5
71688
+ run: () => run6
71285
71689
  });
71286
71690
  async function confirmForcedLockClear(root, args, io) {
71287
71691
  const lock = inspectMetadataLock(root);
@@ -71304,7 +71708,7 @@ async function confirmForcedLockClear(root, args, io) {
71304
71708
  return "cancelled";
71305
71709
  return /^y(es)?$/i.test(answer.trim());
71306
71710
  }
71307
- async function run5(argv, io) {
71711
+ async function run6(argv, io) {
71308
71712
  const root = workspaceRootFor();
71309
71713
  const force = argv.includes("--force");
71310
71714
  let fixLock = null;
@@ -71322,6 +71726,18 @@ async function run5(argv, io) {
71322
71726
  }
71323
71727
  fixLock = clearStaleMetadataLock(root, { force });
71324
71728
  }
71729
+ let sessionHook = null;
71730
+ if (argv.includes("--fix")) {
71731
+ try {
71732
+ const fixed = fixSessionHook({ host: "cli", cwd: io.cwd, workspaceRoot: root });
71733
+ sessionHook = { action: fixed.action, detail: fixed.detail };
71734
+ } catch (error) {
71735
+ sessionHook = {
71736
+ action: "failed",
71737
+ detail: error instanceof Error ? error.message : String(error)
71738
+ };
71739
+ }
71740
+ }
71325
71741
  const report = runDoctor({
71326
71742
  host: "cli",
71327
71743
  cwd: io.cwd,
@@ -71344,7 +71760,7 @@ async function run5(argv, io) {
71344
71760
  budget: knowledge.budget,
71345
71761
  findings: knowledge.findings.length
71346
71762
  } : { unavailable: true };
71347
- io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, knowledge: summary }, null, 2)}
71763
+ io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, ...sessionHook ? { sessionHook } : {}, knowledge: summary }, null, 2)}
71348
71764
  `);
71349
71765
  return report.exitCode;
71350
71766
  }
@@ -71353,6 +71769,9 @@ async function run5(argv, io) {
71353
71769
  io.stdout(`fix-lock: ${what}${fixLock.guardCleared ? "; removed abandoned reclaim guard" : ""}
71354
71770
  `);
71355
71771
  }
71772
+ if (sessionHook)
71773
+ io.stdout(`fix: session hook ${sessionHook.action}: ${sessionHook.detail}
71774
+ `);
71356
71775
  io.stdout(`workit doctor — ${report.ok ? "healthy" : "problems found"} (${report.offline ? "offline" : "online"})
71357
71776
  `);
71358
71777
  for (const check of report.checks) {
@@ -71485,11 +71904,11 @@ var init_redact = __esm(() => {
71485
71904
  });
71486
71905
 
71487
71906
  // packages/workit-core/src/checks.ts
71488
- import { spawn, spawnSync as spawnSync16 } from "node:child_process";
71907
+ import { spawn, spawnSync as spawnSync17 } from "node:child_process";
71489
71908
  import { createHash as createHash15 } from "node:crypto";
71490
71909
  import fs18 from "node:fs";
71491
71910
  import os13 from "node:os";
71492
- import path37 from "node:path";
71911
+ import path38 from "node:path";
71493
71912
  import { StringDecoder } from "node:string_decoder";
71494
71913
  function tailLines(text, lines = TAIL_LINES) {
71495
71914
  const all = text.replace(/\r\n?/gu, `
@@ -71609,7 +72028,7 @@ function planSpawn(argv, options) {
71609
72028
  return { file: argv[0], args: [], verbatim: false, shell: true };
71610
72029
  if (options.platform !== "win32")
71611
72030
  return { file: argv[0], args: argv.slice(1), verbatim: false, shell: false };
71612
- const win = path37.win32;
72031
+ const win = path38.win32;
71613
72032
  const isFile = options.isFile ?? ((file) => {
71614
72033
  try {
71615
72034
  return fs18.statSync(file).isFile();
@@ -71664,7 +72083,7 @@ function runCheckCommand(argv, options) {
71664
72083
  if (!child?.pid)
71665
72084
  return;
71666
72085
  if (windows)
71667
- spawnSync16("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
72086
+ spawnSync17("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
71668
72087
  stdio: "ignore",
71669
72088
  windowsHide: true
71670
72089
  });
@@ -71766,10 +72185,10 @@ function runCheckCommand(argv, options) {
71766
72185
  function storeLog(storeRoot, log) {
71767
72186
  const digest = logDigest(log);
71768
72187
  const ref = `blobs/logs/${digest.slice("sha256:".length)}.log`;
71769
- const file = path37.join(storeRoot, ref);
72188
+ const file = path38.join(storeRoot, ref);
71770
72189
  try {
71771
72190
  if (!fs18.existsSync(file)) {
71772
- fs18.mkdirSync(path37.dirname(file), { recursive: true });
72191
+ fs18.mkdirSync(path38.dirname(file), { recursive: true });
71773
72192
  const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
71774
72193
  fs18.writeFileSync(temp, log, { mode: 384 });
71775
72194
  fs18.renameSync(temp, file);
@@ -71782,7 +72201,7 @@ function storeLog(storeRoot, log) {
71782
72201
  function pruneCheckLogs(storeRoot, options = {}) {
71783
72202
  const limits = { ...LOG_RETENTION, ...options.retention };
71784
72203
  const now = options.now ?? Date.now();
71785
- const dir = path37.join(storeRoot, "blobs", "logs");
72204
+ const dir = path38.join(storeRoot, "blobs", "logs");
71786
72205
  const report = { removed: 0, removedBytes: 0, kept: 0 };
71787
72206
  let names;
71788
72207
  try {
@@ -71792,7 +72211,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
71792
72211
  }
71793
72212
  const files = names.flatMap((name) => {
71794
72213
  try {
71795
- const stat = fs18.statSync(path37.join(dir, name));
72214
+ const stat = fs18.statSync(path38.join(dir, name));
71796
72215
  return stat.isFile() ? [{ name, size: stat.size, mtimeMs: stat.mtimeMs }] : [];
71797
72216
  } catch {
71798
72217
  return [];
@@ -71811,7 +72230,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
71811
72230
  continue;
71812
72231
  if (!options.dryRun)
71813
72232
  try {
71814
- fs18.rmSync(path37.join(dir, file.name), { force: true });
72233
+ fs18.rmSync(path38.join(dir, file.name), { force: true });
71815
72234
  } catch {
71816
72235
  continue;
71817
72236
  }
@@ -71842,9 +72261,9 @@ var init_checks3 = __esm(() => {
71842
72261
  // packages/workit-cli/src/verbs/gc.ts
71843
72262
  var exports_gc = {};
71844
72263
  __export(exports_gc, {
71845
- run: () => run6
72264
+ run: () => run7
71846
72265
  });
71847
- async function run6(argv, io) {
72266
+ async function run7(argv, io) {
71848
72267
  const dryRun = argv.includes("--dry-run");
71849
72268
  const pruneRecovery = argv.includes("--prune-recovery");
71850
72269
  if (pruneRecovery && !dryRun && !argv.includes("--yes"))
@@ -71888,9 +72307,9 @@ var init_gc = __esm(() => {
71888
72307
  // packages/workit-cli/src/verbs/uninstall.ts
71889
72308
  var exports_uninstall = {};
71890
72309
  __export(exports_uninstall, {
71891
- run: () => run7
72310
+ run: () => run8
71892
72311
  });
71893
- async function run7(_argv, io) {
72312
+ async function run8(_argv, io) {
71894
72313
  if (io.json)
71895
72314
  return emit(io, fail("invalid_input", "workit uninstall is interactive and has no --json output", {
71896
72315
  unblock: "run `workit uninstall` in a terminal without --json"
@@ -71906,7 +72325,7 @@ var init_uninstall2 = __esm(() => {
71906
72325
  // packages/workit-cli/src/verbs/grant.ts
71907
72326
  var exports_grant = {};
71908
72327
  __export(exports_grant, {
71909
- run: () => run8,
72328
+ run: () => run9,
71910
72329
  runGrant: () => runGrant
71911
72330
  });
71912
72331
  function show(argv, io) {
@@ -72027,7 +72446,7 @@ var USAGE2 = "workit grant show [<workspace>] [--all] | grant set <workspace> <k
72027
72446
  ...endpoint.endpointIssue ? [` note: ${endpoint.endpointIssue}`] : [],
72028
72447
  ` 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"})`
72029
72448
  ], usage2 = (io, message) => emit(io, fail("invalid_input", `${message}
72030
- usage: ${USAGE2}`)), run8 = (argv, io) => runGrant(argv, io);
72449
+ usage: ${USAGE2}`)), run9 = (argv, io) => runGrant(argv, io);
72031
72450
  var init_grant = __esm(() => {
72032
72451
  init_autonomy();
72033
72452
  init_release_tracks();
@@ -72056,9 +72475,9 @@ var init_grant = __esm(() => {
72056
72475
  // packages/workit-cli/src/verbs/check.ts
72057
72476
  var exports_check = {};
72058
72477
  __export(exports_check, {
72059
- run: () => run9
72478
+ run: () => run10
72060
72479
  });
72061
- import path38 from "node:path";
72480
+ import path39 from "node:path";
72062
72481
  function parse5(argv) {
72063
72482
  const split = argv.indexOf("--");
72064
72483
  const head = split >= 0 ? argv.slice(0, split) : [...argv];
@@ -72207,7 +72626,7 @@ function boundedArgv(argv) {
72207
72626
  const truncated = cut.length !== argv.length || cut.some((item, index) => item !== argv[index]);
72208
72627
  return { argv: cut, truncated };
72209
72628
  }
72210
- async function run9(argv, io) {
72629
+ async function run10(argv, io) {
72211
72630
  const options = parse5(argv);
72212
72631
  if (options instanceof Error)
72213
72632
  return emit(io, fail("invalid_input", options.message, { unblock: USAGE3 }));
@@ -72241,7 +72660,7 @@ async function run9(argv, io) {
72241
72660
  if (options.shell && command.length !== 1)
72242
72661
  return emit(io, fail("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
72243
72662
  }
72244
- const relCwd = posix(path38.relative(top, runCwd));
72663
+ const relCwd = posix(path39.relative(top, runCwd));
72245
72664
  const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
72246
72665
  const actor = actorFromEnv(io.env);
72247
72666
  const branchKey = codeKey(io.cwd, { base: options.base });
@@ -72365,7 +72784,7 @@ async function run9(argv, io) {
72365
72784
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
72366
72785
  return 0;
72367
72786
  }
72368
- 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;
72787
+ 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;
72369
72788
  var init_check = __esm(() => {
72370
72789
  init_check_config();
72371
72790
  init_checks3();
@@ -72387,9 +72806,9 @@ var init_check = __esm(() => {
72387
72806
  var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
72388
72807
 
72389
72808
  // packages/workit-core/src/git/ops.ts
72390
- import { spawnSync as spawnSync17 } from "node:child_process";
72391
- import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
72392
- import path39 from "node:path";
72809
+ import { spawnSync as spawnSync18 } from "node:child_process";
72810
+ import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
72811
+ import path40 from "node:path";
72393
72812
  function operationInProgress(cwd) {
72394
72813
  const markers = [
72395
72814
  ["rebase-merge", "rebase"],
@@ -72402,18 +72821,18 @@ function operationInProgress(cwd) {
72402
72821
  if (!run.ok)
72403
72822
  return null;
72404
72823
  const found = run.stdout.split(`
72405
- `).map((line) => path39.resolve(cwd, line.trim()));
72824
+ `).map((line) => path40.resolve(cwd, line.trim()));
72406
72825
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
72407
72826
  for (const [index, [, operation]] of markers.entries()) {
72408
72827
  const marker = found[index];
72409
- if (!marker || !existsSync18(marker))
72828
+ if (!marker || !existsSync19(marker))
72410
72829
  continue;
72411
- if (index === 1 && existsSync18(path39.join(marker, "applying")))
72830
+ if (index === 1 && existsSync19(path40.join(marker, "applying")))
72412
72831
  return { operation: "am", commitAllowed: false, branch: null };
72413
- if (index === 0 && existsSync18(path39.join(marker, "amend")) && !conflicted()) {
72832
+ if (index === 0 && existsSync19(path40.join(marker, "amend")) && !conflicted()) {
72414
72833
  let headName = "";
72415
72834
  try {
72416
- headName = readFileSync18(path39.join(marker, "head-name"), "utf8").trim();
72835
+ headName = readFileSync19(path40.join(marker, "head-name"), "utf8").trim();
72417
72836
  } catch {}
72418
72837
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
72419
72838
  return { operation, commitAllowed: branch !== null, branch };
@@ -72663,7 +73082,7 @@ function gitCommit(cwd, input) {
72663
73082
  if (paths.some((value) => value.startsWith("-") || !value.trim()))
72664
73083
  return failure3("invalid_input", "paths must not be empty or start with -");
72665
73084
  const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
72666
- const addable = paths.filter((value) => existsSync18(path39.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
73085
+ const addable = paths.filter((value) => existsSync19(path40.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
72667
73086
  if (addable.length) {
72668
73087
  const added = git5(cwd, ["add", "-A", "--", ...addable]);
72669
73088
  if (!added.ok)
@@ -72942,7 +73361,7 @@ ${run.stdout}`;
72942
73361
  } : { ok: false, lease: false, error: gitError(run) };
72943
73362
  }
72944
73363
  var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
72945
- const result = spawnSync17("git", args, {
73364
+ const result = spawnSync18("git", args, {
72946
73365
  cwd,
72947
73366
  encoding: "utf8",
72948
73367
  env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
@@ -72996,10 +73415,10 @@ var init_ops = __esm(() => {
72996
73415
 
72997
73416
  // packages/workit-core/src/forge/reruns.ts
72998
73417
  import fs19 from "node:fs";
72999
- import path40 from "node:path";
73418
+ import path41 from "node:path";
73000
73419
  function rerunLogPath(cwd) {
73001
73420
  const common = gitCommonDir(cwd);
73002
- return common ? path40.join(common, "workit", "ci-reruns.jsonl") : null;
73421
+ return common ? path41.join(common, "workit", "ci-reruns.jsonl") : null;
73003
73422
  }
73004
73423
  function rerunCounts(cwd, key) {
73005
73424
  const counts = new Map;
@@ -73034,7 +73453,7 @@ function recordReruns(cwd, rows) {
73034
73453
  if (!file)
73035
73454
  return false;
73036
73455
  try {
73037
- fs19.mkdirSync(path40.dirname(file), { recursive: true });
73456
+ fs19.mkdirSync(path41.dirname(file), { recursive: true });
73038
73457
  fs19.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
73039
73458
  `).join(""));
73040
73459
  return true;
@@ -73047,7 +73466,7 @@ function withRerunLock(cwd, fn) {
73047
73466
  if (!file)
73048
73467
  return fn();
73049
73468
  const lock = `${file.slice(0, -".jsonl".length)}.lock`;
73050
- fs19.mkdirSync(path40.dirname(lock), { recursive: true });
73469
+ fs19.mkdirSync(path41.dirname(lock), { recursive: true });
73051
73470
  const until = Date.now() + LOCK_WAIT_MS2;
73052
73471
  for (;; ) {
73053
73472
  try {
@@ -73295,6 +73714,33 @@ function buildStatusDoc(cwd, resolved, status, options) {
73295
73714
  babysit: babysitAction(blockers[0]?.next ?? "READY", blockers[0]?.reason ?? null, status.draft || blockers.some((blocker) => blocker.next === "MARK_READY"))
73296
73715
  });
73297
73716
  }
73717
+ function recordPrStatus(cwd, doc, actor) {
73718
+ const checks = doc.checks.state;
73719
+ const state = doc.state;
73720
+ if (state !== "open" && state !== "merged" && state !== "closed")
73721
+ return;
73722
+ if (state === "open" && checks !== "passing" && checks !== "failing" && checks !== "pending")
73723
+ return;
73724
+ try {
73725
+ const ledger = readLedger(cwd);
73726
+ if (!ledger.ok)
73727
+ return;
73728
+ const last = ledger.value.rows.findLast((row) => row.type === "pr.status" && row.observer === "workit_cli" && row.pr === doc.number && row.repo === doc.repo);
73729
+ if (last && last.head === doc.head.sha && last.checks === checks && (last.state ?? "open") === state)
73730
+ return;
73731
+ appendObserved(cwd, {
73732
+ type: "pr.status",
73733
+ actor,
73734
+ branch: doc.head.branch,
73735
+ head: doc.head.sha,
73736
+ pr: doc.number,
73737
+ repo: doc.repo,
73738
+ forge: doc.forge,
73739
+ state,
73740
+ checks
73741
+ });
73742
+ } catch {}
73743
+ }
73298
73744
  function prStatusReport(cwd, resolved, options = {}) {
73299
73745
  const number = selectPr(cwd, resolved, options);
73300
73746
  if (!number.ok)
@@ -73309,6 +73755,8 @@ function prStatusReport(cwd, resolved, options = {}) {
73309
73755
  });
73310
73756
  if (!doc.ok)
73311
73757
  return doc;
73758
+ if (options.actor)
73759
+ recordPrStatus(cwd, doc.data, options.actor);
73312
73760
  return success2({ doc: doc.data, raw: status.data });
73313
73761
  }
73314
73762
  function waitVerdict(doc, options) {
@@ -73434,13 +73882,14 @@ function recordAccepted(cwd, resolved, status, accepted, options) {
73434
73882
  var MAX_LOG_TAILS = 5, GITLAB_MERGEABLE, GITHUB_MERGEABLE, NO_CHECKS_GRACE_MS = 90000;
73435
73883
  var init_report = __esm(() => {
73436
73884
  init_rev();
73885
+ init_ledger();
73437
73886
  init_reruns();
73438
73887
  GITLAB_MERGEABLE = new Set(["mergeable"]);
73439
73888
  GITHUB_MERGEABLE = new Set(["clean", "unstable", "has_hooks"]);
73440
73889
  });
73441
73890
 
73442
73891
  // packages/workit-core/src/forge/pr-ops.ts
73443
- import { spawnSync as spawnSync18 } from "node:child_process";
73892
+ import { spawnSync as spawnSync19 } from "node:child_process";
73444
73893
  function fillFromCommits(cwd, resolved, base) {
73445
73894
  const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
73446
73895
  if (!base.startsWith("-"))
@@ -73448,7 +73897,7 @@ function fillFromCommits(cwd, resolved, base) {
73448
73897
  const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
73449
73898
  if (!from)
73450
73899
  return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
73451
- const log = spawnSync18("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
73900
+ const log = spawnSync19("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
73452
73901
  cwd,
73453
73902
  encoding: "utf8",
73454
73903
  timeout: GIT_TIMEOUTS.local,
@@ -73804,10 +74253,10 @@ var init_pr_ops = __esm(() => {
73804
74253
  });
73805
74254
 
73806
74255
  // packages/workit-core/src/stack.ts
73807
- import { spawnSync as spawnSync19 } from "node:child_process";
74256
+ import { spawnSync as spawnSync20 } from "node:child_process";
73808
74257
  import { createHash as createHash16, randomBytes as randomBytes3 } from "node:crypto";
73809
74258
  import fs20 from "node:fs";
73810
- import path41 from "node:path";
74259
+ import path42 from "node:path";
73811
74260
  function worktreeOf(cwd, branch) {
73812
74261
  const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
73813
74262
  if (!list.ok)
@@ -73851,7 +74300,7 @@ function gitRestack(cwd, input) {
73851
74300
  if (!scratch)
73852
74301
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
73853
74302
  fs20.mkdirSync(scratch, { recursive: true });
73854
- dir = path41.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
74303
+ dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
73855
74304
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
73856
74305
  if (!added.ok)
73857
74306
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -73892,7 +74341,7 @@ function pruneScratch(cwd) {
73892
74341
  if (!scratch || !fs20.existsSync(scratch))
73893
74342
  return;
73894
74343
  for (const name of fs20.readdirSync(scratch)) {
73895
- const dir = path41.join(scratch, name);
74344
+ const dir = path42.join(scratch, name);
73896
74345
  if (!rebaseInProgress(dir) && !trackedDirt(dir))
73897
74346
  gitRun3(cwd, ["worktree", "remove", "--force", dir]);
73898
74347
  }
@@ -73902,7 +74351,7 @@ function stacksDir(cwd) {
73902
74351
  const root = storeRoot(cwd);
73903
74352
  if (!root.ok)
73904
74353
  return stackFail(root.code, root.error, root.unblock);
73905
- return { ok: true, data: path41.join(root.value.root, "stacks") };
74354
+ return { ok: true, data: path42.join(root.value.root, "stacks") };
73906
74355
  }
73907
74356
  function parseStack(raw) {
73908
74357
  if (!isRecord7(raw) || !Array.isArray(raw.branches))
@@ -73954,7 +74403,7 @@ function readStack(cwd, name) {
73954
74403
  const dir = stacksDir(cwd);
73955
74404
  if (!dir.ok)
73956
74405
  return dir;
73957
- const file = path41.join(dir.data, fileFor(name));
74406
+ const file = path42.join(dir.data, fileFor(name));
73958
74407
  let text;
73959
74408
  try {
73960
74409
  text = fs20.readFileSync(file, "utf8");
@@ -73975,7 +74424,7 @@ function writeStack(cwd, stack, now = new Date) {
73975
74424
  const dir = stacksDir(cwd);
73976
74425
  if (!dir.ok)
73977
74426
  return dir;
73978
- const file = path41.join(dir.data, fileFor(stack.name));
74427
+ const file = path42.join(dir.data, fileFor(stack.name));
73979
74428
  const temp = `${file}.${process.pid}.tmp`;
73980
74429
  try {
73981
74430
  fs20.mkdirSync(dir.data, { recursive: true });
@@ -74003,7 +74452,7 @@ function listStacks(cwd) {
74003
74452
  for (const name of names) {
74004
74453
  let parsed = null;
74005
74454
  try {
74006
- parsed = parseStack(JSON.parse(fs20.readFileSync(path41.join(dir.data, name), "utf8")));
74455
+ parsed = parseStack(JSON.parse(fs20.readFileSync(path42.join(dir.data, name), "utf8")));
74007
74456
  } catch {
74008
74457
  parsed = null;
74009
74458
  }
@@ -74062,7 +74511,7 @@ function takeLock(lock) {
74062
74511
  host: localLockHost(),
74063
74512
  nonce: randomBytes3(8).toString("hex")
74064
74513
  };
74065
- fs20.writeFileSync(path41.join(lock, OWNER), JSON.stringify(owner));
74514
+ fs20.writeFileSync(path42.join(lock, OWNER), JSON.stringify(owner));
74066
74515
  return owner;
74067
74516
  }
74068
74517
  function reapLock(lock, observed) {
@@ -74099,7 +74548,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
74099
74548
  return dir;
74100
74549
  if (!validName(name))
74101
74550
  return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
74102
- const lock = path41.join(dir.data, `${fileFor(name)}.lock`);
74551
+ const lock = path42.join(dir.data, `${fileFor(name)}.lock`);
74103
74552
  try {
74104
74553
  fs20.mkdirSync(dir.data, { recursive: true });
74105
74554
  } catch (error) {
@@ -74129,7 +74578,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
74129
74578
  const touch = () => {
74130
74579
  try {
74131
74580
  const now = new Date;
74132
- fs20.utimesSync(path41.join(lock, OWNER), now, now);
74581
+ fs20.utimesSync(path42.join(lock, OWNER), now, now);
74133
74582
  } catch {}
74134
74583
  };
74135
74584
  const beat = setInterval(touch, HEARTBEAT_MS);
@@ -74317,7 +74766,7 @@ function commitKeys(cwd, range) {
74317
74766
  const patches = new Map;
74318
74767
  const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
74319
74768
  if (diff.ok && diff.stdout.trim()) {
74320
- const ids = spawnSync19("git", ["patch-id", "--stable"], {
74769
+ const ids = spawnSync20("git", ["patch-id", "--stable"], {
74321
74770
  cwd,
74322
74771
  input: diff.stdout,
74323
74772
  encoding: "utf8",
@@ -74941,7 +75390,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
74941
75390
  ...unblock ? { unblock } : {},
74942
75391
  ...data ? { data } : {}
74943
75392
  }), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
74944
- const run = spawnSync19("git", args, {
75393
+ const run = spawnSync20("git", args, {
74945
75394
  cwd,
74946
75395
  encoding: "utf8",
74947
75396
  timeout: timeoutMs,
@@ -74969,7 +75418,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
74969
75418
  return !status.ok || status.stdout.trim() !== "";
74970
75419
  }, scratchDir = (cwd) => {
74971
75420
  const dir = stacksDir(cwd);
74972
- return dir.ok ? path41.join(dir.data, "worktrees") : null;
75421
+ return dir.ok ? path42.join(dir.data, "worktrees") : null;
74973
75422
  }, firstLine = (run) => (`${run.stderr}
74974
75423
  ${run.stdout}`.split(`
74975
75424
  `).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) => {
@@ -74984,12 +75433,12 @@ ${run.stdout}`.split(`
74984
75433
  }, 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) => {
74985
75434
  let raw = null;
74986
75435
  try {
74987
- raw = fs20.readFileSync(path41.join(lock, OWNER), "utf8");
75436
+ raw = fs20.readFileSync(path42.join(lock, OWNER), "utf8");
74988
75437
  } catch {
74989
75438
  raw = null;
74990
75439
  }
74991
75440
  let age = null;
74992
- for (const target of [path41.join(lock, OWNER), lock])
75441
+ for (const target of [path42.join(lock, OWNER), lock])
74993
75442
  try {
74994
75443
  age = Date.now() - fs20.statSync(target).mtimeMs;
74995
75444
  break;
@@ -75022,7 +75471,7 @@ var init_stack = __esm(() => {
75022
75471
  });
75023
75472
 
75024
75473
  // packages/workit-core/src/forge/pr-edit.ts
75025
- import { spawnSync as spawnSync20 } from "node:child_process";
75474
+ import { spawnSync as spawnSync21 } from "node:child_process";
75026
75475
  function openPr(cwd, resolved, pr) {
75027
75476
  const grant = requireGrant(cwd, "pr");
75028
75477
  if (!grant.allowed)
@@ -75187,7 +75636,7 @@ function replyToThread(cwd, resolved, input) {
75187
75636
  });
75188
75637
  }
75189
75638
  var label = (resolved, number) => `${resolved.forge.kind === "github" ? "PR #" : "MR !"}${number}`, git6 = (cwd, args) => {
75190
- const run = spawnSync20("git", args, {
75639
+ const run = spawnSync21("git", args, {
75191
75640
  cwd,
75192
75641
  encoding: "utf8",
75193
75642
  timeout: GIT_TIMEOUTS.local,
@@ -75207,15 +75656,15 @@ var init_pr_edit = __esm(() => {
75207
75656
  });
75208
75657
 
75209
75658
  // packages/workit-core/src/forge/exec.ts
75210
- import { spawnSync as spawnSync21 } from "node:child_process";
75659
+ import { spawnSync as spawnSync22 } from "node:child_process";
75211
75660
  import fs21 from "node:fs";
75212
- import path42 from "node:path";
75661
+ import path43 from "node:path";
75213
75662
  function findExecutable(bin, env) {
75214
- const dirs = (env.PATH ?? env.Path ?? "").split(path42.delimiter).filter(Boolean);
75663
+ const dirs = (env.PATH ?? env.Path ?? "").split(path43.delimiter).filter(Boolean);
75215
75664
  const exts = process.platform === "win32" ? [".exe"] : [""];
75216
75665
  for (const dir of dirs)
75217
75666
  for (const ext of exts) {
75218
- const candidate = path42.join(dir, `${bin}${ext}`);
75667
+ const candidate = path43.join(dir, `${bin}${ext}`);
75219
75668
  try {
75220
75669
  const stat = fs21.statSync(candidate);
75221
75670
  if (stat.isFile())
@@ -75242,7 +75691,7 @@ function systemRunner(kind, apiHost, env = process.env) {
75242
75691
  if (!exe)
75243
75692
  return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
75244
75693
  const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
75245
- const result = spawnSync21(exe, [...args], {
75694
+ const result = spawnSync22(exe, [...args], {
75246
75695
  encoding: "utf8",
75247
75696
  env: { ...childEnv, ...token },
75248
75697
  maxBuffer: MAX_BUFFER2,
@@ -76406,7 +76855,7 @@ var init_duration = __esm(() => {
76406
76855
  });
76407
76856
 
76408
76857
  // packages/workit-cli/src/verbs/forge-common.ts
76409
- import { readFileSync as readFileSync19 } from "node:fs";
76858
+ import { readFileSync as readFileSync20 } from "node:fs";
76410
76859
  function parseFlags(argv, spec) {
76411
76860
  const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
76412
76861
  for (let index = 0;index < argv.length; index += 1) {
@@ -76550,7 +76999,7 @@ var init_forge_common = __esm(() => {
76550
76999
  forgeDeps = {
76551
77000
  runner: undefined,
76552
77001
  npm: undefined,
76553
- readStdin: () => readFileSync19(0, "utf8"),
77002
+ readStdin: () => readFileSync20(0, "utf8"),
76554
77003
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
76555
77004
  now: () => Date.now()
76556
77005
  };
@@ -76559,14 +77008,14 @@ var init_forge_common = __esm(() => {
76559
77008
  // packages/workit-cli/src/verbs/pr.ts
76560
77009
  var exports_pr = {};
76561
77010
  __export(exports_pr, {
76562
- run: () => run10
77011
+ run: () => run11
76563
77012
  });
76564
- import { readFileSync as readFileSync20 } from "node:fs";
76565
- import path43 from "node:path";
77013
+ import { readFileSync as readFileSync21 } from "node:fs";
77014
+ import path44 from "node:path";
76566
77015
  function readBodyFile(io, file) {
76567
77016
  try {
76568
77017
  return {
76569
- body: file === "-" ? forgeDeps.readStdin() : readFileSync20(path43.resolve(io.cwd, file), "utf8")
77018
+ body: file === "-" ? forgeDeps.readStdin() : readFileSync21(path44.resolve(io.cwd, file), "utf8")
76570
77019
  };
76571
77020
  } catch {
76572
77021
  return `cannot read --body-file ${file}`;
@@ -76595,7 +77044,8 @@ async function status(argv, io) {
76595
77044
  pr,
76596
77045
  branch: flags.values.branch ?? null,
76597
77046
  logLines: logLines ?? 60,
76598
- identity: connected.data.identity
77047
+ identity: connected.data.identity,
77048
+ actor: actorFromEnv(io.env)
76599
77049
  });
76600
77050
  if (!report.ok)
76601
77051
  return forgeFail(io, report);
@@ -76892,7 +77342,7 @@ async function reply(argv, io) {
76892
77342
  `${[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}` : ""}`
76893
77343
  ]);
76894
77344
  }
76895
- async function run10(argv, io) {
77345
+ async function run11(argv, io) {
76896
77346
  const [sub, ...rest] = argv;
76897
77347
  if (sub === "status")
76898
77348
  return status(rest, io);
@@ -76927,7 +77377,7 @@ var init_pr = __esm(() => {
76927
77377
  // packages/workit-cli/src/verbs/ci.ts
76928
77378
  var exports_ci = {};
76929
77379
  __export(exports_ci, {
76930
- run: () => run11
77380
+ run: () => run12
76931
77381
  });
76932
77382
  async function wait(argv, io) {
76933
77383
  const flags = parseFlags(argv, {
@@ -76966,6 +77416,7 @@ async function wait(argv, io) {
76966
77416
  const number = selectPr(io.cwd, resolved, { pr, branch: flags.values.branch ?? null });
76967
77417
  if (!number.ok)
76968
77418
  return forgeFail(io, number);
77419
+ const actor = actorFromEnv(io.env);
76969
77420
  let polls = 0;
76970
77421
  let errors = 0;
76971
77422
  let status = null;
@@ -76980,6 +77431,7 @@ async function wait(argv, io) {
76980
77431
  const doc = buildStatusDoc(io.cwd, resolved, status, { logLines: 0, behind: false });
76981
77432
  if (!doc.ok)
76982
77433
  return forgeFail(io, doc);
77434
+ recordPrStatus(io.cwd, doc.data, actor);
76983
77435
  verdict = waitVerdict(doc.data, { head, elapsedMs });
76984
77436
  if (verdict.state !== "waiting")
76985
77437
  break;
@@ -77094,7 +77546,7 @@ async function rerun(argv, io) {
77094
77546
  "next: workit ci wait"
77095
77547
  ]);
77096
77548
  }
77097
- async function run11(argv, io) {
77549
+ async function run12(argv, io) {
77098
77550
  const [sub, ...rest] = argv;
77099
77551
  if (sub === "wait")
77100
77552
  return wait(rest, io);
@@ -77106,6 +77558,7 @@ async function run11(argv, io) {
77106
77558
  var WAIT_USAGE = "workit ci wait [--pr <n> | --branch <b>] [--head <sha>] [--timeout 20m] [--interval 30s] [--json]", RERUN_USAGE = "workit ci rerun [--pr <n> | --branch <b>] [--check <name>… | --failed] --reason flake|infra [--force] [--json]", ONE_POLL_MS = 20000, MAX_POLL_ERRORS = 3, seconds = (ms) => `${Math.round(ms / 1000)}s`;
77107
77559
  var init_ci = __esm(() => {
77108
77560
  init_report();
77561
+ init_ledger();
77109
77562
  init_autonomy();
77110
77563
  init_output();
77111
77564
  init_forge_common();
@@ -77141,10 +77594,10 @@ var init_implicit_task = __esm(() => {
77141
77594
  // packages/workit-cli/src/verbs/git.ts
77142
77595
  var exports_git = {};
77143
77596
  __export(exports_git, {
77144
- run: () => run12
77597
+ run: () => run13
77145
77598
  });
77146
- import { readFileSync as readFileSync21 } from "node:fs";
77147
- import path44 from "node:path";
77599
+ import { readFileSync as readFileSync22 } from "node:fs";
77600
+ import path45 from "node:path";
77148
77601
  async function branch(argv, io) {
77149
77602
  const flags = parseFlags(argv, {
77150
77603
  base: "value",
@@ -77235,7 +77688,7 @@ function commitMessage(parsed, io) {
77235
77688
 
77236
77689
  `);
77237
77690
  try {
77238
- return file === "-" ? readFileSync21(0, "utf8") : readFileSync21(path44.resolve(io.cwd, file), "utf8");
77691
+ return file === "-" ? readFileSync22(0, "utf8") : readFileSync22(path45.resolve(io.cwd, file), "utf8");
77239
77692
  } catch (error) {
77240
77693
  return {
77241
77694
  error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
@@ -77338,7 +77791,7 @@ async function push(argv, io) {
77338
77791
  ...data.upstream ? [`upstream: ${data.remote}/${data.branch}`] : []
77339
77792
  ]);
77340
77793
  }
77341
- async function run12(argv, io) {
77794
+ async function run13(argv, io) {
77342
77795
  const [sub, ...rest] = argv;
77343
77796
  if (sub === "branch")
77344
77797
  return branch(rest, io);
@@ -77366,7 +77819,7 @@ var init_git2 = __esm(() => {
77366
77819
  });
77367
77820
 
77368
77821
  // packages/workit-core/src/forge/verify.ts
77369
- import { spawnSync as spawnSync22 } from "node:child_process";
77822
+ import { spawnSync as spawnSync23 } from "node:child_process";
77370
77823
  function finish(cwd, report, actor) {
77371
77824
  const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
77372
77825
  const out = { ...report, delivered };
@@ -77579,7 +78032,7 @@ var NPM_NAME, SEMVER2, SAFE_ARG, systemNpm = (args) => {
77579
78032
  };
77580
78033
  const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
77581
78034
  const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
77582
- const result = process.platform === "win32" ? spawnSync22(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync22("npm", [...args], options);
78035
+ const result = process.platform === "win32" ? spawnSync23(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync23("npm", [...args], options);
77583
78036
  return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
77584
78037
  }, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
77585
78038
  try {
@@ -77604,9 +78057,9 @@ var init_verify = __esm(() => {
77604
78057
  // packages/workit-cli/src/verbs/verify-delivery.ts
77605
78058
  var exports_verify_delivery = {};
77606
78059
  __export(exports_verify_delivery, {
77607
- run: () => run13
78060
+ run: () => run14
77608
78061
  });
77609
- async function run13(argv, io) {
78062
+ async function run14(argv, io) {
77610
78063
  const flags = parseFlags(argv, {
77611
78064
  expect: "value",
77612
78065
  branch: "value",
@@ -77688,7 +78141,7 @@ var init_verify_delivery = __esm(() => {
77688
78141
  // packages/workit-cli/src/verbs/stack.ts
77689
78142
  var exports_stack = {};
77690
78143
  __export(exports_stack, {
77691
- run: () => run14
78144
+ run: () => run15
77692
78145
  });
77693
78146
  async function plan(argv, io) {
77694
78147
  const flags = parseFlags(argv, { name: "value", trunk: "value", track: "value" });
@@ -77870,7 +78323,7 @@ async function land(argv, io) {
77870
78323
  unblock: ${data.stoppedAt.unblock}` : data.complete ? "stack complete" : ""
77871
78324
  ].filter(Boolean));
77872
78325
  }
77873
- async function run14(argv, io) {
78326
+ async function run15(argv, io) {
77874
78327
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
77875
78328
  if (sub === "plan")
77876
78329
  return plan(rest, io);
@@ -77900,10 +78353,10 @@ var init_stack2 = __esm(() => {
77900
78353
  });
77901
78354
 
77902
78355
  // packages/workit-core/src/fanout.ts
77903
- import { spawnSync as spawnSync23 } from "node:child_process";
78356
+ import { spawnSync as spawnSync24 } from "node:child_process";
77904
78357
  import { createHash as createHash17 } from "node:crypto";
77905
78358
  import fs23 from "node:fs";
77906
- import path45 from "node:path";
78359
+ import path46 from "node:path";
77907
78360
  function expandBraces(pattern, limit = 64) {
77908
78361
  for (let index = 0;index < pattern.length; index += 1) {
77909
78362
  if (pattern[index] === "\\") {
@@ -78137,7 +78590,7 @@ function parsePlan(raw, defaults, name) {
78137
78590
  id,
78138
78591
  branch,
78139
78592
  base: optional("base") ?? "",
78140
- worktree: path45.resolve(defaults.root, optional("worktree") ?? path45.join(defaults.worktreeRoot, id)),
78593
+ worktree: path46.resolve(defaults.root, optional("worktree") ?? path46.join(defaults.worktreeRoot, id)),
78141
78594
  tier,
78142
78595
  dependsOn: [...new Set(dependsOn ?? [])],
78143
78596
  scope,
@@ -78366,11 +78819,11 @@ function defaultTrunk(cwd) {
78366
78819
  }
78367
78820
  function mainCheckout(cwd) {
78368
78821
  const common = gitCommonDir(cwd);
78369
- return common && path45.basename(common) === ".git" ? path45.dirname(common) : path45.resolve(cwd);
78822
+ return common && path46.basename(common) === ".git" ? path46.dirname(common) : path46.resolve(cwd);
78370
78823
  }
78371
78824
  function worktreeRoot(cwd) {
78372
78825
  const main = mainCheckout(cwd);
78373
- return path45.join(path45.dirname(main), `${path45.basename(main)}-wt`);
78826
+ return path46.join(path46.dirname(main), `${path46.basename(main)}-wt`);
78374
78827
  }
78375
78828
  function sliceHash(slice) {
78376
78829
  const {
@@ -78388,7 +78841,7 @@ function fanoutsDir(cwd) {
78388
78841
  const root = storeRoot(cwd);
78389
78842
  if (!root.ok)
78390
78843
  return fanoutFail(root.code, root.error, root.unblock);
78391
- return { ok: true, data: path45.join(root.value.root, "fanouts") };
78844
+ return { ok: true, data: path46.join(root.value.root, "fanouts") };
78392
78845
  }
78393
78846
  function parseStored(raw) {
78394
78847
  if (!isRecord8(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -78454,7 +78907,7 @@ function readFanout(cwd, name) {
78454
78907
  const dir = fanoutsDir(cwd);
78455
78908
  if (!dir.ok)
78456
78909
  return dir;
78457
- const file = path45.join(dir.data, stackFileName(name));
78910
+ const file = path46.join(dir.data, stackFileName(name));
78458
78911
  let parsed = null;
78459
78912
  try {
78460
78913
  parsed = parseStored(JSON.parse(fs23.readFileSync(file, "utf8")));
@@ -78479,7 +78932,7 @@ function listFanouts(cwd) {
78479
78932
  for (const entry of names) {
78480
78933
  let parsed = null;
78481
78934
  try {
78482
- parsed = parseStored(JSON.parse(fs23.readFileSync(path45.join(dir.data, entry), "utf8")));
78935
+ parsed = parseStored(JSON.parse(fs23.readFileSync(path46.join(dir.data, entry), "utf8")));
78483
78936
  } catch {
78484
78937
  parsed = null;
78485
78938
  }
@@ -78509,7 +78962,7 @@ function writeFanout(cwd, plan) {
78509
78962
  const dir = fanoutsDir(cwd);
78510
78963
  if (!dir.ok)
78511
78964
  return dir;
78512
- const file = path45.join(dir.data, stackFileName(plan.name));
78965
+ const file = path46.join(dir.data, stackFileName(plan.name));
78513
78966
  const temp = `${file}.${process.pid}.tmp`;
78514
78967
  try {
78515
78968
  fs23.mkdirSync(dir.data, { recursive: true });
@@ -78673,7 +79126,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
78673
79126
  text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
78674
79127
  };
78675
79128
  }, gitRun4 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
78676
- const run = spawnSync23("git", args, {
79129
+ const run = spawnSync24("git", args, {
78677
79130
  cwd,
78678
79131
  encoding: "utf8",
78679
79132
  timeout: timeoutMs,
@@ -78713,7 +79166,7 @@ var init_fanout = __esm(() => {
78713
79166
  // packages/workit-core/src/fanout-worktree.ts
78714
79167
  import { randomBytes as randomBytes4 } from "node:crypto";
78715
79168
  import fs24 from "node:fs";
78716
- import path46 from "node:path";
79169
+ import path47 from "node:path";
78717
79170
  function rowSample(lines) {
78718
79171
  const out = [];
78719
79172
  let bytes = 0;
@@ -78773,7 +79226,7 @@ function adminDir(worktree) {
78773
79226
  const file = dir.stdout.trim();
78774
79227
  let token = null;
78775
79228
  try {
78776
- token = fs24.readFileSync(path46.join(file, TOKEN_FILE), "utf8").trim() || null;
79229
+ token = fs24.readFileSync(path47.join(file, TOKEN_FILE), "utf8").trim() || null;
78777
79230
  } catch {
78778
79231
  token = null;
78779
79232
  }
@@ -78781,7 +79234,7 @@ function adminDir(worktree) {
78781
79234
  const stat = worktreeDeps.stat(file);
78782
79235
  return {
78783
79236
  path: file,
78784
- id: path46.basename(file),
79237
+ id: path47.basename(file),
78785
79238
  ino: stat.ino.toString(),
78786
79239
  token,
78787
79240
  birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
@@ -78797,7 +79250,7 @@ function markAdminDir(worktree) {
78797
79250
  return null;
78798
79251
  const token = randomBytes4(16).toString("hex");
78799
79252
  try {
78800
- fs24.writeFileSync(path46.join(admin.path, TOKEN_FILE), `${token}
79253
+ fs24.writeFileSync(path47.join(admin.path, TOKEN_FILE), `${token}
78801
79254
  `);
78802
79255
  } catch {
78803
79256
  return null;
@@ -78812,7 +79265,7 @@ function excludeScratch(cwd) {
78812
79265
  const common = gitCommonDir(cwd);
78813
79266
  if (!common)
78814
79267
  return;
78815
- const file = path46.join(common, "info", "exclude");
79268
+ const file = path47.join(common, "info", "exclude");
78816
79269
  let text = "";
78817
79270
  try {
78818
79271
  text = fs24.readFileSync(file, "utf8");
@@ -78821,7 +79274,7 @@ function excludeScratch(cwd) {
78821
79274
  }
78822
79275
  if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
78823
79276
  return;
78824
- fs24.mkdirSync(path46.dirname(file), { recursive: true });
79277
+ fs24.mkdirSync(path47.dirname(file), { recursive: true });
78825
79278
  fs24.appendFileSync(file, `${text && !text.endsWith(`
78826
79279
  `) ? `
78827
79280
  ` : ""}# workit fanout: per-worker scratch dirs
@@ -78829,7 +79282,7 @@ ${EXCLUDE_LINE}
78829
79282
  `);
78830
79283
  }
78831
79284
  function ensureScratch(worktree) {
78832
- const scratch = path46.join(worktree, SCRATCH_DIR);
79285
+ const scratch = path47.join(worktree, SCRATCH_DIR);
78833
79286
  fs24.mkdirSync(scratch, { recursive: true });
78834
79287
  return scratch;
78835
79288
  }
@@ -78965,7 +79418,7 @@ function releaseSliceWorktree(cwd, plan, input) {
78965
79418
  const entry = worktrees[index];
78966
79419
  if (index === 0)
78967
79420
  return fanoutFail("blocked", `${target} is the main checkout; it is never released`);
78968
- if (inside2(cwd, entry.path))
79421
+ if (inside3(cwd, entry.path))
78969
79422
  return fanoutFail("blocked", `this command runs inside ${entry.path}`, `run it from the main checkout: workit --cwd <main checkout> fanout worktree release ${slice.id}`);
78970
79423
  if (entry.branch !== null && entry.branch !== `refs/heads/${slice.branch}`)
78971
79424
  return fanoutFail("blocked", `${entry.path} is on ${entry.branch.replace(/^refs\/heads\//u, "")}, not slice ${slice.id}'s ${slice.branch}; nothing was removed`);
@@ -79047,7 +79500,7 @@ function unpushedCount(cwd, branch) {
79047
79500
  return count.ok ? Number(count.stdout.trim()) : null;
79048
79501
  }
79049
79502
  function removeScratch(worktree) {
79050
- const scratch = path46.join(worktree, SCRATCH_DIR);
79503
+ const scratch = path47.join(worktree, SCRATCH_DIR);
79051
79504
  let stat;
79052
79505
  try {
79053
79506
  stat = fs24.lstatSync(scratch);
@@ -79063,16 +79516,16 @@ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000,
79063
79516
  try {
79064
79517
  return fs24.realpathSync.native(target);
79065
79518
  } catch {
79066
- return path46.resolve(target);
79519
+ return path47.resolve(target);
79067
79520
  }
79068
79521
  }, samePath = (a, b) => {
79069
79522
  const left = canonical2(a);
79070
79523
  const right = canonical2(b);
79071
79524
  return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
79072
- }, inside2 = (child, parent) => {
79073
- const relative = path46.relative(canonical2(parent), canonical2(child));
79074
- return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative);
79075
- }, 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(`
79525
+ }, inside3 = (child, parent) => {
79526
+ const relative = path47.relative(canonical2(parent), canonical2(child));
79527
+ return relative === "" || !relative.startsWith("..") && !path47.isAbsolute(relative);
79528
+ }, 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(`
79076
79529
  `)[0] || "unknown error"}`);
79077
79530
  var init_fanout_worktree = __esm(() => {
79078
79531
  init_ops();
@@ -79193,7 +79646,7 @@ var init_fanout_brief = __esm(() => {
79193
79646
  });
79194
79647
 
79195
79648
  // packages/workit-core/src/fanout-landed.ts
79196
- import { spawnSync as spawnSync24 } from "node:child_process";
79649
+ import { spawnSync as spawnSync25 } from "node:child_process";
79197
79650
  function forgeBreaker(resolved) {
79198
79651
  if (!resolved)
79199
79652
  return { forge: null, down: () => null };
@@ -79284,7 +79737,7 @@ function trunkPatchIds(ctx, fork) {
79284
79737
  "--"
79285
79738
  ]);
79286
79739
  if (log.ok && log.stdout.trim()) {
79287
- const run = spawnSync24("git", ["patch-id", "--stable"], {
79740
+ const run = spawnSync25("git", ["patch-id", "--stable"], {
79288
79741
  cwd: ctx.cwd,
79289
79742
  input: log.stdout,
79290
79743
  encoding: "utf8",
@@ -79845,10 +80298,10 @@ var init_fanout_status = __esm(() => {
79845
80298
  // packages/workit-cli/src/verbs/fanout.ts
79846
80299
  var exports_fanout = {};
79847
80300
  __export(exports_fanout, {
79848
- run: () => run15
80301
+ run: () => run16
79849
80302
  });
79850
80303
  import fs25 from "node:fs";
79851
- import path47 from "node:path";
80304
+ import path48 from "node:path";
79852
80305
  function forgeFor(io, offline) {
79853
80306
  if (offline)
79854
80307
  return { forge: null, error: "--offline" };
@@ -79909,7 +80362,7 @@ function plan2(argv, io) {
79909
80362
  return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
79910
80363
  if (flags.positionals.length !== 1)
79911
80364
  return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
79912
- const file = path47.resolve(io.cwd, flags.positionals[0]);
80365
+ const file = path48.resolve(io.cwd, flags.positionals[0]);
79913
80366
  let raw;
79914
80367
  try {
79915
80368
  raw = JSON.parse(fs25.readFileSync(file, "utf8"));
@@ -80065,7 +80518,7 @@ function worktree(argv, io) {
80065
80518
  ...data.notes.map((note) => `note: ${note}`)
80066
80519
  ]);
80067
80520
  }
80068
- async function run15(argv, io) {
80521
+ async function run16(argv, io) {
80069
80522
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
80070
80523
  if (sub === "plan")
80071
80524
  return plan2(rest, io);
@@ -80131,7 +80584,7 @@ var init_fanout2 = __esm(() => {
80131
80584
  // packages/workit-cli/src/verbs/ledger.ts
80132
80585
  var exports_ledger = {};
80133
80586
  __export(exports_ledger, {
80134
- run: () => run16
80587
+ run: () => run17
80135
80588
  });
80136
80589
  import { randomBytes as randomBytes5 } from "node:crypto";
80137
80590
  import { parseArgs } from "node:util";
@@ -80155,7 +80608,7 @@ function targetBranch(io, rows, branch, pr) {
80155
80608
  }
80156
80609
  return { ok: true, value: branch ?? null };
80157
80610
  }
80158
- async function run16(argv, io) {
80611
+ async function run17(argv, io) {
80159
80612
  let parsed;
80160
80613
  try {
80161
80614
  parsed = parseArgs({ args: argv, options: OPTIONS, allowPositionals: true, strict: true });
@@ -95392,7 +95845,7 @@ var init_ast = __esm(() => {
95392
95845
 
95393
95846
  // packages/workit-cli/src/test-audit/rules.ts
95394
95847
  import { builtinModules } from "node:module";
95395
- import path48 from "node:path";
95848
+ import path49 from "node:path";
95396
95849
  function testCallee(call) {
95397
95850
  let callee = call.callee;
95398
95851
  if (callee.type === "CallExpression")
@@ -95885,12 +96338,12 @@ function checkConstantsOnly(ctx, emit) {
95885
96338
  }
95886
96339
  function checkOverMocking(ctx, emit) {
95887
96340
  const { program } = ctx.parsed;
95888
- const unit = path48.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
96341
+ const unit = path49.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
95889
96342
  const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
95890
96343
  const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
95891
96344
  for (const call of mocked) {
95892
96345
  const spec = call.arguments[0].value;
95893
- const base = path48.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
96346
+ const base = path49.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
95894
96347
  if (base === unit)
95895
96348
  emit({
95896
96349
  rule: "over-mocking",
@@ -96278,11 +96731,11 @@ var init_rules = __esm(() => {
96278
96731
  });
96279
96732
 
96280
96733
  // packages/workit-cli/src/test-audit/audit.ts
96281
- import { spawnSync as spawnSync25 } from "node:child_process";
96282
- import { existsSync as existsSync19, readdirSync as readdirSync9, readFileSync as readFileSync22, statSync as statSync7 } from "node:fs";
96283
- import path49 from "node:path";
96734
+ import { spawnSync as spawnSync26 } from "node:child_process";
96735
+ import { existsSync as existsSync20, readdirSync as readdirSync9, readFileSync as readFileSync23, statSync as statSync8 } from "node:fs";
96736
+ import path50 from "node:path";
96284
96737
  function git7(cwd, args) {
96285
- const run = spawnSync25("git", ["-c", "core.quotepath=off", ...args], {
96738
+ const run = spawnSync26("git", ["-c", "core.quotepath=off", ...args], {
96286
96739
  cwd,
96287
96740
  encoding: "utf8",
96288
96741
  maxBuffer: 64 * 1024 * 1024
@@ -96293,7 +96746,7 @@ function walkTests(dir, out) {
96293
96746
  for (const entry of readdirSync9(dir, { withFileTypes: true })) {
96294
96747
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
96295
96748
  continue;
96296
- const full = path49.join(dir, entry.name);
96749
+ const full = path50.join(dir, entry.name);
96297
96750
  if (entry.isDirectory()) {
96298
96751
  if (!SKIP_DIRS.has(entry.name))
96299
96752
  walkTests(full, out);
@@ -96305,10 +96758,10 @@ function expandPaths(cwd, inputs) {
96305
96758
  const files = [];
96306
96759
  const missing = [];
96307
96760
  for (const input of inputs.length > 0 ? inputs : ["."]) {
96308
- const full = path49.resolve(cwd, input);
96309
- if (!existsSync19(full))
96761
+ const full = path50.resolve(cwd, input);
96762
+ if (!existsSync20(full))
96310
96763
  missing.push(input);
96311
- else if (statSync7(full).isDirectory())
96764
+ else if (statSync8(full).isDirectory())
96312
96765
  walkTests(full, files);
96313
96766
  else
96314
96767
  files.push(full);
@@ -96342,11 +96795,11 @@ function changedLines(cwd, base) {
96342
96795
  for (let offset = 0;offset < count; offset += 1)
96343
96796
  lines.add(start + offset);
96344
96797
  }
96345
- changed.set(path49.join(root, file), lines);
96798
+ changed.set(path50.join(root, file), lines);
96346
96799
  }
96347
96800
  const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
96348
96801
  for (const file of untracked.stdout.split("\x00").filter(Boolean))
96349
- changed.set(path49.join(root, file), "all");
96802
+ changed.set(path50.join(root, file), "all");
96350
96803
  return changed;
96351
96804
  }
96352
96805
  function auditFiles(files, display) {
@@ -96359,7 +96812,7 @@ function auditFiles(files, display) {
96359
96812
  const shown = display(file);
96360
96813
  let parsed;
96361
96814
  try {
96362
- parsed = parseSource(readFileSync22(file, "utf8"), file);
96815
+ parsed = parseSource(readFileSync23(file, "utf8"), file);
96363
96816
  } catch (error) {
96364
96817
  parseErrors.push({
96365
96818
  file: shown,
@@ -96408,7 +96861,7 @@ function auditFiles(files, display) {
96408
96861
  durationMs: Math.round(performance.now() - started)
96409
96862
  };
96410
96863
  }
96411
- var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path49.sep).join("/"), SEVERITY_ORDER, ruleIds;
96864
+ var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path50.sep).join("/"), SEVERITY_ORDER, ruleIds;
96412
96865
  var init_audit = __esm(() => {
96413
96866
  init_ast();
96414
96867
  init_rules();
@@ -96422,23 +96875,23 @@ var init_audit = __esm(() => {
96422
96875
  // packages/workit-cli/src/test-audit/mutate.ts
96423
96876
  import { spawn as spawn2 } from "node:child_process";
96424
96877
  import {
96425
- existsSync as existsSync20,
96426
- lstatSync as lstatSync7,
96427
- mkdirSync as mkdirSync9,
96878
+ existsSync as existsSync21,
96879
+ lstatSync as lstatSync8,
96880
+ mkdirSync as mkdirSync10,
96428
96881
  mkdtempSync as mkdtempSync4,
96429
96882
  readdirSync as readdirSync10,
96430
- readFileSync as readFileSync23,
96883
+ readFileSync as readFileSync24,
96431
96884
  readlinkSync as readlinkSync3,
96432
96885
  realpathSync as realpathSync9,
96433
- renameSync as renameSync5,
96434
- rmSync as rmSync7,
96435
- statSync as statSync8,
96886
+ renameSync as renameSync6,
96887
+ rmSync as rmSync8,
96888
+ statSync as statSync9,
96436
96889
  symlinkSync,
96437
- writeFileSync as writeFileSync10,
96890
+ writeFileSync as writeFileSync11,
96438
96891
  copyFileSync as copyFileSync6
96439
96892
  } from "node:fs";
96440
96893
  import os14 from "node:os";
96441
- import path50 from "node:path";
96894
+ import path51 from "node:path";
96442
96895
  function mutantsFor(source, file, lines) {
96443
96896
  const parsed = parseSource(source, file);
96444
96897
  const out = [];
@@ -96511,7 +96964,7 @@ function relatedTests(source, testFiles) {
96511
96964
  for (const match of text.matchAll(SPECIFIER)) {
96512
96965
  const spec = match[1];
96513
96966
  if (spec.startsWith(".")) {
96514
- if (stripExt(path50.posix.join(path50.posix.dirname(file), spec)) === target)
96967
+ if (stripExt(path51.posix.join(path51.posix.dirname(file), spec)) === target)
96515
96968
  return true;
96516
96969
  } else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
96517
96970
  return true;
@@ -96531,12 +96984,12 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
96531
96984
  for (const name of names) {
96532
96985
  if (!name.startsWith(TEMP_PREFIX))
96533
96986
  continue;
96534
- const dir = path50.join(tmp, name);
96987
+ const dir = path51.join(tmp, name);
96535
96988
  try {
96536
- const stat = lstatSync7(dir);
96989
+ const stat = lstatSync8(dir);
96537
96990
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
96538
96991
  continue;
96539
- const pid = Number(readFileSync23(path50.join(dir, PID_FILE), "utf8"));
96992
+ const pid = Number(readFileSync24(path51.join(dir, PID_FILE), "utf8"));
96540
96993
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
96541
96994
  continue;
96542
96995
  } catch (error) {
@@ -96544,23 +96997,23 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
96544
96997
  continue;
96545
96998
  }
96546
96999
  try {
96547
- rmSync7(dir, { recursive: true, force: true });
97000
+ rmSync8(dir, { recursive: true, force: true });
96548
97001
  removed += 1;
96549
97002
  } catch {}
96550
97003
  }
96551
97004
  return removed;
96552
97005
  }
96553
97006
  function linkNodeModules(origin, target, repo, copy) {
96554
- mkdirSync9(target, { recursive: true });
97007
+ mkdirSync10(target, { recursive: true });
96555
97008
  for (const entry of readdirSync10(origin, { withFileTypes: true })) {
96556
- const from = path50.join(origin, entry.name);
96557
- const to = path50.join(target, entry.name);
97009
+ const from = path51.join(origin, entry.name);
97010
+ const to = path51.join(target, entry.name);
96558
97011
  if (entry.name.startsWith("@") && entry.isDirectory()) {
96559
97012
  linkNodeModules(from, to, repo, copy);
96560
97013
  continue;
96561
97014
  }
96562
97015
  if (entry.name.startsWith(".") && entry.name !== ".bin" && entry.isDirectory()) {
96563
- mkdirSync9(to, { recursive: true });
97016
+ mkdirSync10(to, { recursive: true });
96564
97017
  continue;
96565
97018
  }
96566
97019
  if (entry.isSymbolicLink()) {
@@ -96570,9 +97023,9 @@ function linkNodeModules(origin, target, repo, copy) {
96570
97023
  } catch {
96571
97024
  continue;
96572
97025
  }
96573
- const relative = path50.relative(repo, real);
96574
- if (!relative.startsWith("..") && !path50.isAbsolute(relative) && !relative.split(path50.sep).includes("node_modules")) {
96575
- symlinkSync(path50.join(copy, relative), to, "junction");
97026
+ const relative = path51.relative(repo, real);
97027
+ if (!relative.startsWith("..") && !path51.isAbsolute(relative) && !relative.split(path51.sep).includes("node_modules")) {
97028
+ symlinkSync(path51.join(copy, relative), to, "junction");
96576
97029
  continue;
96577
97030
  }
96578
97031
  }
@@ -96587,33 +97040,33 @@ function copySymlink(repo, tree, from, to) {
96587
97040
  real = null;
96588
97041
  }
96589
97042
  const realRepo = realpathSync9(repo);
96590
- if (real && inside3(realRepo, real)) {
96591
- symlinkSync(path50.relative(path50.dirname(to), path50.join(tree, path50.relative(realRepo, real))), to);
97043
+ if (real && inside4(realRepo, real)) {
97044
+ symlinkSync(path51.relative(path51.dirname(to), path51.join(tree, path51.relative(realRepo, real))), to);
96592
97045
  return;
96593
97046
  }
96594
- if (real && statSync8(real).isFile())
97047
+ if (real && statSync9(real).isFile())
96595
97048
  copyFileSync6(real, to);
96596
97049
  else
96597
97050
  symlinkSync(readlinkSync3(from), to);
96598
97051
  }
96599
97052
  function writeInside(copy, file, content) {
96600
- const target = path50.join(copy, file);
97053
+ const target = path51.join(copy, file);
96601
97054
  try {
96602
- const stat = lstatSync7(target);
96603
- if (!stat.isFile() || !inside3(realpathSync9(copy), realpathSync9(path50.dirname(target))))
97055
+ const stat = lstatSync8(target);
97056
+ if (!stat.isFile() || !inside4(realpathSync9(copy), realpathSync9(path51.dirname(target))))
96604
97057
  return false;
96605
97058
  } catch {
96606
97059
  return false;
96607
97060
  }
96608
97061
  const temp = `${target}.workit-mutant-${process.pid}`;
96609
- writeFileSync10(temp, content);
96610
- renameSync5(temp, target);
97062
+ writeFileSync11(temp, content);
97063
+ renameSync6(temp, target);
96611
97064
  return true;
96612
97065
  }
96613
97066
  function copyWorkingTree(repo) {
96614
- const dir = mkdtempSync4(path50.join(os14.tmpdir(), TEMP_PREFIX));
96615
- writeFileSync10(path50.join(dir, PID_FILE), String(process.pid));
96616
- const tree = path50.join(dir, "tree");
97067
+ const dir = mkdtempSync4(path51.join(os14.tmpdir(), TEMP_PREFIX));
97068
+ writeFileSync11(path51.join(dir, PID_FILE), String(process.pid));
97069
+ const tree = path51.join(dir, "tree");
96617
97070
  const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
96618
97071
  if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
96619
97072
  const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
@@ -96624,35 +97077,35 @@ function copyWorkingTree(repo) {
96624
97077
  git7(tree, ["update-ref", "--no-deref", "HEAD", head]);
96625
97078
  git7(tree, ["reset", "-q"]);
96626
97079
  } else
96627
- mkdirSync9(tree, { recursive: true });
97080
+ mkdirSync10(tree, { recursive: true });
96628
97081
  const listed = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00");
96629
97082
  const dirs = new Set([""]);
96630
97083
  for (const relative of listed) {
96631
97084
  if (!relative)
96632
97085
  continue;
96633
- const from = path50.join(repo, relative);
97086
+ const from = path51.join(repo, relative);
96634
97087
  let stat;
96635
97088
  try {
96636
- stat = lstatSync7(from);
97089
+ stat = lstatSync8(from);
96637
97090
  } catch {
96638
97091
  continue;
96639
97092
  }
96640
- const to = path50.join(tree, relative);
96641
- mkdirSync9(path50.dirname(to), { recursive: true });
97093
+ const to = path51.join(tree, relative);
97094
+ mkdirSync10(path51.dirname(to), { recursive: true });
96642
97095
  if (stat.isSymbolicLink())
96643
97096
  copySymlink(repo, tree, from, to);
96644
97097
  else if (stat.isFile())
96645
97098
  copyFileSync6(from, to);
96646
- let parent = path50.dirname(relative);
97099
+ let parent = path51.dirname(relative);
96647
97100
  while (parent !== "." && !dirs.has(parent)) {
96648
97101
  dirs.add(parent);
96649
- parent = path50.dirname(parent);
97102
+ parent = path51.dirname(parent);
96650
97103
  }
96651
97104
  }
96652
97105
  for (const parent of dirs) {
96653
- const modules = path50.join(repo, parent, "node_modules");
96654
- if (existsSync20(modules) && !existsSync20(path50.join(tree, parent, "node_modules")))
96655
- linkNodeModules(modules, path50.join(tree, parent, "node_modules"), repo, tree);
97106
+ const modules = path51.join(repo, parent, "node_modules");
97107
+ if (existsSync21(modules) && !existsSync21(path51.join(tree, parent, "node_modules")))
97108
+ linkNodeModules(modules, path51.join(tree, parent, "node_modules"), repo, tree);
96656
97109
  }
96657
97110
  return { dir, tree };
96658
97111
  }
@@ -96691,20 +97144,20 @@ async function runMutation(options) {
96691
97144
  const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
96692
97145
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
96693
97146
  try {
96694
- return { file, text: readFileSync23(path50.join(repo, file), "utf8") };
97147
+ return { file, text: readFileSync24(path51.join(repo, file), "utf8") };
96695
97148
  } catch {
96696
97149
  return { file, text: "" };
96697
97150
  }
96698
97151
  });
96699
97152
  const candidates = [];
96700
97153
  for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
96701
- const relative = toPosix(path50.relative(repo, absolute));
97154
+ const relative = toPosix(path51.relative(repo, absolute));
96702
97155
  if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
96703
97156
  continue;
96704
- if (NOT_SOURCE.test(relative) || !existsSync20(absolute))
97157
+ if (NOT_SOURCE.test(relative) || !existsSync21(absolute))
96705
97158
  continue;
96706
97159
  try {
96707
- candidates.push(...mutantsFor(readFileSync23(absolute, "utf8"), relative, lines));
97160
+ candidates.push(...mutantsFor(readFileSync24(absolute, "utf8"), relative, lines));
96708
97161
  } catch {}
96709
97162
  }
96710
97163
  const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
@@ -96712,9 +97165,9 @@ async function runMutation(options) {
96712
97165
  for (const mutant of mutants)
96713
97166
  if (!related.has(mutant.file))
96714
97167
  related.set(mutant.file, relatedTests(mutant.file, testFiles));
96715
- const cwdRelative = path50.relative(repo, options.cwd);
97168
+ const cwdRelative = path51.relative(repo, options.cwd);
96716
97169
  const asArg = (file) => {
96717
- const relative = toPosix(path50.relative(cwdRelative, file));
97170
+ const relative = toPosix(path51.relative(cwdRelative, file));
96718
97171
  return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
96719
97172
  };
96720
97173
  const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
@@ -96749,7 +97202,7 @@ async function runMutation(options) {
96749
97202
  return finish2(options, 0, results, counts, started);
96750
97203
  removeStaleCopies();
96751
97204
  const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
96752
- const copyCwd = path50.join(copy, cwdRelative);
97205
+ const copyCwd = path51.join(copy, cwdRelative);
96753
97206
  let current = null;
96754
97207
  const restore = () => {
96755
97208
  if (current)
@@ -96760,7 +97213,7 @@ async function runMutation(options) {
96760
97213
  for (const child of active)
96761
97214
  killTree(child);
96762
97215
  restore();
96763
- rmSync7(copyDir, { recursive: true, force: true });
97216
+ rmSync8(copyDir, { recursive: true, force: true });
96764
97217
  };
96765
97218
  const onSignal = (signal) => {
96766
97219
  cleanup();
@@ -96784,7 +97237,7 @@ async function runMutation(options) {
96784
97237
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
96785
97238
  continue;
96786
97239
  }
96787
- const original = readFileSync23(path50.join(copy, mutant.file), "utf8");
97240
+ const original = readFileSync24(path51.join(copy, mutant.file), "utf8");
96788
97241
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
96789
97242
  current = { file: mutant.file, original };
96790
97243
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -96840,9 +97293,9 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
96840
97293
  } catch (error) {
96841
97294
  return error.code === "EPERM";
96842
97295
  }
96843
- }, inside3 = (root, file) => {
96844
- const relative = path50.relative(root, file);
96845
- return relative === "" || !relative.startsWith("..") && !path50.isAbsolute(relative);
97296
+ }, inside4 = (root, file) => {
97297
+ const relative = path51.relative(root, file);
97298
+ return relative === "" || !relative.startsWith("..") && !path51.isAbsolute(relative);
96846
97299
  }, active, killTree = (child) => {
96847
97300
  try {
96848
97301
  if (child.pid && process.platform !== "win32")
@@ -96888,10 +97341,10 @@ var init_mutate = __esm(() => {
96888
97341
  // packages/workit-cli/src/verbs/test-audit.ts
96889
97342
  var exports_test_audit = {};
96890
97343
  __export(exports_test_audit, {
96891
- run: () => run17
97344
+ run: () => run18
96892
97345
  });
96893
- import { existsSync as existsSync21 } from "node:fs";
96894
- import path51 from "node:path";
97346
+ import { existsSync as existsSync22 } from "node:fs";
97347
+ import path52 from "node:path";
96895
97348
  function parse7(argv) {
96896
97349
  const options = {
96897
97350
  paths: [],
@@ -96989,14 +97442,14 @@ function human(data) {
96989
97442
  }
96990
97443
  return lines;
96991
97444
  }
96992
- async function run17(argv, io) {
97445
+ async function run18(argv, io) {
96993
97446
  const options = parse7(argv);
96994
97447
  if (typeof options === "string")
96995
97448
  return emit(io, fail("invalid_input", `${options}
96996
97449
  usage: ${USAGE10}`));
96997
97450
  const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
96998
- const repo = top.ok ? path51.resolve(top.stdout.trim()) : null;
96999
- const display = (file) => toPosix(path51.relative(io.cwd, file) || file);
97451
+ const repo = top.ok ? path52.resolve(top.stdout.trim()) : null;
97452
+ const display = (file) => toPosix(path52.relative(io.cwd, file) || file);
97000
97453
  let base = null;
97001
97454
  let changed = null;
97002
97455
  if (options.diff || options.mutate) {
@@ -97013,7 +97466,7 @@ usage: ${USAGE10}`));
97013
97466
  }
97014
97467
  let files;
97015
97468
  if (options.diff && changed)
97016
- files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync21(file)).toSorted();
97469
+ files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync22(file)).toSorted();
97017
97470
  else {
97018
97471
  const expanded = expandPaths(io.cwd, options.paths);
97019
97472
  if (expanded.missing.length > 0)
@@ -97100,9 +97553,9 @@ var init_test_audit = __esm(() => {
97100
97553
  // packages/workit-cli/src/verbs/knowledge.ts
97101
97554
  var exports_knowledge = {};
97102
97555
  __export(exports_knowledge, {
97103
- run: () => run18
97556
+ run: () => run19
97104
97557
  });
97105
- async function run18(argv, io) {
97558
+ async function run19(argv, io) {
97106
97559
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
97107
97560
  if (sub !== "lint" || rest.length > 0)
97108
97561
  return emit(io, fail("invalid_input", `usage: ${USAGE11}`));
@@ -97236,11 +97689,11 @@ var init_youtrack_writes = __esm(() => {
97236
97689
  // packages/workit-cli/src/verbs/youtrack.ts
97237
97690
  var exports_youtrack = {};
97238
97691
  __export(exports_youtrack, {
97239
- run: () => run19
97692
+ run: () => run20
97240
97693
  });
97241
- import { readFileSync as readFileSync24 } from "node:fs";
97242
- import path52 from "node:path";
97243
- async function run19(argv, io) {
97694
+ import { readFileSync as readFileSync25 } from "node:fs";
97695
+ import path53 from "node:path";
97696
+ async function run20(argv, io) {
97244
97697
  const [sub, ...rest] = argv;
97245
97698
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
97246
97699
  return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE12);
@@ -97285,7 +97738,7 @@ async function run19(argv, io) {
97285
97738
  let markdown = flags.values.markdown ?? "";
97286
97739
  if (flags.values.file)
97287
97740
  try {
97288
- markdown = readFileSync24(path52.resolve(io.cwd, flags.values.file), "utf8");
97741
+ markdown = readFileSync25(path53.resolve(io.cwd, flags.values.file), "utf8");
97289
97742
  } catch (error) {
97290
97743
  return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE12);
97291
97744
  }
@@ -97351,11 +97804,11 @@ var init_youtrack2 = __esm(() => {
97351
97804
  // packages/workit-cli/src/verbs/changelog.ts
97352
97805
  var exports_changelog = {};
97353
97806
  __export(exports_changelog, {
97354
- run: () => run20
97807
+ run: () => run21
97355
97808
  });
97356
- import { readFileSync as readFileSync25 } from "node:fs";
97357
- import path53 from "node:path";
97358
- async function run20(argv, io) {
97809
+ import { readFileSync as readFileSync26 } from "node:fs";
97810
+ import path54 from "node:path";
97811
+ async function run21(argv, io) {
97359
97812
  const [sub, ...rest] = argv;
97360
97813
  if (sub !== "apply")
97361
97814
  return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE13);
@@ -97400,7 +97853,7 @@ async function run20(argv, io) {
97400
97853
  ]);
97401
97854
  }
97402
97855
  var USAGE13 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
97403
- const text = raw === "-" ? readFileSync25(0, "utf8") : raw.startsWith("@") ? readFileSync25(path53.resolve(cwd, raw.slice(1)), "utf8") : raw;
97856
+ const text = raw === "-" ? readFileSync26(0, "utf8") : raw.startsWith("@") ? readFileSync26(path54.resolve(cwd, raw.slice(1)), "utf8") : raw;
97404
97857
  return JSON.parse(text);
97405
97858
  };
97406
97859
  var init_changelog2 = __esm(() => {
@@ -97412,7 +97865,7 @@ var init_changelog2 = __esm(() => {
97412
97865
  // packages/workit-cli/src/verbs/handoff.ts
97413
97866
  var exports_handoff = {};
97414
97867
  __export(exports_handoff, {
97415
- run: () => run21
97868
+ run: () => run22
97416
97869
  });
97417
97870
  import { parseArgs as parseArgs2 } from "node:util";
97418
97871
  function human2(brief) {
@@ -97482,7 +97935,7 @@ async function resumeBrief(argv, io) {
97482
97935
  }
97483
97936
  return emit(io, ok({ ...data, recorded }), human2);
97484
97937
  }
97485
- async function run21(argv, io) {
97938
+ async function run22(argv, io) {
97486
97939
  if (!argv.some((arg) => arg === "--task" || arg.startsWith("--task=")))
97487
97940
  return resumeBrief(argv, io);
97488
97941
  await Promise.resolve().then(() => init_task());
@@ -97496,7 +97949,7 @@ var init_handoff = __esm(() => {
97496
97949
 
97497
97950
  // packages/workit-cli/src/main.ts
97498
97951
  init_package();
97499
- import path54 from "node:path";
97952
+ import path55 from "node:path";
97500
97953
 
97501
97954
  // packages/workit-cli/src/help.ts
97502
97955
  var HELP_FLAGS = new Set(["--help", "-h"]);
@@ -97715,8 +98168,8 @@ var VERBS = [
97715
98168
  {
97716
98169
  name: "doctor",
97717
98170
  group: "setup",
97718
- usage: "workit doctor [--fix-lock [--force [--yes]]]",
97719
- summary: "Verify the offline installation health (--fix-lock clears a stale workit store lock)",
98171
+ usage: "workit doctor [--fix] [--fix-lock [--force [--yes]]]",
98172
+ summary: "Verify the offline installation health (--fix installs the session trailer hook; --fix-lock clears a stale workit store lock)",
97720
98173
  load: () => Promise.resolve().then(() => (init_doctor2(), exports_doctor))
97721
98174
  },
97722
98175
  {
@@ -98142,7 +98595,7 @@ var defaultIo = (json) => ({
98142
98595
  });
98143
98596
  async function main(argv, overrides = {}, options = {}) {
98144
98597
  const parsed = parseGlobals(argv);
98145
- const cwd = path54.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
98598
+ const cwd = path55.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
98146
98599
  const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
98147
98600
  if (parsed.error)
98148
98601
  return emit(io, fail("invalid_input", parsed.error));