@brainervirus/workit-opencode 8.22.3 → 8.24.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
@@ -4,9 +4,9 @@ function define(plugin) {
4
4
  }
5
5
 
6
6
  // packages/workit-core/src/core.ts
7
- import { spawnSync as spawnSync16 } from "node:child_process";
8
- import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
9
- import path25 from "node:path";
7
+ import { spawnSync as spawnSync17 } from "node:child_process";
8
+ import { existsSync as existsSync10, realpathSync as realpathSync5 } from "node:fs";
9
+ import path27 from "node:path";
10
10
 
11
11
  // packages/workit-core/src/core/task-contract.ts
12
12
  import { createHash, randomUUID } from "node:crypto";
@@ -8430,7 +8430,7 @@ var workspaceConfigSchema = object({
8430
8430
  }).passthrough().optional(),
8431
8431
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
8432
8432
  defaultEndpoint: unknown().optional(),
8433
- verification: _enum(["self", "independent"]).optional(),
8433
+ verification: _enum(["proportional", "self", "independent"]).optional(),
8434
8434
  delegation: unknown().optional(),
8435
8435
  trailers: boolean2().optional(),
8436
8436
  liveSessionGuard: boolean2().optional(),
@@ -9015,6 +9015,124 @@ function resolveRef(cwd, ref) {
9015
9015
  return null;
9016
9016
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
9017
9017
  }
9018
+ var LANE_DIFF = [
9019
+ "-c",
9020
+ "core.quotePath=false",
9021
+ "diff",
9022
+ "--no-color",
9023
+ "--no-ext-diff",
9024
+ "--no-textconv",
9025
+ "--no-renames"
9026
+ ];
9027
+ var laneRange = (fork, head) => {
9028
+ if (!safeArg(fork) || head !== null && !safeArg(head))
9029
+ return null;
9030
+ return head === null ? [fork] : [fork, head];
9031
+ };
9032
+ function diffNumstat(cwd, fork, head) {
9033
+ const range = laneRange(fork, head);
9034
+ if (!range)
9035
+ return null;
9036
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
9037
+ if (!run.ok)
9038
+ return null;
9039
+ const out = [];
9040
+ for (const record of run.stdout.split("\x00")) {
9041
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
9042
+ if (!match)
9043
+ continue;
9044
+ const binary = match[1] === "-";
9045
+ out.push({
9046
+ path: match[3],
9047
+ added: binary ? 0 : Number(match[1]),
9048
+ deleted: binary ? 0 : Number(match[2]),
9049
+ binary
9050
+ });
9051
+ }
9052
+ return out;
9053
+ }
9054
+ function diffRaw(cwd, fork, head) {
9055
+ const range = laneRange(fork, head);
9056
+ if (!range)
9057
+ return null;
9058
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
9059
+ if (!run.ok)
9060
+ return null;
9061
+ const fields = run.stdout.split("\x00");
9062
+ const out = [];
9063
+ for (let index = 0;index + 1 < fields.length; index += 2) {
9064
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
9065
+ if (!meta)
9066
+ return null;
9067
+ out.push({
9068
+ path: fields[index + 1],
9069
+ oldMode: meta[1],
9070
+ newMode: meta[2],
9071
+ oldBlob: meta[3],
9072
+ newBlob: meta[4]
9073
+ });
9074
+ }
9075
+ return out;
9076
+ }
9077
+ function readBlobs(cwd, ids) {
9078
+ const out = new Map;
9079
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
9080
+ if (!wanted.length)
9081
+ return out;
9082
+ const run = spawnSync3("git", ["cat-file", "--batch"], {
9083
+ cwd,
9084
+ input: `${wanted.join(`
9085
+ `)}
9086
+ `,
9087
+ maxBuffer: MAX_BUFFER,
9088
+ timeout: GIT_TIMEOUTS.local,
9089
+ killSignal: "SIGKILL",
9090
+ windowsHide: true
9091
+ });
9092
+ if (run.status !== 0 || !run.stdout)
9093
+ return null;
9094
+ const bytes = run.stdout;
9095
+ let at = 0;
9096
+ for (const id of wanted) {
9097
+ const end = bytes.indexOf(10, at);
9098
+ if (end < 0)
9099
+ return null;
9100
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
9101
+ at = end + 1;
9102
+ if (header[1] === "missing")
9103
+ continue;
9104
+ const size = Number(header[2]);
9105
+ if (header[0] !== id || !Number.isSafeInteger(size))
9106
+ return null;
9107
+ out.set(id, bytes.subarray(at, at + size));
9108
+ at += size + 1;
9109
+ }
9110
+ return out;
9111
+ }
9112
+ function treeFilesNamed(cwd, rev, name) {
9113
+ if (!safeArg(rev))
9114
+ return null;
9115
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
9116
+ if (!run.ok)
9117
+ return null;
9118
+ const out = new Map;
9119
+ for (const record of run.stdout.split("\x00")) {
9120
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
9121
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
9122
+ out.set(match[2], match[1]);
9123
+ }
9124
+ return out;
9125
+ }
9126
+ function fileAt(cwd, rev, file) {
9127
+ if (!safeArg(rev))
9128
+ return null;
9129
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
9130
+ return run.ok ? run.stdout : null;
9131
+ }
9132
+ function untrackedFiles(cwd) {
9133
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
9134
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
9135
+ }
9018
9136
  var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
9019
9137
  function currentBranch(cwd) {
9020
9138
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
@@ -12775,9 +12893,9 @@ var holdsLiveState = (state, dirty) => state === "running" || dirty === true;
12775
12893
 
12776
12894
  // packages/workit-core/src/core/task-evaluation.ts
12777
12895
  import { createHash as createHash12 } from "node:crypto";
12778
- import * as fs14 from "node:fs";
12779
- import { spawnSync as spawnSync10 } from "node:child_process";
12780
- import path17 from "node:path";
12896
+ import * as fs15 from "node:fs";
12897
+ import { spawnSync as spawnSync12 } from "node:child_process";
12898
+ import path20 from "node:path";
12781
12899
 
12782
12900
  // packages/workit-core/src/check-config.ts
12783
12901
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -13007,8 +13125,8 @@ function checkCommand(args, version, onPath = workitAvailable()) {
13007
13125
  }
13008
13126
 
13009
13127
  // packages/workit-core/src/core/policy/derive.ts
13010
- import { existsSync as existsSync4 } from "node:fs";
13011
- import path16 from "node:path";
13128
+ import { existsSync as existsSync6 } from "node:fs";
13129
+ import path19 from "node:path";
13012
13130
 
13013
13131
  // packages/workit-core/src/ledger.ts
13014
13132
  import { spawnSync as spawnSync9 } from "node:child_process";
@@ -13912,7 +14030,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13912
14030
  verdict: failing?.verdict ?? null,
13913
14031
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
13914
14032
  };
