@brainervirus/workit-cursor 8.1.1 → 8.3.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.
@@ -8527,9 +8527,28 @@ function patchId(cwd, base, head) {
8527
8527
  const id = git(cwd, ["patch-id", "--stable"], { input: diff.stdout });
8528
8528
  return line(id)?.split(/\s+/u)[0] ?? null;
8529
8529
  }
8530
+ function remoteNames(cwd) {
8531
+ return (git(cwd, ["remote"]).stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
8532
+ }
8533
+ var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
8530
8534
  function currentBranch(cwd) {
8531
8535
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
8532
8536
  }
8537
+ function pushRemoteName(cwd, branch) {
8538
+ const name = branch ?? currentBranch(cwd);
8539
+ const remotes = remoteNames(cwd);
8540
+ const candidates = [
8541
+ name ? config2(cwd, `branch.${name}.pushRemote`) : null,
8542
+ config2(cwd, "remote.pushDefault"),
8543
+ name ? config2(cwd, `branch.${name}.remote`) : null
8544
+ ];
8545
+ for (const candidate of candidates)
8546
+ if (candidate && candidate !== "." && remotes.includes(candidate))
8547
+ return candidate;
8548
+ if (remotes.includes("origin"))
8549
+ return "origin";
8550
+ return remotes.length === 1 ? remotes[0] : null;
8551
+ }
8533
8552
 
8534
8553
  // packages/workit-core/src/ledger.ts
8535
8554
  import { spawnSync as spawnSync3 } from "node:child_process";
@@ -8537,6 +8556,35 @@ import { createHash as createHash4, randomBytes } from "node:crypto";
8537
8556
  import fs3 from "node:fs";
8538
8557
  import path3 from "node:path";
8539
8558
 
8559
+ // packages/workit-core/src/host-session.ts
8560
+ var HOST_SESSION_VARS = [
8561
+ ["OPENCODE_SESSION_ID", () => "opencode"],
8562
+ ["PI_SESSION_ID", () => "pi"],
8563
+ [
8564
+ "CODEX_THREAD_ID",
8565
+ (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
8566
+ ]
8567
+ ];
8568
+ var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
8569
+ function hostSessionFromEnv(env) {
8570
+ const value = (key) => env[key]?.trim() || null;
8571
+ let inner = null;
8572
+ for (const [key, host] of HOST_SESSION_VARS) {
8573
+ const session = value(key);
8574
+ if (session) {
8575
+ inner = { host: host(env), session };
8576
+ break;
8577
+ }
8578
+ }
8579
+ const declared = value("WORKIT_HOST");
8580
+ if (env.WORKIT_SESSION_ID !== undefined) {
8581
+ const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
8582
+ if (!inherited)
8583
+ return { host: declared, session: value("WORKIT_SESSION_ID") };
8584
+ }
8585
+ return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
8586
+ }
8587
+
8540
8588
  // packages/workit-core/src/store/paths.ts
8541
8589
  import { spawnSync as spawnSync2 } from "node:child_process";
8542
8590
  import { createHash as createHash3 } from "node:crypto";
@@ -8819,6 +8867,7 @@ var checkoutSlug = (root) => {
8819
8867
 
8820
8868
  // packages/workit-core/src/ledger.ts
8821
8869
  var LEDGER_VERSION = 1;
8870
+ var MAX_LINE_BYTES = 4096;
8822
8871
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
8823
8872
  var FAILING = new Set(["failed", "blocked"]);
8824
8873
  var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
@@ -8918,6 +8967,7 @@ function ledgerPath(cwd) {
8918
8967
  const root = storeRoot(cwd);
8919
8968
  return root.ok ? { ok: true, value: path3.join(root.value.root, "ledger", "ledger.jsonl") } : root;
8920
8969
  }
8970
+ var newId2 = () => `${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
8921
8971
  var NETWORK_FS_TYPES = new Set([
8922
8972
  26985,
8923
8973
  20859,
@@ -8930,6 +8980,158 @@ var NETWORK_FS_TYPES = new Set([
8930
8980
  1196443219,
8931
8981
  198183888
8932
8982
  ]);
8983
+ var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
8984
+ var onNetworkFs = (dir) => {
8985
+ if (process.platform !== "linux")
8986
+ return false;
8987
+ try {
8988
+ return isNetworkFsType(fs3.statfsSync(dir).type);
8989
+ } catch {
8990
+ return false;
8991
+ }
8992
+ };
8993
+ var LOCK_WAIT_MS = 5000;
8994
+ var LOCK_STALE_MS = 30000;
8995
+ var LOCK_OWNER = "owner";
8996
+ var lockToken = (lock) => {
8997
+ try {
8998
+ return fs3.readFileSync(path3.join(lock, LOCK_OWNER), "utf8");
8999
+ } catch {
9000
+ return null;
9001
+ }
9002
+ };
9003
+ var lockAgeMs = (lock) => {
9004
+ for (const target of [path3.join(lock, LOCK_OWNER), lock]) {
9005
+ try {
9006
+ return Date.now() - fs3.statSync(target).mtimeMs;
9007
+ } catch {}
9008
+ }
9009
+ return null;
9010
+ };
9011
+ var ledgerLock = {
9012
+ acquire(lock) {
9013
+ try {
9014
+ fs3.mkdirSync(lock);
9015
+ } catch (error) {
9016
+ if (error.code === "EEXIST")
9017
+ return null;
9018
+ throw error;
9019
+ }
9020
+ const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
9021
+ fs3.writeFileSync(path3.join(lock, LOCK_OWNER), token);
9022
+ return token;
9023
+ },
9024
+ release(lock, token) {
9025
+ if (lockToken(lock) !== token)
9026
+ return false;
9027
+ const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
9028
+ try {
9029
+ fs3.renameSync(lock, tomb);
9030
+ } catch {
9031
+ return false;
9032
+ }
9033
+ if (lockToken(tomb) !== token) {
9034
+ try {
9035
+ fs3.renameSync(tomb, lock);
9036
+ } catch {}
9037
+ return false;
9038
+ }
9039
+ fs3.rmSync(tomb, { recursive: true, force: true });
9040
+ return true;
9041
+ },
9042
+ token: lockToken,
9043
+ reap(lock, observed) {
9044
+ const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
9045
+ try {
9046
+ fs3.renameSync(lock, tomb);
9047
+ } catch {
9048
+ return false;
9049
+ }
9050
+ if (lockToken(tomb) === observed) {
9051
+ fs3.rmSync(tomb, { recursive: true, force: true });
9052
+ return true;
9053
+ }
9054
+ try {
9055
+ fs3.renameSync(tomb, lock);
9056
+ } catch {
9057
+ fs3.rmSync(tomb, { recursive: true, force: true });
9058
+ }
9059
+ return false;
9060
+ }
9061
+ };
9062
+ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
9063
+ const lock = `${file}.lock`;
9064
+ const deadline = Date.now() + waitMs;
9065
+ let token = null;
9066
+ for (;; ) {
9067
+ token = ledgerLock.acquire(lock);
9068
+ if (token)
9069
+ break;
9070
+ const observed = lockToken(lock);
9071
+ const age = lockAgeMs(lock);
9072
+ if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
9073
+ continue;
9074
+ if (Date.now() >= deadline)
9075
+ return null;
9076
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
9077
+ }
9078
+ try {
9079
+ return fn();
9080
+ } finally {
9081
+ ledgerLock.release(lock, token);
9082
+ }
9083
+ }
9084
+ function appendLine(file, line, options) {
9085
+ const write = () => {
9086
+ const fd = fs3.openSync(file, "a+");
9087
+ try {
9088
+ const size = fs3.fstatSync(fd).size;
9089
+ let fence = "";
9090
+ if (size > 0) {
9091
+ const last = Buffer.alloc(1);
9092
+ fs3.readSync(fd, last, 0, 1, size - 1);
9093
+ if (last[0] !== 10)
9094
+ fence = `
9095
+ `;
9096
+ }
9097
+ const bytes = Buffer.from(fence + line);
9098
+ const written = fs3.writeSync(fd, bytes);
9099
+ if (written !== bytes.length)
9100
+ return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
9101
+ return { ok: true, value: undefined };
9102
+ } finally {
9103
+ fs3.closeSync(fd);
9104
+ }
9105
+ };
9106
+ try {
9107
+ fs3.mkdirSync(path3.dirname(file), { recursive: true });
9108
+ if (!options.forceLock && !onNetworkFs(path3.dirname(file)))
9109
+ return write();
9110
+ const locked = withLedgerLock(file, write);
9111
+ return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
9112
+ } catch (error) {
9113
+ return err("unavailable", `cannot append to ${file}: ${error.message}`);
9114
+ }
9115
+ }
9116
+ var serialize = (row, now) => {
9117
+ const full = { v: LEDGER_VERSION, id: newId2(), at: now.toISOString(), ...row };
9118
+ return { full, line: `${JSON.stringify(full)}
9119
+ ` };
9120
+ };
9121
+ function appendRaw(cwd, row, options) {
9122
+ const { full, line } = serialize(row, options.now ?? new Date);
9123
+ const bytes = Buffer.byteLength(line);
9124
+ if (bytes > MAX_LINE_BYTES)
9125
+ return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
9126
+ const file = ledgerPath(cwd);
9127
+ if (!file.ok)
9128
+ return file;
9129
+ const written = appendLine(file.value, line, options);
9130
+ return written.ok ? { ok: true, value: full } : written;
9131
+ }
9132
+ function appendHookObserved(cwd, row) {
9133
+ return appendRaw(cwd, { ...row, observer: "host_hook" }, {});
9134
+ }
8933
9135
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8934
9136
  var str = (value) => typeof value === "string" ? value : null;
8935
9137
  var normalizeActor = (value) => {
@@ -10524,6 +10726,7 @@ var runtimeWorkspacePolicySchema = (kind) => {
10524
10726
  }).passthrough();
10525
10727
  return entrySchema;
10526
10728
  };
10729
+ var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs");
10527
10730
  var runtimeWorkspaceIndexSchema = object({
10528
10731
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
10529
10732
  }).passthrough();
@@ -12012,9 +12215,10 @@ var drop = (lock) => {
12012
12215
  };
12013
12216
  var envActor = () => {
12014
12217
  const value = (key) => process.env[key]?.trim() || null;
12218
+ const acting = hostSessionFromEnv(process.env);
12015
12219
  return {
12016
- host: value("WORKIT_HOST") ?? "runtime",
12017
- session: value("WORKIT_SESSION_ID"),
12220
+ host: acting.host ?? "runtime",
12221
+ session: acting.session,
12018
12222
  agentId: value("WORKIT_AGENT_ID")
12019
12223
  };
12020
12224
  };
@@ -16628,6 +16832,7 @@ var validateBranchNameFor = (workspaceRoot, name) => {
16628
16832
  };
16629
16833
  }
16630
16834
  };
16835
+ var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
16631
16836
 
16632
16837
  // packages/workit-core/src/core/route-intent.ts
16633
16838
  var shellBranchTarget = (command) => {
@@ -16653,36 +16858,43 @@ var shellPolicy = (cwd, command) => {
16653
16858
  // packages/workit-core/src/hooks/write-gate.ts
16654
16859
  import { realpathSync as realpathSync5 } from "node:fs";
16655
16860
  import path16 from "node:path";
16656
- var NONE2 = { kind: "none" };
16657
- var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
16658
- var canonical2 = (file) => {
16659
- let rest = "";
16660
- for (let dir = path16.resolve(file);; dir = path16.dirname(dir)) {
16661
- try {
16662
- return path16.join(realpathSync5(dir), rest);
16663
- } catch {
16664
- if (path16.dirname(dir) === dir)
16665
- return path16.resolve(file);
16666
- rest = rest ? path16.join(path16.basename(dir), rest) : path16.basename(dir);
16861
+
16862
+ // packages/workit-core/src/hooks/shell-words.ts
16863
+ var closingQuote = (command, start, dialect) => {
16864
+ const quote = command[start];
16865
+ for (let index = start + 1;index < command.length; index++) {
16866
+ const char = command[index];
16867
+ if (quote === "'") {
16868
+ if (char !== "'")
16869
+ continue;
16870
+ if (dialect === "powershell" && command[index + 1] === "'") {
16871
+ index++;
16872
+ continue;
16873
+ }
16874
+ return index;
16875
+ }
16876
+ if (dialect === "posix" && char === "\\" || dialect === "powershell" && char === "`") {
16877
+ index++;
16878
+ continue;
16667
16879
  }
16880
+ if (char === '"')
16881
+ return index;
16668
16882
  }
16883
+ return -1;
16669
16884
  };
