@brainervirus/workit-opencode 8.16.1 → 8.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 = [];
@@ -22955,6 +23853,18 @@ var WRITE_TOOLS = new Set([
22955
23853
  ]);
22956
23854
  // packages/workit-core/src/hooks/handle.ts
22957
23855
  var offered = new Set;
23856
+ // packages/workit-core/src/hooks/deadline.ts
23857
+ var QUIET = new Set([
23858
+ "stop",
23859
+ "subagent.stop",
23860
+ "shell.post",
23861
+ "compact.pre",
23862
+ "prompt.submit",
23863
+ null
23864
+ ]);
23865
+ var TIMED_OUT = Symbol("timed out");
23866
+ var PROCESS_START = performance.timeOrigin;
23867
+
22958
23868
  // packages/workit-core/src/hooks/hosts/claude-code.ts
22959
23869
  var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
22960
23870
  var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
@@ -22996,7 +23906,7 @@ var OPENCODE_DESCRIPTOR = {
22996
23906
  "compact.pre": { support: "native", native: 'session.hook("compaction")' },
22997
23907
  stop: { support: "partial", native: 'event("session.idle")' }
22998
23908
  },
22999
- shellPolicy: { deny: "native", channel: "effect", failClosed: false },
23909
+ shellPolicy: { deny: "native", channel: "effect", failClosed: false, rewrite: "native" },
23000
23910
  context: {
23001
23911
  sessionStart: "none",
23002
23912
  perTurn: "native",
@@ -23045,11 +23955,11 @@ var OPENCODE_DESCRIPTOR = {
23045
23955
  ]
23046
23956
  };
23047
23957
  // packages/workit-opencode/src/shared/init-apply.ts
23048
- import path32 from "node:path";
23958
+ import path35 from "node:path";
23049
23959
 
23050
23960
  // packages/workit-core/src/core/gitignore.ts
23051
23961
  import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