13915
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14033
+ const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
14034
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
13916
14035
  return {
13917
14036
  branch,
13918
14037
  head,
@@ -13956,151 +14075,1001 @@ var OBSERVED_ONLY = new Set([
13956
14075
  ...PR_ROW_TYPES
13957
14076
  ]);
13958
14077
 
13959
- // packages/workit-core/src/core/policy/derive.ts
13960
- var RULES = {
13961
- test: "check:test",
13962
- self: "verdict:self",
13963
- nonAuthor: "verdict:non-author",
13964
- verified: "verdict:verified",
13965
- product: "decision:product",
13966
- plan: "plan"
13967
- };
13968
- var derived2 = (spec) => ({
13969
- ...spec,
13970
- id: requirementId({
13971
- ruleId: spec.ruleId,
13972
- scope: spec.scope,
13973
- satisfaction: spec.satisfaction,
13974
- before: spec.before,
13975
- dependentAction: spec.dependentAction
13976
- })
13977
- });
13978
- var sorted = (values) => [...new Set(values)].toSorted();
13979
- var normalizedScope = (scope) => ({
13980
- description: scope.description,
13981
- paths: sorted(scope.paths),
13982
- exclusions: sorted(scope.exclusions)
13983
- });
13984
- function deriveRequirements(judgment, scope, constraints, verification = "self") {
13985
- const out = [];
13986
- const base = { scope, dependentAction: null };
13987
- if (judgment.behaviorChange)
13988
- out.push(derived2({
13989
- ...base,
13990
- ruleId: RULES.test,
13991
- dimension: "testing",
13992
- reason: "Behavior changes: the configured test check must pass on the final tree.",
13993
- satisfaction: "Run `workit check test` on the final tree.",
13994
- before: "close",
13995
- acceptanceAllowed: false
13996
- }));
13997
- if (judgment.behaviorChange && judgment.riskTier === "normal")
13998
- out.push(verification === "independent" ? derived2({
13999
- ...base,
14000
- ruleId: RULES.nonAuthor,
14001
- dimension: "review",
14002
- reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
14003
- satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
14004
- before: "close",
14005
- acceptanceAllowed: false
14006
- }) : derived2({
14007
- ...base,
14008
- ruleId: RULES.self,
14009
- dimension: "review",
14010
- reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
14011
- satisfaction: "After an observed passing `workit check test`, record `workit ledger verdict tests-verified --self --how <what you ran>` (or have a verifier record one).",
14012
- before: "close",
14013
- acceptanceAllowed: false
14014
- }));
14015
- if (judgment.riskTier === "high")
14016
- out.push(derived2({
14017
- ...base,
14018
- ruleId: RULES.verified,
14019
- dimension: "review",
14020
- reason: "High risk: an independent verifier must exercise the live surface.",
14021
- satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
14022
- before: "close",
14023
- acceptanceAllowed: false
14024
- }));
14025
- if (judgment.productChoiceOpen)
14026
- out.push(derived2({
14027
- ...base,
14028
- ruleId: RULES.product,
14029
- dimension: "decisions",
14030
- reason: "A product or preference choice is open; the user decides it before code is written.",
14031
- satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
14032
- before: "write",
14033
- acceptanceAllowed: false
14034
- }));
14035
- if (judgment.needsPlan || judgment.riskTier === "high")
14036
- out.push(derived2({
14037
- ...base,
14038
- ruleId: RULES.plan,
14039
- dimension: "artifacts",
14040
- reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
14041
- satisfaction: "Write the plan or spec (e.g. docs/plans/<slug>.md; docs and Markdown writes stay allowed), then record it: `workit policy assess --ref <path>`.",
14042
- before: "write",
14043
- acceptanceAllowed: true
14044
- }));
14045
- for (const constraint of constraints)
14046
- for (const obligation of constraint.requires)
14047
- out.push(derived2({
14048
- ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
14049
- kind: obligation.kind,
14050
- scope: normalizedScope(obligation.scope),
14051
- refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
14052
- method: obligation.method,
14053
- before: obligation.before,
14054
- dependentAction: obligation.dependentAction
14055
- })}`,
14056
- dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
14057
- scope: obligation.scope,
14058
- reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
14059
- satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
14060
- before: obligation.before,
14061
- dependentAction: obligation.dependentAction,
14062
- acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
14063
- }));
14064
- const seen = new Set;
14065
- return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
14078
+ // packages/workit-core/src/core/policy/lane.ts
14079
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
14080
+ import path18 from "node:path";
14081
+
14082
+ // packages/workit-core/src/git/ops.ts
14083
+ import { spawnSync as spawnSync11 } from "node:child_process";
14084
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
14085
+ import path17 from "node:path";
14086
+
14087
+ // packages/workit-core/src/core/pr-create.ts
14088
+ import fs14 from "node:fs";
14089
+ import path16 from "node:path";
14090
+ import { spawnSync as spawnSync10 } from "node:child_process";
14091
+ import { pathToFileURL } from "node:url";
14092
+ // packages/workit-core/src/forge/pr-body.ts
14093
+ function parseGhIssue(value) {
14094
+ const m = /issues\/(\d+)/.exec(value);
14095
+ return m ? m[1] : String(value).trim().replace(/^#/, "");
14066
14096
  }
14067
- var latestJudgment = (task) => task.judgments?.at(-1)?.data ?? null;
14068
- var ledgerView = (root) => {
14069
- let branch;
14070
- let rows;
14071
- return {
14072
- branch: () => branch === undefined ? branch = currentBranch(root) : branch,
14073
- rows: () => {
14074
- if (rows === undefined) {
14075
- const read = readLedger(root);
14076
- rows = read.ok ? read.value.rows : [];
14077
- }
14078
- return rows;
14097
+
14098
+ // packages/workit-core/src/core/pr-create.ts
14099
+ function whichOnPath(tool) {
14100
+ const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
14101
+ for (const dir of (process.env.PATH ?? "").split(path16.delimiter)) {
14102
+ if (!dir)
14103
+ continue;
14104
+ for (const name of names) {
14105
+ const candidate = path16.join(dir, name);
14106
+ try {
14107
+ fs14.accessSync(candidate, fs14.constants.X_OK);
14108
+ return candidate;
14109
+ } catch {}
14079
14110
  }
14080
- };
14111
+ }
14112
+ return null;
14113
+ }
14114
+ var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
14115
+ var pushRemote = (cwd) => {
14116
+ const result = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
14117
+ cwd,
14118
+ encoding: "utf8"
14119
+ });
14120
+ if (result.status !== 0)
14121
+ return null;
14122
+ const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
14123
+ return urls.length === 1 ? urls[0] : null;
14081
14124
  };
14082
- var productDecision = (task, ledger) => {
14083
- const decisions = task.decisions.filter((entry) => entry.data.revoked === null && (entry.data.response === "approved" || entry.data.response === "stated") && entry.data.purpose !== "limitation");
14084
- if (decisions.length)
14085
- return {
14086
- met: true,
14087
- reason: "the user's decision is recorded on the task",
14088
- decisionIds: decisions.map((entry) => entry.id)
14089
- };
14090
- const since = Date.parse(task.createdAt);
14091
- const branch = ledger.branch();
14092
- const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
14093
- return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
14094
- met: false,
14095
- reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
14096
- decisionIds: []
14097
- };
14125
+ var safePushUrl = (remote) => {
14126
+ const raw = remote.trim();
14127
+ if (path16.isAbsolute(raw))
14128
+ return pathToFileURL(raw).href;
14129
+ if (!raw.includes("://")) {
14130
+ const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
14131
+ if (scp)
14132
+ return `${scp[1] || "git"}@${scp[2]}:${scp[3]}`;
14133
+ }
14134
+ try {
14135
+ const url = new URL(raw);
14136
+ if (!["file:", "git:", "http:", "https:", "ssh:"].includes(url.protocol))
14137
+ return null;
14138
+ url.password = "";
14139
+ if (url.protocol !== "ssh:")
14140
+ url.username = "";
14141
+ url.search = "";
14142
+ url.hash = "";
14143
+ return url.toString();
14144
+ } catch {
14145
+ return null;
14146
+ }
14098
14147
  };
14099
- var planRecorded = (task, root) => {
14100
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path16.resolve(root, ref.path)));
14101
- if (cited)
14102
- return {
14103
- met: true,
14148
+ var pushTargetIsStable = (cwd, remote) => {
14149
+ const target = safePushUrl(remote);
14150
+ if (!target)
14151
+ return false;
14152
+ const result = spawnSync10("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
14153
+ if (result.status !== 0 && result.status !== 1)
14154
+ return false;
14155
+ const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
14156
+ return !prefixes.some((prefix) => target.startsWith(prefix));
14157
+ };
14158
+
14159
+ // packages/workit-core/src/forge/redact.ts
14160
+ 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_.-]*`;
14161
+ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
14162
+ var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
14163
+ var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
14164
+
14165
+ // packages/workit-core/src/forge/types.ts
14166
+ var success2 = (data) => ({ ok: true, data });
14167
+ var failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
14168
+
14169
+ // packages/workit-core/src/git/ops.ts
14170
+ var COMMIT_TIMEOUT_MS = 10 * 60000;
14171
+ var git5 = (cwd, args, options = {}) => {
14172
+ const result = spawnSync11("git", args, {
14173
+ cwd,
14174
+ encoding: "utf8",
14175
+ env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
14176
+ maxBuffer: 64 * 1024 * 1024,
14177
+ timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
14178
+ killSignal: "SIGKILL",
14179
+ windowsHide: true,
14180
+ ...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
14181
+ });
14182
+ return {
14183
+ ok: result.status === 0,
14184
+ status: result.status,
14185
+ stdout: result.stdout ?? "",
14186
+ stderr: result.stderr ?? ""
14187
+ };
14188
+ };
14189
+ function operationInProgress(cwd) {
14190
+ const markers = [
14191
+ ["rebase-merge", "rebase"],
14192
+ ["rebase-apply", "rebase"],
14193
+ ["MERGE_HEAD", "merge"],
14194
+ ["CHERRY_PICK_HEAD", "cherry-pick"],
14195
+ ["REVERT_HEAD", "revert"]
14196
+ ];
14197
+ const run = git5(cwd, ["rev-parse", ...markers.flatMap(([name]) => ["--git-path", name])]);
14198
+ if (!run.ok)
14199
+ return null;
14200
+ const found = run.stdout.split(`
14201
+ `).map((line) => path17.resolve(cwd, line.trim()));
14202
+ const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
14203
+ for (const [index, [, operation]] of markers.entries()) {
14204
+ const marker = found[index];
14205
+ if (!marker || !existsSync4(marker))
14206
+ continue;
14207
+ if (index === 1 && existsSync4(path17.join(marker, "applying")))
14208
+ return { operation: "am", commitAllowed: false, branch: null };
14209
+ if (index === 0 && existsSync4(path17.join(marker, "amend")) && !conflicted()) {
14210
+ let headName = "";
14211
+ try {
14212
+ headName = readFileSync4(path17.join(marker, "head-name"), "utf8").trim();
14213
+ } catch {}
14214
+ const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
14215
+ return { operation, commitAllowed: branch !== null, branch };
14216
+ }
14217
+ if ((operation === "revert" || operation === "merge") && !conflicted())
14218
+ return { operation, commitAllowed: true, branch: null };
14219
+ return { operation, commitAllowed: false, branch: null };
14220
+ }
14221
+ return null;
14222
+ }
14223
+ function inProgressFailure(operation, verb) {
14224
+ const command = operation === "am" ? "git am" : `git ${operation}`;
14225
+ return failure3("blocked", `${operation.replace("-", "_")}_in_progress: a ${operation === "am" ? "git am" : operation} is in progress; finish or abort it before workit can ${verb}`, `resolve the conflicts, git add <files>, then ${command} --continue # or ${command} --abort to return to where you started`);
14226
+ }
14227
+ function pushPreflight(cwd, options = {}) {
14228
+ const progress = operationInProgress(cwd);
14229
+ if (progress)
14230
+ return inProgressFailure(progress.operation, "push");
14231
+ const branch = options.branch ?? currentBranch(cwd);
14232
+ if (!branch)
14233
+ return failure3("invalid_input", "HEAD is detached; push a branch");
14234
+ if (isProtectedTarget(cwd, branch))
14235
+ return failure3("blocked", `protected_branch: ${branch} is protected by the workspace branch policy; workit git push never pushes to it`, releaseBranch(cwd, branch) ? `workit git push --release ${branch} # finishing a release or hotfix; needs the release grant` : "workit git branch <feature/name> # push a feature branch and open a PR");
14236
+ const sha = resolveRef(cwd, `refs/heads/${branch}`);
14237
+ if (!sha)
14238
+ return failure3("not_found", `branch ${branch} has no commit to push`);
14239
+ const remote = pushRemoteName(cwd, branch);
14240
+ if (!remote)
14241
+ return failure3("not_found", "no push remote is configured for this branch", "git remote add origin <url>");
14242
+ const raw = pushUrl(cwd, remote);
14243
+ if (!raw)
14244
+ return failure3("blocked", `remote "${remote}" has no single push URL`, `git remote get-url --push --all ${remote} # keep exactly one push URL`);
14245
+ const parsed = parseRemoteUrl(raw);
14246
+ const local = parsed?.protocol === "file";
14247
+ if (!local && !pushTargetIsStable(cwd, raw))
14248
+ return failure3("blocked", `push_target_rewritten: the push URL of "${remote}" is rewritten by a url.*.insteadOf rule, so the destination is not the one checked`, "git config --get-regexp '^url\\.' # remove the rewrite for this remote");
14249
+ return success2({
14250
+ branch,
14251
+ sha,
14252
+ remote,
14253
+ url: redactRemote(raw),
14254
+ rawUrl: raw,
14255
+ local
14256
+ });
14257
+ }
14258
+
14259
+ // packages/workit-core/src/core/policy/attributes.ts
14260
+ function patternRegex(dir, pattern) {
14261
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
14262
+ return null;
14263
+ const anchored = pattern.includes("/");
14264
+ const body = pattern.replace(/^\//u, "");
14265
+ let source = "";
14266
+ for (let index = 0;index < body.length; index += 1) {
14267
+ const char = body[index];
14268
+ if (char === "*" && body[index + 1] === "*") {
14269
+ const slash = body[index + 2] === "/";
14270
+ source += slash ? "(?:.*/)?" : ".*";
14271
+ index += slash ? 2 : 1;
14272
+ } else if (char === "*")
14273
+ source += "[^/]*";
14274
+ else if (char === "?")
14275
+ source += "[^/]";
14276
+ else if (char === "[") {
14277
+ const end = body.indexOf("]", index + 2);
14278
+ if (end < 0)
14279
+ return null;
14280
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
14281
+ source += `[${set}]`;
14282
+ index = end;
14283
+ } else if (char === "\\" && index + 1 < body.length) {
14284
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
14285
+ index += 1;
14286
+ } else
14287
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
14288
+ }
14289
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
14290
+ try {
14291
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
14292
+ } catch {
14293
+ return null;
14294
+ }
14295
+ }
14296
+ function rulesOf(dir, text) {
14297
+ const out = [];
14298
+ for (const raw of text.split(`
14299
+ `)) {
14300
+ const line = raw.trim();
14301
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
14302
+ continue;
14303
+ const [pattern, ...attributes] = line.split(/\s+/u);
14304
+ let generated = null;
14305
+ for (const attribute of attributes) {
14306
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
14307
+ generated = true;
14308
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
14309
+ generated = false;
14310
+ }
14311
+ if (generated === null)
14312
+ continue;
14313
+ const regex = patternRegex(dir, pattern);
14314
+ if (regex)
14315
+ out.push({ regex, generated });
14316
+ }
14317
+ return out;
14318
+ }
14319
+ function generatedByAttributes(files, paths) {
14320
+ const rules = [...files].map(([file, text]) => {
14321
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
14322
+ return {
14323
+ depth: dir ? dir.split("/").length : 0,
14324
+ rules: rulesOf(dir, text)
14325
+ };
14326
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
14327
+ const out = new Set;
14328
+ for (const file of paths) {
14329
+ const last = rules.findLast((rule) => rule.regex.test(file));
14330
+ if (last?.generated)
14331
+ out.add(file);
14332
+ }
14333
+ return out;
14334
+ }
14335
+
14336
+ // packages/workit-core/src/core/policy/substance.ts
14337
+ var SLASH_BLOCK = [["/*", "*/"]];
14338
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
14339
+ var SYNTAXES = Object.fromEntries([
14340
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
14341
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
14342
+ ext,
14343
+ { ...C_LIKE, triple: true }
14344
+ ]),
14345
+ ["go", { ...C_LIKE, rawBacktick: true }],
14346
+ ...["css", "scss", "less"].map((ext) => [
14347
+ ext,
14348
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
14349
+ ]),
14350
+ ...["py", "pyi"].map((ext) => [
14351
+ ext,
14352
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
14353
+ ]),
14354
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
14355
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
14356
+ ext,
14357
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
14358
+ ])
14359
+ ]);
14360
+ var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
14361
+ var extensionOf = (file) => {
14362
+ const name = file.slice(file.lastIndexOf("/") + 1);
14363
+ const dot = name.lastIndexOf(".");
14364
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
14365
+ };
14366
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
14367
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
14368
+ var SPACE = /[ \t\r\f\v]/u;
14369
+ var WORD = /[\w$]/u;
14370
+ var NO_SPACE_AFTER = "([{,;";
14371
+ var NO_SPACE_BEFORE = ")]},;";
14372
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
14373
+ var REGEX_KEYWORDS = new Set([
14374
+ "return",
14375
+ "typeof",
14376
+ "instanceof",
14377
+ "case",
14378
+ "do",
14379
+ "else",
14380
+ "in",
14381
+ "of",
14382
+ "new",
14383
+ "delete",
14384
+ "void",
14385
+ "throw",
14386
+ "yield",
14387
+ "await"
14388
+ ]);
14389
+ function substanceOf(file, text) {
14390
+ const syntax = syntaxOf(file);
14391
+ if (!syntax)
14392
+ return plainSubstance(text);
14393
+ const markup = syntax.block.some(([open]) => open === "<!--");
14394
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
14395
+ return plainSubstance(text);
14396
+ return lex(syntax, text);
14397
+ }
14398
+ function plainSubstance(text) {
14399
+ return text.split(`
14400
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
14401
+ `);
14402
+ }
14403
+ function lex(syntax, text) {
14404
+ const collapse = !syntax.indented;
14405
+ let out = "";
14406
+ let lineStart = 0;
14407
+ let kept = 0;
14408
+ let pendingSpace = false;
14409
+ const frames = [{ kind: "code", braces: 0 }];
14410
+ let index = 0;
14411
+ if (text.startsWith("#!")) {
14412
+ const end = text.indexOf(`
14413
+ `);
14414
+ out = end < 0 ? text : text.slice(0, end);
14415
+ kept = out.length;
14416
+ index = end < 0 ? text.length : end;
14417
+ }
14418
+ const emitCode = (chunk) => {
14419
+ if (collapse && pendingSpace && out.length > lineStart) {
14420
+ const before = out[out.length - 1];
14421
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
14422
+ out += " ";
14423
+ }
14424
+ pendingSpace = false;
14425
+ out += chunk;
14426
+ };
14427
+ const emitVerbatim = (chunk) => {
14428
+ emitCode(chunk);
14429
+ kept = out.length;
14430
+ };
14431
+ const newline = () => {
14432
+ if (!collapse) {
14433
+ let end = out.length;
14434
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
14435
+ end -= 1;
14436
+ out = out.slice(0, end);
14437
+ }
14438
+ pendingSpace = false;
14439
+ if (out.length > lineStart) {
14440
+ out += `
14441
+ `;
14442
+ lineStart = out.length;
14443
+ }
14444
+ };
14445
+ const regexAllowed = () => {
14446
+ let end = out.length;
14447
+ while (end > 0 && /\s/u.test(out[end - 1]))
14448
+ end -= 1;
14449
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
14450
+ return true;
14451
+ let start = end;
14452
+ while (start > 0 && WORD.test(out[start - 1]))
14453
+ start -= 1;
14454
+ return REGEX_KEYWORDS.has(out.slice(start, end));
14455
+ };
14456
+ const scanString = (quote, escapes, multiline) => {
14457
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
14458
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
14459
+ let end = index + quote.length;
14460
+ while (end < text.length) {
14461
+ const char = text[end];
14462
+ if (escapes && char === "\\") {
14463
+ end += 2;
14464
+ continue;
14465
+ }
14466
+ if (text.startsWith(quote, end)) {
14467
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
14468
+ return false;
14469
+ emitVerbatim(text.slice(index, end + quote.length));
14470
+ index = end + quote.length;
14471
+ return true;
14472
+ }
14473
+ if (char === `
14474
+ ` && !multiline)
14475
+ return false;
14476
+ end += 1;
14477
+ }
14478
+ return false;
14479
+ };
14480
+ while (index < text.length) {
14481
+ const frame = frames[frames.length - 1];
14482
+ const char = text[index];
14483
+ if (frame.kind === "template") {
14484
+ if (char === "\\") {
14485
+ emitVerbatim(text.slice(index, index + 2));
14486
+ index += 2;
14487
+ } else if (char === "`") {
14488
+ emitVerbatim(char);
14489
+ frames.pop();
14490
+ index += 1;
14491
+ } else if (text.startsWith("${", index)) {
14492
+ emitVerbatim("${");
14493
+ frames.push({ kind: "code", braces: 0 });
14494
+ index += 2;
14495
+ } else {
14496
+ out += char;
14497
+ kept = out.length;
14498
+ index += 1;
14499
+ }
14500
+ continue;
14501
+ }
14502
+ if (char === `
14503
+ `) {
14504
+ newline();
14505
+ index += 1;
14506
+ continue;
14507
+ }
14508
+ if (SPACE.test(char)) {
14509
+ if (collapse)
14510
+ pendingSpace = true;
14511
+ else
14512
+ out += char;
14513
+ index += 1;
14514
+ continue;
14515
+ }
14516
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
14517
+ if (lineOpener) {
14518
+ const end = text.indexOf(`
14519
+ `, index);
14520
+ const comment = text.slice(index, end < 0 ? text.length : end);
14521
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
14522
+ emitVerbatim(comment);
14523
+ else
14524
+ pendingSpace = true;
14525
+ index = end < 0 ? text.length : end;
14526
+ continue;
14527
+ }
14528
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
14529
+ if (block) {
14530
+ const end = text.indexOf(block[1], index + block[0].length);
14531
+ if (end < 0)
14532
+ return null;
14533
+ const comment = text.slice(index, end + block[1].length);
14534
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
14535
+ emitVerbatim(comment);
14536
+ else
14537
+ pendingSpace = true;
14538
+ if (comment.includes(`
14539
+ `))
14540
+ newline();
14541
+ index = end + block[1].length;
14542
+ continue;
14543
+ }
14544
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
14545
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
14546
+ return null;
14547
+ continue;
14548
+ }
14549
+ if (syntax.quotes.includes(char)) {
14550
+ if (!scanString(char, true, Boolean(syntax.multiline)))
14551
+ return null;
14552
+ continue;
14553
+ }
14554
+ if (syntax.literal?.includes(char)) {
14555
+ if (!scanString(char, false, false))
14556
+ return null;
14557
+ continue;
14558
+ }
14559
+ if (syntax.rawBacktick && char === "`") {
14560
+ if (!scanString("`", false, true))
14561
+ return null;
14562
+ continue;
14563
+ }
14564
+ if (syntax.js && char === "`") {
14565
+ emitVerbatim(char);
14566
+ frames.push({ kind: "template" });
14567
+ index += 1;
14568
+ continue;
14569
+ }
14570
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
14571
+ return null;
14572
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
14573
+ const lineEnd = text.indexOf(`
14574
+ `, index);
14575
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
14576
+ return null;
14577
+ }
14578
+ if (syntax.js && char === "/" && regexAllowed()) {
14579
+ let end = index + 1;
14580
+ let inClass = false;
14581
+ while (end < text.length && (inClass || text[end] !== "/")) {
14582
+ if (text[end] === `
14583
+ `)
14584
+ return null;
14585
+ if (text[end] === "\\")
14586
+ end += 1;
14587
+ else if (text[end] === "[")
14588
+ inClass = true;
14589
+ else if (text[end] === "]")
14590
+ inClass = false;
14591
+ end += 1;
14592
+ }
14593
+ if (end >= text.length)
14594
+ return null;
14595
+ emitVerbatim(text.slice(index, end + 1));
14596
+ index = end + 1;
14597
+ continue;
14598
+ }
14599
+ if (frames.length > 1 && frame.kind === "code") {
14600
+ if (char === "{")
14601
+ frame.braces += 1;
14602
+ else if (char === "}") {
14603
+ if (frame.braces === 0) {
14604
+ emitVerbatim(char);
14605
+ frames.pop();
14606
+ index += 1;
14607
+ continue;
14608
+ }
14609
+ frame.braces -= 1;
14610
+ }
14611
+ }
14612
+ emitCode(char);
14613
+ index += 1;
14614
+ }
14615
+ if (frames.length !== 1)
14616
+ return null;
14617
+ newline();
14618
+ return out;
14619
+ }
14620
+ function unbalanced(body) {
14621
+ let depth = 0;
14622
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
14623
+ if (part === "{")
14624
+ depth += 1;
14625
+ else if (part === "}")
14626
+ depth = Math.max(0, depth - 1);
14627
+ }
14628
+ return depth > 0;
14629
+ }
14630
+ function hasOpaqueLineBreak(text) {
14631
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
14632
+ }
14633
+ var decoder = new TextDecoder("utf-8", { fatal: true });
14634
+ function textOf(bytes) {
14635
+ if (bytes.includes(0))
14636
+ return null;
14637
+ try {
14638
+ return decoder.decode(bytes);
14639
+ } catch {
14640
+ return null;
14641
+ }
14642
+ }
14643
+ function cosmeticChange(file, before, after) {
14644
+ const left = textOf(before);
14645
+ const right = textOf(after);
14646
+ if (left === null || right === null)
14647
+ return false;
14648
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
14649
+ return false;
14650
+ const a = substanceOf(file, left);
14651
+ return a !== null && a === substanceOf(file, right);
14652
+ }
14653
+
14654
+ // packages/workit-core/src/core/policy/lane.ts
14655
+ var LANE_THRESHOLDS = {
14656
+ selfMaxLines: 100,
14657
+ selfMaxFiles: 5,
14658
+ fullMinLines: 500
14659
+ };
14660
+ var RISK_TOKENS = new Set([
14661
+ "auth",
14662
+ "authn",
14663
+ "authz",
14664
+ "oauth",
14665
+ "security",
14666
+ "permission",
14667
+ "permissions",
14668
+ "secret",
14669
+ "secrets",
14670
+ "crypto",
14671
+ "gate",
14672
+ "gates",
14673
+ "policy",
14674
+ "policies",
14675
+ "hook",
14676
+ "hooks",
14677
+ "husky",
14678
+ "lefthook",
14679
+ "release",
14680
+ "releases",
14681
+ "migration",
14682
+ "migrations",
14683
+ "migrate",
14684
+ "workflows"
14685
+ ]);
14686
+ var RISK_FILES = new Set([
14687
+ ".gitlab-ci.yml",
14688
+ "Jenkinsfile",
14689
+ "azure-pipelines.yml",
14690
+ ".pre-commit-config.yaml",
14691
+ ".workit/risk-paths"
14692
+ ]);
14693
+ var RISK_NAMES = new Set([".gitattributes"]);
14694
+ var RISK_PATHS_FILE = ".workit/risk-paths";
14695
+ var LOCKFILES = new Set([
14696
+ "bun.lock",
14697
+ "bun.lockb",
14698
+ "package-lock.json",
14699
+ "npm-shrinkwrap.json",
14700
+ "yarn.lock",
14701
+ "pnpm-lock.yaml",
14702
+ "Cargo.lock",
14703
+ "go.sum",
14704
+ "poetry.lock",
14705
+ "uv.lock",
14706
+ "Pipfile.lock",
14707
+ "Gemfile.lock",
14708
+ "composer.lock"
14709
+ ]);
14710
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
14711
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
14712
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
14713
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
14714
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
14715
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
14716
+ function kindOf(file) {
14717
+ if (file.binary)
14718
+ return "behavior";
14719
+ if (file.generated)
14720
+ return "generated";
14721
+ if (LOCKFILES.has(basename(file.path)))
14722
+ return "lockfile";
14723
+ if (isDoc(file.path))
14724
+ return "doc";
14725
+ if (file.cosmetic)
14726
+ return "cosmetic";
14727
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
14728
+ }
14729
+ var globCache = new Map;
14730
+ function globMatch(pattern, file) {
14731
+ let regex = globCache.get(pattern);
14732
+ if (!regex) {
14733
+ let source = "";
14734
+ for (let index = 0;index < pattern.length; index += 1) {
14735
+ const char = pattern[index];
14736
+ if (char === "*" && pattern[index + 1] === "*") {
14737
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
14738
+ index += pattern[index + 2] === "/" ? 2 : 1;
14739
+ } else if (char === "*")
14740
+ source += "[^/]*";
14741
+ else if (char === "?")
14742
+ source += "[^/]";
14743
+ else
14744
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
14745
+ }
14746
+ regex = new RegExp(`^${source}$`, "u");
14747
+ globCache.set(pattern, regex);
14748
+ }
14749
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
14750
+ }
14751
+ function isRiskPath(file, extra = []) {
14752
+ if (extra.some((pattern) => globMatch(pattern, file)))
14753
+ return true;
14754
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
14755
+ return true;
14756
+ if (TEST_PATH.test(file) || isDoc(file))
14757
+ return false;
14758
+ return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
14759
+ }
14760
+ var parseRiskPaths = (text) => text.split(`
14761
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
14762
+ function computeLane(input) {
14763
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
14764
+ const risky = new Set(riskFiles);
14765
+ const kinds = input.files.map((file) => ({
14766
+ file,
14767
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
14768
+ }));
14769
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
14770
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
14771
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
14772
+ const result = (lane, reasons) => ({
14773
+ lane,
14774
+ reasons,
14775
+ lines,
14776
+ files: behavior.length,
14777
+ riskFiles
14778
+ });
14779
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
14780
+ if (input.riskTier === "high")
14781
+ return result("full", ["the task judgment records high risk"]);
14782
+ if (riskFiles.length)
14783
+ return result("full", [
14784
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
14785
+ ]);
14786
+ if (!behavior.length)
14787
+ return result("none", [
14788
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
14789
+ ]);
14790
+ if (lines >= fullMinLines)
14791
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
14792
+ if (lines > selfMaxLines)
14793
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
14794
+ if (behavior.length > selfMaxFiles)
14795
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
14796
+ if (opaque.length)
14797
+ return result("focused", [
14798
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
14799
+ ]);
14800
+ return result("self", [
14801
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
14802
+ ]);
14803
+ }
14804
+ function untrackedStat(cwd, file) {
14805
+ try {
14806
+ const bytes = readFileSync5(path18.join(cwd, file));
14807
+ if (bytes.includes(0))
14808
+ return { path: file, added: 0, deleted: 0, binary: true };
14809
+ const text = bytes.toString("utf8");
14810
+ if (hasOpaqueLineBreak(text))
14811
+ return { path: file, added: 0, deleted: 0, binary: true };
14812
+ const lines = text.split(`
14813
+ `).length - (text.endsWith(`
14814
+ `) ? 1 : 0);
14815
+ return { path: file, added: lines, deleted: 0 };
14816
+ } catch {
14817
+ return { path: file, added: 0, deleted: 0, binary: true };
14818
+ }
14819
+ }
14820
+ var REGULAR = new Set(["100644", "100755"]);
14821
+ var ABSENT = /^0+$/u;
14822
+ function cosmeticFiles(cwd, fork, head) {
14823
+ const raw = diffRaw(cwd, fork, head);
14824
+ if (!raw)
14825
+ return null;
14826
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
14827
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
14828
+ if (!blobs)
14829
+ return null;
14830
+ const out = new Set;
14831
+ const opaque = new Set;
14832
+ for (const entry of candidates) {
14833
+ const before = blobs.get(entry.oldBlob);
14834
+ let after = blobs.get(entry.newBlob);
14835
+ if (!after && head === null && ABSENT.test(entry.newBlob))
14836
+ try {
14837
+ after = readFileSync5(path18.join(cwd, entry.path));
14838
+ } catch {
14839
+ after = undefined;
14840
+ }
14841
+ if (before && after && cosmeticChange(entry.path, before, after))
14842
+ out.add(entry.path);
14843
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
14844
+ opaque.add(entry.path);
14845
+ }
14846
+ return { cosmetic: out, opaque };
14847
+ }
14848
+ function generatedAt(cwd, fork, paths) {
14849
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
14850
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
14851
+ if (!files || !blobs)
14852
+ return new Set;
14853
+ const texts = new Map;
14854
+ for (const [file, id] of files) {
14855
+ const bytes = blobs.get(id);
14856
+ if (bytes)
14857
+ texts.set(file, bytes.toString("utf8"));
14858
+ }
14859
+ return generatedByAttributes(texts, paths);
14860
+ }
14861
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
14862
+ function laneFiles(cwd, fork, head) {
14863
+ const stats = diffNumstat(cwd, fork, head);
14864
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
14865
+ if (!stats || !cosmetic)
14866
+ return null;
14867
+ const files = [
14868
+ ...stats.map((entry) => ({
14869
+ ...entry,
14870
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
14871
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
14872
+ })),
14873
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
14874
+ ];
14875
+ const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
14876
+ return files.map((entry) => ({
14877
+ ...entry,
14878
+ generated: generated.has(entry.path)
14879
+ }));
14880
+ }
14881
+ function branchLane(cwd, options) {
14882
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
14883
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
14884
+ const head = options.workingTree ? null : options.head;
14885
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
14886
+ const files = fork ? laneFiles(cwd, fork, head) : null;
14887
+ const riskTier = options.judgment?.riskTier ?? null;
14888
+ if (!fork || !files) {
14889
+ const high = riskTier === "high";
14890
+ return {
14891
+ lane: high ? "full" : "focused",
14892
+ reasons: [
14893
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
14894
+ ...high ? ["the task judgment records high risk"] : []
14895
+ ],
14896
+ lines: 0,
14897
+ files: 0,
14898
+ riskFiles: [],
14899
+ base,
14900
+ head: options.head
14901
+ };
14902
+ }
14903
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
14904
+ return {
14905
+ ...computeLane({
14906
+ files,
14907
+ riskTier,
14908
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
14909
+ }),
14910
+ base,
14911
+ head: options.head
14912
+ };
14913
+ }
14914
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
14915
+
14916
+ // packages/workit-core/src/core/policy/derive.ts
14917
+ var RULES = {
14918
+ test: "check:test",
14919
+ self: "verdict:self",
14920
+ nonAuthor: "verdict:non-author",
14921
+ verified: "verdict:verified",
14922
+ lane: "verdict:lane",
14923
+ product: "decision:product",
14924
+ plan: "plan"
14925
+ };
14926
+ var derived2 = (spec) => ({
14927
+ ...spec,
14928
+ id: requirementId({
14929
+ ruleId: spec.ruleId,
14930
+ scope: spec.scope,
14931
+ satisfaction: spec.satisfaction,
14932
+ before: spec.before,
14933
+ dependentAction: spec.dependentAction
14934
+ })
14935
+ });
14936
+ var sorted = (values) => [...new Set(values)].toSorted();
14937
+ var normalizedScope = (scope) => ({
14938
+ description: scope.description,
14939
+ paths: sorted(scope.paths),
14940
+ exclusions: sorted(scope.exclusions)
14941
+ });
14942
+ function deriveRequirements(judgment, scope, constraints, verification = "self") {
14943
+ const out = [];
14944
+ const base = { scope, dependentAction: null };
14945
+ const proportional = verification === "proportional";
14946
+ if (judgment.behaviorChange)
14947
+ out.push(derived2({
14948
+ ...base,
14949
+ ruleId: RULES.test,
14950
+ dimension: "testing",
14951
+ reason: "Behavior changes: the configured test check must pass on the final tree.",
14952
+ satisfaction: "Run `workit check test` on the final tree.",
14953
+ before: "close",
14954
+ acceptanceAllowed: false
14955
+ }));
14956
+ if (proportional && judgment.riskTier === "normal")
14957
+ out.push(derived2({
14958
+ ...base,
14959
+ ruleId: RULES.lane,
14960
+ dimension: "review",
14961
+ reason: "The review lane computed from the diff decides the verdict: none needs none, self the author's own (shown as self-reviewed), focused and full a non-author one.",
14962
+ satisfaction: "Read the lane in `workit policy assess` or `workit pr status`; record what it asks: `workit ledger verdict tests-verified --self --how <what you ran>` for self, or hand focused and full to a verifier.",
14963
+ before: "close",
14964
+ acceptanceAllowed: false
14965
+ }));
14966
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
14967
+ out.push(verification === "independent" ? derived2({
14968
+ ...base,
14969
+ ruleId: RULES.nonAuthor,
14970
+ dimension: "review",
14971
+ reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
14972
+ satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
14973
+ before: "close",
14974
+ acceptanceAllowed: false
14975
+ }) : derived2({
14976
+ ...base,
14977
+ ruleId: RULES.self,
14978
+ dimension: "review",
14979
+ reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
14980
+ satisfaction: "After an observed passing `workit check test`, record `workit ledger verdict tests-verified --self --how <what you ran>` (or have a verifier record one).",
14981
+ before: "close",
14982
+ acceptanceAllowed: false
14983
+ }));
14984
+ if (judgment.riskTier === "high")
14985
+ out.push(derived2({
14986
+ ...base,
14987
+ ruleId: RULES.verified,
14988
+ dimension: "review",
14989
+ reason: "High risk: an independent verifier must exercise the live surface.",
14990
+ satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
14991
+ before: "close",
14992
+ acceptanceAllowed: false
14993
+ }));
14994
+ if (judgment.productChoiceOpen)
14995
+ out.push(derived2({
14996
+ ...base,
14997
+ ruleId: RULES.product,
14998
+ dimension: "decisions",
14999
+ reason: "A product or preference choice is open; the user decides it before code is written.",
15000
+ satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
15001
+ before: "write",
15002
+ acceptanceAllowed: false
15003
+ }));
15004
+ if (judgment.needsPlan || judgment.riskTier === "high")
15005
+ out.push(derived2({
15006
+ ...base,
15007
+ ruleId: RULES.plan,
15008
+ dimension: "artifacts",
15009
+ reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
15010
+ satisfaction: "Write the plan or spec (e.g. docs/plans/<slug>.md; docs and Markdown writes stay allowed), then record it: `workit policy assess --ref <path>`.",
15011
+ before: "write",
15012
+ acceptanceAllowed: true
15013
+ }));
15014
+ for (const constraint of constraints)
15015
+ for (const obligation of constraint.requires)
15016
+ out.push(derived2({
15017
+ ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
15018
+ kind: obligation.kind,
15019
+ scope: normalizedScope(obligation.scope),
15020
+ refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
15021
+ method: obligation.method,
15022
+ before: obligation.before,
15023
+ dependentAction: obligation.dependentAction
15024
+ })}`,
15025
+ dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
15026
+ scope: obligation.scope,
15027
+ reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
15028
+ satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
15029
+ before: obligation.before,
15030
+ dependentAction: obligation.dependentAction,
15031
+ acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
15032
+ }));
15033
+ const seen = new Set;
15034
+ return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
15035
+ }
15036
+ var latestJudgment = (task) => task.judgments?.at(-1)?.data ?? null;
15037
+ var ledgerView = (root) => {
15038
+ let branch;
15039
+ let rows;
15040
+ return {
15041
+ branch: () => branch === undefined ? branch = currentBranch(root) : branch,
15042
+ rows: () => {
15043
+ if (rows === undefined) {
15044
+ const read = readLedger(root);
15045
+ rows = read.ok ? read.value.rows : [];
15046
+ }
15047
+ return rows;
15048
+ }
15049
+ };
15050
+ };
15051
+ var productDecision = (task, ledger) => {
15052
+ const decisions = task.decisions.filter((entry) => entry.data.revoked === null && (entry.data.response === "approved" || entry.data.response === "stated") && entry.data.purpose !== "limitation");
15053
+ if (decisions.length)
15054
+ return {
15055
+ met: true,
15056
+ reason: "the user's decision is recorded on the task",
15057
+ decisionIds: decisions.map((entry) => entry.id)
15058
+ };
15059
+ const since = Date.parse(task.createdAt);
15060
+ const branch = ledger.branch();
15061
+ const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
15062
+ return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
15063
+ met: false,
15064
+ reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
15065
+ decisionIds: []
15066
+ };
15067
+ };
15068
+ var planRecorded = (task, root) => {
15069
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync6(path19.resolve(root, ref.path)));
15070
+ if (cited)
15071
+ return {
15072
+ met: true,
14104
15073
  reason: `plan recorded: ${cited.kind === "file" ? cited.path : cited.kind === "external" ? cited.url : ""}`,
14105
15074
  decisionIds: []
14106
15075
  };
