@brainervirus/workit-opencode 8.16.0 → 8.17.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.
package/dist/plugin.js CHANGED
@@ -12398,6 +12398,17 @@ import fs9 from "node:fs";
12398
12398
  import path9 from "node:path";
12399
12399
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12400
12400
  var CHECKS_FILE = "workit.checks.json";
12401
+ var checksConfigFile = (root) => path9.join(root, CHECKS_FILE);
12402
+ function repoHeavyCommands(root) {
12403
+ try {
12404
+ const value = JSON.parse(fs9.readFileSync(checksConfigFile(root), "utf8"));
12405
+ const resources = isRecord(value) ? value.resources : undefined;
12406
+ const list = isRecord(resources) ? resources.heavyCommands : undefined;
12407
+ return Array.isArray(list) ? list.filter((item) => typeof item === "string") : [];
12408
+ } catch {
12409
+ return [];
12410
+ }
12411
+ }
12401
12412
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12402
12413
  var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
12403
12414
  function splitCommand(command) {
@@ -12510,7 +12521,7 @@ function detectedChecks(root) {
12510
12521
  var GATES = ["testing", "verification"];
12511
12522
  function loadCheckConfig(cwd) {
12512
12523
  const root = checkRoot(cwd);
12513
- const file = path9.join(root, CHECKS_FILE);
12524
+ const file = checksConfigFile(root);
12514
12525
  const none = (error = null) => ({
12515
12526
  source: error ? CHECKS_FILE : "none",
12516
12527
  root,
@@ -13255,6 +13266,7 @@ var workspaceConfigSchema = object({
13255
13266
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
13256
13267
  defaultEndpoint: unknown().optional(),
13257
13268
  verification: _enum(["self", "independent"]).optional(),
13269
+ delegation: unknown().optional(),
13258
13270
  trailers: boolean2().optional(),
13259
13271
  liveSessionGuard: boolean2().optional(),
13260
13272
  fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
@@ -17506,7 +17518,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
17506
17518
  return found;
17507
17519
  }
17508
17520
  var RECENT = new Map;
17509
- function walk2(command, dialect, depth) {
17521
+ function commandLines(command, dialect = "posix") {
17522
+ const lines = [];
17523
+ try {
17524
+ walk2(command, dialect, 0, lines);
17525
+ } catch (error) {
17526
+ throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
17527
+ }
17528
+ return lines;
17529
+ }
17530
+ function walk2(command, dialect, depth, seen) {
17510
17531
  const found = [];
17511
17532
  let dir = null;
17512
17533
  const groups = [];
@@ -17517,7 +17538,8 @@ function walk2(command, dialect, depth) {
17517
17538
  const nested = (script, inner) => {
17518
17539
  if (!script || depth >= MAX_DEPTH)
17519
17540
  return;
17520
- for (const invocation of rawInvocations(script, inner, depth + 1))
17541
+ const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
17542
+ for (const invocation of inside)
17521
17543
  found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
17522
17544
  };
17523
17545
  const visit = (input, stdin, hops) => {
@@ -17529,11 +17551,17 @@ function walk2(command, dialect, depth) {
17529
17551
  const name = commandName(words[0]);
17530
17552
  if (!name)
17531
17553
  return;
17554
+ seen?.push(words);
17555
+ if (name === "-c" || name === "--command")
17556
+ return nested(words[1], "posix");
17532
17557
  const runs = (lines) => {
17533
17558
  if (hops < MAX_DEPTH)
17534
17559
  for (const next of lines)
17535
17560
  visit(next, undefined, hops + 1);
17536
17561
  };
17562
+ const wrapped = seen ? null : workitCheckTail(words);
17563
+ if (wrapped)
17564
+ return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
17537
17565
  if (CD.has(name)) {
17538
17566
  const target = cdTarget(words);
17539
17567
  dir = target === null ? os4.homedir() : joinDir(dir, target);
@@ -17624,6 +17652,51 @@ var LAUNCHERS = new Set([
17624
17652
  "command"
17625
17653
  ]);
17626
17654
  var WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
17655
+ var WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
17656
+ var PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
17657
+ function workitCheckTail(words) {
17658
+ let at = 0;
17659
+ if (PACKAGE_RUNNERS.has(commandName(words[0]))) {
17660
+ at = words.findIndex((word, index) => index > 0 && !word.startsWith("-"));
17661
+ if (at < 0 || !WORKIT_PACKAGE.test(words[at]))
17662
+ return null;
17663
+ } else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
17664
+ return null;
17665
+ const args = words.slice(at + 1);
17666
+ if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
17667
+ return null;
17668
+ const split = args.indexOf("--");
17669
+ const tail = split < 0 ? [] : args.slice(split + 1);
17670
+ if (!tail.length)
17671
+ return null;
17672
+ return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
17673
+ }
17674
+ function simpleCommand(command, dialect = "posix", depth = 0) {
17675
+ let segments;
17676
+ try {
17677
+ segments = segmentsOf(command, dialect);
17678
+ } catch {
17679
+ return null;
17680
+ }
17681
+ if (segments.length !== 1)
17682
+ return null;
17683
+ const [segment] = segments;
17684
+ if (segment.group || segment.pipe || segment.unanalyzable || segment.redirects.length || segment.subs?.length || segment.stdin?.length)
17685
+ return null;
17686
+ const { words } = stripPrefixes(segment.words);
17687
+ const name = commandName(words[0]);
17688
+ if (!name || depth >= MAX_DEPTH)
17689
+ return null;
17690
+ if (SHELLS.has(name)) {
17691
+ const script = shellScript(words, undefined);
17692
+ return script ? simpleCommand(script, "posix", depth + 1) : null;
17693
+ }
17694
+ if (name === "-c" || name === "--command")
17695
+ return words[1] ? simpleCommand(words[1], "posix", depth + 1) : null;
17696
+ if (EVALS.has(name) || POWERSHELLS.has(name) || ["xargs", "find", "parallel", "script", "cmd", "source", "."].includes(name))
17697
+ return null;
17698
+ return words;
17699
+ }
17627
17700
  function workitInvocations(command, dialect = "posix", depth = 0) {
17628
17701
  const found = [];
17629
17702
  for (const { words } of segmentsOf(command, dialect)) {
@@ -21971,17 +22044,816 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
21971
22044
  }
21972
22045
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
21973
22046
  };
21974
- // packages/workit-core/src/hooks/write-gate.ts
21975
- import { realpathSync as realpathSync6 } from "node:fs";
22047
+ // packages/workit-core/src/core/heavy-rules.ts
22048
+ import fs18 from "node:fs";
22049
+ import os6 from "node:os";
22050
+ import path26 from "node:path";
22051
+ var EMPTY = () => ({ deny: [], allow: [] });
22052
+ var readText2 = (file) => {
22053
+ try {
22054
+ return fs18.readFileSync(file, "utf8");
22055
+ } catch (error) {
22056
+ if (error.code === "ENOENT")
22057
+ return null;
22058
+ throw error;
22059
+ }
22060
+ };
22061
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22062
+ function parseJsonc(text) {
22063
+ let out = "";
22064
+ for (let index = 0;index < text.length; index += 1) {
22065
+ const char = text[index];
22066
+ if (char === '"') {
22067
+ const end = /"(?:[^"\\]|\\.)*"/y;
22068
+ end.lastIndex = index;
22069
+ const match = end.exec(text);
22070
+ if (!match)
22071
+ throw new Error("unterminated string");
22072
+ out += match[0];
22073
+ index += match[0].length - 1;
22074
+ } else if (char === "/" && text[index + 1] === "/") {
22075
+ while (index < text.length && text[index] !== `
22076
+ `)
22077
+ index += 1;
22078
+ out += `
22079
+ `;
22080
+ } else if (char === "/" && text[index + 1] === "*") {
22081
+ const close = text.indexOf("*/", index + 2);
22082
+ if (close < 0)
22083
+ throw new Error("unterminated comment");
22084
+ index = close + 1;
22085
+ } else
22086
+ out += char;
22087
+ }
22088
+ return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
22089
+ }
22090
+ function gitTop(cwd) {
22091
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
22092
+ if (fs18.existsSync(path26.join(dir, ".git")))
22093
+ return dir;
22094
+ if (path26.dirname(dir) === dir)
22095
+ return null;
22096
+ }
22097
+ }
22098
+ var projectDirs = (cwd) => {
22099
+ const top = gitTop(cwd);
22100
+ const dirs = [];
22101
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
22102
+ dirs.push(dir);
22103
+ if (!top || dir === top || path26.dirname(dir) === dir)
22104
+ return dirs;
22105
+ }
22106
+ };
22107
+ var homeOf = (env) => env.HOME || os6.homedir();
22108
+ var CLAUDE_MANAGED = {
22109
+ linux: "/etc/claude-code/managed-settings.json",
22110
+ darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
22111
+ win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
22112
+ };
22113
+ function claudeSettingsFiles(cwd, env, platform = process.platform) {
22114
+ const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
22115
+ const user = env.CLAUDE_CONFIG_DIR || path26.join(homeOf(env), ".claude");
22116
+ return [
22117
+ ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
22118
+ path26.join(user, "settings.json"),
22119
+ path26.join(project, ".claude", "settings.json"),
22120
+ path26.join(project, ".claude", "settings.local.json")
22121
+ ];
22122
+ }
22123
+ var bashPattern = (entry) => {
22124
+ const match = /^Bash(?:\((.*)\))?$/su.exec(entry.trim());
22125
+ return match ? match[1] ?? "" : null;
22126
+ };
22127
+ function claudeRules(cwd, env = process.env) {
22128
+ const rules = EMPTY();
22129
+ try {
22130
+ for (const file of claudeSettingsFiles(cwd, env)) {
22131
+ const text = readText2(file);
22132
+ if (text === null)
22133
+ continue;
22134
+ const value = JSON.parse(text);
22135
+ if (!isRecord4(value))
22136
+ return "unreadable";
22137
+ const permissions = value.permissions;
22138
+ if (permissions === undefined)
22139
+ continue;
22140
+ if (!isRecord4(permissions))
22141
+ return "unreadable";
22142
+ for (const [key, bucket] of [
22143
+ ["deny", rules.deny],
22144
+ ["ask", rules.deny],
22145
+ ["allow", rules.allow]
22146
+ ]) {
22147
+ const list = permissions[key];
22148
+ if (list === undefined)
22149
+ continue;
22150
+ if (!Array.isArray(list))
22151
+ return "unreadable";
22152
+ for (const entry of list) {
22153
+ if (typeof entry !== "string")
22154
+ return "unreadable";
22155
+ const pattern = bashPattern(entry);
22156
+ if (pattern !== null)
22157
+ bucket.push(pattern);
22158
+ }
22159
+ }
22160
+ }
22161
+ } catch {
22162
+ return "unreadable";
22163
+ }
22164
+ return rules;
22165
+ }
22166
+ var OPENCODE_NAMES = ["opencode.json", "opencode.jsonc"];
22167
+ var opencodePermission = (permission, rules) => {
22168
+ if (permission === undefined)
22169
+ return true;
22170
+ if (typeof permission === "string") {
22171
+ (permission === "allow" ? rules.allow : rules.deny).push("");
22172
+ return ["allow", "ask", "deny"].includes(permission);
22173
+ }
22174
+ if (!isRecord4(permission))
22175
+ return false;
22176
+ for (const key of ["bash", "shell"]) {
22177
+ const value = permission[key];
22178
+ if (value === undefined)
22179
+ continue;
22180
+ if (typeof value === "string") {
22181
+ if (!["allow", "ask", "deny"].includes(value))
22182
+ return false;
22183
+ (value === "allow" ? rules.allow : rules.deny).push("");
22184
+ continue;
22185
+ }
22186
+ if (!isRecord4(value))
22187
+ return false;
22188
+ for (const [pattern, action] of Object.entries(value)) {
22189
+ if (action !== "allow" && action !== "ask" && action !== "deny")
22190
+ return false;
22191
+ (action === "allow" ? rules.allow : rules.deny).push(pattern);
22192
+ }
22193
+ }
22194
+ return true;
22195
+ };
22196
+ var opencodeConfig = (value, rules) => {
22197
+ if (!isRecord4(value))
22198
+ return false;
22199
+ if (!opencodePermission(value.permission, rules))
22200
+ return false;
22201
+ if (value.agent === undefined)
22202
+ return true;
22203
+ if (!isRecord4(value.agent))
22204
+ return false;
22205
+ return Object.values(value.agent).every((agent) => isRecord4(agent) && opencodePermission(agent.permission, rules));
22206
+ };
22207
+ function opencodeRules(cwd, env = process.env) {
22208
+ const rules = EMPTY();
22209
+ try {
22210
+ const config = env.XDG_CONFIG_HOME || path26.join(homeOf(env), ".config");
22211
+ const files = [
22212
+ ...OPENCODE_NAMES.map((name) => path26.join(config, "opencode", name)),
22213
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path26.join(dir, name), path26.join(dir, ".opencode", name)])),
22214
+ ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
22215
+ ];
22216
+ for (const file of files) {
22217
+ const text = readText2(file);
22218
+ if (text !== null && !opencodeConfig(parseJsonc(text), rules))
22219
+ return "unreadable";
22220
+ }
22221
+ if (env.OPENCODE_CONFIG_CONTENT && !opencodeConfig(parseJsonc(env.OPENCODE_CONFIG_CONTENT), rules))
22222
+ return "unreadable";
22223
+ } catch {
22224
+ return "unreadable";
22225
+ }
22226
+ return rules;
22227
+ }
22228
+ var codexRule = (body) => {
22229
+ const open = /\bpattern\s*=\s*\[/u.exec(body);
22230
+ if (!open)
22231
+ return null;
22232
+ const words = [];
22233
+ let index = open.index + open[0].length;
22234
+ for (;; ) {
22235
+ while (/[\s,]/u.test(body[index] ?? ""))
22236
+ index += 1;
22237
+ if (body[index] === "]")
22238
+ break;
22239
+ if (body[index] !== '"')
22240
+ return null;
22241
+ const literal = /"((?:[^"\\]|\\.)*)"/y;
22242
+ literal.lastIndex = index;
22243
+ const match = literal.exec(body);
22244
+ if (!match)
22245
+ return null;
22246
+ words.push(JSON.parse(`"${match[1]}"`));
22247
+ index = literal.lastIndex;
22248
+ }
22249
+ const decision = /\bdecision\s*=\s*"(\w+)"/u.exec(body)?.[1];
22250
+ if (!words.length || !decision || !["allow", "prompt", "forbidden"].includes(decision))
22251
+ return null;
22252
+ return { pattern: words.join(" "), decision };
22253
+ };
22254
+ function codexRules(cwd, env = process.env) {
22255
+ const rules = EMPTY();
22256
+ try {
22257
+ const home = env.CODEX_HOME || path26.join(homeOf(env), ".codex");
22258
+ const top = gitTop(cwd) ?? cwd;
22259
+ for (const dir of [path26.join(home, "rules"), path26.join(top, ".codex", "rules")]) {
22260
+ let files;
22261
+ try {
22262
+ files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
22263
+ } catch (error) {
22264
+ if (error.code === "ENOENT")
22265
+ continue;
22266
+ return "unreadable";
22267
+ }
22268
+ for (const name of files) {
22269
+ const text = readText2(path26.join(dir, name)) ?? "";
22270
+ for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
22271
+ const rule = codexRule(part);
22272
+ if (!rule)
22273
+ return "unreadable";
22274
+ (rule.decision === "allow" ? rules.allow : rules.deny).push(rule.pattern);
22275
+ }
22276
+ }
22277
+ }
22278
+ } catch {
22279
+ return "unreadable";
22280
+ }
22281
+ return rules;
22282
+ }
22283
+ function cursorRules(cwd, env = process.env) {
22284
+ const rules = EMPTY();
22285
+ try {
22286
+ const top = gitTop(cwd) ?? cwd;
22287
+ for (const file of [
22288
+ path26.join(homeOf(env), ".cursor", "cli-config.json"),
22289
+ path26.join(top, ".cursor", "cli.json")
22290
+ ]) {
22291
+ const text = readText2(file);
22292
+ if (text === null)
22293
+ continue;
22294
+ const value = JSON.parse(text);
22295
+ if (!isRecord4(value))
22296
+ return "unreadable";
22297
+ if (value.permissions === undefined)
22298
+ continue;
22299
+ if (!isRecord4(value.permissions))
22300
+ return "unreadable";
22301
+ for (const [key, bucket] of [
22302
+ ["deny", rules.deny],
22303
+ ["allow", rules.allow]
22304
+ ]) {
22305
+ const list = value.permissions[key];
22306
+ if (list === undefined)
22307
+ continue;
22308
+ if (!Array.isArray(list))
22309
+ return "unreadable";
22310
+ for (const entry of list) {
22311
+ if (typeof entry !== "string")
22312
+ return "unreadable";
22313
+ const match = /^Shell(?:\((.*)\))?$/su.exec(entry.trim());
22314
+ if (match)
22315
+ bucket.push(match[1] ?? "");
22316
+ }
22317
+ }
22318
+ }
22319
+ } catch {
22320
+ return "unreadable";
22321
+ }
22322
+ return rules;
22323
+ }
22324
+ var baseName = (word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "");
22325
+ var firstToken = (pattern) => pattern.trim().split(/[:\s]/u)[0] ?? "";
22326
+ function names(pattern, words) {
22327
+ const token = firstToken(pattern);
22328
+ if (!token || token === "*" || token.includes("*"))
22329
+ return true;
22330
+ const wanted = baseName(token);
22331
+ return words.some((word) => baseName(word) === wanted);
22332
+ }
22333
+ var literalPrefix = (pattern) => pattern.trim().replace(/(?::\*| \*|\*)$/u, "");
22334
+ var coversWrapper = (pattern) => {
22335
+ const token = firstToken(pattern);
22336
+ if (!token || token.includes("*"))
22337
+ return true;
22338
+ const words = literalPrefix(pattern).split(/\s+/u);
22339
+ if (baseName(words[0]) !== "workit")
22340
+ return false;
22341
+ return ["check", "--heavy"].slice(0, words.length - 1).every((word, at) => {
22342
+ const given = words[at + 1];
22343
+ return given === word || word.startsWith(given);
22344
+ });
22345
+ };
22346
+ var coversCommand = (pattern, command) => {
22347
+ const prefix = literalPrefix(pattern);
22348
+ if (!prefix)
22349
+ return true;
22350
+ return !prefix.includes("*") && command.trim().startsWith(prefix);
22351
+ };
22352
+ function wrapAllowed(read, words, command, options = {}) {
22353
+ if (read === "unreadable")
22354
+ return { ok: false, why: "named" };
22355
+ if (read.deny.some((pattern) => names(pattern, words)))
22356
+ return { ok: false, why: "named" };
22357
+ if (!options.bypass && read.allow.some(coversWrapper) && !read.allow.some((pattern) => coversCommand(pattern, command)))
22358
+ return { ok: false, why: "allowlisted" };
22359
+ return { ok: true };
22360
+ }
22361
+
22362
+ // packages/workit-core/src/core/heavy.ts
22363
+ import fs19 from "node:fs";
21976
22364
  import os7 from "node:os";
21977
22365
  import path27 from "node:path";
22366
+ function stateDir(env, platform = process.platform) {
22367
+ if (env.WORKFLOW_TOOLKIT_STATE)
22368
+ return env.WORKFLOW_TOOLKIT_STATE;
22369
+ if (env.XDG_STATE_HOME)
22370
+ return path27.join(env.XDG_STATE_HOME, "workit");
22371
+ const home = env.HOME || os7.homedir();
22372
+ if (platform === "darwin")
22373
+ return path27.join(home, "Library", "Application Support", "workit");
22374
+ if (platform === "win32")
22375
+ return path27.join(env.LOCALAPPDATA ?? path27.join(home, "AppData", "Local"), "workit");
22376
+ return path27.join(home, ".local", "state", "workit");
22377
+ }
22378
+ var HEAVY_COMMANDS_FILE = "heavy-commands.json";
22379
+ var heavyCommandsFile = (env) => path27.join(stateDir(env), HEAVY_COMMANDS_FILE);
22380
+ var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
22381
+ var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
22382
+ var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
22383
+ var WATCH_SCRIPT = /(?:^|[:_.-])(?:watch|dev|serve|start|storybook)(?:$|[:_.-])/iu;
22384
+ var sub = (...names) => new Set(names);
22385
+ var TOOLS = {
22386
+ jest: "any",
22387
+ vitest: "any",
22388
+ mocha: "any",
22389
+ ava: "any",
22390
+ tap: "any",
22391
+ karma: "any",
22392
+ jasmine: "any",
22393
+ nyc: "any",
22394
+ c8: "any",
22395
+ tsc: "any",
22396
+ "vue-tsc": "any",
22397
+ "svelte-check": "any",
22398
+ eslint: "any",
22399
+ stylelint: "any",
22400
+ tslint: "any",
22401
+ webpack: "any",
22402
+ "webpack-cli": "any",
22403
+ rollup: "any",
22404
+ parcel: "any",
22405
+ tsup: "any",
22406
+ vite: sub("build", "optimize"),
22407
+ next: sub("build", "lint"),
22408
+ nuxt: sub("build", "generate"),
22409
+ nuxi: sub("build", "generate"),
22410
+ astro: sub("build", "check"),
22411
+ ng: sub("build", "b", "test", "t", "e2e", "lint"),
22412
+ "react-scripts": sub("build", "test"),
22413
+ playwright: sub("test", "install"),
22414
+ cypress: sub("run"),
22415
+ wdio: "any",
22416
+ testcafe: "any",
22417
+ turbo: "any",
22418
+ lerna: sub("run", "exec", "bootstrap"),
22419
+ "sonar-scanner": "any",
22420
+ sonarless: sub("scan"),
22421
+ "sonarless-mcp": sub("scan"),
22422
+ go: sub("test", "build", "vet", "install", "generate"),
22423
+ "golangci-lint": sub("run"),
22424
+ gotestsum: "any",
22425
+ staticcheck: "any",
22426
+ cargo: sub("build", "b", "test", "t", "check", "c", "clippy", "bench", "nextest", "doc", "install", "tarpaulin", "llvm-cov"),
22427
+ mvn: "any",
22428
+ mvnw: "any",
22429
+ gradle: "any",
22430
+ gradlew: "any",
22431
+ ant: "any",
22432
+ sbt: "any",
22433
+ bazel: sub("build", "test", "run", "coverage"),
22434
+ bazelisk: sub("build", "test", "run", "coverage"),
22435
+ buck2: sub("build", "test", "run"),
22436
+ dotnet: sub("build", "test", "publish", "restore", "pack", "msbuild"),
22437
+ msbuild: "any",
22438
+ pytest: "any",
22439
+ "py.test": "any",
22440
+ tox: "any",
22441
+ nox: "any",
22442
+ mypy: "any",
22443
+ pyright: "any",
22444
+ pylint: "any",
22445
+ coverage: sub("run"),
22446
+ maturin: sub("build", "develop"),
22447
+ pip: sub("install", "wheel"),
22448
+ pip3: sub("install", "wheel"),
22449
+ rspec: "any",
22450
+ rails: sub("test", "assets:precompile"),
22451
+ phpunit: "any",
22452
+ pest: "any",
22453
+ phpstan: "any",
22454
+ psalm: "any",
22455
+ composer: sub("install", "update", "require", "test"),
22456
+ mix: sub("test", "compile", "deps.get", "dialyzer", "credo"),
22457
+ swift: sub("build", "test"),
22458
+ xcodebuild: "any",
22459
+ flutter: sub("build", "test"),
22460
+ dart: sub("test", "compile"),
22461
+ pod: sub("install"),
22462
+ cmake: sub("--build"),
22463
+ ninja: "any",
22464
+ meson: sub("compile", "test"),
22465
+ ctest: "any",
22466
+ scons: "any",
22467
+ "docker-compose": sub("build"),
22468
+ buildah: sub("bud", "build"),
22469
+ earthly: "any"
22470
+ };
22471
+ var PM_VALUES = new Set([
22472
+ "--prefix",
22473
+ "-w",
22474
+ "--workspace",
22475
+ "-C",
22476
+ "--dir",
22477
+ "--filter",
22478
+ "-F",
22479
+ "--cwd",
22480
+ "-p",
22481
+ "--package",
22482
+ "--cache",
22483
+ "--registry"
22484
+ ]);
22485
+ function positional2(words, from, values = PM_VALUES) {
22486
+ let index = from;
22487
+ while (index < words.length && words[index].startsWith("-")) {
22488
+ if (words[index] === "--")
22489
+ return index + 1;
22490
+ index += values.has(words[index]) ? 2 : 1;
22491
+ }
22492
+ return index;
22493
+ }
22494
+ var baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, "");
22495
+ var WORKIT = /^(?:workit|@brainervirus\/workit-cli(?:@.*)?)$/u;
22496
+ var PYTHONS = /^(?:python[\d.]*|py)$/u;
22497
+ var NPM_INSTALL = new Set([
22498
+ "ci",
22499
+ "clean-install",
22500
+ "cit",
22501
+ "install-test",
22502
+ "it",
22503
+ "install",
22504
+ "i",
22505
+ "in",
22506
+ "add",
22507
+ "rebuild",
22508
+ "update",
22509
+ "up"
22510
+ ]);
22511
+ var NPM_RUN = new Set(["run", "run-script", "rum", "urn"]);
22512
+ var NPM_TEST = new Set(["test", "t", "tst"]);
22513
+ var EXEC = new Set(["exec", "x", "dlx"]);
22514
+ function builtIn(words, depth = 0) {
22515
+ if (depth > 6 || !words.length)
22516
+ return null;
22517
+ const name = baseName2(words[0]);
22518
+ const label = () => words.slice(0, 3).join(" ");
22519
+ const inner = (from) => builtIn(words.slice(from), depth + 1);
22520
+ if (WORKIT.test(name))
22521
+ return null;
22522
+ if (INFO_FLAGS.has(words[1] ?? "") && words.length === 2)
22523
+ return null;
22524
+ if (words.some((word) => word === "--watch" || word.startsWith("--watch=") || word === "--watchAll"))
22525
+ return null;
22526
+ if (name === "npx" || name === "bunx" || name === "pnpx")
22527
+ return inner(positional2(words, 1));
22528
+ if (name === "corepack")
22529
+ return inner(1);
22530
+ if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" || name === "deno") {
22531
+ const at = positional2(words, 1);
22532
+ const command = words[at];
22533
+ if (command === undefined)
22534
+ return name === "yarn" ? "yarn (install)" : null;
22535
+ if (name === "deno") {
22536
+ if (["test", "bench", "check", "compile", "lint"].includes(command))
22537
+ return label();
22538
+ if (command === "task" && HEAVY_SCRIPT.test(words[at + 1] ?? ""))
22539
+ return label();
22540
+ return null;
22541
+ }
22542
+ if (NPM_INSTALL.has(command) || NPM_TEST.has(command))
22543
+ return label();
22544
+ if (name === "bun" && command === "build")
22545
+ return label();
22546
+ if (EXEC.has(command))
22547
+ return inner(positional2(words, at + 1));
22548
+ if (name === "yarn" && command === "workspace")
22549
+ return inner(at + 2);
22550
+ if (name === "yarn" && command === "workspaces")
22551
+ return words.slice(at + 1).some((word) => HEAVY_SCRIPT.test(word)) ? label() : null;
22552
+ if (NPM_RUN.has(command)) {
22553
+ const script = words[positional2(words, at + 1)];
22554
+ if (script === undefined || WATCH_SCRIPT.test(script))
22555
+ return null;
22556
+ if (HEAVY_SCRIPT.test(script))
22557
+ return label();
22558
+ return name === "bun" ? inner(positional2(words, at + 1)) : null;
22559
+ }
22560
+ if (name === "npm")
22561
+ return null;
22562
+ if (WATCH_SCRIPT.test(command))
22563
+ return null;
22564
+ if (HEAVY_SCRIPT.test(command) && !/\.[cm]?[jt]sx?$/u.test(command))
22565
+ return label();
22566
+ return inner(at);
22567
+ }
22568
+ if (PYTHONS.test(name)) {
22569
+ const at = words.indexOf("-m");
22570
+ return at > 0 && words[at + 1] ? inner(at + 1) : null;
22571
+ }
22572
+ if (["uv", "poetry", "pdm", "pipenv", "hatch", "rye"].includes(name)) {
22573
+ const at = positional2(words, 1);
22574
+ const command = words[at];
22575
+ if (command === "run")
22576
+ return inner(positional2(words, at + 1));
22577
+ if (command === "pip")
22578
+ return inner(at);
22579
+ return ["sync", "install", "build", "test", "add"].includes(command ?? "") ? label() : null;
22580
+ }
22581
+ if (name === "bundle") {
22582
+ const command = words[positional2(words, 1)];
22583
+ if (command === "exec")
22584
+ return inner(positional2(words, 2));
22585
+ return command === "install" || command === undefined ? label() : null;
22586
+ }
22587
+ if (name === "make" || name === "gmake") {
22588
+ const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
22589
+ if (targets.some((target) => WATCH_SCRIPT.test(target)))
22590
+ return null;
22591
+ return !targets.length || targets.some((target) => HEAVY_SCRIPT.test(target)) ? label() : null;
22592
+ }
22593
+ if (name === "rake") {
22594
+ const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
22595
+ return targets.some((target) => HEAVY_SCRIPT.test(target) || /^spec(?:$|:)/u.test(target)) ? label() : null;
22596
+ }
22597
+ if (name === "nx") {
22598
+ const at = positional2(words, 1);
22599
+ const command = words[at] ?? "";
22600
+ if (["run-many", "affected"].includes(command))
22601
+ return words.some((word) => /^(?:-t|--targets?)(?:=|$)/u.test(word)) && !words.some((word) => WATCH_SCRIPT.test(word.replace(/^--?targets?=/u, ""))) ? label() : null;
22602
+ if (command === "run") {
22603
+ const target = (words[at + 1] ?? "").split(":")[1] ?? "";
22604
+ return HEAVY_SCRIPT.test(target) && !WATCH_SCRIPT.test(target) ? label() : null;
22605
+ }
22606
+ return HEAVY_SCRIPT.test(command) && !WATCH_SCRIPT.test(command) ? label() : null;
22607
+ }
22608
+ if (name === "docker" || name === "podman" || name === "nerdctl") {
22609
+ const at = positional2(words, 1, new Set(["-H", "--host", "--context", "-c", "--config"]));
22610
+ const command = words[at];
22611
+ if (command === "build")
22612
+ return label();
22613
+ if (command === "buildx" || command === "image" || command === "builder")
22614
+ return words[positional2(words, at + 1)] === "build" ? label() : null;
22615
+ if (command === "compose") {
22616
+ const at2 = positional2(words, at + 1, new Set(["-f", "--file", "-p"]));
22617
+ const action = words[at2];
22618
+ if (action === "build")
22619
+ return label();
22620
+ return action === "up" && words.slice(at2 + 1).includes("--build") ? label() : null;
22621
+ }
22622
+ return null;
22623
+ }
22624
+ const rule = TOOLS[name];
22625
+ if (!rule)
22626
+ return null;
22627
+ if (words.slice(1).some((word) => WATCH_FLAG.test(word) || word === "watch" || word === "dev"))
22628
+ return null;
22629
+ if (rule === "any")
22630
+ return label();
22631
+ const command = words.slice(1).find((word) => !word.startsWith("-") && !word.startsWith("+") || rule.has(word));
22632
+ return command !== undefined && rule.has(command) ? label() : null;
22633
+ }
22634
+ var globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u");
22635
+ function parsePattern(pattern) {
22636
+ const words = pattern.trim().split(/\s+/u).filter(Boolean);
22637
+ return words.length ? words.map(globWord) : null;
22638
+ }
22639
+ var matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index]));
22640
+ var readJson = (file) => {
22641
+ try {
22642
+ return JSON.parse(fs19.readFileSync(file, "utf8"));
22643
+ } catch {
22644
+ return;
22645
+ }
22646
+ };
22647
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22648
+ var stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
22649
+ function machinePatterns(env) {
22650
+ const value = readJson(heavyCommandsFile(env));
22651
+ return isRecord5(value) ? stringList(value.patterns) : [];
22652
+ }
22653
+ function repoPatterns(cwd) {
22654
+ return repoHeavyCommands(gitTop(cwd) ?? path27.resolve(cwd));
22655
+ }
22656
+ function heavyLines(lines, options) {
22657
+ const found = [];
22658
+ let patterns = null;
22659
+ for (const line of lines) {
22660
+ const reason = builtIn(line);
22661
+ if (reason) {
22662
+ found.push({ line, source: "catalog", reason });
22663
+ continue;
22664
+ }
22665
+ patterns ??= (options.patterns ?? [
22666
+ ...machinePatterns(options.env ?? process.env).map((pattern) => ({
22667
+ pattern,
22668
+ from: "machine"
22669
+ })),
22670
+ ...repoPatterns(options.cwd).map((pattern) => ({ pattern, from: "repo" }))
22671
+ ]).flatMap((entry) => {
22672
+ const matchers = parsePattern(entry.pattern);
22673
+ return matchers ? [{ ...entry, matchers }] : [];
22674
+ });
22675
+ const hit = patterns.find((entry) => matches(entry.matchers, line));
22676
+ if (hit)
22677
+ found.push({
22678
+ line,
22679
+ source: { pattern: hit.pattern, from: hit.from },
22680
+ reason: `${hit.from} pattern "${hit.pattern}"`
22681
+ });
22682
+ }
22683
+ return found;
22684
+ }
22685
+ var linesOf = (command, dialect) => {
22686
+ try {
22687
+ return commandLines(command, dialect);
22688
+ } catch {
22689
+ return null;
22690
+ }
22691
+ };
22692
+ var quoteWord = (word) => /^[\w@%+=:,./-]+$/u.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
22693
+ var leaseCommand = (words) => `workit check --heavy -- ${words.map(quoteWord).join(" ")}`;
22694
+ function heavyPlan(command, options) {
22695
+ const dialect = options.dialect ?? "posix";
22696
+ if (dialect !== "posix" || !gitTop(options.cwd))
22697
+ return { kind: "none" };
22698
+ const lines = linesOf(command, dialect);
22699
+ if (!lines)
22700
+ return { kind: "none" };
22701
+ const heavy = heavyLines(lines, options);
22702
+ if (!heavy.length)
22703
+ return { kind: "none" };
22704
+ const simple = simpleCommand(command, dialect);
22705
+ const single = simple && !WORKIT.test(baseName2(simple[0])) && heavyLines([simple], options)[0];
22706
+ const judged = single ? [{ words: lines.flat(), text: command }] : heavy.map((match) => ({
22707
+ words: match.line,
22708
+ text: match.line.join(" ")
22709
+ }));
22710
+ if (options.rules !== null)
22711
+ for (const { words, text } of judged) {
22712
+ const verdict = wrapAllowed(options.rules, words, text, { bypass: options.bypass });
22713
+ if (!verdict.ok)
22714
+ return {
22715
+ kind: "passthrough",
22716
+ note: `workit: \`${command.trim()}\` is heavy but runs outside the heavy-check lease because a host permission rule names it (or \`workit\` is allowlisted); the host decides.`
22717
+ };
22718
+ }
22719
+ if (single) {
22720
+ const rewrite = leaseRewrite(command);
22721
+ return rewrite ? { kind: "rewrite", command: rewrite, reason: single.reason } : { kind: "none" };
22722
+ }
22723
+ const parts = heavy.map((match) => leaseCommand(match.line));
22724
+ return {
22725
+ kind: "split",
22726
+ reason: `heavy_command: \`${command.trim()}\` chains heavy work (${heavy.map((match) => match.reason).join("; ")}) with other commands, and a chain cannot run under the machine-wide heavy-check lease. Run each heavy part alone as \`workit check --heavy -- <segment>\` (here: ${parts.map((part) => `\`${part}\``).join("; ")}); run the other parts as their own commands.`,
22727
+ unblock: parts[0] ?? null
22728
+ };
22729
+ }
22730
+ function workitOnPath2(env = process.env) {
22731
+ const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
22732
+ for (const dir of (env.PATH ?? "").split(path27.delimiter))
22733
+ for (const name of names) {
22734
+ if (!dir)
22735
+ continue;
22736
+ try {
22737
+ fs19.accessSync(path27.join(dir, name), fs19.constants.X_OK);
22738
+ return true;
22739
+ } catch {}
22740
+ }
22741
+ return false;
22742
+ }
22743
+ var ASSIGNMENTS = /^\s*((?:[A-Za-z_]\w*=(?:'[^']*'|"(?:[^"\\]|\\.)*"|[^\s'"])*\s+)*)/u;
22744
+ function leaseRewrite(command) {
22745
+ const text = command.trim();
22746
+ const prefix = ASSIGNMENTS.exec(text)?.[1] ?? "";
22747
+ const rest = text.slice(prefix.length).trim();
22748
+ if (!rest)
22749
+ return null;
22750
+ return `${prefix}workit check --heavy -- ${rest}`;
22751
+ }
22752
+
22753
+ // packages/workit-core/src/core/heavy-words.ts
22754
+ import fs20 from "node:fs";
22755
+ import os8 from "node:os";
22756
+ import path28 from "node:path";
22757
+ var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
22758
+ var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
22759
+ var stateDir2 = (env) => {
22760
+ if (env.WORKFLOW_TOOLKIT_STATE)
22761
+ return env.WORKFLOW_TOOLKIT_STATE;
22762
+ if (env.XDG_STATE_HOME)
22763
+ return path28.join(env.XDG_STATE_HOME, "workit");
22764
+ const home = env.HOME || os8.homedir();
22765
+ if (process.platform === "darwin")
22766
+ return path28.join(home, "Library", "Application Support", "workit");
22767
+ if (process.platform === "win32")
22768
+ return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
22769
+ return path28.join(home, ".local", "state", "workit");
22770
+ };
22771
+ var readJson2 = (file) => {
22772
+ try {
22773
+ return JSON.parse(fs20.readFileSync(file, "utf8"));
22774
+ } catch {
22775
+ return;
22776
+ }
22777
+ };
22778
+ var patternWords = (cwd, env) => {
22779
+ const patterns = [];
22780
+ const machine = readJson2(path28.join(stateDir2(env), "heavy-commands.json"));
22781
+ if (Array.isArray(machine?.patterns))
22782
+ patterns.push(...machine.patterns);
22783
+ for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
22784
+ if (fs20.existsSync(path28.join(dir, ".git"))) {
22785
+ patterns.push(...repoHeavyCommands(dir));
22786
+ break;
22787
+ }
22788
+ if (path28.dirname(dir) === dir)
22789
+ break;
22790
+ }
22791
+ const words = new Set;
22792
+ for (const pattern of patterns) {
22793
+ if (typeof pattern !== "string")
22794
+ continue;
22795
+ const first = wordsOf(pattern)[0];
22796
+ if (!first || first.includes("*"))
22797
+ return null;
22798
+ words.add(first);
22799
+ }
22800
+ return words;
22801
+ };
22802
+ function maybeHeavy(command, cwd, env = process.env) {
22803
+ try {
22804
+ const words = wordsOf(command);
22805
+ if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word)))
22806
+ return true;
22807
+ const extra = patternWords(cwd, env);
22808
+ return extra === null || words.some((word) => extra.has(word));
22809
+ } catch {
22810
+ return true;
22811
+ }
22812
+ }
22813
+
22814
+ // packages/workit-core/src/hooks/heavy-gate.ts
22815
+ var rulesFor = (host, cwd, env) => {
22816
+ switch (host) {
22817
+ case "claude_code":
22818
+ return claudeRules(cwd, env);
22819
+ case "opencode":
22820
+ return opencodeRules(cwd, env);
22821
+ case "codex_cli":
22822
+ case "codex_desktop":
22823
+ return codexRules(cwd, env);
22824
+ case "cursor":
22825
+ return cursorRules(cwd, env);
22826
+ case "pi":
22827
+ return null;
22828
+ }
22829
+ };
22830
+ function heavyGate(cwd, command, options) {
22831
+ const env = options.env ?? process.env;
22832
+ try {
22833
+ if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath2(env))
22834
+ return { kind: "none" };
22835
+ return heavyPlan(command, {
22836
+ cwd,
22837
+ env,
22838
+ dialect: options.dialect ?? "posix",
22839
+ rules: rulesFor(options.host, cwd, env),
22840
+ bypass: options.permissionMode === "bypassPermissions"
22841
+ });
22842
+ } catch {
22843
+ return { kind: "none" };
22844
+ }
22845
+ }
22846
+ // packages/workit-core/src/hooks/write-gate.ts
22847
+ import { realpathSync as realpathSync6 } from "node:fs";
22848
+ import os10 from "node:os";
22849
+ import path30 from "node:path";
21978
22850
 
21979
22851
  // packages/workit-core/src/core/live-session.ts
21980
22852
  import { spawnSync as spawnSync17 } from "node:child_process";
21981
22853
  import { createHash as createHash14 } from "node:crypto";
21982
- import fs18 from "node:fs";
21983
- import os6 from "node:os";
21984
- import path26 from "node:path";
22854
+ import fs21 from "node:fs";
22855
+ import os9 from "node:os";
22856
+ import path29 from "node:path";
21985
22857
  var LIVE_SESSION_TTL_MS = 15 * 60000;
21986
22858
  var HEARTBEAT_EVERY_MS = 30000;
21987
22859
  var liveProbe = {
@@ -22002,9 +22874,9 @@ var processAlive = (pid) => {
22002
22874
  var processInfo = (pid) => {
22003
22875
  try {
22004
22876
  if (liveProbe.platform === "linux") {
22005
- const stat = fs18.readFileSync(`/proc/${pid}/stat`, "utf8");
22877
+ const stat = fs21.readFileSync(`/proc/${pid}/stat`, "utf8");
22006
22878
  const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
22007
- const command = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
22879
+ const command = fs21.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
22008
22880
  return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
22009
22881
  }
22010
22882
  if (liveProbe.platform === "win32")
@@ -22055,35 +22927,35 @@ var liveOwner = (input, transport, env = process.env) => {
22055
22927
  };
22056
22928
  };
22057
22929
  var checkoutOf = (target) => {
22058
- let dir = path26.resolve(target);
22059
- while (!fs18.existsSync(dir) && path26.dirname(dir) !== dir)
22060
- dir = path26.dirname(dir);
22061
- if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
22062
- dir = path26.dirname(dir);
22063
- const parts = dir.split(path26.sep);
22930
+ let dir = path29.resolve(target);
22931
+ while (!fs21.existsSync(dir) && path29.dirname(dir) !== dir)
22932
+ dir = path29.dirname(dir);
22933
+ if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
22934
+ dir = path29.dirname(dir);
22935
+ const parts = dir.split(path29.sep);
22064
22936
  const dotGit = parts.lastIndexOf(".git");
22065
22937
  if (dotGit > 0)
22066
- dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
22938
+ dir = parts.slice(0, dotGit).join(path29.sep) || path29.sep;
22067
22939
  const location = resolveStore(dir);
22068
22940
  if (location instanceof Error || !location.shared || !location.top)
22069
22941
  return null;
22070
22942
  let top = location.top;
22071
22943
  try {
22072
- top = fs18.realpathSync.native(top);
22944
+ top = fs21.realpathSync.native(top);
22073
22945
  } catch {}
22074
22946
  return top;
22075
22947
  };
22076
- var markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash14("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22948
+ var markerFile2 = (top) => path29.join(resolveStateDir(), "live-sessions", `${createHash14("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22077
22949
  var checkoutKey = (top, platform = process.platform) => {
22078
- let key = path26.resolve(top);
22950
+ let key = path29.resolve(top);
22079
22951
  try {
22080
- key = fs18.realpathSync.native(key);
22952
+ key = fs21.realpathSync.native(key);
22081
22953
  } catch {}
22082
22954
  return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
22083
22955
  };
22084
22956
  var readMarker = (file) => {
22085
22957
  try {
22086
- const value = JSON.parse(fs18.readFileSync(file, "utf8"));
22958
+ const value = JSON.parse(fs21.readFileSync(file, "utf8"));
22087
22959
  if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
22088
22960
  return null;
22089
22961
  return {
@@ -22106,7 +22978,7 @@ var isLive = (marker, deps) => {
22106
22978
  const age = ageOf2(marker, deps.now ?? new Date);
22107
22979
  if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
22108
22980
  return false;
22109
- if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
22981
+ if (marker.machine !== (deps.machine ?? os9.hostname()) || marker.pid === null)
22110
22982
  return true;
22111
22983
  if (!(deps.alive ?? processAlive)(marker.pid))
22112
22984
  return false;
@@ -22135,7 +23007,7 @@ var liveConflict = (target, owner, deps = {}) => {
22135
23007
  const marker = readMarker(file);
22136
23008
  if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
22137
23009
  return null;
22138
- if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
23010
+ if (sameOwner(marker, owner, deps.machine ?? os9.hostname()))
22139
23011
  return null;
22140
23012
  return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
22141
23013
  } catch {
@@ -22149,7 +23021,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
22149
23021
  return;
22150
23022
  const file = markerFile2(checkout);
22151
23023
  const now = deps.now ?? new Date;
22152
- const machine = deps.machine ?? os6.hostname();
23024
+ const machine = deps.machine ?? os9.hostname();
22153
23025
  const current = readMarker(file);
22154
23026
  if (current) {
22155
23027
  const age = ageOf2(current, now);
@@ -22170,10 +23042,10 @@ var claimLiveSession = (target, owner, deps = {}) => {
22170
23042
  checkout,
22171
23043
  heartbeatAt: now.toISOString()
22172
23044
  };
22173
- fs18.mkdirSync(path26.dirname(file), { recursive: true });
23045
+ fs21.mkdirSync(path29.dirname(file), { recursive: true });
22174
23046
  const temp = `${file}.${process.pid}.tmp`;
22175
- fs18.writeFileSync(temp, JSON.stringify(marker));
22176
- fs18.renameSync(temp, file);
23047
+ fs21.writeFileSync(temp, JSON.stringify(marker));
23048
+ fs21.renameSync(temp, file);
22177
23049
  } catch {}
22178
23050
  };
22179
23051
 
@@ -22182,27 +23054,42 @@ var NONE3 = { kind: "none" };
22182
23054
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
22183
23055
  var canonical2 = (file) => {
22184
23056
  let rest = "";
22185
- for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
23057
+ for (let dir = path30.resolve(file);; dir = path30.dirname(dir)) {
22186
23058
  try {
22187
- return path27.join(realpathSync6(dir), rest);
23059
+ return path30.join(realpathSync6(dir), rest);
22188
23060
  } catch {
22189
- if (path27.dirname(dir) === dir)
22190
- return path27.resolve(file);
22191
- rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
23061
+ if (path30.dirname(dir) === dir)
23062
+ return path30.resolve(file);
23063
+ rest = rest ? path30.join(path30.basename(dir), rest) : path30.basename(dir);
22192
23064
  }
22193
23065
  }
22194
23066
  };
22195
23067
  var exempt = (cwd, root, file, plans) => {
22196
23068
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
22197
- const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
22198
- if (relative.startsWith("..") || path27.isAbsolute(relative))
23069
+ const relative = path30.relative(fold(canonical2(root)), fold(canonical2(path30.resolve(cwd, file))));
23070
+ if (relative.startsWith("..") || path30.isAbsolute(relative))
22199
23071
  return true;
22200
- const posix = relative.split(path27.sep).join("/");
23072
+ const posix = relative.split(path30.sep).join("/");
22201
23073
  const segments = posix.split("/");
22202
23074
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
22203
23075
  };
22204
23076
  var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
22205
23077
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
23078
+ var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
23079
+ var wrappedScript = (input) => {
23080
+ let words = input;
23081
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
23082
+ words = words.slice(1);
23083
+ const tail = workitCheckTail(words);
23084
+ if (!tail)
23085
+ return null;
23086
+ if ("script" in tail)
23087
+ return tail.script;
23088
+ const [shell, flag, script] = tail.argv;
23089
+ if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
23090
+ return script ?? null;
23091
+ return tail.argv.map(quoteWord2).join(" ");
23092
+ };
22206
23093
  var editsOf = (input) => {
22207
23094
  let words = input;
22208
23095
  while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
@@ -22279,10 +23166,18 @@ var editsOf = (input) => {
22279
23166
  return [];
22280
23167
  }
22281
23168
  };
22282
- var shellWrites = (command) => {
23169
+ var shellWrites = (command, depth = 0) => {
22283
23170
  const targets = [];
22284
23171
  let unknown = false;
22285
23172
  for (const segment of segmentsOf(command)) {
23173
+ const inner = depth < 4 ? wrappedScript(segment.words) : null;
23174
+ if (inner !== null) {
23175
+ const nested = shellWrites(inner, depth + 1);
23176
+ if (nested.writes && nested.targets === null)
23177
+ unknown = true;
23178
+ else if (nested.targets)
23179
+ targets.push(...nested.targets);
23180
+ }
22286
23181
  targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
22287
23182
  const edits = editsOf(segment.words);
22288
23183
  if (edits === null)
@@ -22295,7 +23190,7 @@ var shellWrites = (command) => {
22295
23190
  return { writes: true, targets: unknown ? null : targets };
22296
23191
  };
22297
23192
  var cache2 = new Map;
22298
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00");
23193
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path30.resolve(root, file)) ?? "-").join("\x00");
22299
23194
  var gateFor = (cwd) => {
22300
23195
  const store = new TaskStore(cwd);
22301
23196
  const listed = store.listTaskIndex();
@@ -22374,7 +23269,7 @@ var gitMutationDirs = (cwd, command, dialect) => {
22374
23269
  return [];
22375
23270
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
22376
23271
  return [];
22377
- return [path27.resolve(cwd, invocation.dir ?? ".")];
23272
+ return [path30.resolve(cwd, invocation.dir ?? ".")];
22378
23273
  });
22379
23274
  };
22380
23275
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
@@ -22384,15 +23279,15 @@ var cdInto = (dir, words) => {
22384
23279
  const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
22385
23280
  if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
22386
23281
  return;
22387
- return target === undefined ? os7.homedir() : path27.resolve(dir, target);
23282
+ return target === undefined ? os10.homedir() : path30.resolve(dir, target);
22388
23283
  };
22389
- var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
22390
- var segmentTargets = (cwd, command, dialect) => {
23284
+ var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
23285
+ var segmentTargets = (cwd, command, dialect, depth = 0) => {
22391
23286
  const targets = [];
22392
23287
  let dir = cwd;
22393
23288
  let stack = [];
22394
23289
  const groups = [];
22395
- const at = (file) => path27.isAbsolute(file) ? file : dir === undefined ? null : path27.resolve(dir, file);
23290
+ const at = (file) => path30.isAbsolute(file) ? file : dir === undefined ? null : path30.resolve(dir, file);
22396
23291
  for (const segment of segmentsOf(command, dialect)) {
22397
23292
  if (segment.group === "open") {
22398
23293
  groups.push({ dir, stack: [...stack] });
@@ -22420,6 +23315,9 @@ var segmentTargets = (cwd, command, dialect) => {
22420
23315
  dir = stack.length ? stack.pop() : undefined;
22421
23316
  continue;
22422
23317
  }
23318
+ const inner = depth < 4 ? wrappedScript(words) : null;
23319
+ if (inner !== null && dir !== undefined)
23320
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
22423
23321
  for (const file of segment.redirects)
22424
23322
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
22425
23323
  const resolved = at(file);
@@ -22429,15 +23327,15 @@ var segmentTargets = (cwd, command, dialect) => {
22429
23327
  const edits = editsOf(segment.words);
22430
23328
  if (edits === null) {
22431
23329
  if (dir !== undefined)
22432
- targets.push(path27.join(dir, DIR_TARGET));
23330
+ targets.push(path30.join(dir, DIR_TARGET));
22433
23331
  } else
22434
23332
  for (const file of edits) {
22435
23333
  const resolved = at(file);
22436
23334
  if (resolved)
22437
23335
  targets.push(resolved);
22438
23336
  }
22439
- if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
22440
- targets.push(path27.join(dir, DIR_TARGET));
23337
+ if (dir !== undefined && workitInvocations(segment.words.map(quoteWord2).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
23338
+ targets.push(path30.join(dir, DIR_TARGET));
22441
23339
  }
22442
23340
  return targets;
22443
23341
  };
@@ -22472,7 +23370,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
22472
23370
  var guarded = (targets) => {
22473
23371
  const seen = new Set;
22474
23372
  return targets.filter((target) => {
22475
- const dir = path27.dirname(target);
23373
+ const dir = path30.dirname(target);
22476
23374
  if (seen.has(dir))
22477
23375
  return false;
22478
23376
  seen.add(dir);
@@ -22493,7 +23391,7 @@ var claimAll = (owner, targets, deps) => {
22493
23391
  for (const target of guarded(targets))
22494
23392
  claimLiveSession(target, owner, deps);
22495
23393
  };
22496
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)];
23394
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path30.resolve(cwd, file)) : [path30.join(cwd, DIR_TARGET)];
22497
23395
  function writeGate(cwd, paths, owner, deps) {
22498
23396
  if (!owner)
22499
23397
  return beforeWriteGate(cwd, paths);
@@ -22513,7 +23411,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
22513
23411
  try {
22514
23412
  targets = [
22515
23413
  ...segmentTargets(cwd, command, dialect),
22516
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
23414
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path30.join(dir, DIR_TARGET))
22517
23415
  ];
22518
23416
  } catch {
22519
23417
  targets = [];
@@ -22996,7 +23894,7 @@ var OPENCODE_DESCRIPTOR = {
22996
23894
  "compact.pre": { support: "native", native: 'session.hook("compaction")' },
22997
23895
  stop: { support: "partial", native: 'event("session.idle")' }
22998
23896
  },
22999
- shellPolicy: { deny: "native", channel: "effect", failClosed: false },
23897
+ shellPolicy: { deny: "native", channel: "effect", failClosed: false, rewrite: "native" },
23000
23898
  context: {
23001
23899
  sessionStart: "none",
23002
23900
  perTurn: "native",
@@ -23045,11 +23943,11 @@ var OPENCODE_DESCRIPTOR = {
23045
23943
  ]
23046
23944
  };
23047
23945
  // packages/workit-opencode/src/shared/init-apply.ts
23048
- import path32 from "node:path";
23946
+ import path35 from "node:path";
23049
23947
 
23050
23948
  // packages/workit-core/src/core/gitignore.ts
23051
23949
  import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
23052
- import path28 from "node:path";
23950
+ import path31 from "node:path";
23053
23951
  var GITIGNORE_ENTRIES = [
23054
23952
  "# workit: SDD working state (never commit)",
23055
23953
  "docs/*/sdd/",
@@ -23069,7 +23967,7 @@ var GITIGNORE_ENTRIES = [
23069
23967
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
23070
23968
  if (!confirmed)
23071
23969
  return { ok: false, error: "confirmed: true required" };
23072
- const file = path28.join(workspaceRoot, ".gitignore");
23970
+ const file = path31.join(workspaceRoot, ".gitignore");
23073
23971
  const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
23074
23972
  const existingLines = new Set(existing.split(`
23075
23973
  `).map((l) => l.trim()).filter(Boolean));
@@ -23097,25 +23995,25 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
23097
23995
 
23098
23996
  // packages/workit-core/src/core/hygiene.ts
23099
23997
  import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
23100
- import path29 from "node:path";
23998
+ import path32 from "node:path";
23101
23999
  var repoRoot3 = assetRoot();
23102
- var templatesDir = () => path29.join(repoRoot3, "templates", "hygiene");
24000
+ var templatesDir = () => path32.join(repoRoot3, "templates", "hygiene");
23103
24001
  var packageJson = (root) => {
23104
- if (!existsSync10(path29.join(root, "package.json")))
24002
+ if (!existsSync10(path32.join(root, "package.json")))
23105
24003
  return null;
23106
24004
  try {
23107
- return JSON.parse(readFileSync7(path29.join(root, "package.json"), "utf8"));
24005
+ return JSON.parse(readFileSync7(path32.join(root, "package.json"), "utf8"));
23108
24006
  } catch {
23109
24007
  return null;
23110
24008
  }
23111
24009
  };
23112
24010
  var isOpenSource = (root) => {
23113
- if (existsSync10(path29.join(root, "LICENSE")))
24011
+ if (existsSync10(path32.join(root, "LICENSE")))
23114
24012
  return true;
23115
24013
  const pkg = packageJson(root);
23116
24014
  if (pkg && !pkg.private)
23117
24015
  return true;
23118
- return path29.basename(root) === "workflow-toolkit";
24016
+ return path32.basename(root) === "workflow-toolkit";
23119
24017
  };
23120
24018
  var licenseHolder = (root) => {
23121
24019
  const pkg = packageJson(root);
@@ -23138,13 +24036,13 @@ var planHygieneFiles = (root, opts = {}) => {
23138
24036
  files.push("LICENSE", "CONTRIBUTING.md");
23139
24037
  const planned = [];
23140
24038
  for (const file of files) {
23141
- if (existsSync10(path29.join(root, file)))
24039
+ if (existsSync10(path32.join(root, file)))
23142
24040
  continue;
23143
- const tpl = path29.join(templatesDir(), file);
24041
+ const tpl = path32.join(templatesDir(), file);
23144
24042
  if (!existsSync10(tpl))
23145
24043
  continue;
23146
- const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path29.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
23147
- planned.push({ path: path29.join(root, file), content });
24044
+ const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path32.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
24045
+ planned.push({ path: path32.join(root, file), content });
23148
24046
  }
23149
24047
  return planned;
23150
24048
  };
@@ -23154,19 +24052,19 @@ var ensureHygieneFiles = (root, opts) => {
23154
24052
  const created = [];
23155
24053
  for (const planned of planHygieneFiles(root, opts)) {
23156
24054
  writeFileSync5(planned.path, planned.content, "utf8");
23157
- created.push(path29.basename(planned.path));
24055
+ created.push(path32.basename(planned.path));
23158
24056
  }
23159
24057
  return { ok: true, created };
23160
24058
  };
23161
24059
 
23162
24060
  // packages/workit-core/src/core/init.ts
23163
- import fs19 from "node:fs";
23164
- import path31 from "node:path";
24061
+ import fs22 from "node:fs";
24062
+ import path34 from "node:path";
23165
24063
  import { spawnSync as spawnSync19 } from "node:child_process";
23166
24064
 
23167
24065
  // packages/workit-core/src/core/workspace-branch-policy.ts
23168
24066
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
23169
- import path30 from "node:path";
24067
+ import path33 from "node:path";
23170
24068
  import { isDeepStrictEqual } from "node:util";
23171
24069
  var readFileSafe = (p) => {
23172
24070
  try {
@@ -23177,7 +24075,7 @@ var readFileSafe = (p) => {
23177
24075
  };
23178
24076
  function applyWorkspaceBranchPolicy(opts) {
23179
24077
  const { workspace_root, env = process.env } = opts;
23180
- const dir = path30.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
24078
+ const dir = path33.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
23181
24079
  const {
23182
24080
  status,
23183
24081
  path: wsPath,
@@ -23189,7 +24087,7 @@ function applyWorkspaceBranchPolicy(opts) {
23189
24087
  if (status === "malformed" || status === "invalid")
23190
24088
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
23191
24089
  const detection = detectBranchPolicy(workspace_root);
23192
- const name = String(env.WORKFLOW_BP_NAME ?? path30.basename(workspace_root));
24090
+ const name = String(env.WORKFLOW_BP_NAME ?? path33.basename(workspace_root));
23193
24091
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
23194
24092
  const policy = {
23195
24093
  preset: detection.preset,
@@ -23238,7 +24136,7 @@ function applyWorkspaceBranchPolicy(opts) {
23238
24136
  ok: false,
23239
24137
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
23240
24138
  };
23241
- mkdirSync4(path30.dirname(wsPath), { recursive: true });
24139
+ mkdirSync4(path33.dirname(wsPath), { recursive: true });
23242
24140
  const tmp = `${wsPath}.${process.pid}.tmp`;
23243
24141
  try {
23244
24142
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -23271,9 +24169,9 @@ function applyWorkspaceBranchPolicy(opts) {
23271
24169
 
23272
24170
  // packages/workit-core/src/core/init.ts
23273
24171
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
23274
- var readJson = (p) => {
24172
+ var readJson3 = (p) => {
23275
24173
  try {
23276
- const parsed = JSON.parse(fs19.readFileSync(p, "utf8"));
24174
+ const parsed = JSON.parse(fs22.readFileSync(p, "utf8"));
23277
24175
  return isConfigObject(parsed) ? parsed : null;
23278
24176
  } catch {
23279
24177
  return null;
@@ -23303,7 +24201,7 @@ var youtrackJsonContent = (dir) => {
23303
24201
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
23304
24202
  return {
23305
24203
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
23306
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path31.join(dir, "youtrack.token"),
24204
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path34.join(dir, "youtrack.token"),
23307
24205
  ...meetingIssue ? {
23308
24206
  meetingIssue,
23309
24207
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -23333,19 +24231,19 @@ var vcsJsonContent = () => {
23333
24231
  };
23334
24232
  function initApplyData(action, env = process.env) {
23335
24233
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
23336
- fs19.mkdirSync(dir, { recursive: true });
24234
+ fs22.mkdirSync(dir, { recursive: true });
23337
24235
  switch (action) {
23338
24236
  case "youtrack_json": {
23339
- const out = path31.join(dir, "youtrack.json");
23340
- fs19.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
24237
+ const out = path34.join(dir, "youtrack.json");
24238
+ fs22.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23341
24239
  `, "utf8");
23342
24240
  return { action, ok: true, path: out };
23343
24241
  }
23344
24242
  case "youtrack_token_placeholder": {
23345
- const p = path31.join(dir, "youtrack.token");
24243
+ const p = path34.join(dir, "youtrack.token");
23346
24244
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
23347
24245
  `, 384) === "preserved";
23348
- const abs = path31.resolve(p);
24246
+ const abs = path34.resolve(p);
23349
24247
  return {
23350
24248
  action,
23351
24249
  ok: true,
@@ -23357,19 +24255,19 @@ function initApplyData(action, env = process.env) {
23357
24255
  };
23358
24256
  }
23359
24257
  case "youtrack_scaffold": {
23360
- const jsonOut = path31.join(dir, "youtrack.json");
23361
- const tokenOut = path31.join(dir, "youtrack.token");
23362
- fs19.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
24258
+ const jsonOut = path34.join(dir, "youtrack.json");
24259
+ const tokenOut = path34.join(dir, "youtrack.token");
24260
+ fs22.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23363
24261
  `, "utf8");
23364
24262
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
23365
24263
  `, 384) === "preserved";
23366
- const configPath = path31.resolve(jsonOut);
23367
- const tokenPath = path31.resolve(tokenOut);
24264
+ const configPath = path34.resolve(jsonOut);
24265
+ const tokenPath = path34.resolve(tokenOut);
23368
24266
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
23369
24267
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
23370
24268
  let tokenCreate = {};
23371
24269
  try {
23372
- const cfg = readJson(configPath) ?? {};
24270
+ const cfg = readJson3(configPath) ?? {};
23373
24271
  const base = String(cfg.baseUrl ?? "").replace(/\/+$/, "");
23374
24272
  const meeting = String(cfg.meetingIssue ?? "");
23375
24273
  tokenCreate = youTrackTokenCreateUrl().data;
@@ -23416,14 +24314,14 @@ function initApplyData(action, env = process.env) {
23416
24314
  }
23417
24315
  }
23418
24316
  case "vcs_scaffold": {
23419
- const jsonOut = path31.join(dir, "vcs.json");
23420
- fs19.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
24317
+ const jsonOut = path34.join(dir, "vcs.json");
24318
+ fs22.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
23421
24319
  `, "utf8");
23422
- const configPath = path31.resolve(jsonOut);
24320
+ const configPath = path34.resolve(jsonOut);
23423
24321
  const prev = process.env.WORKFLOW_VCS_CONFIG;
23424
24322
  process.env.WORKFLOW_VCS_CONFIG = configPath;
23425
24323
  try {
23426
- const cfg = readJson(configPath) ?? {};
24324
+ const cfg = readJson3(configPath) ?? {};
23427
24325
  const rawProvider = cfg.provider;
23428
24326
  const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
23429
24327
  return {
@@ -23538,7 +24436,7 @@ var executeInitApply = (args, directory, runtime) => {
23538
24436
  protectedNames: args.branch_policy_protected
23539
24437
  }, current);
23540
24438
  writeConfig(next);
23541
- return output(ok({ action: "config", path: path32.join(configDir(), "config.json"), ...next }));
24439
+ return output(ok({ action: "config", path: path35.join(configDir(), "config.json"), ...next }));
23542
24440
  }
23543
24441
  const env = Object.fromEntries(Object.entries({
23544
24442
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -23555,14 +24453,14 @@ var executeInitApply = (args, directory, runtime) => {
23555
24453
 
23556
24454
  // packages/workit-opencode/src/shared/session.ts
23557
24455
  import { realpathSync as realpathSync7 } from "node:fs";
23558
- import path33 from "node:path";
24456
+ import path36 from "node:path";
23559
24457
  var sameWorkspace = (expected, observed) => {
23560
24458
  if (typeof observed !== "string" || !observed)
23561
24459
  return false;
23562
24460
  try {
23563
24461
  return realpathSync7(expected) === realpathSync7(observed);
23564
24462
  } catch {
23565
- return path33.resolve(expected) === path33.resolve(observed);
24463
+ return path36.resolve(expected) === path36.resolve(observed);
23566
24464
  }
23567
24465
  };
23568
24466
 
@@ -23634,7 +24532,7 @@ var workitFamilyOf = (toolName) => {
23634
24532
 
23635
24533
  // packages/workit-opencode/src/tools/context.ts
23636
24534
  import { realpathSync as realpathSync8 } from "node:fs";
23637
- import path34 from "node:path";
24535
+ import path37 from "node:path";
23638
24536
  var createContextTool = () => ({
23639
24537
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
23640
24538
  execute: async (args, context) => {
@@ -23644,7 +24542,7 @@ var createContextTool = () => ({
23644
24542
  let root;
23645
24543
  try {
23646
24544
  const cwd = parsed.data.cwd;
23647
- root = realpathSync8(cwd ? path34.resolve(context.directory, cwd) : context.directory);
24545
+ root = realpathSync8(cwd ? path37.resolve(context.directory, cwd) : context.directory);
23648
24546
  } catch {
23649
24547
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
23650
24548
  }
@@ -23675,19 +24573,19 @@ var workerCoordinatorFor = (task, worker) => {
23675
24573
 
23676
24574
  // packages/workit-core/src/fanout.ts
23677
24575
  import { createHash as createHash16 } from "node:crypto";
23678
- import fs21 from "node:fs";
23679
- import path37 from "node:path";
24576
+ import fs24 from "node:fs";
24577
+ import path40 from "node:path";
23680
24578
 
23681
24579
  // packages/workit-core/src/stack.ts
23682
24580
  import { spawnSync as spawnSync21 } from "node:child_process";
23683
24581
  import { createHash as createHash15, randomBytes as randomBytes4 } from "node:crypto";
23684
- import fs20 from "node:fs";
23685
- import path36 from "node:path";
24582
+ import fs23 from "node:fs";
24583
+ import path39 from "node:path";
23686
24584
 
23687
24585
  // packages/workit-core/src/git/ops.ts
23688
24586
  import { spawnSync as spawnSync20 } from "node:child_process";
23689
24587
  import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
23690
- import path35 from "node:path";
24588
+ import path38 from "node:path";
23691
24589
 
23692
24590
  // packages/workit-core/src/forge/redact.ts
23693
24591
  var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
@@ -23731,18 +24629,18 @@ function operationInProgress(cwd) {
23731
24629
  if (!run.ok)
23732
24630
  return null;
23733
24631
  const found = run.stdout.split(`
23734
- `).map((line) => path35.resolve(cwd, line.trim()));
24632
+ `).map((line) => path38.resolve(cwd, line.trim()));
23735
24633
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
23736
24634
  for (const [index, [, operation]] of markers.entries()) {
23737
24635
  const marker = found[index];
23738
24636
  if (!marker || !existsSync11(marker))
23739
24637
  continue;
23740
- if (index === 1 && existsSync11(path35.join(marker, "applying")))
24638
+ if (index === 1 && existsSync11(path38.join(marker, "applying")))
23741
24639
  return { operation: "am", commitAllowed: false, branch: null };
23742
- if (index === 0 && existsSync11(path35.join(marker, "amend")) && !conflicted()) {
24640
+ if (index === 0 && existsSync11(path38.join(marker, "amend")) && !conflicted()) {
23743
24641
  let headName = "";
23744
24642
  try {
23745
- headName = readFileSync9(path35.join(marker, "head-name"), "utf8").trim();
24643
+ headName = readFileSync9(path38.join(marker, "head-name"), "utf8").trim();
23746
24644
  } catch {}
23747
24645
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
23748
24646
  return { operation, commitAllowed: branch !== null, branch };
@@ -23854,7 +24752,7 @@ function rebasingBranch2(dir) {
23854
24752
  if (!found.ok)
23855
24753
  continue;
23856
24754
  try {
23857
- return fs20.readFileSync(found.stdout.trim(), "utf8").trim();
24755
+ return fs23.readFileSync(found.stdout.trim(), "utf8").trim();
23858
24756
  } catch {}
23859
24757
  }
23860
24758
  return null;
@@ -23862,14 +24760,14 @@ function rebasingBranch2(dir) {
23862
24760
  function rebaseInProgress(dir) {
23863
24761
  for (const name of ["rebase-merge", "rebase-apply"]) {
23864
24762
  const found = gitRun3(dir, ["rev-parse", "--path-format=absolute", "--git-path", name]);
23865
- if (found.ok && fs20.existsSync(found.stdout.trim()))
24763
+ if (found.ok && fs23.existsSync(found.stdout.trim()))
23866
24764
  return true;
23867
24765
  }
23868
24766
  return false;
23869
24767
  }
23870
24768
  var scratchDir = (cwd) => {
23871
24769
  const dir = stacksDir(cwd);
23872
- return dir.ok ? path36.join(dir.data, "worktrees") : null;
24770
+ return dir.ok ? path39.join(dir.data, "worktrees") : null;
23873
24771
  };
23874
24772
  function gitRestack(cwd, input) {
23875
24773
  const owner = worktreeOf(cwd, input.branch);
@@ -23878,8 +24776,8 @@ function gitRestack(cwd, input) {
23878
24776
  const scratch = scratchDir(cwd);
23879
24777
  if (!scratch)
23880
24778
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
23881
- fs20.mkdirSync(scratch, { recursive: true });
23882
- dir = path36.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
24779
+ fs23.mkdirSync(scratch, { recursive: true });
24780
+ dir = path39.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
23883
24781
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
23884
24782
  if (!added.ok)
23885
24783
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -23923,13 +24821,14 @@ function stacksDir(cwd) {
23923
24821
  const root = storeRoot(cwd);
23924
24822
  if (!root.ok)
23925
24823
  return stackFail(root.code, root.error, root.unblock);
23926
- return { ok: true, data: path36.join(root.value.root, "stacks") };
24824
+ return { ok: true, data: path39.join(root.value.root, "stacks") };
23927
24825
  }
23928
24826
  var HEARTBEAT_STALE_MS = 2 * 60000;
23929
24827
 
23930
24828
  // packages/workit-core/src/fanout.ts
23931
24829
  var FANOUT_VERSION = 1;
23932
24830
  var TIERS = ["mundane", "standard", "hard"];
24831
+ var REASONS = ["explore", "parallel", "backstop", "high-risk-verify"];
23933
24832
  var fanoutFail = (code, error, unblock, data) => ({
23934
24833
  ok: false,
23935
24834
  code,
@@ -23985,7 +24884,7 @@ function normalizePattern(raw) {
23985
24884
  return { error: "has unbalanced or nested braces" };
23986
24885
  return { pattern };
23987
24886
  }
23988
- var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
24887
+ var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
23989
24888
  function findCycle(slices) {
23990
24889
  const byId = new Map(slices.map((slice) => [slice.id, slice]));
23991
24890
  const state = new Map;
@@ -24021,6 +24920,7 @@ function sliceHash(slice) {
24021
24920
  hash: _hash,
24022
24921
  hashSince: _since,
24023
24922
  tier: _tier,
24923
+ reason: _reason,
24024
24924
  timebox: _timebox,
24025
24925
  context: _context,
24026
24926
  issues: _issues,
@@ -24032,10 +24932,10 @@ function fanoutsDir(cwd) {
24032
24932
  const root = storeRoot(cwd);
24033
24933
  if (!root.ok)
24034
24934
  return fanoutFail(root.code, root.error, root.unblock);
24035
- return { ok: true, data: path37.join(root.value.root, "fanouts") };
24935
+ return { ok: true, data: path40.join(root.value.root, "fanouts") };
24036
24936
  }
24037
24937
  function parseStored(raw) {
24038
- if (!isRecord5(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
24938
+ if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
24039
24939
  return null;
24040
24940
  if (!Array.isArray(raw.slices))
24041
24941
  return null;
@@ -24052,7 +24952,7 @@ function parseStored(raw) {
24052
24952
  });
24053
24953
  const slices = [];
24054
24954
  for (const item of raw.slices) {
24055
- if (!isRecord5(item) || typeof item.id !== "string" || typeof item.branch !== "string")
24955
+ if (!isRecord7(item) || typeof item.id !== "string" || typeof item.branch !== "string")
24056
24956
  return null;
24057
24957
  slices.push({
24058
24958
  id: item.id,
@@ -24060,6 +24960,7 @@ function parseStored(raw) {
24060
24960
  base: text(item.base) || raw.trunk,
24061
24961
  worktree: text(item.worktree),
24062
24962
  tier: TIERS.includes(item.tier) ? item.tier : "standard",
24963
+ reason: REASONS.includes(item.reason) ? item.reason : null,
24063
24964
  dependsOn: list(item.dependsOn),
24064
24965
  scope: globs(item.scope),
24065
24966
  owns: globs(item.owns),
@@ -24099,10 +25000,10 @@ function readFanout(cwd, name) {
24099
25000
  const dir = fanoutsDir(cwd);
24100
25001
  if (!dir.ok)
24101
25002
  return dir;
24102
- const file = path37.join(dir.data, stackFileName(name));
25003
+ const file = path40.join(dir.data, stackFileName(name));
24103
25004
  let parsed = null;
24104
25005
  try {
24105
- parsed = parseStored(JSON.parse(fs21.readFileSync(file, "utf8")));
25006
+ parsed = parseStored(JSON.parse(fs24.readFileSync(file, "utf8")));
24106
25007
  } catch (error) {
24107
25008
  if (error.code === "ENOENT")
24108
25009
  return { ok: true, data: null };
@@ -24116,7 +25017,7 @@ function listFanouts(cwd) {
24116
25017
  return dir;
24117
25018
  let names = [];
24118
25019
  try {
24119
- names = fs21.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
25020
+ names = fs24.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
24120
25021
  } catch {
24121
25022
  return { ok: true, data: [] };
24122
25023
  }
@@ -24124,7 +25025,7 @@ function listFanouts(cwd) {
24124
25025
  for (const entry of names) {
24125
25026
  let parsed = null;
24126
25027
  try {
24127
- parsed = parseStored(JSON.parse(fs21.readFileSync(path37.join(dir.data, entry), "utf8")));
25028
+ parsed = parseStored(JSON.parse(fs24.readFileSync(path40.join(dir.data, entry), "utf8")));
24128
25029
  } catch {
24129
25030
  parsed = null;
24130
25031
  }
@@ -24233,13 +25134,56 @@ var createV2Lifecycle = (deps) => {
24233
25134
  const unboundLaunch = (coordinator) => {
24234
25135
  const store = new TaskStore(root);
24235
25136
  const current = currentTaskId(store);
24236
- const base = current === null ? 'recovery_required: this branch has no Workit task yet; start it (workit_task start, or `workit task start "<objective>"`), then assign a worker on it with workit_worker before launching a native task' : "recovery_required: no assigned Workit worker is attributable to this coordinator on the current branch's task; assign one with workit_worker before launching a native task";
25137
+ const base = current === null ? 'recovery_required: this branch has no Workit task yet; start it (workit_task start, or `workit task start "<objective>"`), then assign a worker on it with workit_worker before launching a native task' : "recovery_required: no assigned Workit worker is attributable to this coordinator on the current branch's task (worker assignments are branch-scoped: they follow the task of the branch checked out when the launch happens); assign one with workit_worker on this branch's task before launching a native task";
24237
25138
  const listed = store.listTasks();
24238
25139
  if (!listed.ok)
24239
25140
  return base;
24240
25141
  const stale = listed.data.flatMap((task) => task.status === "active" && task.id !== current ? task.workers.filter((worker) => (worker.data.state === "assigned" || worker.data.state === "running") && worker.provenance.session?.kind === "host" && worker.provenance.session.handle === coordinator).map((worker) => `workit_worker {"action":"cancel","taskId":"${task.id}","workerId":"${worker.id}","reason":"stale worker from another branch"}`) : []);
24241
25142
  return stale.length > 0 ? `${base}. Workers of this coordinator are still open on other tasks; cancel any that are stale: ${stale.join("; ")}` : base;
24242
25143
  };
25144
+ const rebindStaleWorker = (coordinator, current, stale) => {
25145
+ if (stale.length !== 1)
25146
+ return false;
25147
+ const { task: from, entry } = stale[0];
25148
+ const store = new TaskStore(root);
25149
+ const target = store.readTask(current);
25150
+ if (!target.ok || target.data.status !== "active")
25151
+ return false;
25152
+ const core = new WorkitCore(store, {
25153
+ root,
25154
+ caller: { host: "opencode", actor: coordinator },
25155
+ capabilities: [],
25156
+ constraints: [],
25157
+ now
25158
+ });
25159
+ const assigned = core.worker({
25160
+ schemaVersion: 1,
25161
+ action: "assign",
25162
+ taskId: current,
25163
+ assignment: entry.data.assignment
25164
+ });
25165
+ if (!assigned.ok)
25166
+ return false;
25167
+ const cancelled = core.worker({
25168
+ schemaVersion: 1,
25169
+ action: "cancel",
25170
+ taskId: from.id,
25171
+ workerId: entry.id,
25172
+ reason: `rebound to task ${current} (worker ${assigned.data.id}) after a branch switch`
25173
+ });
25174
+ const latest = store.readTask(current);
25175
+ if (cancelled.ok && latest.ok)
25176
+ core.task({
25177
+ schemaVersion: 1,
25178
+ action: "progress",
25179
+ taskId: current,
25180
+ progress: {
25181
+ ...latest.data.progress,
25182
+ summary: `${latest.data.progress.summary} (worker ${assigned.data.id} rebound from task ${from.id} after a branch switch)`
25183
+ }
25184
+ });
25185
+ return cancelled.ok;
25186
+ };
24243
25187
  const prepareDispatch = (coordinator, callID) => {
24244
25188
  if (dispatches.has(coordinator))
24245
25189
  return "unsettled";
@@ -24253,7 +25197,16 @@ var createV2Lifecycle = (deps) => {
24253
25197
  return "unmanaged";
24254
25198
  const eligible = managed.flatMap((task) => task.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator).map((entry) => ({ task, entry })));
24255
25199
  const current = currentTaskId(store);
24256
- const next = oldest(eligible.filter((item) => item.task.id === current));
25200
+ let next = oldest(eligible.filter((item) => item.task.id === current));
25201
+ if (!next && current !== null) {
25202
+ const stale = eligible.filter((item) => item.task.id !== current);
25203
+ if (rebindStaleWorker(coordinator, current, stale)) {
25204
+ const refreshed = store.readTask(current);
25205
+ const rebound = refreshed.ok ? refreshed.data.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.handle === coordinator) : [];
25206
+ if (refreshed.ok && rebound.length > 0)
25207
+ next = oldest(rebound.map((entry) => ({ task: refreshed.data, entry })));
25208
+ }
25209
+ }
24257
25210
  if (!next)
24258
25211
  return "unbound";
24259
25212
  dispatches.set(coordinator, {
@@ -24703,6 +25656,11 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
24703
25656
  const gated = shellGate(cwd, resource, owner);
24704
25657
  if (gated.kind === "deny")
24705
25658
  return deny(gated.reason);
25659
+ if (event.effect === "allow") {
25660
+ const plan = heavyGate(cwd, resource, { host: "opencode" });
25661
+ if (plan.kind === "split")
25662
+ return deny(plan.reason);
25663
+ }
24706
25664
  }
24707
25665
  };
24708
25666
 
@@ -24726,7 +25684,7 @@ var childTaskMissing = (store, input) => {
24726
25684
  var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only inspect the task summary (workit_task action "inspect") or record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
24727
25685
 
24728
25686
  // packages/workit-opencode/src/v2/shell.ts
24729
- import path38 from "node:path";
25687
+ import path41 from "node:path";
24730
25688
  var commandOf = (input) => {
24731
25689
  if (input === null || typeof input !== "object")
24732
25690
  return null;
@@ -24735,12 +25693,28 @@ var commandOf = (input) => {
24735
25693
  };
24736
25694
  var hookInput = (root, sessionID, id, shell) => ({
24737
25695
  host: "opencode",
24738
- cwd: shell.workdir ? path38.resolve(root, shell.workdir) : root,
25696
+ cwd: shell.workdir ? path41.resolve(root, shell.workdir) : root,
24739
25697
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
24740
25698
  permissionMode: null,
24741
25699
  transcriptPath: null,
24742
25700
  event: { kind: "shell.pre", command: shell.command, toolUseId: id }
24743
25701
  });
25702
+ var leaseRewrite2 = (input) => {
25703
+ if (input.event.kind !== "shell.pre")
25704
+ return null;
25705
+ const { command } = input.event;
25706
+ const plan = heavyGate(input.cwd, command, { host: "opencode" });
25707
+ if (plan.kind !== "rewrite")
25708
+ return null;
25709
+ if (shellPolicy(input.cwd, command, "posix").kind === "deny")
25710
+ return null;
25711
+ if (rawGitPre(input, command, { nudge: false }).kind === "deny")
25712
+ return null;
25713
+ const owner = liveOwner({ host: "opencode", session: { id: input.session.id, parentId: null } }, "in-process-plugin");
25714
+ if (shellGate(input.cwd, command, owner).kind === "deny")
25715
+ return null;
25716
+ return plan.command;
25717
+ };
24744
25718
  var observeShellBefore = (root, event, workdirs) => {
24745
25719
  try {
24746
25720
  if (event.tool !== "shell")
@@ -24749,6 +25723,12 @@ var observeShellBefore = (root, event, workdirs) => {
24749
25723
  if (!shell)
24750
25724
  return;
24751
25725
  const sessionID = String(event.sessionID);
25726
+ const original = hookInput(root, sessionID, String(event.id), shell);
25727
+ const heavy = leaseRewrite2(original);
25728
+ if (heavy) {
25729
+ event.input.command = heavy;
25730
+ shell.command = heavy;
25731
+ }
24752
25732
  const input = hookInput(root, sessionID, String(event.id), shell);
24753
25733
  workdirs.set(sessionID, input.cwd);
24754
25734
  if (workdirs.size > 256)
@@ -24920,37 +25900,37 @@ var createStopControl = (root, port) => {
24920
25900
 
24921
25901
  // packages/workit-opencode/src/v2/registry.ts
24922
25902
  import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
24923
- import path40 from "node:path";
25903
+ import path43 from "node:path";
24924
25904
 
24925
25905
  // packages/workit-opencode/src/shared/assets.ts
24926
25906
  import { existsSync as existsSync12, realpathSync as realpathSync9 } from "node:fs";
24927
- import path39 from "node:path";
25907
+ import path42 from "node:path";
24928
25908
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24929
25909
  var moduleDir = () => {
24930
25910
  try {
24931
- return realpathSync9(path39.dirname(fileURLToPath2(import.meta.url)));
25911
+ return realpathSync9(path42.dirname(fileURLToPath2(import.meta.url)));
24932
25912
  } catch {
24933
- return path39.dirname(fileURLToPath2(import.meta.url));
25913
+ return path42.dirname(fileURLToPath2(import.meta.url));
24934
25914
  }
24935
25915
  };
24936
25916
  var packageRoot2 = () => {
24937
25917
  const dir = moduleDir();
24938
25918
  for (const candidate of [
24939
- path39.resolve(dir, ".."),
24940
- path39.resolve(dir, "..", ".."),
24941
- path39.resolve(dir, "..", "..", "..")
25919
+ path42.resolve(dir, ".."),
25920
+ path42.resolve(dir, "..", ".."),
25921
+ path42.resolve(dir, "..", "..", "..")
24942
25922
  ]) {
24943
- if (existsSync12(path39.join(candidate, "package.json")))
25923
+ if (existsSync12(path42.join(candidate, "package.json")))
24944
25924
  return candidate;
24945
25925
  }
24946
- return path39.resolve(dir, "..");
25926
+ return path42.resolve(dir, "..");
24947
25927
  };
24948
25928
  var skillsRoot = () => {
24949
25929
  const root = packageRoot2();
24950
- const canonical = path39.resolve(root, "..", "workit-core", "skills");
24951
- if (existsSync12(path39.join(root, "src")) && existsSync12(canonical))
25930
+ const canonical = path42.resolve(root, "..", "workit-core", "skills");
25931
+ if (existsSync12(path42.join(root, "src")) && existsSync12(canonical))
24952
25932
  return canonical;
24953
- return path39.join(root, "assets", "skills");
25933
+ return path42.join(root, "assets", "skills");
24954
25934
  };
24955
25935
 
24956
25936
  // packages/workit-opencode/src/v2/registry.ts
@@ -24977,11 +25957,11 @@ var registerSkills = async (ctx) => {
24977
25957
  if (!existsSync13(skillsDir))
24978
25958
  return new Set;
24979
25959
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
24980
- const own = (skill) => typeof skill.path === "string" && path40.resolve(skill.path).startsWith(skillsDir + path40.sep);
25960
+ const own = (skill) => typeof skill.path === "string" && path43.resolve(skill.path).startsWith(skillsDir + path43.sep);
24981
25961
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
24982
25962
  const packaged = new Map;
24983
25963
  for (const id of WORKIT_METHOD_SKILLS) {
24984
- const file = path40.join(skillsDir, id, "SKILL.md");
25964
+ const file = path43.join(skillsDir, id, "SKILL.md");
24985
25965
  if (!userSkills.has(id) && existsSync13(file))
24986
25966
  packaged.set(id, file);
24987
25967
  }
@@ -25029,22 +26009,22 @@ var registerCommands = async (ctx, registeredSkills) => {
25029
26009
 
25030
26010
  // packages/workit-opencode/src/stale-sources.ts
25031
26011
  import { existsSync as existsSync14, readdirSync as readdirSync6 } from "node:fs";
25032
- import path41 from "node:path";
26012
+ import path44 from "node:path";
25033
26013
  import { fileURLToPath as fileURLToPath3 } from "node:url";
25034
26014
  var typescriptFiles = (directory) => {
25035
26015
  if (!existsSync14(directory))
25036
26016
  return [];
25037
26017
  return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
25038
- const file = path41.join(directory, entry.name);
26018
+ const file = path44.join(directory, entry.name);
25039
26019
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
25040
26020
  });
25041
26021
  };
25042
26022
  var pluginSourceFiles = [
25043
26023
  ...new Set([
25044
26024
  fileURLToPath3(import.meta.url),
25045
- ...typescriptFiles(path41.join(packageRoot2(), "src")),
25046
- path41.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
25047
- ...typescriptFiles(path41.join(packageRoot2(), "..", "workit-core", "src", "core"))
26025
+ ...typescriptFiles(path44.join(packageRoot2(), "src")),
26026
+ path44.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
26027
+ ...typescriptFiles(path44.join(packageRoot2(), "..", "workit-core", "src", "core"))
25048
26028
  ])
25049
26029
  ].filter((file) => existsSync14(file)).toSorted();
25050
26030
 
@@ -25161,8 +26141,11 @@ var setup = async (ctx) => {
25161
26141
  const pendingShells = new Map;
25162
26142
  await ctx.tool.hook("execute.before", async (event) => {
25163
26143
  if (event.tool === "shell") {
25164
- noteShellCall(pendingShells, { sessionID: String(event.sessionID), input: event.input });
25165
- return observeShellBefore(root, event, shellWorkdirs);
26144
+ observeShellBefore(root, event, shellWorkdirs);
26145
+ return noteShellCall(pendingShells, {
26146
+ sessionID: String(event.sessionID),
26147
+ input: event.input
26148
+ });
25166
26149
  }
25167
26150
  if (event.tool === "skill")
25168
26151
  return observeSkillLoad(root, event);