23052
- import path28 from "node:path";
23962
+ import path31 from "node:path";
23053
23963
  var GITIGNORE_ENTRIES = [
23054
23964
  "# workit: SDD working state (never commit)",
23055
23965
  "docs/*/sdd/",
@@ -23069,7 +23979,7 @@ var GITIGNORE_ENTRIES = [
23069
23979
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
23070
23980
  if (!confirmed)
23071
23981
  return { ok: false, error: "confirmed: true required" };
23072
- const file = path28.join(workspaceRoot, ".gitignore");
23982
+ const file = path31.join(workspaceRoot, ".gitignore");
23073
23983
  const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
23074
23984
  const existingLines = new Set(existing.split(`
23075
23985
  `).map((l) => l.trim()).filter(Boolean));
@@ -23097,25 +24007,25 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
23097
24007
 
23098
24008
  // packages/workit-core/src/core/hygiene.ts
23099
24009
  import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
23100
- import path29 from "node:path";
24010
+ import path32 from "node:path";
23101
24011
  var repoRoot3 = assetRoot();
23102
- var templatesDir = () => path29.join(repoRoot3, "templates", "hygiene");
24012
+ var templatesDir = () => path32.join(repoRoot3, "templates", "hygiene");
23103
24013
  var packageJson = (root) => {
23104
- if (!existsSync10(path29.join(root, "package.json")))
24014
+ if (!existsSync10(path32.join(root, "package.json")))
23105
24015
  return null;
23106
24016
  try {
23107
- return JSON.parse(readFileSync7(path29.join(root, "package.json"), "utf8"));
24017
+ return JSON.parse(readFileSync7(path32.join(root, "package.json"), "utf8"));
23108
24018
  } catch {
23109
24019
  return null;
23110
24020
  }
23111
24021
  };
23112
24022
  var isOpenSource = (root) => {
23113
- if (existsSync10(path29.join(root, "LICENSE")))
24023
+ if (existsSync10(path32.join(root, "LICENSE")))
23114
24024
  return true;
23115
24025
  const pkg = packageJson(root);
23116
24026
  if (pkg && !pkg.private)
23117
24027
  return true;
23118
- return path29.basename(root) === "workflow-toolkit";
24028
+ return path32.basename(root) === "workflow-toolkit";
23119
24029
  };
23120
24030
  var licenseHolder = (root) => {
23121
24031
  const pkg = packageJson(root);
@@ -23138,13 +24048,13 @@ var planHygieneFiles = (root, opts = {}) => {
23138
24048
  files.push("LICENSE", "CONTRIBUTING.md");
23139
24049
  const planned = [];
23140
24050
  for (const file of files) {
23141
- if (existsSync10(path29.join(root, file)))
24051
+ if (existsSync10(path32.join(root, file)))
23142
24052
  continue;
23143
- const tpl = path29.join(templatesDir(), file);
24053
+ const tpl = path32.join(templatesDir(), file);
23144
24054
  if (!existsSync10(tpl))
23145
24055
  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 });
24056
+ const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path32.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
24057
+ planned.push({ path: path32.join(root, file), content });
23148
24058
  }
23149
24059
  return planned;
23150
24060
  };
@@ -23154,19 +24064,19 @@ var ensureHygieneFiles = (root, opts) => {
23154
24064
  const created = [];
23155
24065
  for (const planned of planHygieneFiles(root, opts)) {
23156
24066
  writeFileSync5(planned.path, planned.content, "utf8");
23157
- created.push(path29.basename(planned.path));
24067
+ created.push(path32.basename(planned.path));
23158
24068
  }
23159
24069
  return { ok: true, created };
23160
24070
  };
23161
24071
 
23162
24072
  // packages/workit-core/src/core/init.ts
23163
- import fs19 from "node:fs";
23164
- import path31 from "node:path";
24073
+ import fs22 from "node:fs";
24074
+ import path34 from "node:path";
23165
24075
  import { spawnSync as spawnSync19 } from "node:child_process";
23166
24076
 
23167
24077
  // packages/workit-core/src/core/workspace-branch-policy.ts
23168
24078
  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";
24079
+ import path33 from "node:path";
23170
24080
  import { isDeepStrictEqual } from "node:util";
23171
24081
  var readFileSafe = (p) => {
23172
24082
  try {
@@ -23177,7 +24087,7 @@ var readFileSafe = (p) => {
23177
24087
  };
23178
24088
  function applyWorkspaceBranchPolicy(opts) {
23179
24089
  const { workspace_root, env = process.env } = opts;
23180
- const dir = path30.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
24090
+ const dir = path33.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
23181
24091
  const {
23182
24092
  status,
23183
24093
  path: wsPath,
@@ -23189,7 +24099,7 @@ function applyWorkspaceBranchPolicy(opts) {
23189
24099
  if (status === "malformed" || status === "invalid")
23190
24100
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
23191
24101
  const detection = detectBranchPolicy(workspace_root);
23192
- const name = String(env.WORKFLOW_BP_NAME ?? path30.basename(workspace_root));
24102
+ const name = String(env.WORKFLOW_BP_NAME ?? path33.basename(workspace_root));
23193
24103
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
23194
24104
  const policy = {
23195
24105
  preset: detection.preset,
@@ -23238,7 +24148,7 @@ function applyWorkspaceBranchPolicy(opts) {
23238
24148
  ok: false,
23239
24149
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
23240
24150
  };
23241
- mkdirSync4(path30.dirname(wsPath), { recursive: true });
24151
+ mkdirSync4(path33.dirname(wsPath), { recursive: true });
23242
24152
  const tmp = `${wsPath}.${process.pid}.tmp`;
23243
24153
  try {
23244
24154
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -23271,9 +24181,9 @@ function applyWorkspaceBranchPolicy(opts) {
23271
24181
 
23272
24182
  // packages/workit-core/src/core/init.ts
23273
24183
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
23274
- var readJson = (p) => {
24184
+ var readJson3 = (p) => {
23275
24185
  try {
23276
- const parsed = JSON.parse(fs19.readFileSync(p, "utf8"));
24186
+ const parsed = JSON.parse(fs22.readFileSync(p, "utf8"));
23277
24187
  return isConfigObject(parsed) ? parsed : null;
23278
24188
  } catch {
23279
24189
  return null;
@@ -23303,7 +24213,7 @@ var youtrackJsonContent = (dir) => {
23303
24213
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
23304
24214
  return {
23305
24215
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
23306
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path31.join(dir, "youtrack.token"),
24216
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path34.join(dir, "youtrack.token"),
23307
24217
  ...meetingIssue ? {
23308
24218
  meetingIssue,
23309
24219
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -23333,19 +24243,19 @@ var vcsJsonContent = () => {
23333
24243
  };
23334
24244
  function initApplyData(action, env = process.env) {
23335
24245
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
23336
- fs19.mkdirSync(dir, { recursive: true });
24246
+ fs22.mkdirSync(dir, { recursive: true });
23337
24247
  switch (action) {
23338
24248
  case "youtrack_json": {
23339
- const out = path31.join(dir, "youtrack.json");
23340
- fs19.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
24249
+ const out = path34.join(dir, "youtrack.json");
24250
+ fs22.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23341
24251
  `, "utf8");
23342
24252
  return { action, ok: true, path: out };
23343
24253
  }
23344
24254
  case "youtrack_token_placeholder": {
23345
- const p = path31.join(dir, "youtrack.token");
24255
+ const p = path34.join(dir, "youtrack.token");
23346
24256
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
23347
24257
  `, 384) === "preserved";
23348
- const abs = path31.resolve(p);
24258
+ const abs = path34.resolve(p);
23349
24259
  return {
23350
24260
  action,
23351
24261
  ok: true,
@@ -23357,19 +24267,19 @@ function initApplyData(action, env = process.env) {
23357
24267
  };
23358
24268
  }
23359
24269
  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) + `
24270
+ const jsonOut = path34.join(dir, "youtrack.json");
24271
+ const tokenOut = path34.join(dir, "youtrack.token");
24272
+ fs22.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23363
24273
  `, "utf8");
23364
24274
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
23365
24275
  `, 384) === "preserved";
23366
- const configPath = path31.resolve(jsonOut);
23367
- const tokenPath = path31.resolve(tokenOut);
24276
+ const configPath = path34.resolve(jsonOut);
24277
+ const tokenPath = path34.resolve(tokenOut);
23368
24278
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
23369
24279
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
23370
24280
  let tokenCreate = {};
23371
24281
  try {
23372
- const cfg = readJson(configPath) ?? {};
24282
+ const cfg = readJson3(configPath) ?? {};
23373
24283
  const base = String(cfg.baseUrl ?? "").replace(/\/+$/, "");
23374
24284
  const meeting = String(cfg.meetingIssue ?? "");
23375
24285
  tokenCreate = youTrackTokenCreateUrl().data;
@@ -23416,14 +24326,14 @@ function initApplyData(action, env = process.env) {
23416
24326
  }
23417
24327
  }
23418
24328
  case "vcs_scaffold": {
23419
- const jsonOut = path31.join(dir, "vcs.json");
23420
- fs19.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
24329
+ const jsonOut = path34.join(dir, "vcs.json");
24330
+ fs22.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
23421
24331
  `, "utf8");
23422
- const configPath = path31.resolve(jsonOut);
24332
+ const configPath = path34.resolve(jsonOut);
23423
24333
  const prev = process.env.WORKFLOW_VCS_CONFIG;
23424
24334
  process.env.WORKFLOW_VCS_CONFIG = configPath;
23425
24335
  try {
23426
- const cfg = readJson(configPath) ?? {};
24336
+ const cfg = readJson3(configPath) ?? {};
23427
24337
  const rawProvider = cfg.provider;
23428
24338
  const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
23429
24339
  return {
@@ -23538,7 +24448,7 @@ var executeInitApply = (args, directory, runtime) => {
23538
24448
  protectedNames: args.branch_policy_protected
23539
24449
  }, current);
23540
24450
  writeConfig(next);
23541
- return output(ok({ action: "config", path: path32.join(configDir(), "config.json"), ...next }));
24451
+ return output(ok({ action: "config", path: path35.join(configDir(), "config.json"), ...next }));
23542
24452
  }
23543
24453
  const env = Object.fromEntries(Object.entries({
23544
24454
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -23555,14 +24465,14 @@ var executeInitApply = (args, directory, runtime) => {
23555
24465
 
23556
24466
  // packages/workit-opencode/src/shared/session.ts
23557
24467
  import { realpathSync as realpathSync7 } from "node:fs";
23558
- import path33 from "node:path";
24468
+ import path36 from "node:path";
23559
24469
  var sameWorkspace = (expected, observed) => {
23560
24470
  if (typeof observed !== "string" || !observed)
23561
24471
  return false;
23562
24472
  try {
23563
24473
  return realpathSync7(expected) === realpathSync7(observed);
23564
24474
  } catch {
23565
- return path33.resolve(expected) === path33.resolve(observed);
24475
+ return path36.resolve(expected) === path36.resolve(observed);
23566
24476
  }
23567
24477
  };
23568
24478
 
@@ -23634,7 +24544,7 @@ var workitFamilyOf = (toolName) => {
23634
24544
 
23635
24545
  // packages/workit-opencode/src/tools/context.ts
23636
24546
  import { realpathSync as realpathSync8 } from "node:fs";
23637
- import path34 from "node:path";
24547
+ import path37 from "node:path";
23638
24548
  var createContextTool = () => ({
23639
24549
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
23640
24550
  execute: async (args, context) => {
@@ -23644,7 +24554,7 @@ var createContextTool = () => ({
23644
24554
  let root;
23645
24555
  try {
23646
24556
  const cwd = parsed.data.cwd;
23647
- root = realpathSync8(cwd ? path34.resolve(context.directory, cwd) : context.directory);
24557
+ root = realpathSync8(cwd ? path37.resolve(context.directory, cwd) : context.directory);
23648
24558
  } catch {
23649
24559
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
23650
24560
  }
@@ -23675,19 +24585,19 @@ var workerCoordinatorFor = (task, worker) => {
23675
24585
 
23676
24586
  // packages/workit-core/src/fanout.ts
23677
24587
  import { createHash as createHash16 } from "node:crypto";
23678
- import fs21 from "node:fs";
23679
- import path37 from "node:path";
24588
+ import fs24 from "node:fs";
24589
+ import path40 from "node:path";
23680
24590
 
23681
24591
  // packages/workit-core/src/stack.ts
23682
24592
  import { spawnSync as spawnSync21 } from "node:child_process";
23683
24593
  import { createHash as createHash15, randomBytes as randomBytes4 } from "node:crypto";
23684
- import fs20 from "node:fs";
23685
- import path36 from "node:path";
24594
+ import fs23 from "node:fs";
24595
+ import path39 from "node:path";
23686
24596
 
23687
24597
  // packages/workit-core/src/git/ops.ts
23688
24598
  import { spawnSync as spawnSync20 } from "node:child_process";
23689
24599
  import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
23690
- import path35 from "node:path";
24600
+ import path38 from "node:path";
23691
24601
 
23692
24602
  // packages/workit-core/src/forge/redact.ts
23693
24603
  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 +24641,18 @@ function operationInProgress(cwd) {
23731
24641
  if (!run.ok)
23732
24642
  return null;
23733
24643
  const found = run.stdout.split(`
23734
- `).map((line) => path35.resolve(cwd, line.trim()));
24644
+ `).map((line) => path38.resolve(cwd, line.trim()));
23735
24645
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
23736
24646
  for (const [index, [, operation]] of markers.entries()) {
23737
24647
  const marker = found[index];
23738
24648
  if (!marker || !existsSync11(marker))
23739
24649
  continue;
23740
- if (index === 1 && existsSync11(path35.join(marker, "applying")))
24650
+ if (index === 1 && existsSync11(path38.join(marker, "applying")))
23741
24651
  return { operation: "am", commitAllowed: false, branch: null };
23742
- if (index === 0 && existsSync11(path35.join(marker, "amend")) && !conflicted()) {
24652
+ if (index === 0 && existsSync11(path38.join(marker, "amend")) && !conflicted()) {
23743
24653
  let headName = "";
23744
24654
  try {
23745
- headName = readFileSync9(path35.join(marker, "head-name"), "utf8").trim();
24655
+ headName = readFileSync9(path38.join(marker, "head-name"), "utf8").trim();
23746
24656
  } catch {}
23747
24657
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
23748
24658
  return { operation, commitAllowed: branch !== null, branch };
@@ -23854,7 +24764,7 @@ function rebasingBranch2(dir) {
23854
24764
  if (!found.ok)
23855
24765
  continue;
23856
24766
  try {
23857
- return fs20.readFileSync(found.stdout.trim(), "utf8").trim();
24767
+ return fs23.readFileSync(found.stdout.trim(), "utf8").trim();
23858
24768
  } catch {}
23859
24769
  }
23860
24770
  return null;
@@ -23862,14 +24772,14 @@ function rebasingBranch2(dir) {
23862
24772
  function rebaseInProgress(dir) {
23863
24773
  for (const name of ["rebase-merge", "rebase-apply"]) {
23864
24774
  const found = gitRun3(dir, ["rev-parse", "--path-format=absolute", "--git-path", name]);
23865
- if (found.ok && fs20.existsSync(found.stdout.trim()))
24775
+ if (found.ok && fs23.existsSync(found.stdout.trim()))
23866
24776
  return true;
23867
24777
  }
23868
24778
  return false;
23869
24779
  }
23870
24780
  var scratchDir = (cwd) => {
23871
24781
  const dir = stacksDir(cwd);
23872
- return dir.ok ? path36.join(dir.data, "worktrees") : null;
24782
+ return dir.ok ? path39.join(dir.data, "worktrees") : null;
23873
24783
  };
23874
24784
  function gitRestack(cwd, input) {
23875
24785
  const owner = worktreeOf(cwd, input.branch);
@@ -23878,8 +24788,8 @@ function gitRestack(cwd, input) {
23878
24788
  const scratch = scratchDir(cwd);
23879
24789
  if (!scratch)
23880
24790
  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")}`);
24791
+ fs23.mkdirSync(scratch, { recursive: true });
24792
+ dir = path39.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
23883
24793
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
23884
24794
  if (!added.ok)
23885
24795
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -23923,13 +24833,14 @@ function stacksDir(cwd) {
23923
24833
  const root = storeRoot(cwd);
23924
24834
  if (!root.ok)
23925
24835
  return stackFail(root.code, root.error, root.unblock);
23926
- return { ok: true, data: path36.join(root.value.root, "stacks") };
24836
+ return { ok: true, data: path39.join(root.value.root, "stacks") };
23927
24837
  }
23928
24838
  var HEARTBEAT_STALE_MS = 2 * 60000;
23929
24839
 
23930
24840
  // packages/workit-core/src/fanout.ts
23931
24841
  var FANOUT_VERSION = 1;
23932
24842
  var TIERS = ["mundane", "standard", "hard"];
24843
+ var REASONS = ["explore", "parallel", "backstop", "high-risk-verify"];
23933
24844
  var fanoutFail = (code, error, unblock, data) => ({
23934
24845
  ok: false,
23935
24846
  code,
@@ -23985,7 +24896,7 @@ function normalizePattern(raw) {
23985
24896
  return { error: "has unbalanced or nested braces" };
23986
24897
  return { pattern };
23987
24898
  }
23988
- var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
24899
+ var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
23989
24900
  function findCycle(slices) {
23990
24901
  const byId = new Map(slices.map((slice) => [slice.id, slice]));
23991
24902
  const state = new Map;
@@ -24021,6 +24932,7 @@ function sliceHash(slice) {
24021
24932
  hash: _hash,
24022
24933
  hashSince: _since,
24023
24934
  tier: _tier,
24935
+ reason: _reason,
24024
24936
  timebox: _timebox,
24025
24937
  context: _context,
24026
24938
  issues: _issues,
@@ -24032,10 +24944,10 @@ function fanoutsDir(cwd) {
24032
24944
  const root = storeRoot(cwd);
24033
24945
  if (!root.ok)
24034
24946
  return fanoutFail(root.code, root.error, root.unblock);
24035
- return { ok: true, data: path37.join(root.value.root, "fanouts") };
24947
+ return { ok: true, data: path40.join(root.value.root, "fanouts") };
24036
24948
  }
24037
24949
  function parseStored(raw) {
24038
- if (!isRecord5(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
24950
+ if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
24039
24951
  return null;
24040
24952
  if (!Array.isArray(raw.slices))
24041
24953
  return null;
@@ -24052,7 +24964,7 @@ function parseStored(raw) {
24052
24964
  });
24053
24965
  const slices = [];
24054
24966
  for (const item of raw.slices) {
24055
- if (!isRecord5(item) || typeof item.id !== "string" || typeof item.branch !== "string")
24967
+ if (!isRecord7(item) || typeof item.id !== "string" || typeof item.branch !== "string")
24056
24968
  return null;
24057
24969
  slices.push({
24058
24970
  id: item.id,
@@ -24060,6 +24972,7 @@ function parseStored(raw) {
24060
24972
  base: text(item.base) || raw.trunk,
24061
24973
  worktree: text(item.worktree),
24062
24974
  tier: TIERS.includes(item.tier) ? item.tier : "standard",
24975
+ reason: REASONS.includes(item.reason) ? item.reason : null,
24063
24976
  dependsOn: list(item.dependsOn),
24064
24977
  scope: globs(item.scope),
24065
24978
  owns: globs(item.owns),
@@ -24099,10 +25012,10 @@ function readFanout(cwd, name) {
24099
25012
  const dir = fanoutsDir(cwd);
24100
25013
  if (!dir.ok)
24101
25014
  return dir;
24102
- const file = path37.join(dir.data, stackFileName(name));
25015
+ const file = path40.join(dir.data, stackFileName(name));
24103
25016
  let parsed = null;
24104
25017
  try {
24105
- parsed = parseStored(JSON.parse(fs21.readFileSync(file, "utf8")));
25018
+ parsed = parseStored(JSON.parse(fs24.readFileSync(file, "utf8")));
24106
25019
  } catch (error) {
24107
25020
  if (error.code === "ENOENT")
24108
25021
  return { ok: true, data: null };
@@ -24116,7 +25029,7 @@ function listFanouts(cwd) {
24116
25029
  return dir;
24117
25030
  let names = [];
24118
25031
  try {
24119
- names = fs21.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
25032
+ names = fs24.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
24120
25033
  } catch {
24121
25034
  return { ok: true, data: [] };
24122
25035
  }
@@ -24124,7 +25037,7 @@ function listFanouts(cwd) {
24124
25037
  for (const entry of names) {
24125
25038
  let parsed = null;
24126
25039
  try {
24127
- parsed = parseStored(JSON.parse(fs21.readFileSync(path37.join(dir.data, entry), "utf8")));
25040
+ parsed = parseStored(JSON.parse(fs24.readFileSync(path40.join(dir.data, entry), "utf8")));
24128
25041
  } catch {
24129
25042
  parsed = null;
24130
25043
  }
@@ -24755,6 +25668,11 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
24755
25668
  const gated = shellGate(cwd, resource, owner);
24756
25669
  if (gated.kind === "deny")
24757
25670
  return deny(gated.reason);
25671
+ if (event.effect === "allow") {
25672
+ const plan = heavyGate(cwd, resource, { host: "opencode" });
25673
+ if (plan.kind === "split")
25674
+ return deny(plan.reason);
25675
+ }
24758
25676
  }
24759
25677
  };
24760
25678
 
@@ -24778,7 +25696,7 @@ var childTaskMissing = (store, input) => {
24778
25696
  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`;
24779
25697
 
24780
25698
  // packages/workit-opencode/src/v2/shell.ts
24781
- import path38 from "node:path";
25699
+ import path41 from "node:path";
24782
25700
  var commandOf = (input) => {
24783
25701
  if (input === null || typeof input !== "object")
24784
25702
  return null;
@@ -24787,12 +25705,28 @@ var commandOf = (input) => {
24787
25705
  };
24788
25706
  var hookInput = (root, sessionID, id, shell) => ({
24789
25707
  host: "opencode",
24790
- cwd: shell.workdir ? path38.resolve(root, shell.workdir) : root,
25708
+ cwd: shell.workdir ? path41.resolve(root, shell.workdir) : root,
24791
25709
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
24792
25710
  permissionMode: null,
24793
25711
  transcriptPath: null,
24794
25712
  event: { kind: "shell.pre", command: shell.command, toolUseId: id }
24795
25713
  });
25714
+ var leaseRewrite2 = (input) => {
25715
+ if (input.event.kind !== "shell.pre")
25716
+ return null;
25717
+ const { command } = input.event;
25718
+ const plan = heavyGate(input.cwd, command, { host: "opencode" });
25719
+ if (plan.kind !== "rewrite")
25720
+ return null;
25721
+ if (shellPolicy(input.cwd, command, "posix").kind === "deny")
25722
+ return null;
25723
+ if (rawGitPre(input, command, { nudge: false }).kind === "deny")
25724
+ return null;
25725
+ const owner = liveOwner({ host: "opencode", session: { id: input.session.id, parentId: null } }, "in-process-plugin");
25726
+ if (shellGate(input.cwd, command, owner).kind === "deny")
25727
+ return null;
25728
+ return plan.command;
25729
+ };
24796
25730
  var observeShellBefore = (root, event, workdirs) => {
24797
25731
  try {
24798
25732
  if (event.tool !== "shell")
@@ -24801,6 +25735,12 @@ var observeShellBefore = (root, event, workdirs) => {
24801
25735
  if (!shell)
24802
25736
  return;
24803
25737
  const sessionID = String(event.sessionID);
25738
+ const original = hookInput(root, sessionID, String(event.id), shell);
25739
+ const heavy = leaseRewrite2(original);
25740
+ if (heavy) {
25741
+ event.input.command = heavy;
25742
+ shell.command = heavy;
25743
+ }
24804
25744
  const input = hookInput(root, sessionID, String(event.id), shell);
24805
25745
  workdirs.set(sessionID, input.cwd);
24806
25746
  if (workdirs.size > 256)
@@ -24972,37 +25912,37 @@ var createStopControl = (root, port) => {
24972
25912
 
24973
25913
  // packages/workit-opencode/src/v2/registry.ts
24974
25914
  import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
24975
- import path40 from "node:path";
25915
+ import path43 from "node:path";
24976
25916
 
24977
25917
  // packages/workit-opencode/src/shared/assets.ts
24978
25918
  import { existsSync as existsSync12, realpathSync as realpathSync9 } from "node:fs";
24979
- import path39 from "node:path";
25919
+ import path42 from "node:path";
24980
25920
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24981
25921
  var moduleDir = () => {
24982
25922
  try {
24983
- return realpathSync9(path39.dirname(fileURLToPath2(import.meta.url)));
25923
+ return realpathSync9(path42.dirname(fileURLToPath2(import.meta.url)));
24984
25924
  } catch {
24985
- return path39.dirname(fileURLToPath2(import.meta.url));
25925
+ return path42.dirname(fileURLToPath2(import.meta.url));
24986
25926
  }
24987
25927
  };
24988
25928
  var packageRoot2 = () => {
24989
25929
  const dir = moduleDir();
24990
25930
  for (const candidate of [
24991
- path39.resolve(dir, ".."),
24992
- path39.resolve(dir, "..", ".."),
24993
- path39.resolve(dir, "..", "..", "..")
25931
+ path42.resolve(dir, ".."),
25932
+ path42.resolve(dir, "..", ".."),
25933
+ path42.resolve(dir, "..", "..", "..")
24994
25934
  ]) {
24995
- if (existsSync12(path39.join(candidate, "package.json")))
25935
+ if (existsSync12(path42.join(candidate, "package.json")))
24996
25936
  return candidate;
24997
25937
  }
24998
- return path39.resolve(dir, "..");
25938
+ return path42.resolve(dir, "..");
24999
25939
  };
25000
25940
  var skillsRoot = () => {
25001
25941
  const root = packageRoot2();
25002
- const canonical = path39.resolve(root, "..", "workit-core", "skills");
25003
- if (existsSync12(path39.join(root, "src")) && existsSync12(canonical))
25942
+ const canonical = path42.resolve(root, "..", "workit-core", "skills");
25943
+ if (existsSync12(path42.join(root, "src")) && existsSync12(canonical))
25004
25944
  return canonical;
25005
- return path39.join(root, "assets", "skills");
25945
+ return path42.join(root, "assets", "skills");
25006
25946
  };
25007
25947
 
25008
25948
  // packages/workit-opencode/src/v2/registry.ts
@@ -25029,11 +25969,11 @@ var registerSkills = async (ctx) => {
25029
25969
  if (!existsSync13(skillsDir))
25030
25970
  return new Set;
25031
25971
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
25032
- const own = (skill) => typeof skill.path === "string" && path40.resolve(skill.path).startsWith(skillsDir + path40.sep);
25972
+ const own = (skill) => typeof skill.path === "string" && path43.resolve(skill.path).startsWith(skillsDir + path43.sep);
25033
25973
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
25034
25974
  const packaged = new Map;
25035
25975
  for (const id of WORKIT_METHOD_SKILLS) {
25036
- const file = path40.join(skillsDir, id, "SKILL.md");
25976
+ const file = path43.join(skillsDir, id, "SKILL.md");
25037
25977
  if (!userSkills.has(id) && existsSync13(file))
25038
25978
  packaged.set(id, file);
25039
25979
  }
@@ -25081,22 +26021,22 @@ var registerCommands = async (ctx, registeredSkills) => {
25081
26021
 
25082
26022
  // packages/workit-opencode/src/stale-sources.ts
25083
26023
  import { existsSync as existsSync14, readdirSync as readdirSync6 } from "node:fs";
25084
- import path41 from "node:path";
26024
+ import path44 from "node:path";
25085
26025
  import { fileURLToPath as fileURLToPath3 } from "node:url";
25086
26026
  var typescriptFiles = (directory) => {
25087
26027
  if (!existsSync14(directory))
25088
26028
  return [];
25089
26029
  return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
25090
- const file = path41.join(directory, entry.name);
26030
+ const file = path44.join(directory, entry.name);
25091
26031
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
25092
26032
  });
25093
26033
  };
25094
26034
  var pluginSourceFiles = [
25095
26035
  ...new Set([
25096
26036
  fileURLToPath3(import.meta.url),
25097
- ...typescriptFiles(path41.join(packageRoot2(), "src")),
25098
- path41.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
25099
- ...typescriptFiles(path41.join(packageRoot2(), "..", "workit-core", "src", "core"))
26037
+ ...typescriptFiles(path44.join(packageRoot2(), "src")),
26038
+ path44.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
26039
+ ...typescriptFiles(path44.join(packageRoot2(), "..", "workit-core", "src", "core"))
25100
26040
  ])
25101
26041
  ].filter((file) => existsSync14(file)).toSorted();
25102
26042
 
@@ -25213,8 +26153,11 @@ var setup = async (ctx) => {
25213
26153
  const pendingShells = new Map;
25214
26154
  await ctx.tool.hook("execute.before", async (event) => {
25215
26155
  if (event.tool === "shell") {
25216
- noteShellCall(pendingShells, { sessionID: String(event.sessionID), input: event.input });
25217
- return observeShellBefore(root, event, shellWorkdirs);
26156
+ observeShellBefore(root, event, shellWorkdirs);
26157
+ return noteShellCall(pendingShells, {
26158
+ sessionID: String(event.sessionID),
26159
+ input: event.input
26160
+ });
25218
26161
  }
25219
26162
  if (event.tool === "skill")
25220
26163
  return observeSkillLoad(root, event);