@@ -14111,14 +15080,30 @@ var planRecorded = (task, root) => {
14111
15080
  decisionIds: []
14112
15081
  };
14113
15082
  };
14114
- var verdict = (requirement, root, ledger) => {
15083
+ var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
15084
+ var laneVerdict = (task, root, ledger) => {
15085
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
15086
+ return {
15087
+ met: true,
15088
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
15089
+ decisionIds: []
15090
+ };
15091
+ const lane = workingLane(task, root);
15092
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
15093
+ const need = laneNeed(lane.lane);
15094
+ if (need === "none")
15095
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
15096
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
15097
+ return { ...status, reason: `${label}: ${status.reason}` };
15098
+ };
15099
+ var verdict = (ruleId, root, ledger) => {
14115
15100
  const branch = ledger.branch();
14116
15101
  if (!branch)
14117
15102
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
14118
15103
  const check = checkVerdicts(root, branch, ledger.rows());
14119
15104
  const failing = check.accepted.reasons.includes("failing_verdict");
14120
15105
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
14121
- const met = !failing && (requirement.ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : requirement.ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
15106
+ const met = !failing && (ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
14122
15107
  if (met)
14123
15108
  return {
14124
15109
  met: true,
@@ -14126,7 +15111,7 @@ var verdict = (requirement, root, ledger) => {
14126
15111
  decisionIds: []
14127
15112
  };
14128
15113
  const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
14129
- const unblock = requirement.ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${requirement.ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
15114
+ const unblock = ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
14130
15115
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
14131
15116
  };
14132
15117
  function derivedRuleStatus(task, requirement, root, ledger) {
@@ -14138,7 +15123,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
14138
15123
  case RULES.self:
14139
15124
  case RULES.nonAuthor:
14140
15125
  case RULES.verified:
14141
- return verdict(requirement, root, ledger);
15126
+ return verdict(requirement.ruleId, root, ledger);
15127
+ case RULES.lane:
15128
+ return laneVerdict(task, root, ledger);
14142
15129
  default:
14143
15130
  return null;
14144
15131
  }
@@ -14157,14 +15144,14 @@ function writeBlockers(task, root) {
14157
15144
 
14158
15145
  // packages/workit-core/src/core/task-evaluation.ts
14159
15146
  var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
14160
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
14161
- var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
15147
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path20.sep}`);
15148
+ var relative = (root, candidate) => path20.relative(root, candidate).split(path20.sep).join("/") || ".";
14162
15149
  var canonicalPath = (value) => {
14163
15150
  if (typeof value !== "string" || !value)
14164
15151
  return null;
14165
15152
  if (value.includes("\\") || value.split("/").includes(".."))
14166
15153
  return null;
14167
- const normalized = path17.posix.normalize(value);
15154
+ const normalized = path20.posix.normalize(value);
14168
15155
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14169
15156
  return null;
14170
15157
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14200,7 +15187,7 @@ var compareCodeUnits2 = (left, right) => {
14200
15187
  return left.length - right.length;
14201
15188
  };
14202
15189
  var gitPaths = (root) => {
14203
- const result = spawnSync10("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
15190
+ const result = spawnSync12("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
14204
15191
  cwd: root,
14205
15192
  encoding: "buffer"
14206
15193
  });
@@ -14212,21 +15199,21 @@ var gitPaths = (root) => {
14212
15199
  git: !/not a git repository/i.test(stderr)
14213
15200
  };
14214
15201
  }
14215
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
14216
- const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
15202
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path20.sep).join("/")) : [];
15203
+ const stagedDeleted = spawnSync12("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14217
15204
  if (stagedDeleted.status !== 0) {
14218
15205
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14219
15206
  if (!/not a git repository/i.test(stderr))
14220
15207
  return { paths, uncertain: true, git: true };
14221
15208
  } else if (stagedDeleted.stdout) {
14222
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
15209
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path20.sep).join("/")));
14223
15210
  }
14224
15211
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14225
15212
  };
14226
15213
  var walk = (root, directory, output) => {
14227
15214
  let names;
14228
15215
  try {
14229
- names = fs14.readdirSync(directory);
15216
+ names = fs15.readdirSync(directory);
14230
15217
  } catch {
14231
15218
  return true;
14232
15219
  }
@@ -14234,10 +15221,10 @@ var walk = (root, directory, output) => {
14234
15221
  for (const name of names) {
14235
15222
  if (name === ".git" || name === ".workit")
14236
15223
  continue;
14237
- const target = path17.join(directory, name);
15224
+ const target = path20.join(directory, name);
14238
15225
  let stat;
14239
15226
  try {
14240
- stat = fs14.lstatSync(target);
15227
+ stat = fs15.lstatSync(target);
14241
15228
  } catch {
14242
15229
  uncertain = true;
14243
15230
  continue;
@@ -14252,14 +15239,14 @@ var walk = (root, directory, output) => {
14252
15239
  var scopeRoots = (root, scope) => {
14253
15240
  const roots = [];
14254
15241
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14255
- const target = path17.resolve(root, item);
15242
+ const target = path20.resolve(root, item);
14256
15243
  if (!inside(root, target))
14257
15244
  return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
14258
15245
  let ancestor = target;
14259
- while (!fs14.existsSync(ancestor) && ancestor !== root)
14260
- ancestor = path17.dirname(ancestor);
15246
+ while (!fs15.existsSync(ancestor) && ancestor !== root)
15247
+ ancestor = path20.dirname(ancestor);
14261
15248
  try {
14262
- if (!inside(root, fs14.realpathSync(ancestor)))
15249
+ if (!inside(root, fs15.realpathSync(ancestor)))
14263
15250
  return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
14264
15251
  } catch {
14265
15252
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -14271,7 +15258,7 @@ var scopeRoots = (root, scope) => {
14271
15258
  function captureCandidate(root, scope, environment = []) {
14272
15259
  let checkout;
14273
15260
  try {
14274
- checkout = fs14.realpathSync(root);
15261
+ checkout = fs15.realpathSync(root);
14275
15262
  } catch {
14276
15263
  return failure2("invalid_input", "candidate root does not exist");
14277
15264
  }
@@ -14289,11 +15276,11 @@ function captureCandidate(root, scope, environment = []) {
14289
15276
  if (item === ".git" || item === ".workit")
14290
15277
  continue;
14291
15278
  names.add(item);
14292
- if (!fs14.existsSync(target))
15279
+ if (!fs15.existsSync(target))
14293
15280
  continue;
14294
15281
  let stat;
14295
15282
  try {
14296
- stat = fs14.lstatSync(target);
15283
+ stat = fs15.lstatSync(target);
14297
15284
  } catch {
14298
15285
  uncertain = true;
14299
15286
  continue;
@@ -14309,17 +15296,17 @@ function captureCandidate(root, scope, environment = []) {
14309
15296
  for (const item of [...names].toSorted()) {
14310
15297
  if (!scopeMatches(item, normalizedScope))
14311
15298
  continue;
14312
- const target = path17.join(checkout, item);
15299
+ const target = path20.join(checkout, item);
14313
15300
  try {
14314
- const stat = fs14.lstatSync(target);
15301
+ const stat = fs15.lstatSync(target);
14315
15302
  if (stat.isSymbolicLink()) {
14316
- const link = fs14.readlinkSync(target);
15303
+ const link = fs15.readlinkSync(target);
14317
15304
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
14318
15305
  } else if (stat.isFile()) {
14319
15306
  files.push({
14320
15307
  path: item,
14321
15308
  kind: "file",
14322
- digest: digestBytes(fs14.readFileSync(target)),
15309
+ digest: digestBytes(fs15.readFileSync(target)),
14323
15310
  executable: (stat.mode & 73) !== 0
14324
15311
  });
14325
15312
  } else if (!stat.isDirectory()) {
@@ -14344,7 +15331,7 @@ function captureCandidate(root, scope, environment = []) {
14344
15331
  const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
14345
15332
  if (values.some(({ value }) => value === null))
14346
15333
  completeness = "uncertain";
14347
- const headResult = spawnSync10("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
15334
+ const headResult = spawnSync12("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
14348
15335
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
14349
15336
  completeness = "uncertain";
14350
15337
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -14484,7 +15471,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
14484
15471
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14485
15472
  let root;
14486
15473
  try {
14487
- root = fs14.realpathSync(checkoutRoot);
15474
+ root = fs15.realpathSync(checkoutRoot);
14488
15475
  } catch {
14489
15476
  return failure2("permission_denied", "checkout root is unavailable");
14490
15477
  }
@@ -14493,23 +15480,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14493
15480
  continue;
14494
15481
  if (!reference.digest)
14495
15482
  return failure2("invalid_input", "document references require a byte digest");
14496
- const target = path17.resolve(root, reference.path);
14497
- if (target !== root && !target.startsWith(`${root}${path17.sep}`))
15483
+ const target = path20.resolve(root, reference.path);
15484
+ if (target !== root && !target.startsWith(`${root}${path20.sep}`))
14498
15485
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14499
15486
  fields: [
14500
15487
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
14501
15488
  ]
14502
15489
  });
14503
15490
  try {
14504
- const below = path17.relative(root, target);
15491
+ const below = path20.relative(root, target);
14505
15492
  let current = root;
14506
- for (const segment of below.split(path17.sep)) {
15493
+ for (const segment of below.split(path20.sep)) {
14507
15494
  if (!segment)
14508
15495
  continue;
14509
- current = path17.join(current, segment);
14510
- const stat = fs14.lstatSync(current);
14511
- const real = fs14.realpathSync(current);
14512
- if (real !== root && !real.startsWith(`${root}${path17.sep}`))
15496
+ current = path20.join(current, segment);
15497
+ const stat = fs15.lstatSync(current);
15498
+ const real = fs15.realpathSync(current);
15499
+ if (real !== root && !real.startsWith(`${root}${path20.sep}`))
14513
15500
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14514
15501
  fields: [
14515
15502
  {
@@ -14525,7 +15512,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14525
15512
  if (current !== target && !stat.isDirectory())
14526
15513
  return failure2("invalid_input", "document reference ancestor is not a directory");
14527
15514
  }
14528
- if (digestBytes(fs14.readFileSync(target)) !== reference.digest)
15515
+ if (digestBytes(fs15.readFileSync(target)) !== reference.digest)
14529
15516
  return failure2("permission_denied", "decision document bytes have changed");
14530
15517
  } catch {
14531
15518
  return failure2("invalid_input", "decision document is unavailable");
@@ -14942,9 +15929,9 @@ Autonomy contract:
14942
15929
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
14943
15930
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
14944
15931
  - Author is not verifier: your own verdict is \`--self\`, shown as
14945
- self-reviewed, never verified. High risk, \`verification: independent\` and
14946
- merge need a verifier with its own host-derived session (a subagent's
14947
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
15932
+ self-reviewed, never verified. High risk, a focused or full lane and
15933
+ merge under \`verification: self|independent\` need a verifier with its
15934
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
14948
15935
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
14949
15936
  is not evidence of a requested remote push. Before reporting delivery,
14950
15937
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -15106,21 +16093,21 @@ var exportDigest = (bundle) => sha256({
15106
16093
  task: bundle.task
15107
16094
  });
15108
16095
  // packages/workit-core/src/core/route-intent.ts
15109
- import { spawnSync as spawnSync12 } from "node:child_process";
15110
- import { existsSync as existsSync6 } from "node:fs";
15111
- import path20 from "node:path";
16096
+ import { spawnSync as spawnSync14 } from "node:child_process";
16097
+ import { existsSync as existsSync8 } from "node:fs";
16098
+ import path23 from "node:path";
15112
16099
 
15113
16100
  // packages/workit-core/src/hooks/raw-git.ts
15114
- import { spawnSync as spawnSync11 } from "node:child_process";
16101
+ import { spawnSync as spawnSync13 } from "node:child_process";
15115
16102
  import { createHash as createHash14 } from "node:crypto";
15116
- import fs16 from "node:fs";
16103
+ import fs17 from "node:fs";
15117
16104
  import os4 from "node:os";
15118
- import path19 from "node:path";
16105
+ import path22 from "node:path";
15119
16106
 
15120
16107
  // packages/workit-core/src/hooks/session-marker.ts
15121
16108
  import { createHash as createHash13 } from "node:crypto";
15122
- import fs15 from "node:fs";
15123
- import path18 from "node:path";
16109
+ import fs16 from "node:fs";
16110
+ import path21 from "node:path";
15124
16111
  var sessionMarkerFile = (input, session, store) => {
15125
16112
  let dir = store;
15126
16113
  if (dir === undefined) {
@@ -15130,13 +16117,13 @@ var sessionMarkerFile = (input, session, store) => {
15130
16117
  dir = location.dir;
15131
16118
  }
15132
16119
  const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
15133
- return path18.join(dir, "hooks", `skills-${name}.json`);
16120
+ return path21.join(dir, "hooks", `skills-${name}.json`);
15134
16121
  };
15135
16122
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
15136
16123
  var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
15137
16124
  var readRaw = (file) => {
15138
16125
  try {
15139
- return fs15.readFileSync(file, "utf8");
16126
+ return fs16.readFileSync(file, "utf8");
15140
16127
  } catch {
15141
16128
  return "";
15142
16129
  }
@@ -15157,13 +16144,13 @@ var parseMarker = (text) => {
15157
16144
  };
15158
16145
  var readSessionMarker = (file) => parseMarker(readRaw(file));
15159
16146
  var writeAtomic = (file, marker) => {
15160
- fs15.mkdirSync(path18.dirname(file), { recursive: true });
16147
+ fs16.mkdirSync(path21.dirname(file), { recursive: true });
15161
16148
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
15162
16149
  try {
15163
- fs15.writeFileSync(temp, JSON.stringify(marker));
15164
- fs15.renameSync(temp, file);
16150
+ fs16.writeFileSync(temp, JSON.stringify(marker));
16151
+ fs16.renameSync(temp, file);
15165
16152
  } catch (error) {
15166
- fs15.rmSync(temp, { force: true });
16153
+ fs16.rmSync(temp, { force: true });
15167
16154
  throw error;
15168
16155
  }
15169
16156
  };
@@ -15675,17 +16662,17 @@ var GIT_VALUE_OPTIONS = new Set([
15675
16662
  ]);
15676
16663
  var GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
15677
16664
  var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
15678
- var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
16665
+ var ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
15679
16666
  var MAX_DEPTH = 4;
15680
16667
  var MAX_EXPANSIONS = 16;
15681
- var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16668
+ var isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
15682
16669
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
15683
16670
  var joinDir = (base, raw) => {
15684
16671
  const next = raw.replace(HERE, ".");
15685
16672
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
15686
16673
  return;
15687
- const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
15688
- return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
16674
+ const expanded = next === "~" || next.startsWith("~/") ? path22.join(os4.homedir(), next.slice(1)) : next;
16675
+ return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
15689
16676
  };
15690
16677
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
15691
16678
  var cdTarget = (words) => {
@@ -15702,7 +16689,7 @@ var stripPrefixes = (input) => {
15702
16689
  const first = words[0];
15703
16690
  if (first === undefined)
15704
16691
  return { words, env };
15705
- const assignment = ASSIGNMENT.exec(first);
16692
+ const assignment = ASSIGNMENT2.exec(first);
15706
16693
  if (assignment) {
15707
16694
  if (GIT_CONFIG_ENV.test(assignment[1]))
15708
16695
  env[assignment[1]] = assignment[2];
@@ -15761,10 +16748,10 @@ var assignments = (words, vars) => {
15761
16748
  while (rest[0]?.startsWith("-"))
15762
16749
  rest = rest.slice(1);
15763
16750
  }
15764
- if (rest.length === 0 || !rest.every((word) => ASSIGNMENT.test(word)))
16751
+ if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
15765
16752
  return null;
15766
16753
  return rest.map((word) => {
15767
- const [, name, value] = ASSIGNMENT.exec(word);
16754
+ const [, name, value] = ASSIGNMENT2.exec(word);
15768
16755
  return [name, expandWord(value, vars)];
15769
16756
  });
15770
16757
  };
@@ -15952,7 +16939,7 @@ var splitString = (line) => {
15952
16939
  return line.slice(index).join(" ").slice(15);
15953
16940
  if (/^-S./.test(word))
15954
16941
  return [word.slice(2), ...line.slice(index + 1)].join(" ");
15955
- if (!word.startsWith("-") && !ASSIGNMENT.test(word))
16942
+ if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
15956
16943
  return null;
15957
16944
  }
15958
16945
  return null;
@@ -16219,6 +17206,24 @@ function workitInvocations(command, dialect = "posix", depth = 0) {
16219
17206
  }
16220
17207
  return found;
16221
17208
  }
17209
+ var WORKIT_GLOBAL = /^--(?:cwd|session|token|as)(=|$)/;
17210
+ function skipWorkitGlobals(args) {
17211
+ let cwd = null;
17212
+ let at = 0;
17213
+ for (;at < args.length; at++) {
17214
+ const match = WORKIT_GLOBAL.exec(args[at]);
17215
+ if (match?.[1] === "=") {
17216
+ if (args[at].startsWith("--cwd="))
17217
+ cwd = args[at].slice(6);
17218
+ } else if (match && args[at + 1] !== undefined) {
17219
+ if (args[at] === "--cwd")
17220
+ cwd = args[at + 1];
17221
+ at++;
17222
+ } else if (args[at] !== "--json")
17223
+ break;
17224
+ }
17225
+ return { rest: args.slice(at), cwd };
17226
+ }
16222
17227
  var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
16223
17228
  var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
16224
17229
  var pushDestination = (destination) => {
@@ -16497,7 +17502,7 @@ var repoAt = (dir) => {
16497
17502
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
16498
17503
  };
16499
17504
  var isWorkitWorkspace = (repo) => {
16500
- if (fs16.existsSync(repo.location.dir))
17505
+ if (fs17.existsSync(repo.location.dir))
16501
17506
  return true;
16502
17507
  try {
16503
17508
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -16518,17 +17523,17 @@ var headSha2 = (repo) => {
16518
17523
  if (!git || git.reftable)
16519
17524
  return null;
16520
17525
  try {
16521
- const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
17526
+ const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
16522
17527
  if (/^[0-9a-f]{40,64}$/.test(head))
16523
17528
  return head;
16524
17529
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
16525
17530
  if (!ref)
16526
17531
  return null;
16527
- const common = path19.dirname(repo.location.dir);
17532
+ const common = path22.dirname(repo.location.dir);
16528
17533
  try {
16529
- return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
17534
+ return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
16530
17535
  } catch {}
16531
- const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
17536
+ const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
16532
17537
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
16533
17538
  } catch {
16534
17539
  return null;
@@ -16540,7 +17545,7 @@ var gitConfig = (dir, invocation, cache) => {
16540
17545
  if (cached)
16541
17546
  return cached;
16542
17547
  const config = new Map;
16543
- const listed = spawnSync11("git", [...invocation.config ?? [], "config", "-z", "--list"], {
17548
+ const listed = spawnSync13("git", [...invocation.config ?? [], "config", "-z", "--list"], {
16544
17549
  cwd: dir,
16545
17550
  encoding: "utf8",
16546
17551
  timeout: 3000,
@@ -16616,8 +17621,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16616
17621
  const sub = invocation.args[0];
16617
17622
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
16618
17623
  return [invocation];
16619
- const dir = path19.resolve(root, invocation.dir ?? ".");
16620
- if (!fs16.existsSync(dir))
17624
+ const dir = path22.resolve(root, invocation.dir ?? ".");
17625
+ if (!fs17.existsSync(dir))
16621
17626
  return [invocation];
16622
17627
  const config = gitConfig(dir, invocation, cache);
16623
17628
  const alias = configValue(config, `alias.${sub.toLowerCase()}`);
@@ -16639,11 +17644,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16639
17644
  var normalizedUrl = (url, dir) => {
16640
17645
  const trimmed = url.trim();
16641
17646
  if (/^file:\/\//i.test(trimmed))
16642
- return path19.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17647
+ return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
16643
17648
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
16644
17649
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
16645
17650
  if (!scheme && !scp)
16646
- return path19.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17651
+ return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
16647
17652
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
16648
17653
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
16649
17654
  };
@@ -16704,7 +17709,7 @@ var refusal = (action, branch, dir) => {
16704
17709
  };
16705
17710
  var refFacts = (repo, remote) => {
16706
17711
  const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
16707
- const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
17712
+ const listed = spawnSync13("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
16708
17713
  const remotePrefix = `refs/remotes/${remote}/`;
16709
17714
  for (const ref of (listed.stdout ?? "").split(`
16710
17715
  `)) {
@@ -16721,7 +17726,7 @@ var globBranches = (repo, glob) => {
16721
17726
  const source = glob.source.startsWith("refs/") ? glob.source : `refs/heads/${glob.source}`;
16722
17727
  const [sourcePrefix, sourceSuffix = ""] = source.split("*");
16723
17728
  const [destinationPrefix, destinationSuffix = ""] = glob.destination.split("*");
16724
- const listed = spawnSync11("git", [
17729
+ const listed = spawnSync13("git", [
16725
17730
  "for-each-ref",
16726
17731
  "--format=%(refname)",
16727
17732
  sourcePrefix.slice(0, sourcePrefix.lastIndexOf("/") + 1)
@@ -16798,7 +17803,7 @@ var tagRefusal = (action, facts, dir) => {
16798
17803
  return `workit: raw ${quote(`git push${action.allTags ? " --tags" : ""}`)} of ${tags.length ? `tag ${tags.join(", ")}` : "tags"} bypasses the release grant. Push a release tag with ${quote(`workit git push --release ${named}`)} (${shape} tags whose commit the production branch already has; a tag never moves), under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}).`;
16799
17804
  };
16800
17805
  var remoteHeads = (repo, remote) => {
16801
- const listed = spawnSync11("git", ["ls-remote", "--heads", remote], {
17806
+ const listed = spawnSync13("git", ["ls-remote", "--heads", remote], {
16802
17807
  cwd: repo.dir,
16803
17808
  encoding: "utf8",
16804
17809
  timeout: 3000,
@@ -16867,7 +17872,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
16867
17872
  const out = [];
16868
17873
  const repos = new Map;
16869
17874
  const repoFor = (relative) => {
16870
- const dir = path19.resolve(cwd, relative ?? ".");
17875
+ const dir = path22.resolve(cwd, relative ?? ".");
16871
17876
  if (!repos.has(dir)) {
16872
17877
  const repo = repoAt(dir);
16873
17878
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -16952,7 +17957,7 @@ var analysisDenial = (error) => ({
16952
17957
  unblock: SPLIT_IT
16953
17958
  });
16954
17959
  var MARKER_TTL_SECONDS = 3600;
16955
- var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
17960
+ var markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
16956
17961
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
16957
17962
  function noteRawCommit(input, command, key) {
16958
17963
  try {
@@ -16963,14 +17968,14 @@ function noteRawCommit(input, command, key) {
16963
17968
  if (action.kind !== "commit")
16964
17969
  continue;
16965
17970
  const file = markerFile(repo, input.session.id, key);
16966
- fs16.mkdirSync(path19.dirname(file), { recursive: true });
16967
- fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
17971
+ fs17.mkdirSync(path22.dirname(file), { recursive: true });
17972
+ fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
16968
17973
  `);
16969
17974
  }
16970
17975
  } catch {}
