@brainervirus/workit-opencode 8.23.0 → 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;
@@ -16515,7 +17502,7 @@ var repoAt = (dir) => {
16515
17502
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
16516
17503
  };
16517
17504
  var isWorkitWorkspace = (repo) => {
16518
- if (fs16.existsSync(repo.location.dir))
17505
+ if (fs17.existsSync(repo.location.dir))
16519
17506
  return true;
16520
17507
  try {
16521
17508
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -16536,17 +17523,17 @@ var headSha2 = (repo) => {
16536
17523
  if (!git || git.reftable)
16537
17524
  return null;
16538
17525
  try {
16539
- const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
17526
+ const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
16540
17527
  if (/^[0-9a-f]{40,64}$/.test(head))
16541
17528
  return head;
16542
17529
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
16543
17530
  if (!ref)
16544
17531
  return null;
16545
- const common = path19.dirname(repo.location.dir);
17532
+ const common = path22.dirname(repo.location.dir);
16546
17533
  try {
16547
- return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
17534
+ return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
16548
17535
  } catch {}
16549
- const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
17536
+ const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
16550
17537
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
16551
17538
  } catch {
16552
17539
  return null;
@@ -16558,7 +17545,7 @@ var gitConfig = (dir, invocation, cache) => {
16558
17545
  if (cached)
16559
17546
  return cached;
16560
17547
  const config = new Map;
16561
- const listed = spawnSync11("git", [...invocation.config ?? [], "config", "-z", "--list"], {
17548
+ const listed = spawnSync13("git", [...invocation.config ?? [], "config", "-z", "--list"], {
16562
17549
  cwd: dir,
16563
17550
  encoding: "utf8",
16564
17551
  timeout: 3000,
@@ -16634,8 +17621,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16634
17621
  const sub = invocation.args[0];
16635
17622
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
16636
17623
  return [invocation];
16637
- const dir = path19.resolve(root, invocation.dir ?? ".");
16638
- if (!fs16.existsSync(dir))
17624
+ const dir = path22.resolve(root, invocation.dir ?? ".");
17625
+ if (!fs17.existsSync(dir))
16639
17626
  return [invocation];
16640
17627
  const config = gitConfig(dir, invocation, cache);
16641
17628
  const alias = configValue(config, `alias.${sub.toLowerCase()}`);
@@ -16657,11 +17644,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16657
17644
  var normalizedUrl = (url, dir) => {
16658
17645
  const trimmed = url.trim();
16659
17646
  if (/^file:\/\//i.test(trimmed))
16660
- return path19.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17647
+ return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
16661
17648
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
16662
17649
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
16663
17650
  if (!scheme && !scp)
16664
- return path19.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17651
+ return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
16665
17652
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
16666
17653
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
16667
17654
  };
@@ -16722,7 +17709,7 @@ var refusal = (action, branch, dir) => {
16722
17709
  };
16723
17710
  var refFacts = (repo, remote) => {
16724
17711
  const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
16725
- 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 });
16726
17713
  const remotePrefix = `refs/remotes/${remote}/`;
16727
17714
  for (const ref of (listed.stdout ?? "").split(`
16728
17715
  `)) {
@@ -16739,7 +17726,7 @@ var globBranches = (repo, glob) => {
16739
17726
  const source = glob.source.startsWith("refs/") ? glob.source : `refs/heads/${glob.source}`;
16740
17727
  const [sourcePrefix, sourceSuffix = ""] = source.split("*");
16741
17728
  const [destinationPrefix, destinationSuffix = ""] = glob.destination.split("*");
16742
- const listed = spawnSync11("git", [
17729
+ const listed = spawnSync13("git", [
16743
17730
  "for-each-ref",
16744
17731
  "--format=%(refname)",
16745
17732
  sourcePrefix.slice(0, sourcePrefix.lastIndexOf("/") + 1)
@@ -16816,7 +17803,7 @@ var tagRefusal = (action, facts, dir) => {
16816
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")}).`;
16817
17804
  };
16818
17805
  var remoteHeads = (repo, remote) => {
16819
- const listed = spawnSync11("git", ["ls-remote", "--heads", remote], {
17806
+ const listed = spawnSync13("git", ["ls-remote", "--heads", remote], {
16820
17807
  cwd: repo.dir,
16821
17808
  encoding: "utf8",
16822
17809
  timeout: 3000,
@@ -16885,7 +17872,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
16885
17872
  const out = [];
16886
17873
  const repos = new Map;
16887
17874
  const repoFor = (relative) => {
16888
- const dir = path19.resolve(cwd, relative ?? ".");
17875
+ const dir = path22.resolve(cwd, relative ?? ".");
16889
17876
  if (!repos.has(dir)) {
16890
17877
  const repo = repoAt(dir);
16891
17878
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -16970,7 +17957,7 @@ var analysisDenial = (error) => ({
16970
17957
  unblock: SPLIT_IT
16971
17958
  });
16972
17959
  var MARKER_TTL_SECONDS = 3600;
16973
- 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`);
16974
17961
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
16975
17962
  function noteRawCommit(input, command, key) {
16976
17963
  try {
@@ -16981,14 +17968,14 @@ function noteRawCommit(input, command, key) {
16981
17968
  if (action.kind !== "commit")
16982
17969
  continue;
16983
17970
  const file = markerFile(repo, input.session.id, key);
16984
- fs16.mkdirSync(path19.dirname(file), { recursive: true });
16985
- 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 })}
16986
17973
  `);
16987
17974
  }
16988
17975
  } catch {}
16989
17976
  }
16990
17977
  var headCommit = (dir) => {
16991
- const run = spawnSync11("git", [
17978
+ const run = spawnSync13("git", [
16992
17979
  "log",
16993
17980
  "-1",
16994
17981
  "--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
@@ -17005,11 +17992,11 @@ var headCommit = (dir) => {
17005
17992
  var settle2 = (input, repo, file) => {
17006
17993
  let marker;
17007
17994
  try {
17008
- marker = JSON.parse(fs16.readFileSync(file, "utf8"));
17995
+ marker = JSON.parse(fs17.readFileSync(file, "utf8"));
17009
17996
  } catch {
17010
17997
  return;
17011
17998
  }
17012
- fs16.rmSync(file, { force: true });
17999
+ fs17.rmSync(file, { force: true });
17013
18000
  const session = input.session.id;
17014
18001
  const now = Date.now() / 1000;
17015
18002
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -17190,7 +18177,7 @@ var targetsOf = (invocations) => invocations.flatMap((invocation) => invocation.
17190
18177
  branch,
17191
18178
  dir: invocation.dir
17192
18179
  })) : []);
17193
- 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}`], {
17194
18181
  cwd: dir,
17195
18182
  stdio: "ignore",
17196
18183
  timeout: 5000
@@ -17200,8 +18187,8 @@ var judgedDir = (root, dir) => {
17200
18187
  return null;
17201
18188
  if (dir === null)
17202
18189
  return root;
17203
- const resolved = path20.resolve(root, dir);
17204
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18190
+ const resolved = path23.resolve(root, dir);
18191
+ return existsSync8(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
17205
18192
  };
17206
18193
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
17207
18194
  let judged = false;
@@ -17436,7 +18423,7 @@ var judgeInputSummary = (raw) => {
17436
18423
 
17437
18424
  // packages/workit-core/src/autonomy.ts
17438
18425
  import os5 from "node:os";
17439
- import path21 from "node:path";
18426
+ import path24 from "node:path";
17440
18427
 
17441
18428
  // packages/workit-core/src/core/safe-write.ts
17442
18429
  import {
@@ -17470,10 +18457,10 @@ var accountHome = () => {
17470
18457
  }
17471
18458
  };
17472
18459
  var grantsHome = { resolve: accountHome };
17473
- var grantsDir = () => path21.join(grantsHome.resolve(), ".config", "workit");
18460
+ var grantsDir = () => path24.join(grantsHome.resolve(), ".config", "workit");
17474
18461
  var grantsOverride = () => {
17475
- const configured = path21.resolve(resolveConfigDir());
17476
- 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`;
17477
18464
  };
17478
18465
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
17479
18466
  var DEFAULT_GRANTS = Object.freeze({
@@ -17526,8 +18513,16 @@ function endpointView(entry, context) {
17526
18513
  ...issue ? { endpointIssue: issue } : {}
17527
18514
  };
17528
18515
  }
17529
- var DEFAULT_VERIFICATION = "self";
17530
- 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
+ };
17531
18526
  var isGrantValue = (value) => value === true || value === false || value === "verified";
17532
18527
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
17533
18528
  var fromAutoApprove = (value) => {
@@ -19273,10 +20268,10 @@ class WorkitCore {
19273
20268
  return this.summary(changed.data.task);
19274
20269
  }
19275
20270
  }
19276
- // packages/workit-core/src/core/repo-context.ts
19277
- import { spawnSync as spawnSync13 } from "node:child_process";
19278
- import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
19279
- import path22 from "node:path";
20271
+ // packages/workit-core/src/core/repo-context.ts
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";
19280
20275
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
19281
20276
  try {
19282
20277
  gitRevisionParts(value);
@@ -19303,7 +20298,7 @@ var invalidRange = (cwd) => ({
19303
20298
  cwd
19304
20299
  });
19305
20300
  var runGit = (cwd, args) => {
19306
- const result = spawnSync13("git", args, { cwd, encoding: "utf8" });
20301
+ const result = spawnSync15("git", args, { cwd, encoding: "utf8" });
19307
20302
  return {
19308
20303
  stdout: (result.stdout ?? "").trimEnd(),
19309
20304
  stderr: (result.stderr ?? "").trimEnd(),
@@ -19410,7 +20405,7 @@ var findPrTemplate = (cwd) => {
19410
20405
  ]) {
19411
20406
  const parts = rel.split("/");
19412
20407
  const base = parts[parts.length - 1];
19413
- 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;
19414
20409
  const actual = probeCaseInsensitive(dir, base);
19415
20410
  if (actual)
19416
20411
  return [...parts.slice(0, -1), actual].join("/");
@@ -19441,11 +20436,11 @@ var diffStatForRange = (cwd, range) => {
19441
20436
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
19442
20437
  };
19443
20438
  var packageScripts = (cwd, keys) => {
19444
- const pkgPath = path22.join(cwd, "package.json");
19445
- if (!existsSync7(pkgPath))
20439
+ const pkgPath = path25.join(cwd, "package.json");
20440
+ if (!existsSync9(pkgPath))
19446
20441
  return [];
19447
20442
  try {
19448
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20443
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
19449
20444
  return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
19450
20445
  } catch {
19451
20446
  return [];
@@ -19467,9 +20462,9 @@ var documentationFiles = (cwd) => {
19467
20462
  if (entry.isDirectory()) {
19468
20463
  if (excluded.has(entry.name))
19469
20464
  continue;
19470
- walk(path22.join(dir, entry.name), depth + 1);
20465
+ walk(path25.join(dir, entry.name), depth + 1);
19471
20466
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
19472
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
20467
+ matches.push("./" + path25.relative(cwd, path25.join(dir, entry.name)).replaceAll("\\", "/"));
19473
20468
  }
19474
20469
  }
19475
20470
  };
@@ -19477,15 +20472,15 @@ var documentationFiles = (cwd) => {
19477
20472
  return matches.toSorted().slice(0, 200);
19478
20473
  };
19479
20474
  var readTrimmed2 = (file, maxLines) => {
19480
- if (!existsSync7(file))
20475
+ if (!existsSync9(file))
19481
20476
  return "";
19482
- const lines = readFileSync5(file, "utf8").split(`
20477
+ const lines = readFileSync7(file, "utf8").split(`
19483
20478
  `);
19484
20479
  return lines.slice(0, maxLines).join(`
19485
20480
  `);
19486
20481
  };
19487
20482
  function prReadyContext(root, range) {
19488
- const cwd = path22.resolve(repoRoot(root));
20483
+ const cwd = path25.resolve(repoRoot(root));
19489
20484
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19490
20485
  return invalidRange(cwd);
19491
20486
  let stdout = "";
@@ -19543,7 +20538,7 @@ ${status.stderr}` : "");
19543
20538
  stdout += `template_path: ${template}
19544
20539
 
19545
20540
  `;
19546
- stdout += readTrimmed2(path22.join(cwd, template), 220);
20541
+ stdout += readTrimmed2(path25.join(cwd, template), 220);
19547
20542
  } else {
19548
20543
  stdout += `template_path: none
19549
20544
 
@@ -19553,7 +20548,7 @@ ${status.stderr}` : "");
19553
20548
  stdout += `
19554
20549
  `;
19555
20550
  stdout += printSection("Recent Validation Signals");
19556
- if (existsSync7(path22.join(cwd, "package.json"))) {
20551
+ if (existsSync9(path25.join(cwd, "package.json"))) {
19557
20552
  stdout += `package.json detected. Common scripts:
19558
20553
  `;
19559
20554
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -19562,7 +20557,7 @@ ${status.stderr}` : "");
19562
20557
  `) + `
19563
20558
  `;
19564
20559
  }
19565
- 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"))) {
19566
20561
  stdout += `Rust project detected.
19567
20562
  `;
19568
20563
  }
@@ -19595,7 +20590,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
19595
20590
  - Apply with the host-native editor, preserving the repository changelog conventions.
19596
20591
  - If Unreleased already has duplicate category headings, normalize_only first.`;
19597
20592
  function changelogContext(root, range) {
19598
- const cwd = path22.resolve(repoRoot(root));
20593
+ const cwd = path25.resolve(repoRoot(root));
19599
20594
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19600
20595
  return invalidRange(cwd);
19601
20596
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19613,8 +20608,8 @@ function changelogContext(root, range) {
19613
20608
  stdout += CHANGELOG_RULES + `
19614
20609
  `;
19615
20610
  stdout += printSection("Existing CHANGELOG.md");
19616
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
19617
- if (existsSync7(changelogPath)) {
20611
+ const changelogPath = path25.join(cwd, "CHANGELOG.md");
20612
+ if (existsSync9(changelogPath)) {
19618
20613
  stdout += readTrimmed2(changelogPath, 260) + `
19619
20614
  `;
19620
20615
  } else {
@@ -19633,7 +20628,7 @@ function changelogContext(root, range) {
19633
20628
  return { stdout, stderr: "", exitCode: 0, cwd };
19634
20629
  }
19635
20630
  function docsRefreshContext(root, range) {
19636
- const cwd = path22.resolve(repoRoot(root));
20631
+ const cwd = path25.resolve(repoRoot(root));
19637
20632
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19638
20633
  return invalidRange(cwd);
19639
20634
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19655,8 +20650,8 @@ function docsRefreshContext(root, range) {
19655
20650
  `) + `
19656
20651
  `;
19657
20652
  stdout += printSection("README Preview");
19658
- const readme = path22.join(cwd, "README.md");
19659
- if (existsSync7(readme)) {
20653
+ const readme = path25.join(cwd, "README.md");
20654
+ if (existsSync9(readme)) {
19660
20655
  stdout += readTrimmed2(readme, 220) + `
19661
20656
  `;
19662
20657
  } else {
@@ -19664,10 +20659,10 @@ function docsRefreshContext(root, range) {
19664
20659
  `;
19665
20660
  }
19666
20661
  stdout += printSection("Package Scripts");
19667
- const pkgPath = path22.join(cwd, "package.json");
19668
- if (existsSync7(pkgPath)) {
20662
+ const pkgPath = path25.join(cwd, "package.json");
20663
+ if (existsSync9(pkgPath)) {
19669
20664
  try {
19670
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20665
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
19671
20666
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
19672
20667
  `;
19673
20668
  } catch {}
@@ -19678,7 +20673,7 @@ function docsRefreshContext(root, range) {
19678
20673
  return { stdout, stderr: "", exitCode: 0, cwd };
19679
20674
  }
19680
20675
  function releaseNotesContext(root, rangeOrTag) {
19681
- const cwd = path22.resolve(repoRoot(root));
20676
+ const cwd = path25.resolve(repoRoot(root));
19682
20677
  if (!rangeOrTag) {
19683
20678
  return { stdout: "", stderr: `ERROR: release tag or range required
19684
20679
  `, exitCode: 1, cwd };
@@ -19716,7 +20711,7 @@ function releaseNotesContext(root, rangeOrTag) {
19716
20711
  `;
19717
20712
  stdout += printSection("Existing Release Files");
19718
20713
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
19719
- if (existsSync7(path22.join(cwd, rel)))
20714
+ if (existsSync9(path25.join(cwd, rel)))
19720
20715
  stdout += rel + `
19721
20716
  `;
19722
20717
  }
@@ -19744,81 +20739,7 @@ function releaseNotesContext(root, rangeOrTag) {
19744
20739
  }
19745
20740
 
19746
20741
  // packages/workit-core/src/core/tracker-issues.ts
19747
- import { spawnSync as spawnSync15 } from "node:child_process";
19748
-
19749
- // packages/workit-core/src/core/pr-create.ts
19750
- import fs17 from "node:fs";
19751
- import path23 from "node:path";
19752
- import { spawnSync as spawnSync14 } from "node:child_process";
19753
- import { pathToFileURL } from "node:url";
19754
- // packages/workit-core/src/forge/pr-body.ts
19755
- function parseGhIssue(value) {
19756
- const m = /issues\/(\d+)/.exec(value);
19757
- return m ? m[1] : String(value).trim().replace(/^#/, "");
19758
- }
19759
-
19760
- // packages/workit-core/src/core/pr-create.ts
19761
- function whichOnPath(tool) {
19762
- const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
19763
- for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
19764
- if (!dir)
19765
- continue;
19766
- for (const name of names) {
19767
- const candidate = path23.join(dir, name);
19768
- try {
19769
- fs17.accessSync(candidate, fs17.constants.X_OK);
19770
- return candidate;
19771
- } catch {}
19772
- }
19773
- }
19774
- return null;
19775
- }
19776
- var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
19777
- var pushRemote = (cwd) => {
19778
- const result = spawnSync14("git", ["remote", "get-url", "--push", "--all", "origin"], {
19779
- cwd,
19780
- encoding: "utf8"
19781
- });
19782
- if (result.status !== 0)
19783
- return null;
19784
- const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
19785
- return urls.length === 1 ? urls[0] : null;
19786
- };
19787
- var safePushUrl = (remote) => {
19788
- const raw = remote.trim();
19789
- if (path23.isAbsolute(raw))
19790
- return pathToFileURL(raw).href;
19791
- if (!raw.includes("://")) {
19792
- const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
19793
- if (scp)
19794
- return `${scp[1] || "git"}@${scp[2]}:${scp[3]}`;
19795
- }
19796
- try {
19797
- const url = new URL(raw);
19798
- if (!["file:", "git:", "http:", "https:", "ssh:"].includes(url.protocol))
19799
- return null;
19800
- url.password = "";
19801
- if (url.protocol !== "ssh:")
19802
- url.username = "";
19803
- url.search = "";
19804
- url.hash = "";
19805
- return url.toString();
19806
- } catch {
19807
- return null;
19808
- }
19809
- };
19810
- var pushTargetIsStable = (cwd, remote) => {
19811
- const target = safePushUrl(remote);
19812
- if (!target)
19813
- return false;
19814
- const result = spawnSync14("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
19815
- if (result.status !== 0 && result.status !== 1)
19816
- return false;
19817
- const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
19818
- return !prefixes.some((prefix) => target.startsWith(prefix));
19819
- };
19820
-
19821
- // packages/workit-core/src/core/tracker-issues.ts
20742
+ import { spawnSync as spawnSync16 } from "node:child_process";
19822
20743
  var repoPathFromRemote = (remote) => {
19823
20744
  let rest = (remote || "").trim().replace(/\/+$/, "");
19824
20745
  if (!rest)
@@ -19839,7 +20760,7 @@ var defaultCli = (bin, available) => {
19839
20760
  if (!available)
19840
20761
  return null;
19841
20762
  return (args, root) => {
19842
- const out = spawnSync15(bin, args, {
20763
+ const out = spawnSync16(bin, args, {
19843
20764
  cwd: root,
19844
20765
  encoding: "utf8",
19845
20766
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -19918,7 +20839,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
19918
20839
 
19919
20840
  // packages/workit-core/src/core/youtrack.ts
19920
20841
  import fs18 from "node:fs";
19921
- import path24 from "node:path";
20842
+ import path26 from "node:path";
19922
20843
 
19923
20844
  // packages/workit-core/src/core/templates.ts
19924
20845
  var repoRoot2 = assetRoot();
@@ -19931,8 +20852,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
19931
20852
  // packages/workit-core/src/core/youtrack.ts
19932
20853
  var ISSUE_RE = /^[A-Z]+-\d+$/;
19933
20854
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
19934
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(configDir(), "youtrack.json");
19935
- 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");
19936
20857
  var youTrackTokenModeOk = (p) => {
19937
20858
  if (process.platform === "win32")
19938
20859
  return true;
@@ -20000,7 +20921,7 @@ var youTrackToken = () => {
20000
20921
  if ("error" in loaded)
20001
20922
  return loaded;
20002
20923
  const tokenFile = String(loaded.config.tokenFile ?? "");
20003
- 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) : "";
20004
20925
  if (!tokenPath || !fs18.existsSync(tokenPath))
20005
20926
  return { error: "missing youtrack.token" };
20006
20927
  if (!youTrackTokenModeOk(tokenPath))
@@ -20051,7 +20972,7 @@ function youTrackTokenCreateUrl() {
20051
20972
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
20052
20973
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
20053
20974
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
20054
- 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"));
20055
20976
  const tab = String(defaults.profileTab ?? "account-security");
20056
20977
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
20057
20978
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -20060,7 +20981,7 @@ function youTrackTokenCreateUrl() {
20060
20981
  tokenName: name,
20061
20982
  tokenDescription: desc,
20062
20983
  scopes,
20063
- tokenFile: path24.resolve(tokenFile),
20984
+ tokenFile: path26.resolve(tokenFile),
20064
20985
  createUrl,
20065
20986
  docsUrl,
20066
20987
  prefillSupported: false,
@@ -20400,22 +21321,22 @@ function resolveGitRevision(root, value) {
20400
21321
  }
20401
21322
  function resolveInside(root, candidate) {
20402
21323
  const base = realpathSync5(root);
20403
- const target = path25.resolve(base, candidate);
20404
- if (target !== base && !target.startsWith(base + path25.sep)) {
21324
+ const target = path27.resolve(base, candidate);
21325
+ if (target !== base && !target.startsWith(base + path27.sep)) {
20405
21326
  throw new Error("path must stay inside repository root");
20406
21327
  }
20407
21328
  let ancestor = target;
20408
- while (!existsSync8(ancestor))
20409
- ancestor = path25.dirname(ancestor);
21329
+ while (!existsSync10(ancestor))
21330
+ ancestor = path27.dirname(ancestor);
20410
21331
  const canonicalAncestor = realpathSync5(ancestor);
20411
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path25.sep)) {
21332
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path27.sep)) {
20412
21333
  throw new Error("path must stay inside repository root");
20413
21334
  }
20414
21335
  return target;
20415
21336
  }
20416
21337
  function run2(root, executable, args, env = {}) {
20417
21338
  const cwd = realpathSync5(root);
20418
- const result = spawnSync16(executable, args, {
21339
+ const result = spawnSync17(executable, args, {
20419
21340
  cwd,
20420
21341
  encoding: "utf8",
20421
21342
  env: { ...process.env, ...env }
@@ -20452,7 +21373,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
20452
21373
  };
20453
21374
  });
20454
21375
  // packages/workit-core/src/hooks/context.ts
20455
- import path26 from "node:path";
21376
+ import path28 from "node:path";
20456
21377
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
20457
21378
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
20458
21379
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -20537,7 +21458,7 @@ function entryCompactContext(store, entry, session, context) {
20537
21458
  return null;
20538
21459
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
20539
21460
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
20540
- 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)] : []));
20541
21462
  const observedFiles = filesKey(files);
20542
21463
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
20543
21464
  if (!compact.ok || !compact.data)
@@ -20570,7 +21491,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
20570
21491
  // packages/workit-core/src/core/heavy-rules.ts
20571
21492
  import fs19 from "node:fs";
20572
21493
  import os6 from "node:os";
20573
- import path27 from "node:path";
21494
+ import path29 from "node:path";
20574
21495
  var EMPTY2 = () => ({ deny: [], allow: [] });
20575
21496
  var readText2 = (file) => {
20576
21497
  try {
@@ -20611,19 +21532,19 @@ function parseJsonc(text) {
20611
21532
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
20612
21533
  }
20613
21534
  function gitTop(cwd) {
20614
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
20615
- 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")))
20616
21537
  return dir;
20617
- if (path27.dirname(dir) === dir)
21538
+ if (path29.dirname(dir) === dir)
20618
21539
  return null;
20619
21540
  }
20620
21541
  }
20621
21542
  var projectDirs = (cwd) => {
20622
21543
  const top = gitTop(cwd);
20623
21544
  const dirs = [];
20624
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
21545
+ for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
20625
21546
  dirs.push(dir);
20626
- if (!top || dir === top || path27.dirname(dir) === dir)
21547
+ if (!top || dir === top || path29.dirname(dir) === dir)
20627
21548
  return dirs;
20628
21549
  }
20629
21550
  };
@@ -20635,12 +21556,12 @@ var CLAUDE_MANAGED = {
20635
21556
  };
20636
21557
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
20637
21558
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
20638
- const user = env.CLAUDE_CONFIG_DIR || path27.join(homeOf(env), ".claude");
21559
+ const user = env.CLAUDE_CONFIG_DIR || path29.join(homeOf(env), ".claude");
20639
21560
  return [
20640
21561
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
20641
- path27.join(user, "settings.json"),
20642
- path27.join(project, ".claude", "settings.json"),
20643
- 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")
20644
21565
  ];
20645
21566
  }
20646
21567
  var bashPattern = (entry) => {
@@ -20730,10 +21651,10 @@ var opencodeConfig = (value, rules) => {
20730
21651
  function opencodeRules(cwd, env = process.env) {
20731
21652
  const rules = EMPTY2();
20732
21653
  try {
20733
- const config = env.XDG_CONFIG_HOME || path27.join(homeOf(env), ".config");
21654
+ const config = env.XDG_CONFIG_HOME || path29.join(homeOf(env), ".config");
20734
21655
  const files = [
20735
- ...OPENCODE_NAMES.map((name) => path27.join(config, "opencode", name)),
20736
- ...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)])),
20737
21658
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
20738
21659
  ];
20739
21660
  for (const file of files) {
@@ -20777,9 +21698,9 @@ var codexRule = (body) => {
20777
21698
  function codexRules(cwd, env = process.env) {
20778
21699
  const rules = EMPTY2();
20779
21700
  try {
20780
- const home = env.CODEX_HOME || path27.join(homeOf(env), ".codex");
21701
+ const home = env.CODEX_HOME || path29.join(homeOf(env), ".codex");
20781
21702
  const top = gitTop(cwd) ?? cwd;
20782
- 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")]) {
20783
21704
  let files;
20784
21705
  try {
20785
21706
  files = fs19.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -20789,7 +21710,7 @@ function codexRules(cwd, env = process.env) {
20789
21710
  return "unreadable";
20790
21711
  }
20791
21712
  for (const name of files) {
20792
- const text = readText2(path27.join(dir, name)) ?? "";
21713
+ const text = readText2(path29.join(dir, name)) ?? "";
20793
21714
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
20794
21715
  const rule = codexRule(part);
20795
21716
  if (!rule)
@@ -20808,8 +21729,8 @@ function cursorRules(cwd, env = process.env) {
20808
21729
  try {
20809
21730
  const top = gitTop(cwd) ?? cwd;
20810
21731
  for (const file of [
20811
- path27.join(homeOf(env), ".cursor", "cli-config.json"),
20812
- path27.join(top, ".cursor", "cli.json")
21732
+ path29.join(homeOf(env), ".cursor", "cli-config.json"),
21733
+ path29.join(top, ".cursor", "cli.json")
20813
21734
  ]) {
20814
21735
  const text = readText2(file);
20815
21736
  if (text === null)
@@ -20885,21 +21806,21 @@ function wrapAllowed(read, words, command, options = {}) {
20885
21806
  // packages/workit-core/src/core/heavy.ts
20886
21807
  import fs20 from "node:fs";
20887
21808
  import os7 from "node:os";
20888
- import path28 from "node:path";
21809
+ import path30 from "node:path";
20889
21810
  function stateDir(env, platform = process.platform) {
20890
21811
  if (env.WORKFLOW_TOOLKIT_STATE)
20891
21812
  return env.WORKFLOW_TOOLKIT_STATE;
20892
21813
  if (env.XDG_STATE_HOME)
20893
- return path28.join(env.XDG_STATE_HOME, "workit");
21814
+ return path30.join(env.XDG_STATE_HOME, "workit");
20894
21815
  const home = env.HOME || os7.homedir();
20895
21816
  if (platform === "darwin")
20896
- return path28.join(home, "Library", "Application Support", "workit");
21817
+ return path30.join(home, "Library", "Application Support", "workit");
20897
21818
  if (platform === "win32")
20898
- return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
20899
- 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");
20900
21821
  }
20901
21822
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
20902
- var heavyCommandsFile = (env) => path28.join(stateDir(env), HEAVY_COMMANDS_FILE);
21823
+ var heavyCommandsFile = (env) => path30.join(stateDir(env), HEAVY_COMMANDS_FILE);
20903
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;
20904
21825
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
20905
21826
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -21174,7 +22095,7 @@ function machinePatterns(env) {
21174
22095
  return isRecord5(value) ? stringList(value.patterns) : [];
21175
22096
  }
21176
22097
  function repoPatterns(cwd) {
21177
- return repoHeavyCommands(gitTop(cwd) ?? path28.resolve(cwd));
22098
+ return repoHeavyCommands(gitTop(cwd) ?? path30.resolve(cwd));
21178
22099
  }
21179
22100
  function heavyLines(lines, options) {
21180
22101
  const found = [];
@@ -21252,12 +22173,12 @@ function heavyPlan(command, options) {
21252
22173
  }
21253
22174
  function workitOnPath2(env = process.env) {
21254
22175
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
21255
- for (const dir of (env.PATH ?? "").split(path28.delimiter))
22176
+ for (const dir of (env.PATH ?? "").split(path30.delimiter))
21256
22177
  for (const name of names) {
21257
22178
  if (!dir)
21258
22179
  continue;
21259
22180
  try {
21260
- fs20.accessSync(path28.join(dir, name), fs20.constants.X_OK);
22181
+ fs20.accessSync(path30.join(dir, name), fs20.constants.X_OK);
21261
22182
  return true;
21262
22183
  } catch {}
21263
22184
  }
@@ -21276,20 +22197,20 @@ function leaseRewrite(command) {
21276
22197
  // packages/workit-core/src/core/heavy-words.ts
21277
22198
  import fs21 from "node:fs";
21278
22199
  import os8 from "node:os";
21279
- import path29 from "node:path";
22200
+ import path31 from "node:path";
21280
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(" "));
21281
22202
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
21282
22203
  var stateDir2 = (env) => {
21283
22204
  if (env.WORKFLOW_TOOLKIT_STATE)
21284
22205
  return env.WORKFLOW_TOOLKIT_STATE;
21285
22206
  if (env.XDG_STATE_HOME)
21286
- return path29.join(env.XDG_STATE_HOME, "workit");
22207
+ return path31.join(env.XDG_STATE_HOME, "workit");
21287
22208
  const home = env.HOME || os8.homedir();
21288
22209
  if (process.platform === "darwin")
21289
- return path29.join(home, "Library", "Application Support", "workit");
22210
+ return path31.join(home, "Library", "Application Support", "workit");
21290
22211
  if (process.platform === "win32")
21291
- return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
21292
- 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");
21293
22214
  };
21294
22215
  var readJson2 = (file) => {
21295
22216
  try {
@@ -21300,15 +22221,15 @@ var readJson2 = (file) => {
21300
22221
  };
21301
22222
  var patternWords = (cwd, env) => {
21302
22223
  const patterns = [];
21303
- const machine = readJson2(path29.join(stateDir2(env), "heavy-commands.json"));
22224
+ const machine = readJson2(path31.join(stateDir2(env), "heavy-commands.json"));
21304
22225
  if (Array.isArray(machine?.patterns))
21305
22226
  patterns.push(...machine.patterns);
21306
- for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
21307
- 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"))) {
21308
22229
  patterns.push(...repoHeavyCommands(dir));
21309
22230
  break;
21310
22231
  }
21311
- if (path29.dirname(dir) === dir)
22232
+ if (path31.dirname(dir) === dir)
21312
22233
  break;
21313
22234
  }
21314
22235
  const words = new Set;
@@ -21367,21 +22288,21 @@ function heavyGate(cwd, command, options) {
21367
22288
  }
21368
22289
  }
21369
22290
  // packages/workit-core/src/hooks/write-gate.ts
21370
- import { existsSync as existsSync9, realpathSync as realpathSync6 } from "node:fs";
22291
+ import { existsSync as existsSync11, realpathSync as realpathSync6 } from "node:fs";
21371
22292
  import os10 from "node:os";
21372
- import path31 from "node:path";
22293
+ import path33 from "node:path";
21373
22294
 
21374
22295
  // packages/workit-core/src/core/live-session.ts
21375
- import { spawnSync as spawnSync17 } from "node:child_process";
22296
+ import { spawnSync as spawnSync18 } from "node:child_process";
21376
22297
  import { createHash as createHash15 } from "node:crypto";
21377
22298
  import fs22 from "node:fs";
21378
22299
  import os9 from "node:os";
21379
- import path30 from "node:path";
22300
+ import path32 from "node:path";
21380
22301
  var LIVE_SESSION_TTL_MS = 15 * 60000;
21381
22302
  var HEARTBEAT_EVERY_MS = 30000;
21382
22303
  var liveProbe = {
21383
22304
  platform: process.platform,
21384
- run: (args) => spawnSync17("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
22305
+ run: (args) => spawnSync18("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
21385
22306
  start: (pid) => processStartOf(pid)
21386
22307
  };
21387
22308
  var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
@@ -21450,15 +22371,15 @@ var liveOwner = (input, transport, env = process.env) => {
21450
22371
  };
21451
22372
  };
21452
22373
  var checkoutOf = (target) => {
21453
- let dir = path30.resolve(target);
21454
- while (!fs22.existsSync(dir) && path30.dirname(dir) !== dir)
21455
- dir = path30.dirname(dir);
22374
+ let dir = path32.resolve(target);
22375
+ while (!fs22.existsSync(dir) && path32.dirname(dir) !== dir)
22376
+ dir = path32.dirname(dir);
21456
22377
  if (fs22.existsSync(dir) && !fs22.statSync(dir).isDirectory())
21457
- dir = path30.dirname(dir);
21458
- const parts = dir.split(path30.sep);
22378
+ dir = path32.dirname(dir);
22379
+ const parts = dir.split(path32.sep);
21459
22380
  const dotGit = parts.lastIndexOf(".git");
21460
22381
  if (dotGit > 0)
21461
- dir = parts.slice(0, dotGit).join(path30.sep) || path30.sep;
22382
+ dir = parts.slice(0, dotGit).join(path32.sep) || path32.sep;
21462
22383
  const location = resolveStore(dir);
21463
22384
  if (location instanceof Error || !location.shared || !location.top)
21464
22385
  return null;
@@ -21468,9 +22389,9 @@ var checkoutOf = (target) => {
21468
22389
  } catch {}
21469
22390
  return top;
21470
22391
  };
21471
- 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`);
21472
22393
  var checkoutKey = (top, platform = process.platform) => {
21473
- let key = path30.resolve(top);
22394
+ let key = path32.resolve(top);
21474
22395
  try {
21475
22396
  key = fs22.realpathSync.native(key);
21476
22397
  } catch {}
@@ -21565,7 +22486,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
21565
22486
  checkout,
21566
22487
  heartbeatAt: now.toISOString()
21567
22488
  };
21568
- fs22.mkdirSync(path30.dirname(file), { recursive: true });
22489
+ fs22.mkdirSync(path32.dirname(file), { recursive: true });
21569
22490
  const temp = `${file}.${process.pid}.tmp`;
21570
22491
  fs22.writeFileSync(temp, JSON.stringify(marker));
21571
22492
  fs22.renameSync(temp, file);
@@ -21577,22 +22498,22 @@ var NONE3 = { kind: "none" };
21577
22498
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
21578
22499
  var canonical2 = (file) => {
21579
22500
  let rest = "";
21580
- for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
22501
+ for (let dir = path33.resolve(file);; dir = path33.dirname(dir)) {
21581
22502
  try {
21582
- return path31.join(realpathSync6.native(dir), rest);
22503
+ return path33.join(realpathSync6.native(dir), rest);
21583
22504
  } catch {
21584
- if (path31.dirname(dir) === dir)
21585
- return path31.resolve(file);
21586
- 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);
21587
22508
  }
21588
22509
  }
21589
22510
  };
21590
22511
  var exempt = (cwd, root, file, plans) => {
21591
22512
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
21592
- const relative = path31.relative(fold(canonical2(root)), fold(canonical2(path31.resolve(cwd, file))));
21593
- 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))
21594
22515
  return true;
21595
- const posix = relative.split(path31.sep).join("/");
22516
+ const posix = relative.split(path33.sep).join("/");
21596
22517
  const segments = posix.split("/");
21597
22518
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
21598
22519
  };
@@ -21769,10 +22690,10 @@ var linkSources = (args, at) => {
21769
22690
  const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
21770
22691
  const target = dest === undefined ? null : at(dest);
21771
22692
  const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
21772
- const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync9(target) || /[\\/]$/.test(dest ?? "") ? target : path31.dirname(target);
22693
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync11(target) || /[\\/]$/.test(dest ?? "") ? target : path33.dirname(target);
21773
22694
  return sources.flatMap((source) => {
21774
- if (symbolic && base !== null && !path31.isAbsolute(homeExpanded(source)))
21775
- return [path31.resolve(base, source)];
22695
+ if (symbolic && base !== null && !path33.isAbsolute(homeExpanded(source)))
22696
+ return [path33.resolve(base, source)];
21776
22697
  const resolved = at(source);
21777
22698
  return resolved ? [resolved] : [];
21778
22699
  });
@@ -21801,7 +22722,7 @@ var shellWrites = (command, depth = 0) => {
21801
22722
  return { writes: true, targets: unknown ? null : targets };
21802
22723
  };
21803
22724
  var cache2 = new Map;
21804
- 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");
21805
22726
  var gateFor = (cwd) => {
21806
22727
  const store = new TaskStore(cwd);
21807
22728
  const listed = store.listTaskIndex();
@@ -21880,13 +22801,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
21880
22801
  return [];
21881
22802
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
21882
22803
  return [];
21883
- return [path31.resolve(cwd, invocation.dir ?? ".")];
22804
+ return [path33.resolve(cwd, invocation.dir ?? ".")];
21884
22805
  });
21885
22806
  };
21886
22807
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
21887
22808
  var PUSHD2 = new Set(["pushd", "push-location"]);
21888
22809
  var POPD2 = new Set(["popd", "pop-location"]);
21889
- var UNKNOWN_DIR = path31.resolve(path31.parse(process.cwd()).root, ".workit-unknown-dir");
22810
+ var UNKNOWN_DIR = path33.resolve(path33.parse(process.cwd()).root, ".workit-unknown-dir");
21890
22811
  var cdInto = (dir, words, strict = false) => {
21891
22812
  const lost = strict ? UNKNOWN_DIR : undefined;
21892
22813
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
@@ -21895,7 +22816,7 @@ var cdInto = (dir, words, strict = false) => {
21895
22816
  return lost;
21896
22817
  if (dir === undefined)
21897
22818
  return lost;
21898
- return target === undefined ? os10.homedir() : path31.resolve(dir, target);
22819
+ return target === undefined ? os10.homedir() : path33.resolve(dir, target);
21899
22820
  };
21900
22821
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
21901
22822
  var homeExpanded = (file) => {
@@ -21909,7 +22830,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21909
22830
  const groups = [];
21910
22831
  const at = (file) => {
21911
22832
  const home = homeExpanded(file);
21912
- 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);
21913
22834
  };
21914
22835
  for (const segment of segmentsOf(command, dialect)) {
21915
22836
  if (segment.group === "open") {
@@ -21949,7 +22870,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21949
22870
  for (const file of opaque ?? []) {
21950
22871
  const resolved = at(file);
21951
22872
  if (resolved)
21952
- targets.push(file === "." ? path31.join(resolved, DIR_TARGET) : resolved);
22873
+ targets.push(file === "." ? path33.join(resolved, DIR_TARGET) : resolved);
21953
22874
  }
21954
22875
  const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
21955
22876
  if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
@@ -21964,7 +22885,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21964
22885
  const edits = editsOf(segment.words);
21965
22886
  if (edits === null) {
21966
22887
  if (dir !== undefined)
21967
- targets.push(path31.join(dir, DIR_TARGET));
22888
+ targets.push(path33.join(dir, DIR_TARGET));
21968
22889
  } else
21969
22890
  for (const file of edits) {
21970
22891
  const resolved = at(file);
@@ -21975,20 +22896,20 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21975
22896
  for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
21976
22897
  const { at: runsIn, args } = workitCwd(dir, invocation);
21977
22898
  if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
21978
- targets.push(path31.join(runsIn, DIR_TARGET));
22899
+ targets.push(path33.join(runsIn, DIR_TARGET));
21979
22900
  }
21980
22901
  }
21981
22902
  return targets;
21982
22903
  };
21983
22904
  var workitCwd = (dir, args) => {
21984
22905
  const { rest, cwd } = skipWorkitGlobals(args);
21985
- return { at: cwd === null ? dir : path31.resolve(dir, homeExpanded(cwd)), args: rest };
22906
+ return { at: cwd === null ? dir : path33.resolve(dir, homeExpanded(cwd)), args: rest };
21986
22907
  };
21987
22908
  var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
21988
22909
  try {
21989
22910
  return [
21990
22911
  ...segmentTargets(cwd, command, dialect, 0, strict),
21991
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path31.join(dir, DIR_TARGET))
22912
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path33.join(dir, DIR_TARGET))
21992
22913
  ];
21993
22914
  } catch {
21994
22915
  return [];
@@ -22025,7 +22946,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
22025
22946
  var guarded = (targets) => {
22026
22947
  const seen = new Set;
22027
22948
  return targets.filter((target) => {
22028
- const dir = path31.dirname(target);
22949
+ const dir = path33.dirname(target);
22029
22950
  if (seen.has(dir))
22030
22951
  return false;
22031
22952
  seen.add(dir);
@@ -22046,7 +22967,7 @@ var claimAll = (owner, targets, deps) => {
22046
22967
  for (const target of guarded(targets))
22047
22968
  claimLiveSession(target, owner, deps);
22048
22969
  };
22049
- 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)];
22050
22971
  var issuedTarget = (cwd, targets) => {
22051
22972
  const state = resolveStateDir();
22052
22973
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -22054,7 +22975,7 @@ var issuedTarget = (cwd, targets) => {
22054
22975
  };
22055
22976
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
22056
22977
  const win = platform === "win32";
22057
- const p = win ? path31.win32 : path31.posix;
22978
+ const p = win ? path33.win32 : path33.posix;
22058
22979
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
22059
22980
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
22060
22981
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -22144,7 +23065,25 @@ function beforeWriteGate(cwd, paths) {
22144
23065
  }
22145
23066
  }
22146
23067
  // packages/workit-core/src/hooks/stop.ts
22147
- 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
22148
23087
  var NONE4 = { kind: "none" };
22149
23088
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
22150
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;
@@ -22165,7 +23104,7 @@ var asksUser = (message) => {
22165
23104
  };
22166
23105
  var short = (sha) => (sha ?? "?").slice(0, 12);
22167
23106
  var gitList = (cwd, args) => {
22168
- const run = spawnSync18("git", args, {
23107
+ const run = spawnSync19("git", args, {
22169
23108
  cwd,
22170
23109
  encoding: "utf8",
22171
23110
  timeout: 3000,
@@ -22237,29 +23176,32 @@ var redChecks = (rows, pr, head, endpoint) => {
22237
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)" : ""}.`
22238
23177
  };
22239
23178
  };
22240
- var taskJudgment = (cwd) => {
22241
- try {
22242
- const store = new TaskStore(cwd);
22243
- const listed = store.listTaskIndex();
22244
- const key = store.currentKey();
22245
- if (!listed.ok || !key.ok)
22246
- return null;
22247
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
22248
- const task = entry ? store.readTask(entry.id) : null;
22249
- return task?.ok === true ? latestJudgment(task.data) : null;
22250
- } catch {
22251
- return null;
22252
- }
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;
22253
23182
  };
22254
- var unverified = (cwd, branch, all, descriptor, verification) => {
23183
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
22255
23184
  const check = checkVerdicts(cwd, branch, all);
22256
23185
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
22257
23186
  return null;
22258
- const judgment = taskJudgment(cwd);
23187
+ const judgment = currentJudgment(cwd);
22259
23188
  const high = judgment?.riskTier === "high";
22260
- if (judgment && !judgment.behaviorChange && !high)
22261
- return null;
22262
- 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
+ }
22263
23205
  if (!independent && check.review === "self-reviewed")
22264
23206
  return null;
22265
23207
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -22306,7 +23248,7 @@ function stopObligation(input, descriptor) {
22306
23248
  }
22307
23249
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
22308
23250
  return null;
22309
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
23251
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
22310
23252
  return left ? { ...left, head } : null;
22311
23253
  } catch {
22312
23254
  return null;
@@ -22576,9 +23518,9 @@ var WRITE_TOOLS = new Set([
22576
23518
  ]);
22577
23519
  // packages/workit-core/src/hooks/standing.ts
22578
23520
  import fs23 from "node:fs";
22579
- import path32 from "node:path";
23521
+ import path34 from "node:path";
22580
23522
  var readPlans = (store) => {
22581
- const dir = path32.join(store, "fanouts");
23523
+ const dir = path34.join(store, "fanouts");
22582
23524
  let names;
22583
23525
  try {
22584
23526
  names = fs23.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -22588,7 +23530,7 @@ var readPlans = (store) => {
22588
23530
  const plans = [];
22589
23531
  for (const name of names) {
22590
23532
  try {
22591
- const raw = JSON.parse(fs23.readFileSync(path32.join(dir, name), "utf8"));
23533
+ const raw = JSON.parse(fs23.readFileSync(path34.join(dir, name), "utf8"));
22592
23534
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
22593
23535
  continue;
22594
23536
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -22796,11 +23738,11 @@ var OPENCODE_DESCRIPTOR = {
22796
23738
  ]
22797
23739
  };
22798
23740
  // packages/workit-opencode/src/shared/init-apply.ts
22799
- import path37 from "node:path";
23741
+ import path39 from "node:path";
22800
23742
 
22801
23743
  // packages/workit-core/src/core/gitignore.ts
22802
- import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
22803
- 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";
22804
23746
  var GITIGNORE_ENTRIES = [
22805
23747
  "# workit: SDD working state (never commit)",
22806
23748
  "docs/*/sdd/",
@@ -22820,8 +23762,8 @@ var GITIGNORE_ENTRIES = [
22820
23762
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22821
23763
  if (!confirmed)
22822
23764
  return { ok: false, error: "confirmed: true required" };
22823
- const file = path33.join(workspaceRoot, ".gitignore");
22824
- const existing = existsSync10(file) ? readFileSync6(file, "utf8") : "";
23765
+ const file = path35.join(workspaceRoot, ".gitignore");
23766
+ const existing = existsSync12(file) ? readFileSync8(file, "utf8") : "";
22825
23767
  const existingLines = new Set(existing.split(`
22826
23768
  `).map((l) => l.trim()).filter(Boolean));
22827
23769
  const added = [];
@@ -22840,33 +23782,33 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22840
23782
  ` : "") + append.join(`
22841
23783
  `) + `
22842
23784
  `, "utf8");
22843
- } else if (!existsSync10(file)) {
23785
+ } else if (!existsSync12(file)) {
22844
23786
  writeFileSync4(file, "", "utf8");
22845
23787
  }
22846
23788
  return { ok: true, path: file, added };
22847
23789
  };
22848
23790
 
22849
23791
  // packages/workit-core/src/core/hygiene.ts
22850
- import { existsSync as existsSync11, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
22851
- 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";
22852
23794
  var repoRoot3 = assetRoot();
22853
- var templatesDir = () => path34.join(repoRoot3, "templates", "hygiene");
23795
+ var templatesDir = () => path36.join(repoRoot3, "templates", "hygiene");
22854
23796
  var packageJson = (root) => {
22855
- if (!existsSync11(path34.join(root, "package.json")))
23797
+ if (!existsSync13(path36.join(root, "package.json")))
22856
23798
  return null;
22857
23799
  try {
22858
- return JSON.parse(readFileSync7(path34.join(root, "package.json"), "utf8"));
23800
+ return JSON.parse(readFileSync9(path36.join(root, "package.json"), "utf8"));
22859
23801
  } catch {
22860
23802
  return null;
22861
23803
  }
22862
23804
  };
22863
23805
  var isOpenSource = (root) => {
22864
- if (existsSync11(path34.join(root, "LICENSE")))
23806
+ if (existsSync13(path36.join(root, "LICENSE")))
22865
23807
  return true;
22866
23808
  const pkg = packageJson(root);
22867
23809
  if (pkg && !pkg.private)
22868
23810
  return true;
22869
- return path34.basename(root) === "workflow-toolkit";
23811
+ return path36.basename(root) === "workflow-toolkit";
22870
23812
  };
22871
23813
  var licenseHolder = (root) => {
22872
23814
  const pkg = packageJson(root);
@@ -22889,13 +23831,13 @@ var planHygieneFiles = (root, opts = {}) => {
22889
23831
  files.push("LICENSE", "CONTRIBUTING.md");
22890
23832
  const planned = [];
22891
23833
  for (const file of files) {
22892
- if (existsSync11(path34.join(root, file)))
23834
+ if (existsSync13(path36.join(root, file)))
22893
23835
  continue;
22894
- const tpl = path34.join(templatesDir(), file);
22895
- if (!existsSync11(tpl))
23836
+ const tpl = path36.join(templatesDir(), file);
23837
+ if (!existsSync13(tpl))
22896
23838
  continue;
22897
- const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path34.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22898
- 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 });
22899
23841
  }
22900
23842
  return planned;
22901
23843
  };
@@ -22905,30 +23847,30 @@ var ensureHygieneFiles = (root, opts) => {
22905
23847
  const created = [];
22906
23848
  for (const planned of planHygieneFiles(root, opts)) {
22907
23849
  writeFileSync5(planned.path, planned.content, "utf8");
22908
- created.push(path34.basename(planned.path));
23850
+ created.push(path36.basename(planned.path));
22909
23851
  }
22910
23852
  return { ok: true, created };
22911
23853
  };
22912
23854
 
22913
23855
  // packages/workit-core/src/core/init.ts
22914
23856
  import fs24 from "node:fs";
22915
- import path36 from "node:path";
22916
- import { spawnSync as spawnSync19 } from "node:child_process";
23857
+ import path38 from "node:path";
23858
+ import { spawnSync as spawnSync20 } from "node:child_process";
22917
23859
 
22918
23860
  // packages/workit-core/src/core/workspace-branch-policy.ts
22919
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
22920
- 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";
22921
23863
  import { isDeepStrictEqual } from "node:util";
22922
23864
  var readFileSafe = (p) => {
22923
23865
  try {
22924
- return readFileSync8(p, "utf8");
23866
+ return readFileSync10(p, "utf8");
22925
23867
  } catch {
22926
23868
  return null;
22927
23869
  }
22928
23870
  };
22929
23871
  function applyWorkspaceBranchPolicy(opts) {
22930
23872
  const { workspace_root, env = process.env } = opts;
22931
- const dir = path35.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
23873
+ const dir = path37.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22932
23874
  const {
22933
23875
  status,
22934
23876
  path: wsPath,
@@ -22940,7 +23882,7 @@ function applyWorkspaceBranchPolicy(opts) {
22940
23882
  if (status === "malformed" || status === "invalid")
22941
23883
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
22942
23884
  const detection = detectBranchPolicy(workspace_root);
22943
- const name = String(env.WORKFLOW_BP_NAME ?? path35.basename(workspace_root));
23885
+ const name = String(env.WORKFLOW_BP_NAME ?? path37.basename(workspace_root));
22944
23886
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
22945
23887
  const policy = {
22946
23888
  preset: detection.preset,
@@ -22989,7 +23931,7 @@ function applyWorkspaceBranchPolicy(opts) {
22989
23931
  ok: false,
22990
23932
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
22991
23933
  };
22992
- mkdirSync4(path35.dirname(wsPath), { recursive: true });
23934
+ mkdirSync4(path37.dirname(wsPath), { recursive: true });
22993
23935
  const tmp = `${wsPath}.${process.pid}.tmp`;
22994
23936
  try {
22995
23937
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -23036,7 +23978,7 @@ var resolveInitProvider = (cwd) => {
23036
23978
  return env.toLowerCase();
23037
23979
  const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
23038
23980
  try {
23039
- const out = spawnSync19("git", ["remote", "get-url", "origin"], {
23981
+ const out = spawnSync20("git", ["remote", "get-url", "origin"], {
23040
23982
  cwd: root,
23041
23983
  encoding: "utf8"
23042
23984
  });
@@ -23054,7 +23996,7 @@ var youtrackJsonContent = (dir) => {
23054
23996
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
23055
23997
  return {
23056
23998
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
23057
- 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"),
23058
24000
  ...meetingIssue ? {
23059
24001
  meetingIssue,
23060
24002
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -23087,16 +24029,16 @@ function initApplyData(action, env = process.env) {
23087
24029
  fs24.mkdirSync(dir, { recursive: true });
23088
24030
  switch (action) {
23089
24031
  case "youtrack_json": {
23090
- const out = path36.join(dir, "youtrack.json");
24032
+ const out = path38.join(dir, "youtrack.json");
23091
24033
  fs24.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23092
24034
  `, "utf8");
23093
24035
  return { action, ok: true, path: out };
23094
24036
  }
23095
24037
  case "youtrack_token_placeholder": {
23096
- const p = path36.join(dir, "youtrack.token");
24038
+ const p = path38.join(dir, "youtrack.token");
23097
24039
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
23098
24040
  `, 384) === "preserved";
23099
- const abs = path36.resolve(p);
24041
+ const abs = path38.resolve(p);
23100
24042
  return {
23101
24043
  action,
23102
24044
  ok: true,
@@ -23108,14 +24050,14 @@ function initApplyData(action, env = process.env) {
23108
24050
  };
23109
24051
  }
23110
24052
  case "youtrack_scaffold": {
23111
- const jsonOut = path36.join(dir, "youtrack.json");
23112
- const tokenOut = path36.join(dir, "youtrack.token");
24053
+ const jsonOut = path38.join(dir, "youtrack.json");
24054
+ const tokenOut = path38.join(dir, "youtrack.token");
23113
24055
  fs24.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23114
24056
  `, "utf8");
23115
24057
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
23116
24058
  `, 384) === "preserved";
23117
- const configPath = path36.resolve(jsonOut);
23118
- const tokenPath = path36.resolve(tokenOut);
24059
+ const configPath = path38.resolve(jsonOut);
24060
+ const tokenPath = path38.resolve(tokenOut);
23119
24061
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
23120
24062
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
23121
24063
  let tokenCreate = {};
@@ -23167,10 +24109,10 @@ function initApplyData(action, env = process.env) {
23167
24109
  }
23168
24110
  }
23169
24111
  case "vcs_scaffold": {
23170
- const jsonOut = path36.join(dir, "vcs.json");
24112
+ const jsonOut = path38.join(dir, "vcs.json");
23171
24113
  fs24.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
23172
24114
  `, "utf8");
23173
- const configPath = path36.resolve(jsonOut);
24115
+ const configPath = path38.resolve(jsonOut);
23174
24116
  const prev = process.env.WORKFLOW_VCS_CONFIG;
23175
24117
  process.env.WORKFLOW_VCS_CONFIG = configPath;
23176
24118
  try {
@@ -23289,7 +24231,7 @@ var executeInitApply = (args, directory, runtime) => {
23289
24231
  protectedNames: args.branch_policy_protected
23290
24232
  }, current);
23291
24233
  writeConfig(next);
23292
- 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 }));
23293
24235
  }
23294
24236
  const env = Object.fromEntries(Object.entries({
23295
24237
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -23306,14 +24248,14 @@ var executeInitApply = (args, directory, runtime) => {
23306
24248
 
23307
24249
  // packages/workit-opencode/src/shared/session.ts
23308
24250
  import { realpathSync as realpathSync7 } from "node:fs";
23309
- import path38 from "node:path";
24251
+ import path40 from "node:path";
23310
24252
  var sameWorkspace = (expected, observed) => {
23311
24253
  if (typeof observed !== "string" || !observed)
23312
24254
  return false;
23313
24255
  try {
23314
24256
  return realpathSync7(expected) === realpathSync7(observed);
23315
24257
  } catch {
23316
- return path38.resolve(expected) === path38.resolve(observed);
24258
+ return path40.resolve(expected) === path40.resolve(observed);
23317
24259
  }
23318
24260
  };
23319
24261
 
@@ -23385,7 +24327,7 @@ var workitFamilyOf = (toolName) => {
23385
24327
 
23386
24328
  // packages/workit-opencode/src/tools/context.ts
23387
24329
  import { realpathSync as realpathSync8 } from "node:fs";
23388
- import path39 from "node:path";
24330
+ import path41 from "node:path";
23389
24331
  var createContextTool = () => ({
23390
24332
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
23391
24333
  execute: async (args, context) => {
@@ -23395,7 +24337,7 @@ var createContextTool = () => ({
23395
24337
  let root;
23396
24338
  try {
23397
24339
  const cwd = parsed.data.cwd;
23398
- root = realpathSync8(cwd ? path39.resolve(context.directory, cwd) : context.directory);
24340
+ root = realpathSync8(cwd ? path41.resolve(context.directory, cwd) : context.directory);
23399
24341
  } catch {
23400
24342
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
23401
24343
  }
@@ -23427,118 +24369,13 @@ var workerCoordinatorFor = (task, worker) => {
23427
24369
  // packages/workit-core/src/fanout.ts
23428
24370
  import { createHash as createHash17 } from "node:crypto";
23429
24371
  import fs26 from "node:fs";
23430
- import path42 from "node:path";
24372
+ import path43 from "node:path";
23431
24373
 
23432
24374
  // packages/workit-core/src/stack.ts
23433
24375
  import { spawnSync as spawnSync21 } from "node:child_process";
23434
24376
  import { createHash as createHash16, randomBytes as randomBytes4 } from "node:crypto";
23435
24377
  import fs25 from "node:fs";
23436
- import path41 from "node:path";
23437
-
23438
- // packages/workit-core/src/git/ops.ts
23439
- import { spawnSync as spawnSync20 } from "node:child_process";
23440
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
23441
- import path40 from "node:path";
23442
-
23443
- // packages/workit-core/src/forge/redact.ts
23444
- 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_.-]*`;
23445
- var ASSIGNMENT2 = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
23446
- var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
23447
- var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
23448
-
23449
- // packages/workit-core/src/forge/types.ts
23450
- var success2 = (data) => ({ ok: true, data });
23451
- var failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
23452
-
23453
- // packages/workit-core/src/git/ops.ts
23454
- var COMMIT_TIMEOUT_MS = 10 * 60000;
23455
- var git5 = (cwd, args, options = {}) => {
23456
- const result = spawnSync20("git", args, {
23457
- cwd,
23458
- encoding: "utf8",
23459
- env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
23460
- maxBuffer: 64 * 1024 * 1024,
23461
- timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
23462
- killSignal: "SIGKILL",
23463
- windowsHide: true,
23464
- ...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
23465
- });
23466
- return {
23467
- ok: result.status === 0,
23468
- status: result.status,
23469
- stdout: result.stdout ?? "",
23470
- stderr: result.stderr ?? ""
23471
- };
23472
- };
23473
- function operationInProgress(cwd) {
23474
- const markers = [
23475
- ["rebase-merge", "rebase"],
23476
- ["rebase-apply", "rebase"],
23477
- ["MERGE_HEAD", "merge"],
23478
- ["CHERRY_PICK_HEAD", "cherry-pick"],
23479
- ["REVERT_HEAD", "revert"]
23480
- ];
23481
- const run = git5(cwd, ["rev-parse", ...markers.flatMap(([name]) => ["--git-path", name])]);
23482
- if (!run.ok)
23483
- return null;
23484
- const found = run.stdout.split(`
23485
- `).map((line) => path40.resolve(cwd, line.trim()));
23486
- const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
23487
- for (const [index, [, operation]] of markers.entries()) {
23488
- const marker = found[index];
23489
- if (!marker || !existsSync12(marker))
23490
- continue;
23491
- if (index === 1 && existsSync12(path40.join(marker, "applying")))
23492
- return { operation: "am", commitAllowed: false, branch: null };
23493
- if (index === 0 && existsSync12(path40.join(marker, "amend")) && !conflicted()) {
23494
- let headName = "";
23495
- try {
23496
- headName = readFileSync9(path40.join(marker, "head-name"), "utf8").trim();
23497
- } catch {}
23498
- const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
23499
- return { operation, commitAllowed: branch !== null, branch };
23500
- }
23501
- if ((operation === "revert" || operation === "merge") && !conflicted())
23502
- return { operation, commitAllowed: true, branch: null };
23503
- return { operation, commitAllowed: false, branch: null };
23504
- }
23505
- return null;
23506
- }
23507
- function inProgressFailure(operation, verb) {
23508
- const command = operation === "am" ? "git am" : `git ${operation}`;
23509
- 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`);
23510
- }
23511
- function pushPreflight(cwd, options = {}) {
23512
- const progress = operationInProgress(cwd);
23513
- if (progress)
23514
- return inProgressFailure(progress.operation, "push");
23515
- const branch = options.branch ?? currentBranch(cwd);
23516
- if (!branch)
23517
- return failure3("invalid_input", "HEAD is detached; push a branch");
23518
- if (isProtectedTarget(cwd, branch))
23519
- 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");
23520
- const sha = resolveRef(cwd, `refs/heads/${branch}`);
23521
- if (!sha)
23522
- return failure3("not_found", `branch ${branch} has no commit to push`);
23523
- const remote = pushRemoteName(cwd, branch);
23524
- if (!remote)
23525
- return failure3("not_found", "no push remote is configured for this branch", "git remote add origin <url>");
23526
- const raw = pushUrl(cwd, remote);
23527
- if (!raw)
23528
- return failure3("blocked", `remote "${remote}" has no single push URL`, `git remote get-url --push --all ${remote} # keep exactly one push URL`);
23529
- const parsed = parseRemoteUrl(raw);
23530
- const local = parsed?.protocol === "file";
23531
- if (!local && !pushTargetIsStable(cwd, raw))
23532
- 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");
23533
- return success2({
23534
- branch,
23535
- sha,
23536
- remote,
23537
- url: redactRemote(raw),
23538
- rawUrl: raw,
23539
- local
23540
- });
23541
- }
24378
+ import path42 from "node:path";
23542
24379
 
23543
24380
  // packages/workit-core/src/forge/report.ts
23544
24381
  var GITLAB_MERGEABLE = new Set(["mergeable"]);
@@ -23620,7 +24457,7 @@ function rebaseInProgress(dir) {
23620
24457
  }
23621
24458
  var scratchDir = (cwd) => {
23622
24459
  const dir = stacksDir(cwd);
23623
- return dir.ok ? path41.join(dir.data, "worktrees") : null;
24460
+ return dir.ok ? path42.join(dir.data, "worktrees") : null;
23624
24461
  };
23625
24462
  function gitRestack(cwd, input) {
23626
24463
  const owner = worktreeOf(cwd, input.branch);
@@ -23630,7 +24467,7 @@ function gitRestack(cwd, input) {
23630
24467
  if (!scratch)
23631
24468
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
23632
24469
  fs25.mkdirSync(scratch, { recursive: true });
23633
- 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")}`);
23634
24471
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
23635
24472
  if (!added.ok)
23636
24473
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -23674,7 +24511,7 @@ function stacksDir(cwd) {
23674
24511
  const root = storeRoot(cwd);
23675
24512
  if (!root.ok)
23676
24513
  return stackFail(root.code, root.error, root.unblock);
23677
- return { ok: true, data: path41.join(root.value.root, "stacks") };
24514
+ return { ok: true, data: path42.join(root.value.root, "stacks") };
23678
24515
  }
23679
24516
  var HEARTBEAT_STALE_MS = 2 * 60000;
23680
24517
 
@@ -23785,7 +24622,7 @@ function fanoutsDir(cwd) {
23785
24622
  const root = storeRoot(cwd);
23786
24623
  if (!root.ok)
23787
24624
  return fanoutFail(root.code, root.error, root.unblock);
23788
- return { ok: true, data: path42.join(root.value.root, "fanouts") };
24625
+ return { ok: true, data: path43.join(root.value.root, "fanouts") };
23789
24626
  }
23790
24627
  function parseStored(raw) {
23791
24628
  if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -23853,7 +24690,7 @@ function readFanout(cwd, name) {
23853
24690
  const dir = fanoutsDir(cwd);
23854
24691
  if (!dir.ok)
23855
24692
  return dir;
23856
- const file = path42.join(dir.data, stackFileName(name));
24693
+ const file = path43.join(dir.data, stackFileName(name));
23857
24694
  let parsed = null;
23858
24695
  try {
23859
24696
  parsed = parseStored(JSON.parse(fs26.readFileSync(file, "utf8")));
@@ -23878,7 +24715,7 @@ function listFanouts(cwd) {
23878
24715
  for (const entry of names) {
23879
24716
  let parsed = null;
23880
24717
  try {
23881
- 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")));
23882
24719
  } catch {
23883
24720
  parsed = null;
23884
24721
  }
@@ -24570,7 +25407,7 @@ var childTaskMissing = (store, input) => {
24570
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`;
24571
25408
 
24572
25409
  // packages/workit-opencode/src/v2/shell.ts
24573
- import path43 from "node:path";
25410
+ import path44 from "node:path";
24574
25411
  var commandOf = (input) => {
24575
25412
  if (input === null || typeof input !== "object")
24576
25413
  return null;
@@ -24579,7 +25416,7 @@ var commandOf = (input) => {
24579
25416
  };
24580
25417
  var hookInput = (root, sessionID, id, shell) => ({
24581
25418
  host: "opencode",
24582
- cwd: shell.workdir ? path43.resolve(root, shell.workdir) : root,
25419
+ cwd: shell.workdir ? path44.resolve(root, shell.workdir) : root,
24583
25420
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
24584
25421
  permissionMode: null,
24585
25422
  transcriptPath: null,
@@ -24785,38 +25622,38 @@ var createStopControl = (root, port) => {
24785
25622
  };
24786
25623
 
24787
25624
  // packages/workit-opencode/src/v2/registry.ts
24788
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
24789
- import path45 from "node:path";
25625
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
25626
+ import path46 from "node:path";
24790
25627
 
24791
25628
  // packages/workit-opencode/src/shared/assets.ts
24792
- import { existsSync as existsSync13, realpathSync as realpathSync9 } from "node:fs";
24793
- import path44 from "node:path";
25629
+ import { existsSync as existsSync14, realpathSync as realpathSync9 } from "node:fs";
25630
+ import path45 from "node:path";
24794
25631
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24795
25632
  var moduleDir = () => {
24796
25633
  try {
24797
- return realpathSync9(path44.dirname(fileURLToPath2(import.meta.url)));
25634
+ return realpathSync9(path45.dirname(fileURLToPath2(import.meta.url)));
24798
25635
  } catch {
24799
- return path44.dirname(fileURLToPath2(import.meta.url));
25636
+ return path45.dirname(fileURLToPath2(import.meta.url));
24800
25637
  }
24801
25638
  };
24802
25639
  var packageRoot2 = () => {
24803
25640
  const dir = moduleDir();
24804
25641
  for (const candidate of [
24805
- path44.resolve(dir, ".."),
24806
- path44.resolve(dir, "..", ".."),
24807
- path44.resolve(dir, "..", "..", "..")
25642
+ path45.resolve(dir, ".."),
25643
+ path45.resolve(dir, "..", ".."),
25644
+ path45.resolve(dir, "..", "..", "..")
24808
25645
  ]) {
24809
- if (existsSync13(path44.join(candidate, "package.json")))
25646
+ if (existsSync14(path45.join(candidate, "package.json")))
24810
25647
  return candidate;
24811
25648
  }
24812
- return path44.resolve(dir, "..");
25649
+ return path45.resolve(dir, "..");
24813
25650
  };
24814
25651
  var skillsRoot = () => {
24815
25652
  const root = packageRoot2();
24816
- const canonical = path44.resolve(root, "..", "workit-core", "skills");
24817
- if (existsSync13(path44.join(root, "src")) && existsSync13(canonical))
25653
+ const canonical = path45.resolve(root, "..", "workit-core", "skills");
25654
+ if (existsSync14(path45.join(root, "src")) && existsSync14(canonical))
24818
25655
  return canonical;
24819
- return path44.join(root, "assets", "skills");
25656
+ return path45.join(root, "assets", "skills");
24820
25657
  };
24821
25658
 
24822
25659
  // packages/workit-opencode/src/v2/registry.ts
@@ -24840,15 +25677,15 @@ var parseFrontmatter = (markdown) => {
24840
25677
  };
24841
25678
  var registerSkills = async (ctx) => {
24842
25679
  const skillsDir = skillsRoot();
24843
- if (!existsSync14(skillsDir))
25680
+ if (!existsSync15(skillsDir))
24844
25681
  return new Set;
24845
25682
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
24846
- 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);
24847
25684
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
24848
25685
  const packaged = new Map;
24849
25686
  for (const id of WORKIT_METHOD_SKILLS) {
24850
- const file = path45.join(skillsDir, id, "SKILL.md");
24851
- if (!userSkills.has(id) && existsSync14(file))
25687
+ const file = path46.join(skillsDir, id, "SKILL.md");
25688
+ if (!userSkills.has(id) && existsSync15(file))
24852
25689
  packaged.set(id, file);
24853
25690
  }
24854
25691
  await ctx.skill.transform((editor) => {
@@ -24856,7 +25693,7 @@ var registerSkills = async (ctx) => {
24856
25693
  for (const [id, file] of packaged) {
24857
25694
  if (existing.has(id))
24858
25695
  continue;
24859
- const parsed = parseFrontmatter(readFileSync10(file, "utf8"));
25696
+ const parsed = parseFrontmatter(readFileSync11(file, "utf8"));
24860
25697
  editor.add({
24861
25698
  id,
24862
25699
  name: parsed.name || id,
@@ -24894,25 +25731,25 @@ var registerCommands = async (ctx, registeredSkills) => {
24894
25731
  };
24895
25732
 
24896
25733
  // packages/workit-opencode/src/stale-sources.ts
24897
- import { existsSync as existsSync15, readdirSync as readdirSync6 } from "node:fs";
24898
- import path46 from "node:path";
25734
+ import { existsSync as existsSync16, readdirSync as readdirSync6 } from "node:fs";
25735
+ import path47 from "node:path";
24899
25736
  import { fileURLToPath as fileURLToPath3 } from "node:url";
24900
25737
  var typescriptFiles = (directory) => {
24901
- if (!existsSync15(directory))
25738
+ if (!existsSync16(directory))
24902
25739
  return [];
24903
25740
  return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
24904
- const file = path46.join(directory, entry.name);
25741
+ const file = path47.join(directory, entry.name);
24905
25742
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
24906
25743
  });
24907
25744
  };
24908
25745
  var pluginSourceFiles = [
24909
25746
  ...new Set([
24910
25747
  fileURLToPath3(import.meta.url),
24911
- ...typescriptFiles(path46.join(packageRoot2(), "src")),
24912
- path46.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
24913
- ...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"))
24914
25751
  ])
24915
- ].filter((file) => existsSync15(file)).toSorted();
25752
+ ].filter((file) => existsSync16(file)).toSorted();
24916
25753
 
24917
25754
  // packages/workit-opencode/src/v2/plugin.ts
24918
25755
  var sessionFacts = async (ctx, sessionID) => {