16670
- var exempt = (cwd, root, file, plans) => {
16671
- const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
16672
- const relative = path16.relative(fold(canonical2(root)), fold(canonical2(path16.resolve(cwd, file))));
16673
- if (relative.startsWith("..") || path16.isAbsolute(relative))
16674
- return true;
16675
- const posix = relative.split(path16.sep).join("/");
16676
- const segments = posix.split("/");
16677
- return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
16885
+ var unquote = (body, quote, dialect) => {
16886
+ if (quote === "'")
16887
+ return dialect === "powershell" ? body.replaceAll("''", "'") : body;
16888
+ return dialect === "posix" ? body.replace(/\\(["\\$`])/g, "$1") : body.replace(/`(.)/g, "$1");
16678
16889
  };
16679
- var segmentsOf = (command) => {
16890
+ var segmentsOf = (command, dialect = "posix") => {
16680
16891
  const segments = [];
16681
16892
  let current = { words: [], redirects: [] };
16682
16893
  let word = "";
16683
16894
  let inWord = false;
16684
16895
  let pending = null;
16685
16896
  const heredocs = [];
16897
+ const escape = dialect === "posix" ? "\\" : "`";
16686
16898
  const flushWord = () => {
16687
16899
  if (!inWord)
16688
16900
  return;
@@ -16706,11 +16918,11 @@ var segmentsOf = (command) => {
16706
16918
  for (let index = 0;index < command.length; index++) {
16707
16919
  const char = command[index];
16708
16920
  if (char === "'" || char === '"') {
16709
- const end = command.indexOf(char, index + 1);
16710
- word += command.slice(index + 1, end < 0 ? undefined : end);
16921
+ const end = closingQuote(command, index, dialect);
16922
+ word += unquote(command.slice(index + 1, end < 0 ? undefined : end), char, dialect);
16711
16923
  inWord = true;
16712
16924
  index = end < 0 ? command.length : end;
16713
- } else if (char === "\\" && index + 1 < command.length) {
16925
+ } else if (char === escape && index + 1 < command.length) {
16714
16926
  word += command[++index];
16715
16927
  inWord = true;
16716
16928
  } else if (char === `
@@ -16727,7 +16939,14 @@ var segmentsOf = (command) => {
16727
16939
  }
16728
16940
  } else if (/\s/.test(char))
16729
16941
  flushWord();
16730
- else if (";&|()".includes(char)) {
16942
+ else if (char === "(" || char === ")") {
16943
+ if (char === "(" && inWord && word.endsWith("$")) {
16944
+ word = word.slice(0, -1);
16945
+ inWord = word !== "";
16946
+ }
16947
+ flushSegment();
16948
+ segments.push({ words: [], redirects: [], group: char === "(" ? "open" : "close" });
16949
+ } else if (";&|".includes(char)) {
16731
16950
  if (char === "&" && command[index + 1] === ">")
16732
16951
  continue;
16733
16952
  flushSegment();
@@ -16745,7 +16964,7 @@ var segmentsOf = (command) => {
16745
16964
  inWord = true;
16746
16965
  }
16747
16966
  pending = "redirect";
16748
- } else if (char === "<") {
16967
+ } else if (char === "<" && dialect === "posix") {
16749
16968
  flushWord();
16750
16969
  if (command[index + 1] === "<") {
16751
16970
  index++;
@@ -16762,6 +16981,31 @@ var segmentsOf = (command) => {
16762
16981
  flushSegment();
16763
16982
  return segments;
16764
16983
  };
16984
+
16985
+ // packages/workit-core/src/hooks/write-gate.ts
16986
+ var NONE2 = { kind: "none" };
16987
+ var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
16988
+ var canonical2 = (file) => {
16989
+ let rest = "";
16990
+ for (let dir = path16.resolve(file);; dir = path16.dirname(dir)) {
16991
+ try {
16992
+ return path16.join(realpathSync5(dir), rest);
16993
+ } catch {
16994
+ if (path16.dirname(dir) === dir)
16995
+ return path16.resolve(file);
16996
+ rest = rest ? path16.join(path16.basename(dir), rest) : path16.basename(dir);
16997
+ }
16998
+ }
16999
+ };
17000
+ var exempt = (cwd, root, file, plans) => {
17001
+ const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
17002
+ const relative = path16.relative(fold(canonical2(root)), fold(canonical2(path16.resolve(cwd, file))));
17003
+ if (relative.startsWith("..") || path16.isAbsolute(relative))
17004
+ return true;
17005
+ const posix = relative.split(path16.sep).join("/");
17006
+ const segments = posix.split("/");
17007
+ return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
17008
+ };
16765
17009
  var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
16766
17010
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
16767
17011
  var editsOf = (input) => {
@@ -16911,14 +17155,473 @@ function writeGate(cwd, paths) {
16911
17155
  return NONE2;
16912
17156
  }
16913
17157
  }
17158
+ // packages/workit-core/src/hooks/raw-git.ts
17159
+ import { spawnSync as spawnSync9 } from "node:child_process";
17160
+ import { createHash as createHash10 } from "node:crypto";
17161
+ import fs12 from "node:fs";
17162
+ import os4 from "node:os";
17163
+ import path17 from "node:path";
17164
+ var NONE3 = { kind: "none" };
17165
+ var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
17166
+ var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
17167
+ var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
17168
+ var CD = new Set(["cd", "chdir", "set-location", "sl"]);
17169
+ var PUSHD = new Set(["pushd", "push-location"]);
17170
+ var POPD = new Set(["popd", "pop-location"]);
17171
+ var GIT_VALUE_OPTIONS = new Set([
17172
+ "-c",
17173
+ "--git-dir",
17174
+ "--work-tree",
17175
+ "--namespace",
17176
+ "--exec-path",
17177
+ "--config-env",
17178
+ "--super-prefix"
17179
+ ]);
17180
+ var isAbsolute = (value) => path17.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
17181
+ var joinDir = (base, next) => {
17182
+ if (base === undefined || /[$`*?]/.test(next) || next === "-")
17183
+ return;
17184
+ const expanded = next === "~" || next.startsWith("~/") ? path17.join(os4.homedir(), next.slice(1)) : next;
17185
+ return base === null || isAbsolute(expanded) ? expanded : path17.join(base, expanded);
17186
+ };
17187
+ var cdTarget = (words) => {
17188
+ const args = words.slice(1);
17189
+ const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
17190
+ if (flag >= 0)
17191
+ return args[flag + 1] ?? null;
17192
+ return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
17193
+ };
17194
+ var stripPrefixes = (input) => {
17195
+ let words = input;
17196
+ for (;; ) {
17197
+ const first = words[0];
17198
+ if (first === undefined)
17199
+ return words;
17200
+ if (/^\w+=/.test(first))
17201
+ words = words.slice(1);
17202
+ else if (PREFIXES3.has(first)) {
17203
+ words = words.slice(1);
17204
+ while (words[0]?.startsWith("-"))
17205
+ words = words.slice(words[0] === "-u" ? 2 : 1);
17206
+ } else if (first === "timeout") {
17207
+ words = words.slice(1);
17208
+ while (words[0]?.startsWith("-"))
17209
+ words = words.slice(1);
17210
+ words = words.slice(1);
17211
+ } else
17212
+ return words;
17213
+ }
17214
+ };
17215
+ var commandName = (word) => (word?.split(/[\\/]/).at(-1) ?? "").toLowerCase().replace(/\.exe$/, "");
17216
+ function rawInvocations(command, dialect = "posix", depth = 0) {
17217
+ const found = [];
17218
+ let dir = null;
17219
+ const groups = [];
17220
+ let stack = [];
17221
+ for (const segment of segmentsOf(command, dialect)) {
17222
+ if (segment.group === "open") {
17223
+ groups.push({ dir, stack: [...stack] });
17224
+ continue;
17225
+ }
17226
+ if (segment.group === "close") {
17227
+ const saved = groups.pop();
17228
+ if (saved)
17229
+ ({ dir, stack } = saved);
17230
+ continue;
17231
+ }
17232
+ const words = stripPrefixes(segment.words);
17233
+ const name = commandName(words[0]);
17234
+ if (CD.has(name)) {
17235
+ const target = cdTarget(words);
17236
+ dir = target === null ? os4.homedir() : joinDir(dir, target);
17237
+ continue;
17238
+ }
17239
+ if (PUSHD.has(name)) {
17240
+ const target = cdTarget(words);
17241
+ stack.push(dir);
17242
+ dir = target === null || target === "-" ? undefined : joinDir(dir, target);
17243
+ continue;
17244
+ }
17245
+ if (POPD.has(name)) {
17246
+ dir = stack.length ? stack.pop() : undefined;
17247
+ continue;
17248
+ }
17249
+ if (SHELLS.has(name) && depth < 3) {
17250
+ const flag = words.findIndex((word, index) => index > 0 && SCRIPT_FLAG.test(word));
17251
+ const script = flag > 0 ? words[flag + 1] : undefined;
17252
+ if (script)
17253
+ for (const inner of rawInvocations(script, "posix", depth + 1))
17254
+ found.push({
17255
+ ...inner,
17256
+ dir: inner.dir === null ? dir : inner.dir === undefined ? undefined : joinDir(dir, inner.dir)
17257
+ });
17258
+ continue;
17259
+ }
17260
+ if (name === "gh" || name === "glab") {
17261
+ found.push({ tool: name, dir, args: words.slice(1) });
17262
+ continue;
17263
+ }
17264
+ if (name !== "git")
17265
+ continue;
17266
+ let here = dir;
17267
+ let index = 1;
17268
+ for (;index < words.length; index++) {
17269
+ const word = words[index];
17270
+ if (word === "-C")
17271
+ here = words[index + 1] ? joinDir(here, words[++index]) : here;
17272
+ else if (GIT_VALUE_OPTIONS.has(word))
17273
+ index++;
17274
+ else if (!word.startsWith("-"))
17275
+ break;
17276
+ }
17277
+ found.push({ tool: "git", dir: here, args: words.slice(index) });
17278
+ }
17279
+ return found;
17280
+ }
17281
+ var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
17282
+ var pushAction = (args) => {
17283
+ let force = false;
17284
+ let lease = false;
17285
+ let wholesale = false;
17286
+ let remote = null;
17287
+ const positionals = [];
17288
+ for (let index = 1;index < args.length; index++) {
17289
+ const arg = args[index];
17290
+ if (arg === "--") {
17291
+ positionals.push(...args.slice(index + 1));
17292
+ break;
17293
+ }
17294
+ if (arg === "--dry-run" || /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg))
17295
+ return null;
17296
+ if (arg === "--repo")
17297
+ remote = args[++index] ?? null;
17298
+ else if (arg.startsWith("--repo="))
17299
+ remote = arg.slice("--repo=".length);
17300
+ else if (PUSH_VALUE_OPTIONS.has(arg))
17301
+ index++;
17302
+ else if (arg === "--force" || /^-[a-zA-Z]*f[a-zA-Z]*$/.test(arg))
17303
+ force = true;
17304
+ else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
17305
+ lease = true;
17306
+ else if (["--all", "--mirror", "--branches", "--tags"].includes(arg))
17307
+ wholesale = true;
17308
+ else if (!arg.startsWith("-"))
17309
+ positionals.push(arg);
17310
+ }
17311
+ const specs = remote === null ? positionals.slice(1) : positionals;
17312
+ remote ??= positionals[0] ?? null;
17313
+ const targets = [];
17314
+ for (const raw of specs) {
17315
+ if (raw.startsWith("+"))
17316
+ force = true;
17317
+ const spec = raw.replace(/^\+/, "");
17318
+ const colon = spec.indexOf(":");
17319
+ const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
17320
+ if (!destination)
17321
+ continue;
17322
+ if (destination === "HEAD")
17323
+ targets.push(null);
17324
+ else if (destination.startsWith("refs/heads/"))
17325
+ targets.push(destination.slice(11));
17326
+ else if (!destination.startsWith("refs/"))
17327
+ targets.push(destination);
17328
+ }
17329
+ if (specs.length === 0 && !wholesale)
17330
+ targets.push(null);
17331
+ return { kind: "push", targets, blindForce: force && !lease, remote };
17332
+ };
17333
+ var VALUE_OPTIONS = {
17334
+ git: new Set([
17335
+ "-m",
17336
+ "--message",
17337
+ "-F",
17338
+ "--file",
17339
+ "-C",
17340
+ "-c",
17341
+ "--reuse-message",
17342
+ "--reedit-message",
17343
+ "--author",
17344
+ "--date",
17345
+ "--trailer",
17346
+ "-t",
17347
+ "--template",
17348
+ "--cleanup",
17349
+ "--fixup",
17350
+ "--squash",
17351
+ "-o",
17352
+ "--push-option",
17353
+ "--repo",
17354
+ "--receive-pack",
17355
+ "--exec"
17356
+ ]),
17357
+ gh: new Set(["-b", "--body", "-F", "--body-file", "-t", "--title", "--subject", "-A", "--author-email"].concat(["-B", "--base", "-H", "--head", "-l", "--label", "-a", "--assignee"]).concat(["-r", "--reviewer", "-m", "--milestone", "-p", "--project", "-T", "--template"]).concat(["--match-head-commit", "-R", "--repo"])),
17358
+ glab: new Set(["-m", "--message", "--squash-message", "--sha", "-t", "--title", "-d", "--description"].concat(["-b", "--target-branch", "-s", "--source-branch", "-l", "--label"]).concat(["-a", "--assignee", "--reviewer", "-R", "--repo"]))
17359
+ };
17360
+ var asksHelp = (invocation) => {
17361
+ const { tool, args } = invocation;
17362
+ if (args[0] === "help" || tool !== "git" && args[1] === "help")
17363
+ return true;
17364
+ const values = VALUE_OPTIONS[tool];
17365
+ for (let index = 0;index < args.length; index++) {
17366
+ const arg = args[index];
17367
+ if (arg === "--")
17368
+ return false;
17369
+ if (arg === "--help" || arg === "-h")
17370
+ return true;
17371
+ if (values.has(arg))
17372
+ index++;
17373
+ }
17374
+ return false;
17375
+ };
17376
+ function rawAction(invocation) {
17377
+ const args = invocation.args;
17378
+ if (asksHelp(invocation))
17379
+ return null;
17380
+ const [first, second] = args;
17381
+ if (invocation.tool === "git") {
17382
+ if (first === "commit")
17383
+ return args.includes("--dry-run") ? null : { kind: "commit", amend: args.includes("--amend") };
17384
+ if (first === "push")
17385
+ return pushAction(args);
17386
+ return null;
17387
+ }
17388
+ const forge = invocation.tool;
17389
+ const noun = forge === "gh" ? "pr" : "mr";
17390
+ if (first !== noun)
17391
+ return null;
17392
+ if (second === "merge")
17393
+ return { kind: "merge", forge };
17394
+ if (second === "create")
17395
+ return { kind: "pr-create", forge };
17396
+ if (["view", "checks", "status", "ci", "list"].includes(second ?? ""))
17397
+ return { kind: "pr-read", forge };
17398
+ return null;
17399
+ }
17400
+ var repoAt = (dir) => {
17401
+ const location = resolveStore(dir);
17402
+ if (location instanceof Error || !location.shared)
17403
+ return null;
17404
+ return { location, dir, branch: resolveTaskKey(dir, location).branch };
17405
+ };
17406
+ var isWorkitWorkspace = (repo) => {
17407
+ if (fs12.existsSync(repo.location.dir))
17408
+ return true;
17409
+ try {
17410
+ return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
17411
+ } catch {
17412
+ return false;
17413
+ }
17414
+ };
17415
+ var guardedBranch = (dir, branch) => {
17416
+ try {
17417
+ const policy = resolveBranchPolicyFor(dir);
17418
+ return policy.protected.has(branch.toLowerCase()) || policy.defaultTargetBranch.toLowerCase() === branch.toLowerCase();
17419
+ } catch {
17420
+ return false;
17421
+ }
17422
+ };
17423
+ var toPushRemote = (repo, remote) => {
17424
+ if (remote === null)
17425
+ return true;
17426
+ try {
17427
+ const pushRemote = pushRemoteName(repo.dir, repo.branch);
17428
+ return pushRemote === null || pushRemote === remote;
17429
+ } catch {
17430
+ return false;
17431
+ }
17432
+ };
17433
+ var headSha2 = (repo) => {
17434
+ const git = repo.location.git;
17435
+ if (!git || git.reftable)
17436
+ return null;
17437
+ try {
17438
+ const head = fs12.readFileSync(path17.join(git.gitDir, "HEAD"), "utf8").trim();
17439
+ if (/^[0-9a-f]{40,64}$/.test(head))
17440
+ return head;
17441
+ const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
17442
+ if (!ref)
17443
+ return null;
17444
+ const common = path17.dirname(repo.location.dir);
17445
+ try {
17446
+ return fs12.readFileSync(path17.join(common, ref), "utf8").trim() || null;
17447
+ } catch {}
17448
+ const packed = fs12.readFileSync(path17.join(common, "packed-refs"), "utf8");
17449
+ return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
17450
+ } catch {
17451
+ return null;
17452
+ }
17453
+ };
17454
+ var quote = (text) => `\`${text}\``;
17455
+ var refusal = (action, branch) => {
17456
+ if (action.kind === "merge")
17457
+ return `workit: raw ${quote(action.forge === "gh" ? "gh pr merge" : "glab mr merge")} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
17458
+ return `workit: raw ${quote("git push")}${action.kind === "push" && action.blindForce ? " --force" : ""} onto ${branch}, a protected branch or the default target, bypasses workit's protected-branch check. Push your feature branch with ${quote("workit git push")} (${quote("workit git push --force-with-lease")} to rewrite it) and land it with ${quote("workit pr merge")}.`;
17459
+ };
17460
+ var nudgeFor = (action, host, session) => {
17461
+ switch (action.kind) {
17462
+ case "commit": {
17463
+ const prefix = host === "cursor" && session ? `WORKIT_SESSION_ID=${session} ` : "";
17464
+ return action.amend ? `workit: raw ${quote("git commit --amend")} is recorded for this session; for new commits prefer ${quote(`${prefix}workit git commit -m '<msg>'`)} (adds the Workit-Session trailer and checks branch and commit policy).` : `workit: prefer ${quote(`${prefix}workit git commit -m '<msg>' [--all | -- <paths>]`)}: it adds the Workit-Session trailer and checks branch and commit policy.`;
17465
+ }
17466
+ case "push":
17467
+ return action.blindForce ? `workit: ${quote("git push --force")} overwrites without a lease; use ${quote("workit git push --force-with-lease")}.` : `workit: prefer ${quote("workit git push")}: it refuses protected branches and verifies the remote tip.`;
17468
+ case "pr-create":
17469
+ return `workit: prefer ${quote("workit pr create --fill")} (or --title/--body): it records the PR the merge gate checks.`;
17470
+ case "pr-read":
17471
+ return `workit: ${quote("workit pr status")} shows the PR, its checks and the verdict state; ${quote("workit ci wait")} waits for checks.`;
17472
+ case "merge":
17473
+ return null;
17474
+ }
17475
+ };
17476
+ var actionsIn = (cwd, command, dialect) => {
17477
+ const out = [];
17478
+ const repos = new Map;
17479
+ for (const invocation of rawInvocations(command, dialect)) {
17480
+ if (invocation.dir === undefined)
17481
+ continue;
17482
+ const action = rawAction(invocation);
17483
+ if (!action)
17484
+ continue;
17485
+ const dir = path17.resolve(cwd, invocation.dir ?? ".");
17486
+ if (!repos.has(dir)) {
17487
+ const repo = repoAt(dir);
17488
+ repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
17489
+ }
17490
+ const repo = repos.get(dir);
17491
+ if (repo)
17492
+ out.push({ action, repo });
17493
+ }
17494
+ return out;
17495
+ };
17496
+ var mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command);
17497
+ var dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix";
17498
+ function rawGitPre(input, command) {
17499
+ try {
17500
+ if (!mentionsRawTool(command))
17501
+ return NONE3;
17502
+ const nudges = [];
17503
+ for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
17504
+ if (action.kind === "merge")
17505
+ return deny(refusal(action, null));
17506
+ if (action.kind === "push")
17507
+ for (const target of action.targets) {
17508
+ const branch = target ?? repo.branch;
17509
+ if (branch && guardedBranch(repo.dir, branch) && toPushRemote(repo, action.remote))
17510
+ return deny(refusal(action, branch));
17511
+ }
17512
+ const nudge = nudgeFor(action, input.host, input.session.id);
17513
+ if (nudge && !nudges.includes(nudge))
17514
+ nudges.push(nudge);
17515
+ }
17516
+ return nudges.length ? { kind: "context", text: nudges.join(`
17517
+ `) } : NONE3;
17518
+ } catch {
17519
+ return NONE3;
17520
+ }
17521
+ }
17522
+ var deny = (reason) => ({ kind: "deny", reason, unblock: null });
17523
+ var NEXT_COMMAND = "next";
17524
+ var MARKER_TTL_SECONDS = 3600;
17525
+ var markerFile = (repo, session, key) => path17.join(repo.location.dir, "hooks", `commit-${createHash10("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
17526
+ var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash10("sha256").update(command).digest("hex")}`;
17527
+ function noteRawCommit(input, command, key) {
17528
+ try {
17529
+ if (!input.session.id || !/\bcommit\b/.test(command))
17530
+ return;
17531
+ const at = Math.floor(Date.now() / 1000);
17532
+ for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
17533
+ if (action.kind !== "commit")
17534
+ continue;
17535
+ const file = markerFile(repo, input.session.id, key);
17536
+ fs12.mkdirSync(path17.dirname(file), { recursive: true });
17537
+ fs12.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
17538
+ `);
17539
+ }
17540
+ } catch {}
17541
+ }
17542
+ var headCommit = (dir) => {
17543
+ const run = spawnSync9("git", [
17544
+ "log",
17545
+ "-1",
17546
+ "--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
17547
+ ], {
17548
+ cwd: dir,
17549
+ encoding: "utf8",
17550
+ timeout: 5000,
17551
+ windowsHide: true,
17552
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" }
17553
+ });
17554
+ const [sha, committed, subject = "", trailer = ""] = (run.status === 0 ? run.stdout.trim() : "").split("\x1F");
17555
+ return sha && /^[0-9a-f]{40,64}$/.test(sha) ? { sha, committed: Number(committed), subject, trailer: trailer.trim() || null } : null;
17556
+ };
17557
+ var settle2 = (input, repo, file) => {
17558
+ let marker;
17559
+ try {
17560
+ marker = JSON.parse(fs12.readFileSync(file, "utf8"));
17561
+ } catch {
17562
+ return;
17563
+ }
17564
+ fs12.rmSync(file, { force: true });
17565
+ const session = input.session.id;
17566
+ const now = Date.now() / 1000;
17567
+ if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
17568
+ return;
17569
+ if (!session || !repo.branch)
17570
+ return;
17571
+ const head = headCommit(repo.dir);
17572
+ if (!head || head.sha === marker.head || head.committed < marker.at)
17573
+ return;
17574
+ if (head.trailer && head.trailer !== session)
17575
+ return;
17576
+ const ledger = readLedger(repo.dir);
17577
+ if (!ledger.ok)
17578
+ return;
17579
+ const claimed = ledger.value.rows.some((row) => row.type === "commit.recorded" && (row.sha === head.sha || row.head === head.sha));
17580
+ if (claimed)
17581
+ return;
17582
+ appendHookObserved(repo.dir, {
17583
+ type: "commit.recorded",
17584
+ actor: { host: input.host, session, agentId: input.session.agentId },
17585
+ branch: repo.branch,
17586
+ head: head.sha,
17587
+ sha: head.sha,
17588
+ session,
17589
+ agentId: input.session.agentId,
17590
+ subject: head.subject.slice(0, 200),
17591
+ files: [],
17592
+ fileCount: null,
17593
+ via: "raw_shell"
17594
+ });
17595
+ };
17596
+ function rawGitPost(input, command, key) {
17597
+ try {
17598
+ if (!input.session.id || !/\bcommit\b/.test(command))
17599
+ return;
17600
+ for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input)))
17601
+ if (action.kind === "commit")
17602
+ settle2(input, repo, markerFile(repo, input.session.id, key));
17603
+ } catch {}
17604
+ }
17605
+ function settlePendingCommit(input) {
17606
+ try {
17607
+ if (!input.session.id)
17608
+ return;
17609
+ const repo = repoAt(input.cwd);
17610
+ if (!repo)
17611
+ return;
17612
+ const file = markerFile(repo, input.session.id, NEXT_COMMAND);
17613
+ if (fs12.existsSync(file))
17614
+ settle2(input, repo, file);
17615
+ } catch {}
17616
+ }
16914
17617
  // packages/workit-core/src/hooks/hosts/fields.ts
16915
17618
  import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
16916
- import path17 from "node:path";
17619
+ import path18 from "node:path";
16917
17620
  var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
16918
17621
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
16919
17622
  var optionalText = (value) => typeof value === "string" ? value : null;
16920
17623
  var existingDirectory = (value) => {
16921
- if (!nonEmpty2(value) || !path17.isAbsolute(value) || !existsSync6(value))
17624
+ if (!nonEmpty2(value) || !path18.isAbsolute(value) || !existsSync6(value))
16922
17625
  return null;
16923
17626
  try {
16924
17627
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -16926,8 +17629,8 @@ var existingDirectory = (value) => {
16926
17629
  return null;
16927
17630
  }
16928
17631
  };
16929
- var SHELLS = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
16930
- var SCRIPT_FLAG = /^(?:-\w*c|-command|\/c)$/i;
17632
+ var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
17633
+ var SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
16931
17634
  var quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);
16932
17635
  var commandText = (value) => {
16933
17636
  if (nonEmpty2(value))
@@ -16938,9 +17641,9 @@ var commandText = (value) => {
16938
17641
  if (!argv.every((arg) => typeof arg === "string"))
16939
17642
  return null;
16940
17643
  const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
16941
- if (SHELLS.has(shell))
17644
+ if (SHELLS2.has(shell))
16942
17645
  for (let index = 1;index < argv.length - 1; index++) {
16943
- if (SCRIPT_FLAG.test(argv[index]))
17646
+ if (SCRIPT_FLAG2.test(argv[index]))
16944
17647
  return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
16945
17648
  if (!/^[-/]/.test(argv[index]))
16946
17649
  break;
@@ -16981,7 +17684,7 @@ var writePaths = (input) => {
16981
17684
  };
16982
17685
  // packages/workit-core/src/hooks/handle.ts
16983
17686
  var usable2 = (value) => value === "native" || value === "partial";
16984
- var NONE3 = { kind: "none" };
17687
+ var NONE4 = { kind: "none" };
16985
17688
  var offered = new Set;
16986
17689
  var CLAUDE_WORKTREE_IMPLEMENTER = "workit:implementer";
16987
17690
  var CLAUDE_JUDGES = new Set(["workit:verifier", "workit:reviewer"]);
@@ -17000,7 +17703,7 @@ function handleHook(input, deps) {
17000
17703
  switch (event.kind) {
17001
17704
  case "session.start": {
17002
17705
  if (!usable2(descriptor.context.sessionStart))
17003
- return NONE3;
17706
+ return NONE4;
17004
17707
  const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
17005
17708
  const offer = event.source === "startup" && (key === null || !offered.has(key));
17006
17709
  if (offer && key !== null)
@@ -17015,19 +17718,34 @@ function handleHook(input, deps) {
17015
17718
  }
17016
17719
  case "context.turn": {
17017
17720
  if (!usable2(descriptor.context.perTurn))
17018
- return NONE3;
17721
+ return NONE4;
17019
17722
  const text = turnContextText(input, descriptor);
17020
- return text ? { kind: "context", text } : NONE3;
17723
+ return text ? { kind: "context", text } : NONE4;
17021
17724
  }
17022
17725
  case "shell.pre": {
17023
- const policy = usable2(descriptor.shellPolicy.deny) ? shellPolicy(input.cwd, event.command) : NONE3;
17024
- if (policy.kind !== "none" || !usable2(descriptor.events["write.pre"].support))
17726
+ const canDeny = usable2(descriptor.shellPolicy.deny);
17727
+ const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
17728
+ if (policy.kind !== "none")
17025
17729
  return policy;
17026
- const writes = shellWrites(event.command);
17027
- return writes.writes ? writeGate(input.cwd, writes.targets) : NONE3;
17730
+ const postTool = usable2(descriptor.events["shell.post"].support);
17731
+ if (!postTool)
17732
+ settlePendingCommit(input);
17733
+ const raw = canDeny ? rawGitPre(input, event.command) : NONE4;
17734
+ if (raw.kind === "deny")
17735
+ return raw;
17736
+ const writes = usable2(descriptor.events["write.pre"].support) ? shellWrites(event.command) : { writes: false, targets: null };
17737
+ const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE4;
17738
+ if (gate.kind === "deny")
17739
+ return gate;
17740
+ noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
17741
+ return raw;
17028
17742
  }
17743
+ case "shell.post":
17744
+ if (usable2(descriptor.events["shell.post"].support))
17745
+ rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
17746
+ return NONE4;
17029
17747
  case "write.pre":
17030
- return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE3;
17748
+ return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE4;
17031
17749
  case "subagent.start":
17032
17750
  return {
17033
17751
  kind: "context",
@@ -17039,19 +17757,18 @@ function handleHook(input, deps) {
17039
17757
  userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
17040
17758
  };
17041
17759
  case "tool.pre":
17042
- case "shell.post":
17043
17760
  case "subagent.stop":
17044
17761
  case "prompt.submit":
17045
17762
  case "stop":
17046
- return NONE3;
17763
+ return NONE4;
17047
17764
  }
17048
17765
  }
17049
17766
  var failureDecision = (descriptor, event, error) => {
17050
17767
  if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
17051
- return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE3;
17768
+ return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE4;
17052
17769
  if (event === "session.start" || event === "subagent.start")
17053
17770
  return { kind: "context", text: `[workit diagnostic: ${error}]` };
17054
- return NONE3;
17771
+ return NONE4;
17055
17772
  };
17056
17773
  var dispatchHook = (adapter, raw, env = process.env) => {
17057
17774
  const parsed = adapter.parse(raw, env);
@@ -17174,6 +17891,7 @@ var EVENTS = {
17174
17891
  };
17175
17892
  var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
17176
17893
  var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
17894
+ var dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix";
17177
17895
  var toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
17178
17896
  var eventOf = (name, value) => {
17179
17897
  const toolUseId = optionalText(value.tool_use_id);
@@ -17198,7 +17916,7 @@ var eventOf = (name, value) => {
17198
17916
  if (!SHELL_TOOLS.has(value.tool_name))
17199
17917
  return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
17200
17918
  const command = toolCommand(value);
17201
- return command ? { ok: true, event: { kind: "shell.pre", command, toolUseId } } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
17919
+ return command ? { ok: true, event: { kind: "shell.pre", command, toolUseId, dialect: dialectOf2(value) } } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
17202
17920
  }
17203
17921
  case "PostToolUse": {
17204
17922
  const command = toolCommand(value);
@@ -17212,7 +17930,8 @@ var eventOf = (name, value) => {
17212
17930
  command,
17213
17931
  stdout: optionalText(response.stdout) ?? "",
17214
17932
  exitCode: typeof response.exit_code === "number" ? response.exit_code : null,
17215
- toolUseId
17933
+ toolUseId,
17934
+ dialect: dialectOf2(value)
17216
17935
  }
17217
17936
  };
17218
17937
  }
@@ -17253,7 +17972,13 @@ var eventOf = (name, value) => {
17253
17972
  };
17254
17973
  }
17255
17974
  };
17256
- var CONTEXT_EVENTS = new Set(["SessionStart", "UserPromptSubmit", "SubagentStart"]);
17975
+ var CONTEXT_EVENTS = new Set([
17976
+ "SessionStart",
17977
+ "UserPromptSubmit",
17978
+ "SubagentStart",
17979
+ "PreToolUse",
17980
+ "PostToolUse"
17981
+ ]);
17257
17982
  var render = (decision, native) => {
17258
17983
  if (decision.kind === "deny" && native === "PreToolUse")
17259
17984
  return {
@@ -17335,7 +18060,7 @@ var CODEX_DESCRIPTOR = {
17335
18060
  "shell.pre": { support: "native", native: "PreToolUse" },
17336
18061
  "tool.pre": { support: "native", native: "PreToolUse" },
17337
18062
  "write.pre": { support: "undocumented", native: null },
17338
- "shell.post": undocumented,
18063
+ "shell.post": { support: "native", native: "PostToolUse" },
17339
18064
  "subagent.start": { support: "native", native: "SubagentStart" },
17340
18065
  "subagent.stop": { support: "native", native: "SubagentStop" },
17341
18066
  "prompt.submit": undocumented,
@@ -17356,7 +18081,7 @@ var CODEX_DESCRIPTOR = {
17356
18081
  worktreeIsolation: "undocumented",
17357
18082
  maxConcurrency: "undocumented"
17358
18083
  },
17359
- provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "undocumented" },
18084
+ provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
17360
18085
  interaction: { questions: "none", writeBoundary: "partial" },
17361
18086
  stopControl: "undocumented",
17362
18087
  shellAvailable: "native",
@@ -17422,6 +18147,7 @@ var CODEX_DESCRIPTOR = {
17422
18147
  var EVENTS2 = {
17423
18148
  SessionStart: "session.start",
17424
18149
  PreToolUse: "shell.pre",
18150
+ PostToolUse: "shell.post",
17425
18151
  SubagentStart: "subagent.start",
17426
18152
  SubagentStop: "subagent.stop"
17427
18153
  };
@@ -17435,10 +18161,11 @@ var parseCodexHookInput = (value) => {
17435
18161
  const cwd = existingDirectory(value.cwd);
17436
18162
  if (!cwd)
17437
18163
  return { ok: false, error: "cwd must be an existing absolute directory" };
17438
- if (event === "PreToolUse" && !nonEmpty2(value.tool_name))
18164
+ const toolEvent = event === "PreToolUse" || event === "PostToolUse";
18165
+ if (toolEvent && !nonEmpty2(value.tool_name))
17439
18166
  return { ok: false, error: "tool_name is required" };
17440
18167
  const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
17441
- if (event === "PreToolUse" && isShellTool(value.tool_name) && !command)
18168
+ if (toolEvent && isShellTool(value.tool_name) && !command)
17442
18169
  return { ok: false, error: "tool_input.command is required for shell tools" };
17443
18170
  if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
17444
18171
  return { ok: false, error: "agent_id is required" };
@@ -17454,7 +18181,8 @@ var parseCodexHookInput = (value) => {
17454
18181
  ...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
17455
18182
  ...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
17456
18183
  ...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
17457
- ...event === "PreToolUse" ? { tool_input: value.tool_input } : {},
18184
+ ...toolEvent ? { tool_input: value.tool_input } : {},
18185
+ ...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
17458
18186
  ...command ? { command } : {},
17459
18187
  ...nonEmpty2(value.tool_use_id) ? { tool_use_id: value.tool_use_id } : {},
17460
18188
  ...nonEmpty2(value.agent_id) ? { agent_id: value.agent_id } : {},
@@ -17479,6 +18207,16 @@ var protocolEvent = (input) => {
17479
18207
  toolUseId
17480
18208
  } : { kind: "tool.pre", tool: input.tool_name, toolUseId };
17481
18209
  }
18210
+ case "PostToolUse": {
18211
+ const toolUseId = input.tool_use_id ?? null;
18212
+ return isShellTool(input.tool_name) ? {
18213
+ kind: "shell.post",
18214
+ command: input.command ?? "",
18215
+ stdout: input.tool_response ?? "",
18216
+ exitCode: null,
18217
+ toolUseId
18218
+ } : { kind: "tool.pre", tool: input.tool_name, toolUseId };
18219
+ }
17482
18220
  case "SubagentStart":
17483
18221
  return {
17484
18222
  kind: "subagent.start",
@@ -17543,11 +18281,11 @@ var codexAdapter = {
17543
18281
  };
17544
18282
  },
17545
18283
  render: render2,
17546
- addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. A verifier or reviewer records workit ledger verdict under a session the lead assigns (WORKIT_SESSION_ID=<lead>-v<n>), never the author's. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
18284
+ addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under a session the lead assigns (WORKIT_HOST=codex_cli WORKIT_SESSION_ID=<lead>-v<n>), never the author's. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
17547
18285
  };
17548
18286
 
17549
18287
  // packages/workit-core/src/hooks/hosts/cursor.ts
17550
- import path18 from "node:path";
18288
+ import path19 from "node:path";
17551
18289
  var undocumented2 = { support: "undocumented", native: null };
17552
18290
  var none = { support: "none", native: null };
17553
18291
  var CURSOR_DESCRIPTOR = {
@@ -17728,7 +18466,7 @@ var protocolEvent2 = (input) => {
17728
18466
  return { kind: "compact.pre", trigger: "auto" };
17729
18467
  }
17730
18468
  };
17731
- var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path18.sep) ? root : `${root}${path18.sep}`);
18469
+ var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path19.sep) ? root : `${root}${path19.sep}`);
17732
18470
  var hookCwd = (input) => {
17733
18471
  const cwd = existingDirectory(input.cwd);
17734
18472
  if (cwd && input.hook_event_name === "beforeShellExecution")
@@ -17751,6 +18489,8 @@ var render3 = (decision, native) => {
17751
18489
  json: cursorDeny(decision.reason),
17752
18490
  exitCode: native !== null && BLOCKING.has(native) ? 2 : 0
17753
18491
  };
18492
+ if (decision.kind === "context" && native === "beforeShellExecution")
18493
+ return { json: { permission: "allow", agent_message: decision.text }, exitCode: 0 };
17754
18494
  if (decision.kind === "context")
17755
18495
  return { json: { additional_context: decision.text }, exitCode: 0 };
17756
18496
  if (decision.kind === "notice")
@@ -17787,7 +18527,8 @@ var cursorAdapter = {
17787
18527
  }
17788
18528
  };
17789
18529
  },
17790
- render: render3
18530
+ render: render3,
18531
+ addendum: (input) => input.session.id ? `<workit-session>Cursor puts no session id in the shell: prefix workit commands that record authorship with WORKIT_SESSION_ID=${input.session.id} (e.g. \`WORKIT_SESSION_ID=${input.session.id} workit git commit -m …\`). A verifier uses its own id, never this one.</workit-session>` : null
17791
18532
  };
17792
18533
 
17793
18534
  // packages/workit-core/src/hooks/run.ts