16971
17976
  }
16972
17977
  var headCommit = (dir) => {
16973
- const run = spawnSync11("git", [
17978
+ const run = spawnSync13("git", [
16974
17979
  "log",
16975
17980
  "-1",
16976
17981
  "--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
@@ -16987,11 +17992,11 @@ var headCommit = (dir) => {
16987
17992
  var settle2 = (input, repo, file) => {
16988
17993
  let marker;
16989
17994
  try {
16990
- marker = JSON.parse(fs16.readFileSync(file, "utf8"));
17995
+ marker = JSON.parse(fs17.readFileSync(file, "utf8"));
16991
17996
  } catch {
16992
17997
  return;
16993
17998
  }
16994
- fs16.rmSync(file, { force: true });
17999
+ fs17.rmSync(file, { force: true });
16995
18000
  const session = input.session.id;
16996
18001
  const now = Date.now() / 1000;
16997
18002
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -17172,7 +18177,7 @@ var targetsOf = (invocations) => invocations.flatMap((invocation) => invocation.
17172
18177
  branch,
17173
18178
  dir: invocation.dir
17174
18179
  })) : []);
17175
- var localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
18180
+ var localBranchExists = (dir, branch) => spawnSync14("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
17176
18181
  cwd: dir,
17177
18182
  stdio: "ignore",
17178
18183
  timeout: 5000
@@ -17182,8 +18187,8 @@ var judgedDir = (root, dir) => {
17182
18187
  return null;
17183
18188
  if (dir === null)
17184
18189
  return root;
17185
- const resolved = path20.resolve(root, dir);
17186
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18190
+ const resolved = path23.resolve(root, dir);
18191
+ return existsSync8(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
17187
18192
  };
17188
18193
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
17189
18194
  let judged = false;
@@ -17418,7 +18423,7 @@ var judgeInputSummary = (raw) => {
17418
18423
 
17419
18424
  // packages/workit-core/src/autonomy.ts
17420
18425
  import os5 from "node:os";
17421
- import path21 from "node:path";
18426
+ import path24 from "node:path";
17422
18427
 
17423
18428
  // packages/workit-core/src/core/safe-write.ts
17424
18429
  import {
@@ -17452,10 +18457,10 @@ var accountHome = () => {
17452
18457
  }
17453
18458
  };
17454
18459
  var grantsHome = { resolve: accountHome };
17455
- var grantsDir = () => path21.join(grantsHome.resolve(), ".config", "workit");
18460
+ var grantsDir = () => path24.join(grantsHome.resolve(), ".config", "workit");
17456
18461
  var grantsOverride = () => {
17457
- const configured = path21.resolve(resolveConfigDir());
17458
- return configured === path21.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path21.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
18462
+ const configured = path24.resolve(resolveConfigDir());
18463
+ return configured === path24.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path24.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
17459
18464
  };
17460
18465
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
17461
18466
  var DEFAULT_GRANTS = Object.freeze({
@@ -17508,8 +18513,16 @@ function endpointView(entry, context) {
17508
18513
  ...issue ? { endpointIssue: issue } : {}
17509
18514
  };
17510
18515
  }
17511
- var DEFAULT_VERIFICATION = "self";
17512
- var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
18516
+ var VERIFICATION_MODES = [
18517
+ "proportional",
18518
+ "self",
18519
+ "independent"
18520
+ ];
18521
+ var DEFAULT_VERIFICATION = "proportional";
18522
+ var verificationOf = (entry) => {
18523
+ const value = entry?.verification;
18524
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
18525
+ };
17513
18526
  var isGrantValue = (value) => value === true || value === false || value === "verified";
17514
18527
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
17515
18528
  var fromAutoApprove = (value) => {
@@ -19256,9 +20269,9 @@ class WorkitCore {
19256
20269
  }
19257
20270
  }
19258
20271
  // packages/workit-core/src/core/repo-context.ts
19259
- import { spawnSync as spawnSync13 } from "node:child_process";
19260
- import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
19261
- import path22 from "node:path";
20272
+ import { spawnSync as spawnSync15 } from "node:child_process";
20273
+ import { existsSync as existsSync9, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
20274
+ import path25 from "node:path";
19262
20275
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
19263
20276
  try {
19264
20277
  gitRevisionParts(value);
@@ -19285,7 +20298,7 @@ var invalidRange = (cwd) => ({
19285
20298
  cwd
19286
20299
  });
19287
20300
  var runGit = (cwd, args) => {
19288
- const result = spawnSync13("git", args, { cwd, encoding: "utf8" });
20301
+ const result = spawnSync15("git", args, { cwd, encoding: "utf8" });
19289
20302
  return {
19290
20303
  stdout: (result.stdout ?? "").trimEnd(),
19291
20304
  stderr: (result.stderr ?? "").trimEnd(),
@@ -19392,7 +20405,7 @@ var findPrTemplate = (cwd) => {
19392
20405
  ]) {
19393
20406
  const parts = rel.split("/");
19394
20407
  const base = parts[parts.length - 1];
19395
- const dir = parts.length > 1 ? path22.join(cwd, ...parts.slice(0, -1)) : cwd;
20408
+ const dir = parts.length > 1 ? path25.join(cwd, ...parts.slice(0, -1)) : cwd;
19396
20409
  const actual = probeCaseInsensitive(dir, base);
19397
20410
  if (actual)
19398
20411
  return [...parts.slice(0, -1), actual].join("/");
@@ -19423,11 +20436,11 @@ var diffStatForRange = (cwd, range) => {
19423
20436
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
19424
20437
  };
19425
20438
  var packageScripts = (cwd, keys) => {
19426
- const pkgPath = path22.join(cwd, "package.json");
19427
- if (!existsSync7(pkgPath))
20439
+ const pkgPath = path25.join(cwd, "package.json");
20440
+ if (!existsSync9(pkgPath))
19428
20441
  return [];
19429
20442
  try {
19430
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20443
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
19431
20444
  return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
19432
20445
  } catch {
19433
20446
  return [];
@@ -19449,9 +20462,9 @@ var documentationFiles = (cwd) => {
19449
20462
  if (entry.isDirectory()) {
19450
20463
  if (excluded.has(entry.name))
19451
20464
  continue;
19452
- walk(path22.join(dir, entry.name), depth + 1);
20465
+ walk(path25.join(dir, entry.name), depth + 1);
19453
20466
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
19454
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
20467
+ matches.push("./" + path25.relative(cwd, path25.join(dir, entry.name)).replaceAll("\\", "/"));
19455
20468
  }
19456
20469
  }
19457
20470
  };
@@ -19459,15 +20472,15 @@ var documentationFiles = (cwd) => {
19459
20472
  return matches.toSorted().slice(0, 200);
19460
20473
  };
19461
20474
  var readTrimmed2 = (file, maxLines) => {
19462
- if (!existsSync7(file))
20475
+ if (!existsSync9(file))
19463
20476
  return "";
19464
- const lines = readFileSync5(file, "utf8").split(`
20477
+ const lines = readFileSync7(file, "utf8").split(`
19465
20478
  `);
19466
20479
  return lines.slice(0, maxLines).join(`
19467
20480
  `);
19468
20481
  };
19469
20482
  function prReadyContext(root, range) {
19470
- const cwd = path22.resolve(repoRoot(root));
20483
+ const cwd = path25.resolve(repoRoot(root));
19471
20484
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19472
20485
  return invalidRange(cwd);
19473
20486
  let stdout = "";
@@ -19525,7 +20538,7 @@ ${status.stderr}` : "");
19525
20538
  stdout += `template_path: ${template}
19526
20539
 
19527
20540
  `;
19528
- stdout += readTrimmed2(path22.join(cwd, template), 220);
20541
+ stdout += readTrimmed2(path25.join(cwd, template), 220);
19529
20542
  } else {
19530
20543
  stdout += `template_path: none
19531
20544
 
@@ -19535,7 +20548,7 @@ ${status.stderr}` : "");
19535
20548
  stdout += `
19536
20549
  `;
19537
20550
  stdout += printSection("Recent Validation Signals");
19538
- if (existsSync7(path22.join(cwd, "package.json"))) {
20551
+ if (existsSync9(path25.join(cwd, "package.json"))) {
19539
20552
  stdout += `package.json detected. Common scripts:
19540
20553
  `;
19541
20554
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -19544,7 +20557,7 @@ ${status.stderr}` : "");
19544
20557
  `) + `
19545
20558
  `;
19546
20559
  }
19547
- if (existsSync7(path22.join(cwd, "Cargo.toml")) || existsSync7(path22.join(cwd, "src-tauri/Cargo.toml"))) {
20560
+ if (existsSync9(path25.join(cwd, "Cargo.toml")) || existsSync9(path25.join(cwd, "src-tauri/Cargo.toml"))) {
19548
20561
  stdout += `Rust project detected.
19549
20562
  `;
19550
20563
  }
@@ -19577,7 +20590,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
19577
20590
  - Apply with the host-native editor, preserving the repository changelog conventions.
19578
20591
  - If Unreleased already has duplicate category headings, normalize_only first.`;
19579
20592
  function changelogContext(root, range) {
19580
- const cwd = path22.resolve(repoRoot(root));
20593
+ const cwd = path25.resolve(repoRoot(root));
19581
20594
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19582
20595
  return invalidRange(cwd);
19583
20596
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19595,8 +20608,8 @@ function changelogContext(root, range) {
19595
20608
  stdout += CHANGELOG_RULES + `
19596
20609
  `;
19597
20610
  stdout += printSection("Existing CHANGELOG.md");
19598
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
19599
- if (existsSync7(changelogPath)) {
20611
+ const changelogPath = path25.join(cwd, "CHANGELOG.md");
20612
+ if (existsSync9(changelogPath)) {
19600
20613
  stdout += readTrimmed2(changelogPath, 260) + `
19601
20614
  `;
19602
20615
  } else {
@@ -19615,7 +20628,7 @@ function changelogContext(root, range) {
19615
20628
  return { stdout, stderr: "", exitCode: 0, cwd };
19616
20629
  }
19617
20630
  function docsRefreshContext(root, range) {
19618
- const cwd = path22.resolve(repoRoot(root));
20631
+ const cwd = path25.resolve(repoRoot(root));
19619
20632
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19620
20633
  return invalidRange(cwd);
19621
20634
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19637,8 +20650,8 @@ function docsRefreshContext(root, range) {
19637
20650
  `) + `
19638
20651
  `;
19639
20652
  stdout += printSection("README Preview");
19640
- const readme = path22.join(cwd, "README.md");
19641
- if (existsSync7(readme)) {
20653
+ const readme = path25.join(cwd, "README.md");
20654
+ if (existsSync9(readme)) {
19642
20655
  stdout += readTrimmed2(readme, 220) + `
19643
20656
  `;
19644
20657
  } else {
@@ -19646,10 +20659,10 @@ function docsRefreshContext(root, range) {
19646
20659
  `;
19647
20660
  }
19648
20661
  stdout += printSection("Package Scripts");
19649
- const pkgPath = path22.join(cwd, "package.json");
19650
- if (existsSync7(pkgPath)) {
20662
+ const pkgPath = path25.join(cwd, "package.json");
20663
+ if (existsSync9(pkgPath)) {
19651
20664
  try {
19652
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20665
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
19653
20666
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
19654
20667
  `;
19655
20668
  } catch {}
@@ -19660,7 +20673,7 @@ function docsRefreshContext(root, range) {
19660
20673
  return { stdout, stderr: "", exitCode: 0, cwd };
19661
20674
  }
19662
20675
  function releaseNotesContext(root, rangeOrTag) {
19663
- const cwd = path22.resolve(repoRoot(root));
20676
+ const cwd = path25.resolve(repoRoot(root));
19664
20677
  if (!rangeOrTag) {
19665
20678
  return { stdout: "", stderr: `ERROR: release tag or range required
19666
20679
  `, exitCode: 1, cwd };
@@ -19698,109 +20711,35 @@ function releaseNotesContext(root, rangeOrTag) {
19698
20711
  `;
19699
20712
  stdout += printSection("Existing Release Files");
19700
20713
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
19701
- if (existsSync7(path22.join(cwd, rel)))
20714
+ if (existsSync9(path25.join(cwd, rel)))
19702
20715
  stdout += rel + `
19703
- `;
19704
- }
19705
- stdout += printSection("Release Notes Draft");
19706
- stdout += `## Release notes (${resolvedRange})
19707
-
19708
- `;
19709
- const commits = commitLogForRange(cwd, resolvedRange).split(`
19710
- `).map((line) => line.trim()).filter(Boolean);
19711
- stdout += commits.length ? `${commits.map((line) => `- ${line}`).join(`
19712
- `)}
19713
- ` : `- No commits in range.
19714
- `;
19715
- stdout += `
19716
- ### Changed files
19717
-
19718
- `;
19719
- const files = changedFilesForRange(cwd, resolvedRange).split(`
19720
- `).map((line) => line.trim()).filter(Boolean);
19721
- stdout += files.length ? `${files.map((line) => `- ${line}`).join(`
19722
- `)}
19723
- ` : `- None.
19724
- `;
19725
- return { stdout, stderr: "", exitCode: 0, cwd };
19726
- }
19727
-
19728
- // packages/workit-core/src/core/tracker-issues.ts
19729
- import { spawnSync as spawnSync15 } from "node:child_process";
19730
-
19731
- // packages/workit-core/src/core/pr-create.ts
19732
- import fs17 from "node:fs";
19733
- import path23 from "node:path";
19734
- import { spawnSync as spawnSync14 } from "node:child_process";
19735
- import { pathToFileURL } from "node:url";
19736
- // packages/workit-core/src/forge/pr-body.ts
19737
- function parseGhIssue(value) {
19738
- const m = /issues\/(\d+)/.exec(value);
19739
- return m ? m[1] : String(value).trim().replace(/^#/, "");
19740
- }
19741
-
19742
- // packages/workit-core/src/core/pr-create.ts
19743
- function whichOnPath(tool) {
19744
- const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
19745
- for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
19746
- if (!dir)
19747
- continue;
19748
- for (const name of names) {
19749
- const candidate = path23.join(dir, name);
19750
- try {
19751
- fs17.accessSync(candidate, fs17.constants.X_OK);
19752
- return candidate;
19753
- } catch {}
19754
- }
19755
- }
19756
- return null;
19757
- }
19758
- var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
19759
- var pushRemote = (cwd) => {
19760
- const result = spawnSync14("git", ["remote", "get-url", "--push", "--all", "origin"], {
19761
- cwd,
19762
- encoding: "utf8"
19763
- });
19764
- if (result.status !== 0)
19765
- return null;
19766
- const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
19767
- return urls.length === 1 ? urls[0] : null;
19768
- };
19769
- var safePushUrl = (remote) => {
19770
- const raw = remote.trim();
19771
- if (path23.isAbsolute(raw))
19772
- return pathToFileURL(raw).href;
19773
- if (!raw.includes("://")) {
19774
- const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
19775
- if (scp)
19776
- return `${scp[1] || "git"}@${scp[2]}:${scp[3]}`;
19777
- }
19778
- try {
19779
- const url = new URL(raw);
19780
- if (!["file:", "git:", "http:", "https:", "ssh:"].includes(url.protocol))
19781
- return null;
19782
- url.password = "";
19783
- if (url.protocol !== "ssh:")
19784
- url.username = "";
19785
- url.search = "";
19786
- url.hash = "";
19787
- return url.toString();
19788
- } catch {
19789
- return null;
19790
- }
19791
- };
19792
- var pushTargetIsStable = (cwd, remote) => {
19793
- const target = safePushUrl(remote);
19794
- if (!target)
19795
- return false;
19796
- const result = spawnSync14("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
19797
- if (result.status !== 0 && result.status !== 1)
19798
- return false;
19799
- const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
19800
- return !prefixes.some((prefix) => target.startsWith(prefix));
19801
- };
20716
+ `;
20717
+ }
20718
+ stdout += printSection("Release Notes Draft");
20719
+ stdout += `## Release notes (${resolvedRange})
20720
+
20721
+ `;
20722
+ const commits = commitLogForRange(cwd, resolvedRange).split(`
20723
+ `).map((line) => line.trim()).filter(Boolean);
20724
+ stdout += commits.length ? `${commits.map((line) => `- ${line}`).join(`
20725
+ `)}
20726
+ ` : `- No commits in range.
20727
+ `;
20728
+ stdout += `
20729
+ ### Changed files
20730
+
20731
+ `;
20732
+ const files = changedFilesForRange(cwd, resolvedRange).split(`
20733
+ `).map((line) => line.trim()).filter(Boolean);
20734
+ stdout += files.length ? `${files.map((line) => `- ${line}`).join(`
20735
+ `)}
20736
+ ` : `- None.
20737
+ `;
20738
+ return { stdout, stderr: "", exitCode: 0, cwd };
20739
+ }
19802
20740
 
19803
20741
  // packages/workit-core/src/core/tracker-issues.ts
20742
+ import { spawnSync as spawnSync16 } from "node:child_process";
19804
20743
  var repoPathFromRemote = (remote) => {
19805
20744
  let rest = (remote || "").trim().replace(/\/+$/, "");
19806
20745
  if (!rest)
@@ -19821,7 +20760,7 @@ var defaultCli = (bin, available) => {
19821
20760
  if (!available)
19822
20761
  return null;
19823
20762
  return (args, root) => {
19824
- const out = spawnSync15(bin, args, {
20763
+ const out = spawnSync16(bin, args, {
19825
20764
  cwd: root,
19826
20765
  encoding: "utf8",
19827
20766
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -19900,7 +20839,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
19900
20839
 
19901
20840
  // packages/workit-core/src/core/youtrack.ts
19902
20841
  import fs18 from "node:fs";
19903
- import path24 from "node:path";
20842
+ import path26 from "node:path";
19904
20843
 
19905
20844
  // packages/workit-core/src/core/templates.ts
19906
20845
  var repoRoot2 = assetRoot();
@@ -19913,8 +20852,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
19913
20852
  // packages/workit-core/src/core/youtrack.ts
19914
20853
  var ISSUE_RE = /^[A-Z]+-\d+$/;
19915
20854
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
19916
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(configDir(), "youtrack.json");
19917
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(resolveConfigDir(), "youtrack.json");
20855
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path26.join(configDir(), "youtrack.json");
20856
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path26.join(resolveConfigDir(), "youtrack.json");
19918
20857
  var youTrackTokenModeOk = (p) => {
19919
20858
  if (process.platform === "win32")
19920
20859
  return true;
@@ -19982,7 +20921,7 @@ var youTrackToken = () => {
19982
20921
  if ("error" in loaded)
19983
20922
  return loaded;
19984
20923
  const tokenFile = String(loaded.config.tokenFile ?? "");
19985
- const tokenPath = tokenFile ? path24.isAbsolute(tokenFile) ? path24.resolve(tokenFile) : path24.resolve(process.cwd(), tokenFile) : "";
20924
+ const tokenPath = tokenFile ? path26.isAbsolute(tokenFile) ? path26.resolve(tokenFile) : path26.resolve(process.cwd(), tokenFile) : "";
19986
20925
  if (!tokenPath || !fs18.existsSync(tokenPath))
19987
20926
  return { error: "missing youtrack.token" };
19988
20927
  if (!youTrackTokenModeOk(tokenPath))
@@ -20033,7 +20972,7 @@ function youTrackTokenCreateUrl() {
20033
20972
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
20034
20973
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
20035
20974
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
20036
- const tokenFile = String(config.tokenFile ?? path24.join(path24.dirname(loaded.path), "youtrack.token"));
20975
+ const tokenFile = String(config.tokenFile ?? path26.join(path26.dirname(loaded.path), "youtrack.token"));
20037
20976
  const tab = String(defaults.profileTab ?? "account-security");
20038
20977
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
20039
20978
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -20042,7 +20981,7 @@ function youTrackTokenCreateUrl() {
20042
20981
  tokenName: name,
20043
20982
  tokenDescription: desc,
20044
20983
  scopes,
20045
- tokenFile: path24.resolve(tokenFile),
20984
+ tokenFile: path26.resolve(tokenFile),
20046
20985
  createUrl,
20047
20986
  docsUrl,
20048
20987
  prefillSupported: false,
@@ -20382,22 +21321,22 @@ function resolveGitRevision(root, value) {
20382
21321
  }
20383
21322
  function resolveInside(root, candidate) {
20384
21323
  const base = realpathSync5(root);
20385
- const target = path25.resolve(base, candidate);
20386
- if (target !== base && !target.startsWith(base + path25.sep)) {
21324
+ const target = path27.resolve(base, candidate);
21325
+ if (target !== base && !target.startsWith(base + path27.sep)) {
20387
21326
  throw new Error("path must stay inside repository root");
20388
21327
  }
20389
21328
  let ancestor = target;
20390
- while (!existsSync8(ancestor))
20391
- ancestor = path25.dirname(ancestor);
21329
+ while (!existsSync10(ancestor))
21330
+ ancestor = path27.dirname(ancestor);
20392
21331
  const canonicalAncestor = realpathSync5(ancestor);
20393
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path25.sep)) {
21332
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path27.sep)) {
20394
21333
  throw new Error("path must stay inside repository root");
20395
21334
  }
20396
21335
  return target;
20397
21336
  }
20398
21337
  function run2(root, executable, args, env = {}) {
20399
21338
  const cwd = realpathSync5(root);
20400
- const result = spawnSync16(executable, args, {
21339
+ const result = spawnSync17(executable, args, {
20401
21340
  cwd,
20402
21341
  encoding: "utf8",
20403
21342
  env: { ...process.env, ...env }
@@ -20434,7 +21373,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
20434
21373
  };
20435
21374
  });
20436
21375
  // packages/workit-core/src/hooks/context.ts
20437
- import path26 from "node:path";
21376
+ import path28 from "node:path";
20438
21377
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
20439
21378
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
20440
21379
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -20519,7 +21458,7 @@ function entryCompactContext(store, entry, session, context) {
20519
21458
  return null;
20520
21459
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
20521
21460
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
20522
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path26.resolve(store.root, ref.path)] : []));
21461
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path28.resolve(store.root, ref.path)] : []));
20523
21462
  const observedFiles = filesKey(files);
20524
21463
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
20525
21464
  if (!compact.ok || !compact.data)
@@ -20552,7 +21491,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
20552
21491
  // packages/workit-core/src/core/heavy-rules.ts
20553
21492
  import fs19 from "node:fs";
20554
21493
  import os6 from "node:os";
20555
- import path27 from "node:path";
21494
+ import path29 from "node:path";
20556
21495
  var EMPTY2 = () => ({ deny: [], allow: [] });
20557
21496
  var readText2 = (file) => {
20558
21497
  try {
@@ -20593,19 +21532,19 @@ function parseJsonc(text) {
20593
21532
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
20594
21533
  }
20595
21534
  function gitTop(cwd) {
20596
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
20597
- if (fs19.existsSync(path27.join(dir, ".git")))
21535
+ for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
21536
+ if (fs19.existsSync(path29.join(dir, ".git")))
20598
21537
  return dir;
20599
- if (path27.dirname(dir) === dir)
21538
+ if (path29.dirname(dir) === dir)
20600
21539
  return null;
20601
21540
  }
20602
21541
  }
20603
21542
  var projectDirs = (cwd) => {
20604
21543
  const top = gitTop(cwd);
20605
21544
  const dirs = [];
20606
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
21545
+ for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
20607
21546
  dirs.push(dir);
20608
- if (!top || dir === top || path27.dirname(dir) === dir)
21547
+ if (!top || dir === top || path29.dirname(dir) === dir)
20609
21548
  return dirs;
20610
21549
  }
20611
21550
  };
@@ -20617,12 +21556,12 @@ var CLAUDE_MANAGED = {
20617
21556
  };
20618
21557
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
20619
21558
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
20620
- const user = env.CLAUDE_CONFIG_DIR || path27.join(homeOf(env), ".claude");
21559
+ const user = env.CLAUDE_CONFIG_DIR || path29.join(homeOf(env), ".claude");
20621
21560
  return [
20622
21561
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
20623
- path27.join(user, "settings.json"),
20624
- path27.join(project, ".claude", "settings.json"),
20625
- path27.join(project, ".claude", "settings.local.json")
21562
+ path29.join(user, "settings.json"),
21563
+ path29.join(project, ".claude", "settings.json"),
21564
+ path29.join(project, ".claude", "settings.local.json")
20626
21565
  ];
20627
21566
  }
20628
21567
  var bashPattern = (entry) => {
@@ -20712,10 +21651,10 @@ var opencodeConfig = (value, rules) => {
20712
21651
  function opencodeRules(cwd, env = process.env) {
20713
21652
  const rules = EMPTY2();
20714
21653
  try {
20715
- const config = env.XDG_CONFIG_HOME || path27.join(homeOf(env), ".config");
21654
+ const config = env.XDG_CONFIG_HOME || path29.join(homeOf(env), ".config");
20716
21655
  const files = [
20717
- ...OPENCODE_NAMES.map((name) => path27.join(config, "opencode", name)),
20718
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path27.join(dir, name), path27.join(dir, ".opencode", name)])),
21656
+ ...OPENCODE_NAMES.map((name) => path29.join(config, "opencode", name)),
21657
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path29.join(dir, name), path29.join(dir, ".opencode", name)])),
20719
21658
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
20720
21659
  ];
20721
21660
  for (const file of files) {
@@ -20759,9 +21698,9 @@ var codexRule = (body) => {
20759
21698
  function codexRules(cwd, env = process.env) {
20760
21699
  const rules = EMPTY2();
20761
21700
  try {
20762
- const home = env.CODEX_HOME || path27.join(homeOf(env), ".codex");
21701
+ const home = env.CODEX_HOME || path29.join(homeOf(env), ".codex");
20763
21702
  const top = gitTop(cwd) ?? cwd;
20764
- for (const dir of [path27.join(home, "rules"), path27.join(top, ".codex", "rules")]) {
21703
+ for (const dir of [path29.join(home, "rules"), path29.join(top, ".codex", "rules")]) {
20765
21704
  let files;
20766
21705
  try {
20767
21706
  files = fs19.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -20771,7 +21710,7 @@ function codexRules(cwd, env = process.env) {
20771
21710
  return "unreadable";
20772
21711
  }
20773
21712
  for (const name of files) {
20774
- const text = readText2(path27.join(dir, name)) ?? "";
21713
+ const text = readText2(path29.join(dir, name)) ?? "";
20775
21714
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
20776
21715
  const rule = codexRule(part);
20777
21716
  if (!rule)
@@ -20790,8 +21729,8 @@ function cursorRules(cwd, env = process.env) {
20790
21729
  try {
20791
21730
  const top = gitTop(cwd) ?? cwd;
20792
21731
  for (const file of [
20793
- path27.join(homeOf(env), ".cursor", "cli-config.json"),
20794
- path27.join(top, ".cursor", "cli.json")
21732
+ path29.join(homeOf(env), ".cursor", "cli-config.json"),
21733
+ path29.join(top, ".cursor", "cli.json")
20795
21734
  ]) {
20796
21735
  const text = readText2(file);
20797
21736
  if (text === null)
@@ -20867,21 +21806,21 @@ function wrapAllowed(read, words, command, options = {}) {
20867
21806
  // packages/workit-core/src/core/heavy.ts
20868
21807
  import fs20 from "node:fs";
20869
21808
  import os7 from "node:os";
20870
- import path28 from "node:path";
21809
+ import path30 from "node:path";
20871
21810
  function stateDir(env, platform = process.platform) {
20872
21811
  if (env.WORKFLOW_TOOLKIT_STATE)
20873
21812
  return env.WORKFLOW_TOOLKIT_STATE;
20874
21813
  if (env.XDG_STATE_HOME)
20875
- return path28.join(env.XDG_STATE_HOME, "workit");
21814
+ return path30.join(env.XDG_STATE_HOME, "workit");
20876
21815
  const home = env.HOME || os7.homedir();
20877
21816
  if (platform === "darwin")
20878
- return path28.join(home, "Library", "Application Support", "workit");
21817
+ return path30.join(home, "Library", "Application Support", "workit");
20879
21818
  if (platform === "win32")
20880
- return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
20881
- return path28.join(home, ".local", "state", "workit");
21819
+ return path30.join(env.LOCALAPPDATA ?? path30.join(home, "AppData", "Local"), "workit");
21820
+ return path30.join(home, ".local", "state", "workit");
20882
21821
  }
20883
21822
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
20884
- var heavyCommandsFile = (env) => path28.join(stateDir(env), HEAVY_COMMANDS_FILE);
21823
+ var heavyCommandsFile = (env) => path30.join(stateDir(env), HEAVY_COMMANDS_FILE);
20885
21824
  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;
20886
21825
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
20887
21826
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -21156,7 +22095,7 @@ function machinePatterns(env) {
21156
22095
  return isRecord5(value) ? stringList(value.patterns) : [];
21157
22096
  }
21158
22097
  function repoPatterns(cwd) {
21159
- return repoHeavyCommands(gitTop(cwd) ?? path28.resolve(cwd));
22098
+ return repoHeavyCommands(gitTop(cwd) ?? path30.resolve(cwd));
21160
22099
  }
21161
22100
  function heavyLines(lines, options) {
21162
22101
  const found = [];
@@ -21234,12 +22173,12 @@ function heavyPlan(command, options) {
21234
22173
  }
21235
22174
  function workitOnPath2(env = process.env) {
21236
22175
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
21237
- for (const dir of (env.PATH ?? "").split(path28.delimiter))
22176
+ for (const dir of (env.PATH ?? "").split(path30.delimiter))
21238
22177
  for (const name of names) {
21239
22178
  if (!dir)
21240
22179
  continue;
21241
22180
  try {
21242
- fs20.accessSync(path28.join(dir, name), fs20.constants.X_OK);
22181
+ fs20.accessSync(path30.join(dir, name), fs20.constants.X_OK);
21243
22182
  return true;
21244
22183
  } catch {}
21245
22184
  }
@@ -21258,20 +22197,20 @@ function leaseRewrite(command) {
21258
22197
  // packages/workit-core/src/core/heavy-words.ts
21259
22198
  import fs21 from "node:fs";
21260
22199
  import os8 from "node:os";
21261
- import path29 from "node:path";
22200
+ import path31 from "node:path";
21262
22201
  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(" "));
21263
22202
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
21264
22203
  var stateDir2 = (env) => {
21265
22204
  if (env.WORKFLOW_TOOLKIT_STATE)
21266
22205
  return env.WORKFLOW_TOOLKIT_STATE;
21267
22206
  if (env.XDG_STATE_HOME)
21268
- return path29.join(env.XDG_STATE_HOME, "workit");
22207
+ return path31.join(env.XDG_STATE_HOME, "workit");
21269
22208
  const home = env.HOME || os8.homedir();
21270
22209
  if (process.platform === "darwin")
21271
- return path29.join(home, "Library", "Application Support", "workit");
22210
+ return path31.join(home, "Library", "Application Support", "workit");
21272
22211
  if (process.platform === "win32")
21273
- return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
21274
- return path29.join(home, ".local", "state", "workit");
22212
+ return path31.join(env.LOCALAPPDATA ?? path31.join(home, "AppData", "Local"), "workit");
22213
+ return path31.join(home, ".local", "state", "workit");
21275
22214
  };
21276
22215
  var readJson2 = (file) => {
21277
22216
  try {
@@ -21282,15 +22221,15 @@ var readJson2 = (file) => {
21282
22221
  };
21283
22222
  var patternWords = (cwd, env) => {
21284
22223
  const patterns = [];
21285
- const machine = readJson2(path29.join(stateDir2(env), "heavy-commands.json"));
22224
+ const machine = readJson2(path31.join(stateDir2(env), "heavy-commands.json"));
21286
22225
  if (Array.isArray(machine?.patterns))
21287
22226
  patterns.push(...machine.patterns);
21288
- for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
21289
- if (fs21.existsSync(path29.join(dir, ".git"))) {
22227
+ for (let dir = path31.resolve(cwd);; dir = path31.dirname(dir)) {
22228
+ if (fs21.existsSync(path31.join(dir, ".git"))) {
21290
22229
  patterns.push(...repoHeavyCommands(dir));
21291
22230
  break;
21292
22231
  }
21293
- if (path29.dirname(dir) === dir)
22232
+ if (path31.dirname(dir) === dir)
21294
22233
  break;
21295
22234
  }
21296
22235
  const words = new Set;
@@ -21349,21 +22288,21 @@ function heavyGate(cwd, command, options) {
21349
22288
  }
21350
22289
  }
21351
22290
  // packages/workit-core/src/hooks/write-gate.ts
21352
- import { realpathSync as realpathSync6 } from "node:fs";
22291
+ import { existsSync as existsSync11, realpathSync as realpathSync6 } from "node:fs";
21353
22292
  import os10 from "node:os";
21354
- import path31 from "node:path";
22293
+ import path33 from "node:path";
21355
22294
 
21356
22295
  // packages/workit-core/src/core/live-session.ts
21357
- import { spawnSync as spawnSync17 } from "node:child_process";
22296
+ import { spawnSync as spawnSync18 } from "node:child_process";
21358
22297
  import { createHash as createHash15 } from "node:crypto";
21359
22298
  import fs22 from "node:fs";
21360
22299
  import os9 from "node:os";
21361
- import path30 from "node:path";
22300
+ import path32 from "node:path";
21362
22301
  var LIVE_SESSION_TTL_MS = 15 * 60000;
21363
22302
  var HEARTBEAT_EVERY_MS = 30000;
21364
22303
  var liveProbe = {
21365
22304
  platform: process.platform,
21366
- run: (args) => spawnSync17("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
22305
+ run: (args) => spawnSync18("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
21367
22306
  start: (pid) => processStartOf(pid)
21368
22307
  };
21369
22308
  var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
@@ -21432,15 +22371,15 @@ var liveOwner = (input, transport, env = process.env) => {
21432
22371
  };
21433
22372
  };
21434
22373
  var checkoutOf = (target) => {
21435
- let dir = path30.resolve(target);
21436
- while (!fs22.existsSync(dir) && path30.dirname(dir) !== dir)
21437
- dir = path30.dirname(dir);
22374
+ let dir = path32.resolve(target);
22375
+ while (!fs22.existsSync(dir) && path32.dirname(dir) !== dir)
22376
+ dir = path32.dirname(dir);
21438
22377
  if (fs22.existsSync(dir) && !fs22.statSync(dir).isDirectory())
21439
- dir = path30.dirname(dir);
21440
- const parts = dir.split(path30.sep);
22378
+ dir = path32.dirname(dir);
22379
+ const parts = dir.split(path32.sep);
21441
22380
  const dotGit = parts.lastIndexOf(".git");
21442
22381
  if (dotGit > 0)
21443
- dir = parts.slice(0, dotGit).join(path30.sep) || path30.sep;
22382
+ dir = parts.slice(0, dotGit).join(path32.sep) || path32.sep;
21444
22383
  const location = resolveStore(dir);
21445
22384
  if (location instanceof Error || !location.shared || !location.top)
21446
22385
  return null;
@@ -21450,9 +22389,9 @@ var checkoutOf = (target) => {
21450
22389
  } catch {}
21451
22390
  return top;
21452
22391
  };
21453
- var markerFile2 = (top) => path30.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22392
+ var markerFile2 = (top) => path32.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
21454
22393
  var checkoutKey = (top, platform = process.platform) => {
21455
- let key = path30.resolve(top);
22394
+ let key = path32.resolve(top);
21456
22395
  try {
21457
22396
  key = fs22.realpathSync.native(key);
21458
22397
  } catch {}
@@ -21547,7 +22486,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
21547
22486
  checkout,
21548
22487
  heartbeatAt: now.toISOString()
21549
22488
  };
21550
- fs22.mkdirSync(path30.dirname(file), { recursive: true });
22489
+ fs22.mkdirSync(path32.dirname(file), { recursive: true });
21551
22490
  const temp = `${file}.${process.pid}.tmp`;
21552
22491
  fs22.writeFileSync(temp, JSON.stringify(marker));
21553
22492
  fs22.renameSync(temp, file);
@@ -21559,26 +22498,26 @@ var NONE3 = { kind: "none" };
21559
22498
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
21560
22499
  var canonical2 = (file) => {
21561
22500
  let rest = "";
21562
- for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
22501
+ for (let dir = path33.resolve(file);; dir = path33.dirname(dir)) {
21563
22502
  try {
21564
- return path31.join(realpathSync6.native(dir), rest);
22503
+ return path33.join(realpathSync6.native(dir), rest);
21565
22504
  } catch {
21566
- if (path31.dirname(dir) === dir)
21567
- return path31.resolve(file);
21568
- rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
22505
+ if (path33.dirname(dir) === dir)
22506
+ return path33.resolve(file);
22507
+ rest = rest ? path33.join(path33.basename(dir), rest) : path33.basename(dir);
21569
22508
  }
21570
22509
  }
21571
22510
  };
21572
22511
  var exempt = (cwd, root, file, plans) => {
21573
22512
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
21574
- const relative = path31.relative(fold(canonical2(root)), fold(canonical2(path31.resolve(cwd, file))));
21575
- if (relative.startsWith("..") || path31.isAbsolute(relative))
22513
+ const relative = path33.relative(fold(canonical2(root)), fold(canonical2(path33.resolve(cwd, file))));
22514
+ if (relative.startsWith("..") || path33.isAbsolute(relative))
21576
22515
  return true;
21577
- const posix = relative.split(path31.sep).join("/");
22516
+ const posix = relative.split(path33.sep).join("/");
21578
22517
  const segments = posix.split("/");
21579
22518
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
21580
22519
  };
21581
- var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
22520
+ var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs", "busybox"]);
21582
22521
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
21583
22522
  var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
21584
22523
  var wrappedScript = (input) => {
@@ -21590,9 +22529,9 @@ var wrappedScript = (input) => {
21590
22529
  return null;
21591
22530
  if ("script" in tail)
21592
22531
  return tail.script;
21593
- const [shell, flag, script] = tail.argv;
21594
- if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
21595
- return script ?? null;
22532
+ const script = shellScript2(tail.argv);
22533
+ if (SCRIPT_SHELLS.has((tail.argv[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && script)
22534
+ return script;
21596
22535
  return tail.argv.map(quoteWord2).join(" ");
21597
22536
  };
21598
22537
  var editsOf = (input) => {
@@ -21671,6 +22610,94 @@ var editsOf = (input) => {
21671
22610
  return [];
21672
22611
  }
21673
22612
  };
22613
+ var shellScript2 = (words) => {
22614
+ if (!SCRIPT_SHELLS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
22615
+ return null;
22616
+ for (let index = 1;index < words.length; index++) {
22617
+ const word = words[index];
22618
+ if (/^-[a-z]*c$/.test(word))
22619
+ return words[index + 1] ?? null;
22620
+ if (/^[-+][oO]$/.test(word))
22621
+ index++;
22622
+ else if (!/^[-+]/.test(word))
22623
+ return null;
22624
+ }
22625
+ return null;
22626
+ };
22627
+ var XARGS_VALUE = /^-[IinLPdsEa]$/;
22628
+ var runsWriter = (words) => {
22629
+ const filled = [...words.filter((word) => word !== "{}" && word !== ";" && word !== "+"), "x"];
22630
+ const edits = editsOf(filled);
22631
+ return edits === null || edits.length > 0 || opaqueWrites(filled) !== null;
22632
+ };
22633
+ var opaqueWrites = (input) => {
22634
+ let words = input;
22635
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0]))) {
22636
+ if (words[0] === "xargs") {
22637
+ let at = 1;
22638
+ while (words[at]?.startsWith("-"))
22639
+ at += XARGS_VALUE.test(words[at]) ? 2 : 1;
22640
+ return runsWriter(words.slice(at)) ? ["."] : null;
22641
+ }
22642
+ words = words.slice(1);
22643
+ }
22644
+ const [verb, ...args] = words;
22645
+ const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
22646
+ const files = operands(args);
22647
+ const has = (pattern) => args.some((arg) => pattern.test(arg));
22648
+ const listed = (named) => named.length ? named : ["."];
22649
+ if (name === "unlink" || name === "shred")
22650
+ return listed(files);
22651
+ if (/^(?:chmod|chown|chgrp|chattr)$/.test(name))
22652
+ return listed(has(/^--reference/) ? files : files.slice(1));
22653
+ if (name === "rsync") {
22654
+ const dest = files.at(-1);
22655
+ const remote = /^[\w.@-]*:/.test(dest ?? "") && !/^[A-Za-z]:[\\/]/.test(dest ?? "");
22656
+ return dest === undefined ? ["."] : remote ? [] : [dest];
22657
+ }
22658
+ if (name === "find") {
22659
+ const exec = args.findIndex((arg) => /^-(?:exec|execdir|ok|okdir)$/.test(arg));
22660
+ const writes = has(/^-(?:delete|fprint\w*|fls)$/) || exec >= 0 && runsWriter(args.slice(exec + 1));
22661
+ const end = args.findIndex((arg) => /^[-(!]/.test(arg));
22662
+ return writes ? listed(end < 0 ? args : args.slice(0, end)) : null;
22663
+ }
22664
+ if (/^g?awk$/.test(name)) {
22665
+ if (!has(/^-i|^--include/))
22666
+ return null;
22667
+ const named = [];
22668
+ let program = !has(/^-f$|^--file/);
22669
+ for (let index = 0;index < args.length; index++)
22670
+ if (/^(?:-[ifvF]|--(?:include|file|assign|field-separator))$/.test(args[index]))
22671
+ index++;
22672
+ else if (args[index].startsWith("-"))
22673
+ continue;
22674
+ else if (program)
22675
+ program = false;
22676
+ else
22677
+ named.push(args[index]);
22678
+ return listed(named);
22679
+ }
22680
+ if (name === "perl")
22681
+ return has(/^-[a-zA-Z]*[eE]/) ? ["."] : null;
22682
+ if (/^(?:python[\d.]*|node|bun|deno|ruby|php)$/.test(name))
22683
+ return has(/^-[a-zA-Z]*[ce]$|^--eval$|^-p$/) ? ["."] : null;
22684
+ return shellScript2(words) === null ? null : ["."];
22685
+ };
22686
+ var linkSources = (args, at) => {
22687
+ const names = operands([...args]);
22688
+ const into = args.indexOf("-t");
22689
+ const dest = into >= 0 ? args[into + 1] : names.length > 1 ? names.at(-1) : ".";
22690
+ const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
22691
+ const target = dest === undefined ? null : at(dest);
22692
+ const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
22693
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync11(target) || /[\\/]$/.test(dest ?? "") ? target : path33.dirname(target);
22694
+ return sources.flatMap((source) => {
22695
+ if (symbolic && base !== null && !path33.isAbsolute(homeExpanded(source)))
22696
+ return [path33.resolve(base, source)];
22697
+ const resolved = at(source);
22698
+ return resolved ? [resolved] : [];
22699
+ });
22700
+ };
21674
22701
  var shellWrites = (command, depth = 0) => {
21675
22702
  const targets = [];
21676
22703
  let unknown = false;
@@ -21695,7 +22722,7 @@ var shellWrites = (command, depth = 0) => {
21695
22722
  return { writes: true, targets: unknown ? null : targets };
21696
22723
  };
21697
22724
  var cache2 = new Map;
21698
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path31.resolve(root, file)) ?? "-").join("\x00");
22725
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path33.resolve(root, file)) ?? "-").join("\x00");
21699
22726
  var gateFor = (cwd) => {
21700
22727
  const store = new TaskStore(cwd);
21701
22728
  const listed = store.listTaskIndex();
@@ -21774,32 +22801,36 @@ var gitMutationDirs = (cwd, command, dialect) => {
21774
22801
  return [];
21775
22802
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
21776
22803
  return [];
21777
- return [path31.resolve(cwd, invocation.dir ?? ".")];
22804
+ return [path33.resolve(cwd, invocation.dir ?? ".")];
21778
22805
  });
21779
22806
  };
21780
22807
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
21781
22808
  var PUSHD2 = new Set(["pushd", "push-location"]);
21782
22809
  var POPD2 = new Set(["popd", "pop-location"]);
21783
- var cdInto = (dir, words) => {
22810
+ var UNKNOWN_DIR = path33.resolve(path33.parse(process.cwd()).root, ".workit-unknown-dir");
22811
+ var cdInto = (dir, words, strict = false) => {
22812
+ const lost = strict ? UNKNOWN_DIR : undefined;
21784
22813
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
21785
22814
  const target = named === undefined ? undefined : homeExpanded(named);
21786
- if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
21787
- return;
21788
- return target === undefined ? os10.homedir() : path31.resolve(dir, target);
22815
+ if (target === "-" || target && /^~|[$`*?]/.test(target))
22816
+ return lost;
22817
+ if (dir === undefined)
22818
+ return lost;
22819
+ return target === undefined ? os10.homedir() : path33.resolve(dir, target);
21789
22820
  };
21790
22821
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
21791
22822
  var homeExpanded = (file) => {
21792
22823
  const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
21793
22824
  return match ? (process.env.HOME || os10.homedir()) + file.slice(match[0].length) : file;
21794
22825
  };
21795
- var segmentTargets = (cwd, command, dialect, depth = 0) => {
22826
+ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21796
22827
  const targets = [];
21797
22828
  let dir = cwd;
21798
22829
  let stack = [];
21799
22830
  const groups = [];
21800
22831
  const at = (file) => {
21801
22832
  const home = homeExpanded(file);
21802
- return path31.isAbsolute(home) ? home : dir === undefined ? null : path31.resolve(dir, home);
22833
+ return path33.isAbsolute(home) ? home : dir === undefined ? null : path33.resolve(dir, home);
21803
22834
  };
21804
22835
  for (const segment of segmentsOf(command, dialect)) {
21805
22836
  if (segment.group === "open") {
@@ -21816,21 +22847,35 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
21816
22847
  const words = lead < 0 ? [] : segment.words.slice(lead);
21817
22848
  const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
21818
22849
  if (CD2.has(name)) {
21819
- dir = cdInto(dir, words);
22850
+ dir = cdInto(dir, words, strict);
21820
22851
  continue;
21821
22852
  }
21822
22853
  if (PUSHD2.has(name)) {
21823
22854
  stack.push(dir);
21824
- dir = cdInto(dir, words);
22855
+ dir = cdInto(dir, words, strict);
21825
22856
  continue;
21826
22857
  }
21827
22858
  if (POPD2.has(name)) {
21828
- dir = stack.length ? stack.pop() : undefined;
22859
+ dir = stack.length ? stack.pop() : strict ? UNKNOWN_DIR : undefined;
21829
22860
  continue;
21830
22861
  }
21831
- const inner = depth < 4 ? wrappedScript(words) : null;
22862
+ const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
21832
22863
  if (inner !== null && dir !== undefined)
21833
- targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
22864
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
22865
+ if (strict && dir !== undefined) {
22866
+ const runIn = dir;
22867
+ for (const sub of depth < 4 ? segment.subs ?? [] : [])
22868
+ targets.push(...segmentTargets(runIn, sub, dialect, depth + 1, true));
22869
+ const opaque = segment.unanalyzable ? ["."] : inner === null ? opaqueWrites(words) : null;
22870
+ for (const file of opaque ?? []) {
22871
+ const resolved = at(file);
22872
+ if (resolved)
22873
+ targets.push(file === "." ? path33.join(resolved, DIR_TARGET) : resolved);
22874
+ }
22875
+ const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
22876
+ if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
22877
+ targets.push(...linkSources(words.slice(ln + 1), at));
22878
+ }
21834
22879
  for (const file of segment.redirects)
21835
22880
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
21836
22881
  const resolved = at(file);
@@ -21840,18 +22885,36 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
21840
22885
  const edits = editsOf(segment.words);
21841
22886
  if (edits === null) {
21842
22887
  if (dir !== undefined)
21843
- targets.push(path31.join(dir, DIR_TARGET));
22888
+ targets.push(path33.join(dir, DIR_TARGET));
21844
22889
  } else
21845
22890
  for (const file of edits) {
21846
22891
  const resolved = at(file);
21847
22892
  if (resolved)
21848
22893
  targets.push(resolved);
21849
22894
  }
21850
- 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), "")))
21851
- targets.push(path31.join(dir, DIR_TARGET));
22895
+ if (dir !== undefined)
22896
+ for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
22897
+ const { at: runsIn, args } = workitCwd(dir, invocation);
22898
+ if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
22899
+ targets.push(path33.join(runsIn, DIR_TARGET));
22900
+ }
21852
22901
  }
21853
22902
  return targets;
21854
22903
  };
22904
+ var workitCwd = (dir, args) => {
22905
+ const { rest, cwd } = skipWorkitGlobals(args);
22906
+ return { at: cwd === null ? dir : path33.resolve(dir, homeExpanded(cwd)), args: rest };
22907
+ };
22908
+ var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
22909
+ try {
22910
+ return [
22911
+ ...segmentTargets(cwd, command, dialect, 0, strict),
22912
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path33.join(dir, DIR_TARGET))
22913
+ ];
22914
+ } catch {
22915
+ return [];
22916
+ }
22917
+ };
21855
22918
  var overridden = (command, dialect) => {
21856
22919
  const segments = segmentsOf(command, dialect);
21857
22920
  if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
@@ -21883,7 +22946,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
21883
22946
  var guarded = (targets) => {
21884
22947
  const seen = new Set;
21885
22948
  return targets.filter((target) => {
21886
- const dir = path31.dirname(target);
22949
+ const dir = path33.dirname(target);
21887
22950
  if (seen.has(dir))
21888
22951
  return false;
21889
22952
  seen.add(dir);
@@ -21904,7 +22967,7 @@ var claimAll = (owner, targets, deps) => {
21904
22967
  for (const target of guarded(targets))
21905
22968
  claimLiveSession(target, owner, deps);
21906
22969
  };
21907
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path31.resolve(cwd, file)) : [path31.join(cwd, DIR_TARGET)];
22970
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path33.resolve(cwd, file)) : [path33.join(cwd, DIR_TARGET)];
21908
22971
  var issuedTarget = (cwd, targets) => {
21909
22972
  const state = resolveStateDir();
21910
22973
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -21912,7 +22975,7 @@ var issuedTarget = (cwd, targets) => {
21912
22975
  };
21913
22976
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
21914
22977
  const win = platform === "win32";
21915
- const p = win ? path31.win32 : path31.posix;
22978
+ const p = win ? path33.win32 : path33.posix;
21916
22979
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
21917
22980
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
21918
22981
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -21957,16 +23020,8 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
21957
23020
  } catch {}
21958
23021
  const writes = shellWrites(command);
21959
23022
  let targets = [];
21960
- if (owner) {
21961
- try {
21962
- targets = [
21963
- ...segmentTargets(cwd, command, dialect),
21964
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path31.join(dir, DIR_TARGET))
21965
- ];
21966
- } catch {
21967
- targets = [];
21968
- }
21969
- }
23023
+ if (owner)
23024
+ targets = shellMutationTargets(cwd, command, dialect);
21970
23025
  if (owner && targets.length) {
21971
23026
  const live = liveGate(owner, targets, deps);
21972
23027
  if (live.kind !== "none") {
@@ -22010,7 +23065,25 @@ function beforeWriteGate(cwd, paths) {
22010
23065
  }
22011
23066
  }
22012
23067
  // packages/workit-core/src/hooks/stop.ts
22013
- import { spawnSync as spawnSync18 } from "node:child_process";
23068
+ import { spawnSync as spawnSync19 } from "node:child_process";
23069
+
23070
+ // packages/workit-core/src/core/policy/current-judgment.ts
23071
+ function currentJudgment(cwd) {
23072
+ try {
23073
+ const store = new TaskStore(cwd);
23074
+ const listed = store.listTaskIndex();
23075
+ const key = store.currentKey();
23076
+ if (!listed.ok || !key.ok)
23077
+ return null;
23078
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
23079
+ const task = entry ? store.readTask(entry.id) : null;
23080
+ return task?.ok === true ? latestJudgment(task.data) : null;
23081
+ } catch {
23082
+ return null;
23083
+ }
23084
+ }
23085
+
23086
+ // packages/workit-core/src/hooks/stop.ts
22014
23087
  var NONE4 = { kind: "none" };
22015
23088
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
22016
23089
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -22031,7 +23104,7 @@ var asksUser = (message) => {
22031
23104
  };
22032
23105
  var short = (sha) => (sha ?? "?").slice(0, 12);
22033
23106
  var gitList = (cwd, args) => {
22034
- const run = spawnSync18("git", args, {
23107
+ const run = spawnSync19("git", args, {
22035
23108
  cwd,
22036
23109
  encoding: "utf8",
22037
23110
  timeout: 3000,
@@ -22103,29 +23176,32 @@ var redChecks = (rows, pr, head, endpoint) => {
22103
23176
  reason: `PR #${pr}'s checks were last seen ${state} at ${short(head)}, and this workspace's endpoint is \`${endpoint}\`: run \`workit ci wait --pr ${pr}\`${state === "failing" ? " after fixing the failures `workit pr status` lists (then push)" : ""}.`
22104
23177
  };
22105
23178
  };
22106
- var taskJudgment = (cwd) => {
22107
- try {
22108
- const store = new TaskStore(cwd);
22109
- const listed = store.listTaskIndex();
22110
- const key = store.currentKey();
22111
- if (!listed.ok || !key.ok)
22112
- return null;
22113
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
22114
- const task = entry ? store.readTask(entry.id) : null;
22115
- return task?.ok === true ? latestJudgment(task.data) : null;
22116
- } catch {
22117
- return null;
22118
- }
23179
+ var prBase = (rows) => {
23180
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
23181
+ return typeof seen?.base === "string" ? seen.base : null;
22119
23182
  };
22120
- var unverified = (cwd, branch, all, descriptor, verification) => {
23183
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
22121
23184
  const check = checkVerdicts(cwd, branch, all);
22122
23185
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
22123
23186
  return null;
22124
- const judgment = taskJudgment(cwd);
23187
+ const judgment = currentJudgment(cwd);
22125
23188
  const high = judgment?.riskTier === "high";
22126
- if (judgment && !judgment.behaviorChange && !high)
22127
- return null;
22128
- const independent = verification === "independent" || high;
23189
+ let independent;
23190
+ if (verification === "proportional") {
23191
+ const lane = branchLane(cwd, {
23192
+ base: prBase(all.filter((row) => row.branch === branch)),
23193
+ head,
23194
+ judgment
23195
+ });
23196
+ const need = laneNeed(lane.lane);
23197
+ if (need === "none")
23198
+ return null;
23199
+ independent = need === "non-author";
23200
+ } else {
23201
+ if (judgment && !judgment.behaviorChange && !high)
23202
+ return null;
23203
+ independent = verification === "independent" || high;
23204
+ }
22129
23205
  if (!independent && check.review === "self-reviewed")
22130
23206
  return null;
22131
23207
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -22172,7 +23248,7 @@ function stopObligation(input, descriptor) {
22172
23248
  }
22173
23249
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
22174
23250
  return null;
22175
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
23251
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
22176
23252
  return left ? { ...left, head } : null;
22177
23253
  } catch {
22178
23254
  return null;
@@ -22442,9 +23518,9 @@ var WRITE_TOOLS = new Set([
22442
23518
  ]);
22443
23519
  // packages/workit-core/src/hooks/standing.ts
22444
23520
  import fs23 from "node:fs";
22445
- import path32 from "node:path";
23521
+ import path34 from "node:path";
22446
23522
  var readPlans = (store) => {
22447
- const dir = path32.join(store, "fanouts");
23523
+ const dir = path34.join(store, "fanouts");
22448
23524
  let names;
22449
23525
  try {
22450
23526
  names = fs23.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -22454,7 +23530,7 @@ var readPlans = (store) => {
22454
23530
  const plans = [];
22455
23531
  for (const name of names) {
22456
23532
  try {
22457
- const raw = JSON.parse(fs23.readFileSync(path32.join(dir, name), "utf8"));
23533
+ const raw = JSON.parse(fs23.readFileSync(path34.join(dir, name), "utf8"));
22458
23534
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
22459
23535
  continue;
22460
23536
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -22544,6 +23620,19 @@ var standingSinceStart = (input) => {
22544
23620
  // packages/workit-core/src/hooks/cwd-normalize.ts
22545
23621
  var FILTERS = new Set(["tail", "head", "grep"]);
22546
23622
 
23623
+ // packages/workit-core/src/hooks/worktree-confine.ts
23624
+ var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
23625
+ var MUTATING_REMOTE = new Set([
23626
+ "add",
23627
+ "remove",
23628
+ "rm",
23629
+ "rename",
23630
+ "set-url",
23631
+ "set-head",
23632
+ "set-branches",
23633
+ "prune"
23634
+ ]);
23635
+
22547
23636
  // packages/workit-core/src/hooks/handle.ts
22548
23637
  var offered = new Set;
22549
23638
  var PRE_TOOL = new Set(["shell.pre", "write.pre", "tool.pre"]);
@@ -22649,11 +23738,11 @@ var OPENCODE_DESCRIPTOR = {
22649
23738
  ]
22650
23739
  };
22651
23740
  // packages/workit-opencode/src/shared/init-apply.ts
22652
- import path37 from "node:path";
23741
+ import path39 from "node:path";
22653
23742
 
22654
23743
  // packages/workit-core/src/core/gitignore.ts
22655
- import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
22656
- import path33 from "node:path";
23744
+ import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
23745
+ import path35 from "node:path";
22657
23746
  var GITIGNORE_ENTRIES = [
22658
23747
  "# workit: SDD working state (never commit)",
22659
23748
  "docs/*/sdd/",
@@ -22673,8 +23762,8 @@ var GITIGNORE_ENTRIES = [
22673
23762
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22674
23763
  if (!confirmed)
22675
23764
  return { ok: false, error: "confirmed: true required" };
22676
- const file = path33.join(workspaceRoot, ".gitignore");
22677
- const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
23765
+ const file = path35.join(workspaceRoot, ".gitignore");
23766
+ const existing = existsSync12(file) ? readFileSync8(file, "utf8") : "";
22678
23767
  const existingLines = new Set(existing.split(`
22679
23768
  `).map((l) => l.trim()).filter(Boolean));
22680
23769
  const added = [];
@@ -22693,33 +23782,33 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22693
23782
  ` : "") + append.join(`
22694
23783
  `) + `
22695
23784
  `, "utf8");
22696
- } else if (!existsSync9(file)) {
23785
+ } else if (!existsSync12(file)) {
22697
23786
  writeFileSync4(file, "", "utf8");
22698
23787
  }
22699
23788
  return { ok: true, path: file, added };
22700
23789
  };
22701
23790
 
22702
23791
  // packages/workit-core/src/core/hygiene.ts
22703
- import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
22704
- import path34 from "node:path";
23792
+ import { existsSync as existsSync13, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
23793
+ import path36 from "node:path";
22705
23794
  var repoRoot3 = assetRoot();
22706
- var templatesDir = () => path34.join(repoRoot3, "templates", "hygiene");
23795
+ var templatesDir = () => path36.join(repoRoot3, "templates", "hygiene");
22707
23796
  var packageJson = (root) => {
22708
- if (!existsSync10(path34.join(root, "package.json")))
23797
+ if (!existsSync13(path36.join(root, "package.json")))
22709
23798
  return null;
22710
23799
  try {
22711
- return JSON.parse(readFileSync7(path34.join(root, "package.json"), "utf8"));
23800
+ return JSON.parse(readFileSync9(path36.join(root, "package.json"), "utf8"));
22712
23801
  } catch {
22713
23802
  return null;
22714
23803
  }
22715
23804
  };
22716
23805
  var isOpenSource = (root) => {
22717
- if (existsSync10(path34.join(root, "LICENSE")))
23806
+ if (existsSync13(path36.join(root, "LICENSE")))
22718
23807
  return true;
22719
23808
  const pkg = packageJson(root);
22720
23809
  if (pkg && !pkg.private)
22721
23810
  return true;
22722
- return path34.basename(root) === "workflow-toolkit";
23811
+ return path36.basename(root) === "workflow-toolkit";
22723
23812
  };
22724
23813
  var licenseHolder = (root) => {
22725
23814
  const pkg = packageJson(root);
@@ -22742,13 +23831,13 @@ var planHygieneFiles = (root, opts = {}) => {
22742
23831
  files.push("LICENSE", "CONTRIBUTING.md");
22743
23832
  const planned = [];
22744
23833
  for (const file of files) {
22745
- if (existsSync10(path34.join(root, file)))
23834
+ if (existsSync13(path36.join(root, file)))
22746
23835
  continue;
22747
- const tpl = path34.join(templatesDir(), file);
22748
- if (!existsSync10(tpl))
23836
+ const tpl = path36.join(templatesDir(), file);
23837
+ if (!existsSync13(tpl))
22749
23838
  continue;
22750
- const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path34.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22751
- planned.push({ path: path34.join(root, file), content });
23839
+ const content = readFileSync9(tpl, "utf8").replace(/<PROJECT>/g, path36.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
23840
+ planned.push({ path: path36.join(root, file), content });
22752
23841
  }
22753
23842
  return planned;
22754
23843
  };
@@ -22758,30 +23847,30 @@ var ensureHygieneFiles = (root, opts) => {
22758
23847
  const created = [];
22759
23848
  for (const planned of planHygieneFiles(root, opts)) {
22760
23849
  writeFileSync5(planned.path, planned.content, "utf8");
22761
- created.push(path34.basename(planned.path));
23850
+ created.push(path36.basename(planned.path));
22762
23851
  }
22763
23852
  return { ok: true, created };
22764
23853
  };
22765
23854
 
22766
23855
  // packages/workit-core/src/core/init.ts
22767
23856
  import fs24 from "node:fs";
22768
- import path36 from "node:path";
22769
- import { spawnSync as spawnSync19 } from "node:child_process";
23857
+ import path38 from "node:path";
23858
+ import { spawnSync as spawnSync20 } from "node:child_process";
22770
23859
 
22771
23860
  // packages/workit-core/src/core/workspace-branch-policy.ts
22772
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
22773
- import path35 from "node:path";
23861
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
23862
+ import path37 from "node:path";
22774
23863
  import { isDeepStrictEqual } from "node:util";
22775
23864
  var readFileSafe = (p) => {
22776
23865
  try {
22777
- return readFileSync8(p, "utf8");
23866
+ return readFileSync10(p, "utf8");
22778
23867
  } catch {
22779
23868
  return null;
22780
23869
  }
22781
23870
  };
22782
23871
  function applyWorkspaceBranchPolicy(opts) {
22783
23872
  const { workspace_root, env = process.env } = opts;
22784
- const dir = path35.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
23873
+ const dir = path37.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22785
23874
  const {
22786
23875
  status,
22787
23876
  path: wsPath,
@@ -22793,7 +23882,7 @@ function applyWorkspaceBranchPolicy(opts) {
22793
23882
  if (status === "malformed" || status === "invalid")
22794
23883
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
22795
23884
  const detection = detectBranchPolicy(workspace_root);
22796
- const name = String(env.WORKFLOW_BP_NAME ?? path35.basename(workspace_root));
23885
+ const name = String(env.WORKFLOW_BP_NAME ?? path37.basename(workspace_root));
22797
23886
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
22798
23887
  const policy = {
22799
23888
  preset: detection.preset,
@@ -22842,7 +23931,7 @@ function applyWorkspaceBranchPolicy(opts) {
22842
23931
  ok: false,
22843
23932
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
22844
23933
  };
22845
- mkdirSync4(path35.dirname(wsPath), { recursive: true });
23934
+ mkdirSync4(path37.dirname(wsPath), { recursive: true });
22846
23935
  const tmp = `${wsPath}.${process.pid}.tmp`;
22847
23936
  try {
22848
23937
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -22889,7 +23978,7 @@ var resolveInitProvider = (cwd) => {
22889
23978
  return env.toLowerCase();
22890
23979
  const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
22891
23980
  try {
22892
- const out = spawnSync19("git", ["remote", "get-url", "origin"], {
23981
+ const out = spawnSync20("git", ["remote", "get-url", "origin"], {
22893
23982
  cwd: root,
22894
23983
  encoding: "utf8"
22895
23984
  });
@@ -22907,7 +23996,7 @@ var youtrackJsonContent = (dir) => {
22907
23996
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
22908
23997
  return {
22909
23998
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
22910
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path36.join(dir, "youtrack.token"),
23999
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path38.join(dir, "youtrack.token"),
22911
24000
  ...meetingIssue ? {
22912
24001
  meetingIssue,
22913
24002
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -22940,16 +24029,16 @@ function initApplyData(action, env = process.env) {
22940
24029
  fs24.mkdirSync(dir, { recursive: true });
22941
24030
  switch (action) {
22942
24031
  case "youtrack_json": {
22943
- const out = path36.join(dir, "youtrack.json");
24032
+ const out = path38.join(dir, "youtrack.json");
22944
24033
  fs24.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22945
24034
  `, "utf8");
22946
24035
  return { action, ok: true, path: out };
22947
24036
  }
22948
24037
  case "youtrack_token_placeholder": {
22949
- const p = path36.join(dir, "youtrack.token");
24038
+ const p = path38.join(dir, "youtrack.token");
22950
24039
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
22951
24040
  `, 384) === "preserved";
22952
- const abs = path36.resolve(p);
24041
+ const abs = path38.resolve(p);
22953
24042
  return {
22954
24043
  action,
22955
24044
  ok: true,
@@ -22961,14 +24050,14 @@ function initApplyData(action, env = process.env) {
22961
24050
  };
22962
24051
  }
22963
24052
  case "youtrack_scaffold": {
22964
- const jsonOut = path36.join(dir, "youtrack.json");
22965
- const tokenOut = path36.join(dir, "youtrack.token");
24053
+ const jsonOut = path38.join(dir, "youtrack.json");
24054
+ const tokenOut = path38.join(dir, "youtrack.token");
22966
24055
  fs24.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22967
24056
  `, "utf8");
22968
24057
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
22969
24058
  `, 384) === "preserved";
22970
- const configPath = path36.resolve(jsonOut);
22971
- const tokenPath = path36.resolve(tokenOut);
24059
+ const configPath = path38.resolve(jsonOut);
24060
+ const tokenPath = path38.resolve(tokenOut);
22972
24061
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
22973
24062
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
22974
24063
  let tokenCreate = {};
@@ -23020,10 +24109,10 @@ function initApplyData(action, env = process.env) {
23020
24109
  }
23021
24110
  }
23022
24111
  case "vcs_scaffold": {
23023
- const jsonOut = path36.join(dir, "vcs.json");
24112
+ const jsonOut = path38.join(dir, "vcs.json");
23024
24113
  fs24.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
23025
24114
  `, "utf8");
23026
- const configPath = path36.resolve(jsonOut);
24115
+ const configPath = path38.resolve(jsonOut);
23027
24116
  const prev = process.env.WORKFLOW_VCS_CONFIG;
23028
24117
  process.env.WORKFLOW_VCS_CONFIG = configPath;
23029
24118
  try {
@@ -23142,7 +24231,7 @@ var executeInitApply = (args, directory, runtime) => {
23142
24231
  protectedNames: args.branch_policy_protected
23143
24232
  }, current);
23144
24233
  writeConfig(next);
23145
- return output(ok({ action: "config", path: path37.join(configDir(), "config.json"), ...next }));
24234
+ return output(ok({ action: "config", path: path39.join(configDir(), "config.json"), ...next }));
23146
24235
  }
23147
24236
  const env = Object.fromEntries(Object.entries({
23148
24237
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -23159,14 +24248,14 @@ var executeInitApply = (args, directory, runtime) => {
23159
24248
 
23160
24249
  // packages/workit-opencode/src/shared/session.ts
23161
24250
  import { realpathSync as realpathSync7 } from "node:fs";
23162
- import path38 from "node:path";
24251
+ import path40 from "node:path";
23163
24252
  var sameWorkspace = (expected, observed) => {
23164
24253
  if (typeof observed !== "string" || !observed)
23165
24254
  return false;
23166
24255
  try {
23167
24256
  return realpathSync7(expected) === realpathSync7(observed);
23168
24257
  } catch {
23169
- return path38.resolve(expected) === path38.resolve(observed);
24258
+ return path40.resolve(expected) === path40.resolve(observed);
23170
24259
  }
23171
24260
  };
23172
24261
 
@@ -23238,7 +24327,7 @@ var workitFamilyOf = (toolName) => {
23238
24327
 
23239
24328
  // packages/workit-opencode/src/tools/context.ts
23240
24329
  import { realpathSync as realpathSync8 } from "node:fs";
23241
- import path39 from "node:path";
24330
+ import path41 from "node:path";
23242
24331
  var createContextTool = () => ({
23243
24332
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
23244
24333
  execute: async (args, context) => {
@@ -23248,7 +24337,7 @@ var createContextTool = () => ({
23248
24337
  let root;
23249
24338
  try {
23250
24339
  const cwd = parsed.data.cwd;
23251
- root = realpathSync8(cwd ? path39.resolve(context.directory, cwd) : context.directory);
24340
+ root = realpathSync8(cwd ? path41.resolve(context.directory, cwd) : context.directory);
23252
24341
  } catch {
23253
24342
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
23254
24343
  }
@@ -23280,118 +24369,13 @@ var workerCoordinatorFor = (task, worker) => {
23280
24369
  // packages/workit-core/src/fanout.ts
23281
24370
  import { createHash as createHash17 } from "node:crypto";
23282
24371
  import fs26 from "node:fs";
23283
- import path42 from "node:path";
24372
+ import path43 from "node:path";
23284
24373
 
23285
24374
  // packages/workit-core/src/stack.ts
23286
24375
  import { spawnSync as spawnSync21 } from "node:child_process";
23287
24376
  import { createHash as createHash16, randomBytes as randomBytes4 } from "node:crypto";
23288
24377
  import fs25 from "node:fs";
23289
- import path41 from "node:path";
23290
-
23291
- // packages/workit-core/src/git/ops.ts
23292
- import { spawnSync as spawnSync20 } from "node:child_process";
23293
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
23294
- import path40 from "node:path";
23295
-
23296
- // packages/workit-core/src/forge/redact.ts
23297
- 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_.-]*`;
23298
- var ASSIGNMENT2 = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
23299
- var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
23300
- var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
23301
-
23302
- // packages/workit-core/src/forge/types.ts
23303
- var success2 = (data) => ({ ok: true, data });
23304
- var failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
23305
-
23306
- // packages/workit-core/src/git/ops.ts
23307
- var COMMIT_TIMEOUT_MS = 10 * 60000;
23308
- var git5 = (cwd, args, options = {}) => {
23309
- const result = spawnSync20("git", args, {
23310
- cwd,
23311
- encoding: "utf8",
23312
- env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
23313
- maxBuffer: 64 * 1024 * 1024,
23314
- timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
23315
- killSignal: "SIGKILL",
23316
- windowsHide: true,
23317
- ...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
23318
- });
23319
- return {
23320
- ok: result.status === 0,
23321
- status: result.status,
23322
- stdout: result.stdout ?? "",
23323
- stderr: result.stderr ?? ""
23324
- };
23325
- };
23326
- function operationInProgress(cwd) {
23327
- const markers = [
23328
- ["rebase-merge", "rebase"],
23329
- ["rebase-apply", "rebase"],
23330
- ["MERGE_HEAD", "merge"],
23331
- ["CHERRY_PICK_HEAD", "cherry-pick"],
23332
- ["REVERT_HEAD", "revert"]
23333
- ];
23334
- const run = git5(cwd, ["rev-parse", ...markers.flatMap(([name]) => ["--git-path", name])]);
23335
- if (!run.ok)
23336
- return null;
23337
- const found = run.stdout.split(`
23338
- `).map((line) => path40.resolve(cwd, line.trim()));
23339
- const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
23340
- for (const [index, [, operation]] of markers.entries()) {
23341
- const marker = found[index];
23342
- if (!marker || !existsSync11(marker))
23343
- continue;
23344
- if (index === 1 && existsSync11(path40.join(marker, "applying")))
23345
- return { operation: "am", commitAllowed: false, branch: null };
23346
- if (index === 0 && existsSync11(path40.join(marker, "amend")) && !conflicted()) {
23347
- let headName = "";
23348
- try {
23349
- headName = readFileSync9(path40.join(marker, "head-name"), "utf8").trim();
23350
- } catch {}
23351
- const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
23352
- return { operation, commitAllowed: branch !== null, branch };
23353
- }
23354
- if ((operation === "revert" || operation === "merge") && !conflicted())
23355
- return { operation, commitAllowed: true, branch: null };
23356
- return { operation, commitAllowed: false, branch: null };
23357
- }
23358
- return null;
23359
- }
23360
- function inProgressFailure(operation, verb) {
23361
- const command = operation === "am" ? "git am" : `git ${operation}`;
23362
- return failure3("blocked", `${operation.replace("-", "_")}_in_progress: a ${operation === "am" ? "git am" : operation} is in progress; finish or abort it before workit can ${verb}`, `resolve the conflicts, git add <files>, then ${command} --continue # or ${command} --abort to return to where you started`);
23363
- }
23364
- function pushPreflight(cwd, options = {}) {
23365
- const progress = operationInProgress(cwd);
23366
- if (progress)
23367
- return inProgressFailure(progress.operation, "push");
23368
- const branch = options.branch ?? currentBranch(cwd);
23369
- if (!branch)
23370
- return failure3("invalid_input", "HEAD is detached; push a branch");
23371
- if (isProtectedTarget(cwd, branch))
23372
- return failure3("blocked", `protected_branch: ${branch} is protected by the workspace branch policy; workit git push never pushes to it`, releaseBranch(cwd, branch) ? `workit git push --release ${branch} # finishing a release or hotfix; needs the release grant` : "workit git branch <feature/name> # push a feature branch and open a PR");
23373
- const sha = resolveRef(cwd, `refs/heads/${branch}`);
23374
- if (!sha)
23375
- return failure3("not_found", `branch ${branch} has no commit to push`);
23376
- const remote = pushRemoteName(cwd, branch);
23377
- if (!remote)
23378
- return failure3("not_found", "no push remote is configured for this branch", "git remote add origin <url>");
23379
- const raw = pushUrl(cwd, remote);
23380
- if (!raw)
23381
- return failure3("blocked", `remote "${remote}" has no single push URL`, `git remote get-url --push --all ${remote} # keep exactly one push URL`);
23382
- const parsed = parseRemoteUrl(raw);
23383
- const local = parsed?.protocol === "file";
23384
- if (!local && !pushTargetIsStable(cwd, raw))
23385
- return failure3("blocked", `push_target_rewritten: the push URL of "${remote}" is rewritten by a url.*.insteadOf rule, so the destination is not the one checked`, "git config --get-regexp '^url\\.' # remove the rewrite for this remote");
23386
- return success2({
23387
- branch,
23388
- sha,
23389
- remote,
23390
- url: redactRemote(raw),
23391
- rawUrl: raw,
23392
- local
23393
- });
23394
- }
24378
+ import path42 from "node:path";
23395
24379
 
23396
24380
  // packages/workit-core/src/forge/report.ts
23397
24381
  var GITLAB_MERGEABLE = new Set(["mergeable"]);
@@ -23473,7 +24457,7 @@ function rebaseInProgress(dir) {
23473
24457
  }
23474
24458
  var scratchDir = (cwd) => {
23475
24459
  const dir = stacksDir(cwd);
23476
- return dir.ok ? path41.join(dir.data, "worktrees") : null;
24460
+ return dir.ok ? path42.join(dir.data, "worktrees") : null;
23477
24461
  };
23478
24462
  function gitRestack(cwd, input) {
23479
24463
  const owner = worktreeOf(cwd, input.branch);
@@ -23483,7 +24467,7 @@ function gitRestack(cwd, input) {
23483
24467
  if (!scratch)
23484
24468
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
23485
24469
  fs25.mkdirSync(scratch, { recursive: true });
23486
- dir = path41.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
24470
+ dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
23487
24471
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
23488
24472
  if (!added.ok)
23489
24473
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -23527,7 +24511,7 @@ function stacksDir(cwd) {
23527
24511
  const root = storeRoot(cwd);
23528
24512
  if (!root.ok)
23529
24513
  return stackFail(root.code, root.error, root.unblock);
23530
- return { ok: true, data: path41.join(root.value.root, "stacks") };
24514
+ return { ok: true, data: path42.join(root.value.root, "stacks") };
23531
24515
  }
23532
24516
  var HEARTBEAT_STALE_MS = 2 * 60000;
23533
24517
 
@@ -23638,7 +24622,7 @@ function fanoutsDir(cwd) {
23638
24622
  const root = storeRoot(cwd);
23639
24623
  if (!root.ok)
23640
24624
  return fanoutFail(root.code, root.error, root.unblock);
23641
- return { ok: true, data: path42.join(root.value.root, "fanouts") };
24625
+ return { ok: true, data: path43.join(root.value.root, "fanouts") };
23642
24626
  }
23643
24627
  function parseStored(raw) {
23644
24628
  if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -23706,7 +24690,7 @@ function readFanout(cwd, name) {
23706
24690
  const dir = fanoutsDir(cwd);
23707
24691
  if (!dir.ok)
23708
24692
  return dir;
23709
- const file = path42.join(dir.data, stackFileName(name));
24693
+ const file = path43.join(dir.data, stackFileName(name));
23710
24694
  let parsed = null;
23711
24695
  try {
23712
24696
  parsed = parseStored(JSON.parse(fs26.readFileSync(file, "utf8")));
@@ -23731,7 +24715,7 @@ function listFanouts(cwd) {
23731
24715
  for (const entry of names) {
23732
24716
  let parsed = null;
23733
24717
  try {
23734
- parsed = parseStored(JSON.parse(fs26.readFileSync(path42.join(dir.data, entry), "utf8")));
24718
+ parsed = parseStored(JSON.parse(fs26.readFileSync(path43.join(dir.data, entry), "utf8")));
23735
24719
  } catch {
23736
24720
  parsed = null;
23737
24721
  }
@@ -24423,7 +25407,7 @@ var childTaskMissing = (store, input) => {
24423
25407
  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`;
24424
25408
 
24425
25409
  // packages/workit-opencode/src/v2/shell.ts
24426
- import path43 from "node:path";
25410
+ import path44 from "node:path";
24427
25411
  var commandOf = (input) => {
24428
25412
  if (input === null || typeof input !== "object")
24429
25413
  return null;
@@ -24432,7 +25416,7 @@ var commandOf = (input) => {
24432
25416
  };
24433
25417
  var hookInput = (root, sessionID, id, shell) => ({
24434
25418
  host: "opencode",
24435
- cwd: shell.workdir ? path43.resolve(root, shell.workdir) : root,
25419
+ cwd: shell.workdir ? path44.resolve(root, shell.workdir) : root,
24436
25420
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
24437
25421
  permissionMode: null,
24438
25422
  transcriptPath: null,
@@ -24638,38 +25622,38 @@ var createStopControl = (root, port) => {
24638
25622
  };
24639
25623
 
24640
25624
  // packages/workit-opencode/src/v2/registry.ts
24641
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
24642
- import path45 from "node:path";
25625
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
25626
+ import path46 from "node:path";
24643
25627
 
24644
25628
  // packages/workit-opencode/src/shared/assets.ts
24645
- import { existsSync as existsSync12, realpathSync as realpathSync9 } from "node:fs";
24646
- import path44 from "node:path";
25629
+ import { existsSync as existsSync14, realpathSync as realpathSync9 } from "node:fs";
25630
+ import path45 from "node:path";
24647
25631
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24648
25632
  var moduleDir = () => {
24649
25633
  try {
24650
- return realpathSync9(path44.dirname(fileURLToPath2(import.meta.url)));
25634
+ return realpathSync9(path45.dirname(fileURLToPath2(import.meta.url)));
24651
25635
  } catch {
24652
- return path44.dirname(fileURLToPath2(import.meta.url));
25636
+ return path45.dirname(fileURLToPath2(import.meta.url));
24653
25637
  }
24654
25638
  };
24655
25639
  var packageRoot2 = () => {
24656
25640
  const dir = moduleDir();
24657
25641
  for (const candidate of [
24658
- path44.resolve(dir, ".."),
24659
- path44.resolve(dir, "..", ".."),
24660
- path44.resolve(dir, "..", "..", "..")
25642
+ path45.resolve(dir, ".."),
25643
+ path45.resolve(dir, "..", ".."),
25644
+ path45.resolve(dir, "..", "..", "..")
24661
25645
  ]) {
24662
- if (existsSync12(path44.join(candidate, "package.json")))
25646
+ if (existsSync14(path45.join(candidate, "package.json")))
24663
25647
  return candidate;
24664
25648
  }
24665
- return path44.resolve(dir, "..");
25649
+ return path45.resolve(dir, "..");
24666
25650
  };
24667
25651
  var skillsRoot = () => {
24668
25652
  const root = packageRoot2();
24669
- const canonical = path44.resolve(root, "..", "workit-core", "skills");
24670
- if (existsSync12(path44.join(root, "src")) && existsSync12(canonical))
25653
+ const canonical = path45.resolve(root, "..", "workit-core", "skills");
25654
+ if (existsSync14(path45.join(root, "src")) && existsSync14(canonical))
24671
25655
  return canonical;
24672
- return path44.join(root, "assets", "skills");
25656
+ return path45.join(root, "assets", "skills");
24673
25657
  };
24674
25658
 
24675
25659
  // packages/workit-opencode/src/v2/registry.ts
@@ -24693,15 +25677,15 @@ var parseFrontmatter = (markdown) => {
24693
25677
  };
24694
25678
  var registerSkills = async (ctx) => {
24695
25679
  const skillsDir = skillsRoot();
24696
- if (!existsSync13(skillsDir))
25680
+ if (!existsSync15(skillsDir))
24697
25681
  return new Set;
24698
25682
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
24699
- const own = (skill) => typeof skill.path === "string" && path45.resolve(skill.path).startsWith(skillsDir + path45.sep);
25683
+ const own = (skill) => typeof skill.path === "string" && path46.resolve(skill.path).startsWith(skillsDir + path46.sep);
24700
25684
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
24701
25685
  const packaged = new Map;
24702
25686
  for (const id of WORKIT_METHOD_SKILLS) {
24703
- const file = path45.join(skillsDir, id, "SKILL.md");
24704
- if (!userSkills.has(id) && existsSync13(file))
25687
+ const file = path46.join(skillsDir, id, "SKILL.md");
25688
+ if (!userSkills.has(id) && existsSync15(file))
24705
25689
  packaged.set(id, file);
24706
25690
  }
24707
25691
  await ctx.skill.transform((editor) => {
@@ -24709,7 +25693,7 @@ var registerSkills = async (ctx) => {
24709
25693
  for (const [id, file] of packaged) {
24710
25694
  if (existing.has(id))
24711
25695
  continue;
24712
- const parsed = parseFrontmatter(readFileSync10(file, "utf8"));
25696
+ const parsed = parseFrontmatter(readFileSync11(file, "utf8"));
24713
25697
  editor.add({
24714
25698
  id,
24715
25699
  name: parsed.name || id,
@@ -24747,25 +25731,25 @@ var registerCommands = async (ctx, registeredSkills) => {
24747
25731
  };
24748
25732
 
24749
25733
  // packages/workit-opencode/src/stale-sources.ts
24750
- import { existsSync as existsSync14, readdirSync as readdirSync6 } from "node:fs";
24751
- import path46 from "node:path";
25734
+ import { existsSync as existsSync16, readdirSync as readdirSync6 } from "node:fs";
25735
+ import path47 from "node:path";
24752
25736
  import { fileURLToPath as fileURLToPath3 } from "node:url";
24753
25737
  var typescriptFiles = (directory) => {
24754
- if (!existsSync14(directory))
25738
+ if (!existsSync16(directory))
24755
25739
  return [];
24756
25740
  return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
24757
- const file = path46.join(directory, entry.name);
25741
+ const file = path47.join(directory, entry.name);
24758
25742
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
24759
25743
  });
24760
25744
  };
24761
25745
  var pluginSourceFiles = [
24762
25746
  ...new Set([
24763
25747
  fileURLToPath3(import.meta.url),
24764
- ...typescriptFiles(path46.join(packageRoot2(), "src")),
24765
- path46.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
24766
- ...typescriptFiles(path46.join(packageRoot2(), "..", "workit-core", "src", "core"))
25748
+ ...typescriptFiles(path47.join(packageRoot2(), "src")),
25749
+ path47.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
25750
+ ...typescriptFiles(path47.join(packageRoot2(), "..", "workit-core", "src", "core"))
24767
25751
  ])
24768
- ].filter((file) => existsSync14(file)).toSorted();
25752
+ ].filter((file) => existsSync16(file)).toSorted();
24769
25753
 
24770
25754
  // packages/workit-opencode/src/v2/plugin.ts
24771
25755
  var sessionFacts = async (ctx, sessionID) => {