@brainervirus/workit-opencode 2.6.0 → 2.7.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
@@ -15,12 +15,12 @@ var __export = (target, all) => {
15
15
 
16
16
  // packages/workit-opencode/src/v1/server.ts
17
17
  import { existsSync as existsSync12 } from "node:fs";
18
- import path26 from "node:path";
18
+ import path28 from "node:path";
19
19
 
20
20
  // packages/workit-core/src/core.ts
21
- import { spawnSync as spawnSync4 } from "node:child_process";
21
+ import { spawnSync as spawnSync6 } from "node:child_process";
22
22
  import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
23
- import path11 from "node:path";
23
+ import path13 from "node:path";
24
24
 
25
25
  // packages/workit-core/src/core/task-contract.ts
26
26
  import { createHash, randomUUID } from "node:crypto";
@@ -8137,7 +8137,37 @@ var candidateSchema = object({
8137
8137
  path: ["id"]
8138
8138
  });
8139
8139
  });
8140
- var evidenceSchema = object({
8140
+ var checkObservationSchema = object({
8141
+ observer: literal("workit_cli"),
8142
+ name: nonEmpty.nullable(),
8143
+ configured: boolean2(),
8144
+ argv: array(text).min(1),
8145
+ shell: boolean2(),
8146
+ cwd: nonEmpty,
8147
+ exitCode: safeInteger,
8148
+ durationMs: safeInteger.min(0),
8149
+ timedOut: boolean2(),
8150
+ head: text.nullable(),
8151
+ tree: text.nullable(),
8152
+ dirty: boolean2().nullable(),
8153
+ signal: text.nullable(),
8154
+ treeAfter: text.nullable(),
8155
+ modifiedWorktree: boolean2(),
8156
+ base: text.nullable(),
8157
+ patchId: text.nullable(),
8158
+ environment: object({
8159
+ platform: nonEmpty,
8160
+ arch: nonEmpty,
8161
+ vars: record(nonEmpty, text.nullable())
8162
+ }).strict(),
8163
+ logDigest: text.nullable(),
8164
+ logRef: text.nullable(),
8165
+ logTail: array(text).max(40),
8166
+ ledgerRowId: text.nullable(),
8167
+ attestation: object({ host: nonEmpty, session: text.nullable(), agentId: text.nullable() }).strict().nullable()
8168
+ }).strict();
8169
+ var CHECK_OBSERVATION_PATH = "evidence.*.data.observation";
8170
+ var evidenceFields = {
8141
8171
  kind: _enum(["check", "review", "investigation", "artifact"]),
8142
8172
  claim: text,
8143
8173
  requirementIds: array(digest),
@@ -8148,7 +8178,9 @@ var evidenceSchema = object({
8148
8178
  refs: array(refSchema),
8149
8179
  exitCode: safeInteger.nullable(),
8150
8180
  reviewContext: refSchema.nullable()
8151
- }).strict();
8181
+ };
8182
+ var evidenceSchema = object({ ...evidenceFields, observation: checkObservationSchema.optional() }).strict();
8183
+ var reportedEvidenceSchema = object(evidenceFields).strict();
8152
8184
  var evidenceEvaluationSchema = object({
8153
8185
  evidenceId: id,
8154
8186
  status: _enum(["passed", "failed", "missing", "skipped", "stale"]),
@@ -8480,7 +8512,7 @@ var evidenceOperations = {
8480
8512
  action: literal("record"),
8481
8513
  ...taskId,
8482
8514
  expectedRevision: revision.optional(),
8483
- evidence: evidenceSchema
8515
+ evidence: reportedEvidenceSchema
8484
8516
  })
8485
8517
  };
8486
8518
  var findingOperations = {
@@ -10980,10 +11012,10 @@ function assertProductWriteAllowed(input) {
10980
11012
  }
10981
11013
 
10982
11014
  // packages/workit-core/src/core/task-evaluation.ts
10983
- import { createHash as createHash7 } from "node:crypto";
10984
- import * as fs8 from "node:fs";
10985
- import { spawnSync as spawnSync3 } from "node:child_process";
10986
- import path10 from "node:path";
11015
+ import { createHash as createHash8 } from "node:crypto";
11016
+ import * as fs10 from "node:fs";
11017
+ import { spawnSync as spawnSync5 } from "node:child_process";
11018
+ import path12 from "node:path";
10987
11019
 
10988
11020
  // packages/workit-core/src/core/authority.ts
10989
11021
  import { createHash as createHash6 } from "node:crypto";
@@ -12749,16 +12781,346 @@ function reconcileAction(input) {
12749
12781
  var bindingCovers = scopeCovers;
12750
12782
  var verifyDecisionContent = verifyContentRefs;
12751
12783
 
12784
+ // packages/workit-core/src/check-config.ts
12785
+ import { spawnSync as spawnSync3 } from "node:child_process";
12786
+ import fs8 from "node:fs";
12787
+ import path10 from "node:path";
12788
+ var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12789
+ var CHECKS_FILE = "workit.checks.json";
12790
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12791
+ var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
12792
+ function splitCommand(command) {
12793
+ const argv = [];
12794
+ let current = "";
12795
+ let quote = null;
12796
+ let started = false;
12797
+ for (const char of command) {
12798
+ if (quote) {
12799
+ if (char === quote)
12800
+ quote = null;
12801
+ else
12802
+ current += char;
12803
+ continue;
12804
+ }
12805
+ if (char === '"' || char === "'") {
12806
+ quote = char;
12807
+ started = true;
12808
+ continue;
12809
+ }
12810
+ if (/\s/u.test(char)) {
12811
+ if (started)
12812
+ argv.push(current);
12813
+ current = "";
12814
+ started = false;
12815
+ continue;
12816
+ }
12817
+ if (SHELL_META.test(char))
12818
+ return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
12819
+ current += char;
12820
+ started = true;
12821
+ }
12822
+ if (quote)
12823
+ return new Error(`"${command}" has an unterminated quote`);
12824
+ if (started)
12825
+ argv.push(current);
12826
+ return argv.length ? argv : new Error("empty command");
12827
+ }
12828
+ var git = (cwd, args) => {
12829
+ const run = spawnSync3("git", args, {
12830
+ cwd,
12831
+ encoding: "utf8",
12832
+ timeout: 20000,
12833
+ killSignal: "SIGKILL",
12834
+ windowsHide: true
12835
+ });
12836
+ const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
12837
+ return value || null;
12838
+ };
12839
+ function checkRoot(cwd) {
12840
+ return path10.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12841
+ }
12842
+ var packageManager = (root) => {
12843
+ const has = (file) => fs8.existsSync(path10.join(root, file));
12844
+ if (has("bun.lock") || has("bun.lockb"))
12845
+ return "bun";
12846
+ if (has("pnpm-lock.yaml"))
12847
+ return "pnpm";
12848
+ if (has("yarn.lock"))
12849
+ return "yarn";
12850
+ return "npm";
12851
+ };
12852
+ var scriptCheck = (pm, name) => {
12853
+ const argv = [pm, "run", name];
12854
+ const accepts = [argv];
12855
+ if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
12856
+ accepts.push([pm, name]);
12857
+ return { name, argv, accepts };
12858
+ };
12859
+ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
12860
+ function detectedChecks(root) {
12861
+ const read = (file) => {
12862
+ try {
12863
+ return fs8.readFileSync(path10.join(root, file), "utf8");
12864
+ } catch {
12865
+ return null;
12866
+ }
12867
+ };
12868
+ const checks = [];
12869
+ const add = (check) => {
12870
+ if (!checks.some((item) => item.name === check.name))
12871
+ checks.push(check);
12872
+ };
12873
+ const manifestText = read("package.json");
12874
+ if (manifestText !== null) {
12875
+ let manifest = null;
12876
+ try {
12877
+ manifest = JSON.parse(manifestText);
12878
+ } catch {}
12879
+ const scripts = isRecord2(manifest) && isRecord2(manifest.scripts) ? manifest.scripts : {};
12880
+ const pm = packageManager(root);
12881
+ for (const name of SCRIPT_CHECKS) {
12882
+ const script = scripts[name];
12883
+ if (typeof script === "string" && script.trim() !== "")
12884
+ add(scriptCheck(pm, name));
12885
+ }
12886
+ }
12887
+ const plain = (name, argv) => ({ name, argv, accepts: [argv] });
12888
+ if (read("go.mod") !== null)
12889
+ add(plain("test", ["go", "test", "./..."]));
12890
+ if (read("Cargo.toml") !== null)
12891
+ add(plain("test", ["cargo", "test"]));
12892
+ if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
12893
+ add(plain("test", ["pytest"]));
12894
+ const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
12895
+ if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
12896
+ add(plain("test", ["make", "test"]));
12897
+ return checks;
12898
+ }
12899
+ var GATES = ["testing", "verification"];
12900
+ function loadCheckConfig(cwd) {
12901
+ const root = checkRoot(cwd);
12902
+ const file = path10.join(root, CHECKS_FILE);
12903
+ const none = (error = null) => ({
12904
+ source: error ? CHECKS_FILE : "none",
12905
+ root,
12906
+ checks: [],
12907
+ gates: {},
12908
+ error
12909
+ });
12910
+ let text = null;
12911
+ try {
12912
+ text = fs8.readFileSync(file, "utf8");
12913
+ } catch (error) {
12914
+ if (error.code !== "ENOENT")
12915
+ return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
12916
+ }
12917
+ if (text === null) {
12918
+ const checks = detectedChecks(root);
12919
+ return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
12920
+ }
12921
+ let parsed;
12922
+ try {
12923
+ parsed = JSON.parse(text);
12924
+ } catch {
12925
+ return none(`${CHECKS_FILE} is not valid JSON`);
12926
+ }
12927
+ if (!isRecord2(parsed) || !isRecord2(parsed.checks))
12928
+ return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
12929
+ const checks = [];
12930
+ for (const [name, value] of Object.entries(parsed.checks)) {
12931
+ if (!CHECK_NAME.test(name))
12932
+ return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
12933
+ const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
12934
+ if (argv instanceof Error)
12935
+ return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
12936
+ checks.push({ name, argv, accepts: [argv] });
12937
+ }
12938
+ const gates = {};
12939
+ if (parsed.gates !== undefined) {
12940
+ if (!isRecord2(parsed.gates))
12941
+ return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
12942
+ for (const [gate, value] of Object.entries(parsed.gates)) {
12943
+ if (!GATES.includes(gate))
12944
+ return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
12945
+ const names = typeof value === "string" ? [value] : value;
12946
+ if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
12947
+ return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
12948
+ gates[gate] = names;
12949
+ }
12950
+ }
12951
+ return { source: CHECKS_FILE, root, checks, gates, error: null };
12952
+ }
12953
+ var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
12954
+ function matchesNamedCheck(config, name, argv, cwd) {
12955
+ if (config.error || name === null || cwd !== ".")
12956
+ return false;
12957
+ const check = config.checks.find((item) => item.name === name);
12958
+ return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
12959
+ }
12960
+ function gateCheckNames(config, dimension) {
12961
+ const explicit = config.gates[dimension];
12962
+ if (explicit)
12963
+ return explicit;
12964
+ const names = config.checks.map((check) => check.name);
12965
+ if (dimension === "testing")
12966
+ return names.includes("test") ? ["test"] : [];
12967
+ return names;
12968
+ }
12969
+ var workitOnPath;
12970
+ function workitAvailable(env = process.env) {
12971
+ if (workitOnPath !== undefined)
12972
+ return workitOnPath;
12973
+ const windows = process.platform === "win32";
12974
+ const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
12975
+ const dirs = (env.PATH ?? env.Path ?? "").split(path10.delimiter).filter(Boolean);
12976
+ workitOnPath = dirs.some((dir) => exts.some((ext) => {
12977
+ try {
12978
+ return fs8.statSync(path10.join(dir, `workit${ext}`)).isFile();
12979
+ } catch {
12980
+ return false;
12981
+ }
12982
+ }));
12983
+ return workitOnPath;
12984
+ }
12985
+ function checkCommand(args, version, onPath = workitAvailable()) {
12986
+ return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
12987
+ }
12988
+
12989
+ // packages/workit-core/src/git/rev.ts
12990
+ import { spawnSync as spawnSync4 } from "node:child_process";
12991
+ import { createHash as createHash7 } from "node:crypto";
12992
+ import fs9 from "node:fs";
12993
+ import os2 from "node:os";
12994
+ import path11 from "node:path";
12995
+ var MAX_BUFFER = 256 * 1024 * 1024;
12996
+ var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
12997
+ var git2 = (cwd, args, options = {}) => {
12998
+ const result = spawnSync4("git", args, {
12999
+ cwd,
13000
+ encoding: "utf8",
13001
+ env: options.env ?? process.env,
13002
+ input: options.input,
13003
+ maxBuffer: MAX_BUFFER,
13004
+ timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
13005
+ killSignal: "SIGKILL",
13006
+ windowsHide: true
13007
+ });
13008
+ const error = result.error;
13009
+ return {
13010
+ ok: result.status === 0,
13011
+ stdout: result.stdout ?? "",
13012
+ stderr: result.stderr ?? "",
13013
+ timedOut: error?.code === "ETIMEDOUT"
13014
+ };
13015
+ };
13016
+ var line = (run) => {
13017
+ if (!run.ok)
13018
+ return null;
13019
+ const value = run.stdout.trim();
13020
+ return value ? value : null;
13021
+ };
13022
+ function headSha(cwd) {
13023
+ return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
13024
+ }
13025
+ var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
13026
+ var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
13027
+ var seedIndex = (from, to) => {
13028
+ fs9.copyFileSync(from, to);
13029
+ const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
13030
+ const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
13031
+ fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
13032
+ };
13033
+ function worktreeTree(cwd, options = {}) {
13034
+ const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
13035
+ const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
13036
+ if (!top || !indexPath)
13037
+ return null;
13038
+ const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
13039
+ const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
13040
+ const scratch = fs9.mkdtempSync(path11.join(os2.tmpdir(), "workit-tree-"));
13041
+ try {
13042
+ const tempIndex = path11.join(scratch, "index");
13043
+ if (fs9.existsSync(indexPath))
13044
+ seedIndex(indexPath, tempIndex);
13045
+ const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
13046
+ const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
13047
+ env,
13048
+ timeoutMs
13049
+ });
13050
+ if (!untracked.ok)
13051
+ return null;
13052
+ const skipped = [];
13053
+ for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
13054
+ try {
13055
+ const stat = fs9.statSync(path11.join(top, file));
13056
+ if (stat.isFile() && stat.size > cap)
13057
+ skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
13058
+ } catch {}
13059
+ }
13060
+ skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
13061
+ const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
13062
+ env,
13063
+ timeoutMs,
13064
+ input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
13065
+ }) : git2(top, ["add", "-A"], { env, timeoutMs });
13066
+ if (!added.ok)
13067
+ return null;
13068
+ const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
13069
+ if (!tree)
13070
+ return null;
13071
+ const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
13072
+ const key = skipped.length ? `sha256:${createHash7("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
13073
+ return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
13074
+ } finally {
13075
+ fs9.rmSync(scratch, { recursive: true, force: true });
13076
+ }
13077
+ }
13078
+ function worktreeSignal(cwd, options = {}) {
13079
+ const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
13080
+ if (!top)
13081
+ return null;
13082
+ const status = git2(top, [
13083
+ "status",
13084
+ "--porcelain=v1",
13085
+ "-z",
13086
+ "--untracked-files=all",
13087
+ "--no-renames",
13088
+ "--ignore-submodules=dirty"
13089
+ ], {
13090
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
13091
+ timeoutMs: options.timeoutMs ?? GIT_TIMEOUTS.worktree
13092
+ });
13093
+ if (!status.ok)
13094
+ return null;
13095
+ const hash = createHash7("sha256");
13096
+ hash.update(`${headSha(top) ?? "unborn"}\x00`);
13097
+ for (const entry of status.stdout.split("\x00")) {
13098
+ if (entry.length < 4)
13099
+ continue;
13100
+ const file = entry.slice(3);
13101
+ const absolute = path11.join(top, file);
13102
+ let stat = "-";
13103
+ try {
13104
+ const info = fs9.lstatSync(absolute, { bigint: true });
13105
+ stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
13106
+ if (info.isDirectory())
13107
+ stat += `:${headSha(absolute) ?? "-"}`;
13108
+ } catch {}
13109
+ hash.update(`${entry.slice(0, 2)}\x00${file}\x00${stat}\x00`);
13110
+ }
13111
+ return `sig:${hash.digest("hex")}`;
13112
+ }
13113
+
12752
13114
  // packages/workit-core/src/core/task-evaluation.ts
12753
- var digestBytes3 = (value) => createHash7("sha256").update(value).digest("hex");
12754
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
12755
- var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
13115
+ var digestBytes3 = (value) => createHash8("sha256").update(value).digest("hex");
13116
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path12.sep}`);
13117
+ var relative = (root, candidate) => path12.relative(root, candidate).split(path12.sep).join("/") || ".";
12756
13118
  var canonicalPath = (value) => {
12757
13119
  if (typeof value !== "string" || !value)
12758
13120
  return null;
12759
13121
  if (value.includes("\\") || value.split("/").includes(".."))
12760
13122
  return null;
12761
- const normalized = path10.posix.normalize(value);
13123
+ const normalized = path12.posix.normalize(value);
12762
13124
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
12763
13125
  return null;
12764
13126
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -12794,7 +13156,7 @@ var compareCodeUnits2 = (left, right) => {
12794
13156
  return left.length - right.length;
12795
13157
  };
12796
13158
  var gitPaths = (root) => {
12797
- const result = spawnSync3("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
13159
+ const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
12798
13160
  cwd: root,
12799
13161
  encoding: "buffer"
12800
13162
  });
@@ -12806,21 +13168,21 @@ var gitPaths = (root) => {
12806
13168
  git: !/not a git repository/i.test(stderr)
12807
13169
  };
12808
13170
  }
12809
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
12810
- const stagedDeleted = spawnSync3("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
13171
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")) : [];
13172
+ const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
12811
13173
  if (stagedDeleted.status !== 0) {
12812
13174
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
12813
13175
  if (!/not a git repository/i.test(stderr))
12814
13176
  return { paths, uncertain: true, git: true };
12815
13177
  } else if (stagedDeleted.stdout) {
12816
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
13178
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")));
12817
13179
  }
12818
13180
  return { paths: [...new Set(paths)], uncertain: false, git: true };
12819
13181
  };
12820
13182
  var walk = (root, directory, output) => {
12821
13183
  let names;
12822
13184
  try {
12823
- names = fs8.readdirSync(directory);
13185
+ names = fs10.readdirSync(directory);
12824
13186
  } catch {
12825
13187
  return true;
12826
13188
  }
@@ -12828,10 +13190,10 @@ var walk = (root, directory, output) => {
12828
13190
  for (const name of names) {
12829
13191
  if (name === ".git" || name === ".workit")
12830
13192
  continue;
12831
- const target = path10.join(directory, name);
13193
+ const target = path12.join(directory, name);
12832
13194
  let stat;
12833
13195
  try {
12834
- stat = fs8.lstatSync(target);
13196
+ stat = fs10.lstatSync(target);
12835
13197
  } catch {
12836
13198
  uncertain = true;
12837
13199
  continue;
@@ -12846,14 +13208,14 @@ var walk = (root, directory, output) => {
12846
13208
  var scopeRoots = (root, scope) => {
12847
13209
  const roots = [];
12848
13210
  for (const item of scope.paths.length ? scope.paths : ["."]) {
12849
- const target = path10.resolve(root, item);
13211
+ const target = path12.resolve(root, item);
12850
13212
  if (!inside(root, target))
12851
13213
  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` }] });
12852
13214
  let ancestor = target;
12853
- while (!fs8.existsSync(ancestor) && ancestor !== root)
12854
- ancestor = path10.dirname(ancestor);
13215
+ while (!fs10.existsSync(ancestor) && ancestor !== root)
13216
+ ancestor = path12.dirname(ancestor);
12855
13217
  try {
12856
- if (!inside(root, fs8.realpathSync(ancestor)))
13218
+ if (!inside(root, fs10.realpathSync(ancestor)))
12857
13219
  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` }] });
12858
13220
  } catch {
12859
13221
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -12865,7 +13227,7 @@ var scopeRoots = (root, scope) => {
12865
13227
  function captureCandidate(root, scope, environment = []) {
12866
13228
  let checkout;
12867
13229
  try {
12868
- checkout = fs8.realpathSync(root);
13230
+ checkout = fs10.realpathSync(root);
12869
13231
  } catch {
12870
13232
  return failure2("invalid_input", "candidate root does not exist");
12871
13233
  }
@@ -12883,11 +13245,11 @@ function captureCandidate(root, scope, environment = []) {
12883
13245
  if (item === ".git" || item === ".workit")
12884
13246
  continue;
12885
13247
  names.add(item);
12886
- if (!fs8.existsSync(target))
13248
+ if (!fs10.existsSync(target))
12887
13249
  continue;
12888
13250
  let stat;
12889
13251
  try {
12890
- stat = fs8.lstatSync(target);
13252
+ stat = fs10.lstatSync(target);
12891
13253
  } catch {
12892
13254
  uncertain = true;
12893
13255
  continue;
@@ -12903,17 +13265,17 @@ function captureCandidate(root, scope, environment = []) {
12903
13265
  for (const item of [...names].toSorted()) {
12904
13266
  if (!scopeMatches(item, normalizedScope))
12905
13267
  continue;
12906
- const target = path10.join(checkout, item);
13268
+ const target = path12.join(checkout, item);
12907
13269
  try {
12908
- const stat = fs8.lstatSync(target);
13270
+ const stat = fs10.lstatSync(target);
12909
13271
  if (stat.isSymbolicLink()) {
12910
- const link = fs8.readlinkSync(target);
13272
+ const link = fs10.readlinkSync(target);
12911
13273
  files.push({ path: item, kind: "symlink", digest: digestBytes3(link), executable: null });
12912
13274
  } else if (stat.isFile()) {
12913
13275
  files.push({
12914
13276
  path: item,
12915
13277
  kind: "file",
12916
- digest: digestBytes3(fs8.readFileSync(target)),
13278
+ digest: digestBytes3(fs10.readFileSync(target)),
12917
13279
  executable: (stat.mode & 73) !== 0
12918
13280
  });
12919
13281
  } else if (!stat.isDirectory()) {
@@ -12938,7 +13300,7 @@ function captureCandidate(root, scope, environment = []) {
12938
13300
  const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
12939
13301
  if (values.some(({ value }) => value === null))
12940
13302
  completeness = "uncertain";
12941
- const headResult = spawnSync3("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
13303
+ const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
12942
13304
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
12943
13305
  completeness = "uncertain";
12944
13306
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -12976,7 +13338,24 @@ var sameSession = (left, right) => {
12976
13338
  return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
12977
13339
  };
12978
13340
  var evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope);
12979
- function evaluateEvidence(task, candidate) {
13341
+ var memo2 = (compute) => {
13342
+ let value;
13343
+ return () => {
13344
+ if (value === undefined)
13345
+ value = compute();
13346
+ return value;
13347
+ };
13348
+ };
13349
+ var treeFreshness = (root) => ({
13350
+ mode: "tree",
13351
+ current: memo2(() => worktreeTree(root)?.key ?? null)
13352
+ });
13353
+ var signalFreshness = (root) => ({
13354
+ mode: "signal",
13355
+ current: memo2(() => worktreeSignal(root))
13356
+ });
13357
+ var isObservedCheck = (entry) => entry.data.kind === "check" && entry.data.observation?.observer === "workit_cli" && entry.provenance?.kind === "host_observed" && entry.provenance.host === "workit_cli";
13358
+ function evaluateEvidence(task, candidate, freshness) {
12980
13359
  const current = candidate ?? task.candidates.at(-1) ?? null;
12981
13360
  return task.evidence.map((entry) => {
12982
13361
  const evidence = entry.data;
@@ -12986,6 +13365,25 @@ function evaluateEvidence(task, candidate) {
12986
13365
  status: "failed",
12987
13366
  reason: "failed evidence remains historical"
12988
13367
  };
13368
+ const observation = isObservedCheck(entry) ? evidence.observation : undefined;
13369
+ if (observation && observation.tree !== null) {
13370
+ if (task.status === "closed")
13371
+ return { evidenceId: entry.id, status: evidence.result, reason: "recorded before close" };
13372
+ if (observation.modifiedWorktree)
13373
+ return {
13374
+ evidenceId: entry.id,
13375
+ status: "stale",
13376
+ reason: "the check changed the worktree while it ran; ignore or clean its outputs"
13377
+ };
13378
+ const recorded = freshness?.mode === "signal" ? observation.signal : observation.tree;
13379
+ if (recorded !== null && freshness && recorded === freshness.current())
13380
+ return { evidenceId: entry.id, status: evidence.result, reason: "worktree is unchanged" };
13381
+ return {
13382
+ evidenceId: entry.id,
13383
+ status: "stale",
13384
+ reason: freshness?.mode === "signal" ? "the worktree may have changed since the check ran (quick signal; inspect re-checks exactly)" : "the worktree changed since the check ran"
13385
+ };
13386
+ }
12989
13387
  if (evidence.kind === "check" || evidence.kind === "review") {
12990
13388
  if (!evidence.beforeCandidateId || !evidence.candidateId)
12991
13389
  return {
@@ -13069,8 +13467,45 @@ var expectedKinds = (dimension, ruleId) => {
13069
13467
  return "decision";
13070
13468
  return "worker report";
13071
13469
  };
13072
- function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
13073
- const evidence = evaluateEvidence(task, candidate);
13470
+ var observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close";
13471
+ function evaluateObservedGate(task, requirement, statuses, config) {
13472
+ const names = gateCheckNames(config, requirement.dimension);
13473
+ const observed = task.evidence.filter(isObservedCheck);
13474
+ const fresh = observed.filter((entry) => statuses.get(entry.id) === "passed" && entry.data.observation?.exitCode === 0);
13475
+ const named = (entry) => {
13476
+ const observation = entry.data.observation;
13477
+ return observation.configured && !observation.shell && names.includes(observation.name ?? "") && matchesNamedCheck(config, observation.name, observation.argv, observation.cwd);
13478
+ };
13479
+ const considered = observed.map((entry) => entry.id);
13480
+ const run = checkCommand(names[0] ?? "<name>", runtimeVersion());
13481
+ if (config.error)
13482
+ return {
13483
+ satisfied: [],
13484
+ considered,
13485
+ reason: `check config is invalid (${config.error}); fix it, then run ${run}`
13486
+ };
13487
+ if (!names.length)
13488
+ return {
13489
+ satisfied: [],
13490
+ considered,
13491
+ reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE} (committed, so it shows in the diff), e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
13492
+ };
13493
+ const satisfied = fresh.filter(named);
13494
+ if (satisfied.length)
13495
+ return {
13496
+ satisfied: satisfied.map((entry) => entry.id),
13497
+ considered,
13498
+ reason: `a fresh passing configured check (${names.join("|")}) was observed by workit check`
13499
+ };
13500
+ const reported = task.evidence.some((entry) => entry.data.kind === "check" && !isObservedCheck(entry) && entry.data.requirementIds.includes(requirement.id));
13501
+ const reason = fresh.length ? `ad-hoc checks do not satisfy a configured gate; run ${run}` : observed.some((entry) => statuses.get(entry.id) === "stale") ? `observed checks are stale (the worktree changed); run ${run}` : observed.some((entry) => statuses.get(entry.id) === "failed") ? `the observed check failed; fix it and run ${run}` : reported ? `agent-reported checks are notes, not evidence; run ${run}` : `no CLI-observed check (needs kind:check observed by workit check); run ${run}`;
13502
+ return { satisfied: [], considered, reason };
13503
+ }
13504
+ function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller, freshness = treeFreshness(checkoutRoot)) {
13505
+ const evidence = evaluateEvidence(task, candidate, freshness);
13506
+ const statuses = new Map(evidence.map((item) => [item.evidenceId, item.status]));
13507
+ let checkConfig;
13508
+ const configFor = () => checkConfig ??= loadCheckConfig(checkoutRoot);
13074
13509
  if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
13075
13510
  return task.policy.requirements.map((requirement) => ({
13076
13511
  requirementId: requirement.id,
@@ -13080,8 +13515,19 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
13080
13515
  reason: "stored policy version is unsupported; reassessment is required"
13081
13516
  }));
13082
13517
  return (task.policy?.requirements ?? []).map((requirement) => {
13518
+ const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
13519
+ if (gate?.satisfied.length)
13520
+ return {
13521
+ requirementId: requirement.id,
13522
+ status: "satisfied",
13523
+ evidenceIds: gate.satisfied,
13524
+ decisionIds: [],
13525
+ reason: gate.reason
13526
+ };
13083
13527
  const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
13084
13528
  const passed = related.filter(({ entry, evaluation }) => {
13529
+ if (gate)
13530
+ return false;
13085
13531
  if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
13086
13532
  return false;
13087
13533
  if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
@@ -13144,6 +13590,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
13144
13590
  reason: "an applicable approved limitation permits the missing evidence"
13145
13591
  };
13146
13592
  const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
13593
+ if (gate)
13594
+ return {
13595
+ requirementId: requirement.id,
13596
+ status: unavailable ? "unavailable" : "unsatisfied",
13597
+ evidenceIds: [...new Set([...gate.considered, ...related.map(({ entry }) => entry.id)])],
13598
+ decisionIds: [],
13599
+ reason: unavailable ? "required capability is unavailable" : gate.reason
13600
+ };
13147
13601
  if (!unavailable && related.length > 0 && !passed.length)
13148
13602
  return {
13149
13603
  requirementId: requirement.id,
@@ -13320,8 +13774,11 @@ policy, and reapplies the call, returning busy under persistent contention. A
13320
13774
  revision_conflict means a revision you passed is stale: re-read the record
13321
13775
  before deciding whether to retry.
13322
13776
  A solo edit does not need writer acquisition; use it when concurrent checkout
13323
- writers need coordination. Record only observed facts and checks. Evidence can
13324
- become stale when its bound candidate changes; reconcile findings against the
13777
+ writers need coordination. Record only observed facts and checks. Close-time
13778
+ testing and verification gates accept only a configured check the CLI ran:
13779
+ \`workit check <name>\` (\`npx -y @brainervirus/workit-cli check <name>\` when
13780
+ \`workit\` is not on PATH); a recorded check result is a note and an ad-hoc
13781
+ \`workit check -- <cmd>\` never satisfies a gate. Evidence can become stale when its bound candidate or tree changes; reconcile findings against the
13325
13782
  current candidate before recording completion.
13326
13783
 
13327
13784
  Workit validates domain policy against the actual action target, configured
@@ -13382,7 +13839,7 @@ function reconcileResume(view, observations = []) {
13382
13839
  const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
13383
13840
  if (!candidate.ok)
13384
13841
  return candidate;
13385
- const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
13842
+ const staleEvidenceIds = evaluateEvidence(view.task, candidate.data, treeFreshness(view.workspace.root)).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
13386
13843
  const blockers = [...view.task.progress.blockers];
13387
13844
  if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
13388
13845
  blockers.push({
@@ -14750,10 +15207,7 @@ class WorkitCore {
14750
15207
  if (finding.data.disposition !== "fixed")
14751
15208
  return finding;
14752
15209
  const withEntry = { ...current, candidates, evidence: [...current.evidence, entry] };
14753
- const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data).map((item) => [
14754
- item.evidenceId,
14755
- item.status
14756
- ]));
15210
+ const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data, treeFreshness(this.store.root)).map((item) => [item.evidenceId, item.status]));
14757
15211
  const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
14758
15212
  kind: item.data.kind,
14759
15213
  candidateId: item.data.candidateId,
@@ -14768,6 +15222,71 @@ class WorkitCore {
14768
15222
  return changed;
14769
15223
  return success(changed.data.revision, null, changed.data.evidence.at(-1));
14770
15224
  }
15225
+ observeCheck(request) {
15226
+ return this.retryOmittedRevisions(request, () => this.observeCheckOnce(request));
15227
+ }
15228
+ observeCheckOnce(request) {
15229
+ const root = this.contextRootError();
15230
+ if (!root.ok)
15231
+ return root;
15232
+ if (this.context.caller.host !== "workit_cli" || (this.context.workerId ?? null) !== null)
15233
+ return failure2("permission_denied", "only the workit CLI records observed checks");
15234
+ const observation = checkObservationSchema.safeParse(request?.observation);
15235
+ if (!observation.success)
15236
+ return failure2("invalid_input", "check observation is invalid", {
15237
+ fields: observation.error.issues.map((issue) => ({
15238
+ path: issue.path.join("."),
15239
+ reason: issue.message
15240
+ }))
15241
+ });
15242
+ const task = this.store.readTask(request.taskId);
15243
+ if (!task.ok)
15244
+ return task;
15245
+ if (task.data.status === "closed")
15246
+ return failure2("invalid_transition", "closed task cannot record evidence");
15247
+ const observed = observation.data;
15248
+ let candidate = null;
15249
+ if (observed.tree === null) {
15250
+ const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
15251
+ if (!captured.ok)
15252
+ return captured;
15253
+ candidate = captured.data;
15254
+ }
15255
+ const label = observed.name ?? observed.argv.join(" ");
15256
+ const data = {
15257
+ kind: "check",
15258
+ claim: `workit check ${label}`.slice(0, 500),
15259
+ requirementIds: [],
15260
+ ...candidate ? { beforeCandidateId: candidate.id, candidateId: candidate.id } : {},
15261
+ result: observed.exitCode === 0 ? "passed" : "failed",
15262
+ summary: `${observed.configured ? "configured" : "ad-hoc"} check exited ${observed.exitCode}${observed.timedOut ? " (timed out)" : ""} in ${observed.durationMs} ms`,
15263
+ refs: [],
15264
+ exitCode: observed.exitCode,
15265
+ reviewContext: null,
15266
+ observation: observed
15267
+ };
15268
+ const changed = this.store.mutateTask(task.data.id, request.expectedRevision ?? task.data.revision, (current, mutation) => {
15269
+ const candidates = candidate === null || current.candidates.some((item) => item.id === candidate.id) ? current.candidates : [...current.candidates, candidate];
15270
+ const critical = [...new Set([...current.critical ?? [], CHECK_OBSERVATION_PATH])];
15271
+ return success(mutation.revision, null, {
15272
+ ...current,
15273
+ critical,
15274
+ candidates,
15275
+ evidence: [
15276
+ ...current.evidence,
15277
+ {
15278
+ id: newId(),
15279
+ recordedAt: mutation.now,
15280
+ provenance: provenance(this.context, "host_observed"),
15281
+ data
15282
+ }
15283
+ ]
15284
+ });
15285
+ }, trustedNow(this.context));
15286
+ if (!changed.ok)
15287
+ return changed;
15288
+ return success(changed.data.revision, null, changed.data.evidence.at(-1));
15289
+ }
14771
15290
  decision(request) {
14772
15291
  return this.recordDecision(request);
14773
15292
  }
@@ -15003,7 +15522,7 @@ class WorkitCore {
15003
15522
  const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
15004
15523
  if (!current.ok)
15005
15524
  return current;
15006
- const evaluations = evaluateEvidence(task.data, current.data);
15525
+ const evaluations = evaluateEvidence(task.data, current.data, treeFreshness(this.store.root));
15007
15526
  const verified = evidence.some((entry) => {
15008
15527
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
15009
15528
  return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
@@ -15707,7 +16226,8 @@ class WorkitCore {
15707
16226
  if (!current.ok)
15708
16227
  return current;
15709
16228
  const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
15710
- const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
16229
+ const tree = treeFreshness(this.store.root);
16230
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
15711
16231
  return success(task.revision, workspace.data.revision, {
15712
16232
  id: task.id,
15713
16233
  revision: task.revision,
@@ -15724,7 +16244,7 @@ class WorkitCore {
15724
16244
  writer: evaluationWorkspace.writer
15725
16245
  });
15726
16246
  }
15727
- compactContext(taskId) {
16247
+ compactContext(taskId, freshness) {
15728
16248
  const root = this.contextRootError();
15729
16249
  if (!root.ok)
15730
16250
  return root;
@@ -15734,12 +16254,12 @@ class WorkitCore {
15734
16254
  const helper = this.helperTaskGuard(task.data, true);
15735
16255
  if (!helper.ok)
15736
16256
  return helper;
15737
- const view = this.view(task.data, false);
16257
+ const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
15738
16258
  if (!view.ok)
15739
16259
  return view;
15740
16260
  return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
15741
16261
  }
15742
- view(task, capture = true) {
16262
+ view(task, capture = true, freshness = treeFreshness(this.store.root)) {
15743
16263
  const workspace = this.store.readWorkspace();
15744
16264
  if (!workspace.ok)
15745
16265
  return workspace;
@@ -15750,13 +16270,14 @@ class WorkitCore {
15750
16270
  if (!current.ok)
15751
16271
  return current;
15752
16272
  const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
15753
- const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
16273
+ const tree = freshness;
16274
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
15754
16275
  return success(task.revision, workspace.data.revision, {
15755
16276
  task,
15756
16277
  workspace: evaluationWorkspace,
15757
16278
  capabilities: this.context.capabilities,
15758
16279
  requirements,
15759
- evidence: evaluateEvidence(task, current.data)
16280
+ evidence: evaluateEvidence(task, current.data, tree)
15760
16281
  });
15761
16282
  }
15762
16283
  reconcileResume(view, observations = []) {
@@ -15983,13 +16504,14 @@ class WorkitCore {
15983
16504
  return current;
15984
16505
  const withCandidate = current.data;
15985
16506
  const taskForView = withCandidate.id === task.data.candidates.at(-1)?.id ? task.data : { ...task.data, candidates: [...task.data.candidates, withCandidate] };
15986
- const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller);
16507
+ const tree = treeFreshness(this.store.root);
16508
+ const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller, tree);
15987
16509
  const view = {
15988
16510
  task: taskForView,
15989
16511
  workspace: workspace.data,
15990
16512
  capabilities: this.context.capabilities,
15991
16513
  requirements,
15992
- evidence: evaluateEvidence(taskForView, withCandidate)
16514
+ evidence: evaluateEvidence(taskForView, withCandidate, tree)
15993
16515
  };
15994
16516
  const closure = evaluateClosure(input.outcome, view);
15995
16517
  if (!closure.ok)
@@ -16053,22 +16575,22 @@ function resolveGitRevision(root, value) {
16053
16575
  }
16054
16576
  function resolveInside(root, candidate) {
16055
16577
  const base = realpathSync7(root);
16056
- const target = path11.resolve(base, candidate);
16057
- if (target !== base && !target.startsWith(base + path11.sep)) {
16578
+ const target = path13.resolve(base, candidate);
16579
+ if (target !== base && !target.startsWith(base + path13.sep)) {
16058
16580
  throw new Error("path must stay inside repository root");
16059
16581
  }
16060
16582
  let ancestor = target;
16061
16583
  while (!existsSync5(ancestor))
16062
- ancestor = path11.dirname(ancestor);
16584
+ ancestor = path13.dirname(ancestor);
16063
16585
  const canonicalAncestor = realpathSync7(ancestor);
16064
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
16586
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path13.sep)) {
16065
16587
  throw new Error("path must stay inside repository root");
16066
16588
  }
16067
16589
  return target;
16068
16590
  }
16069
16591
  function run2(root, executable, args, env = {}) {
16070
16592
  const cwd = realpathSync7(root);
16071
- const result = spawnSync4(executable, args, {
16593
+ const result = spawnSync6(executable, args, {
16072
16594
  cwd,
16073
16595
  encoding: "utf8",
16074
16596
  env: { ...process.env, ...env }
@@ -16118,8 +16640,8 @@ if (false) {}
16118
16640
 
16119
16641
  // packages/workit-core/src/core/logger.ts
16120
16642
  import { appendFileSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
16121
- import os2 from "node:os";
16122
- import path12 from "node:path";
16643
+ import os3 from "node:os";
16644
+ import path14 from "node:path";
16123
16645
  var REDACTED = "[REDACTED]";
16124
16646
  var DEFAULT_MAX_RATE = 20;
16125
16647
  var DEFAULT_RATE_WINDOW_MS = 1000;
@@ -16173,19 +16695,19 @@ var resolveStateDir = () => {
16173
16695
  const override = process.env.WORKFLOW_TOOLKIT_STATE;
16174
16696
  if (override)
16175
16697
  return override;
16176
- const home = os2.homedir();
16698
+ const home = os3.homedir();
16177
16699
  if (process.env.XDG_STATE_HOME)
16178
- return path12.join(process.env.XDG_STATE_HOME, "workit");
16179
- switch (os2.platform()) {
16700
+ return path14.join(process.env.XDG_STATE_HOME, "workit");
16701
+ switch (os3.platform()) {
16180
16702
  case "darwin":
16181
- return path12.join(home, "Library", "Application Support", "workit");
16703
+ return path14.join(home, "Library", "Application Support", "workit");
16182
16704
  case "win32":
16183
- return path12.join(process.env.LOCALAPPDATA ?? path12.join(home, "AppData", "Local"), "workit");
16705
+ return path14.join(process.env.LOCALAPPDATA ?? path14.join(home, "AppData", "Local"), "workit");
16184
16706
  default:
16185
- return path12.join(process.env.HOME ?? home, ".local", "state", "workit");
16707
+ return path14.join(process.env.HOME ?? home, ".local", "state", "workit");
16186
16708
  }
16187
16709
  };
16188
- var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
16710
+ var homePattern = new RegExp(`${escapeRegExp(os3.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
16189
16711
  var applyPatterns = (value) => {
16190
16712
  let out = value;
16191
16713
  out = out.replace(SECRET_VALUE, REDACTED);
@@ -16255,14 +16777,14 @@ var pruneOldFiles = (dir) => {
16255
16777
  const files = readdirSync4(dir).filter((name) => DAY_FILE.test(name)).toSorted().toReversed();
16256
16778
  for (const file of files.slice(RETAINED_DAYS)) {
16257
16779
  try {
16258
- unlinkSync2(path12.join(dir, file));
16780
+ unlinkSync2(path14.join(dir, file));
16259
16781
  } catch {}
16260
16782
  }
16261
16783
  } catch {}
16262
16784
  };
16263
16785
  var createLogger = (options = {}) => {
16264
16786
  const stateDir = options.stateDir ?? resolveStateDir();
16265
- const logDir = path12.join(stateDir, "logs");
16787
+ const logDir = path14.join(stateDir, "logs");
16266
16788
  const now = options.now ?? (() => new Date);
16267
16789
  const maxRate = options.maxRate ?? DEFAULT_MAX_RATE;
16268
16790
  const rateWindowMs = options.rateWindowMs ?? DEFAULT_RATE_WINDOW_MS;
@@ -16296,7 +16818,7 @@ var createLogger = (options = {}) => {
16296
16818
  const line = `${JSON.stringify(event)}
16297
16819
  `;
16298
16820
  mkdirSync3(logDir, { recursive: true, mode: 448 });
16299
- appendFileSync(path12.join(logDir, dailyFileName(date)), line, { mode: 384 });
16821
+ appendFileSync(path14.join(logDir, dailyFileName(date)), line, { mode: 384 });
16300
16822
  pruneOldFiles(logDir);
16301
16823
  } catch {
16302
16824
  try {
@@ -16307,7 +16829,7 @@ var createLogger = (options = {}) => {
16307
16829
  context: { redaction_failed: true }
16308
16830
  };
16309
16831
  mkdirSync3(logDir, { recursive: true, mode: 448 });
16310
- appendFileSync(path12.join(logDir, dailyFileName(date)), `${JSON.stringify(event)}
16832
+ appendFileSync(path14.join(logDir, dailyFileName(date)), `${JSON.stringify(event)}
16311
16833
  `, {
16312
16834
  mode: 384
16313
16835
  });
@@ -16356,47 +16878,47 @@ OpenCode Workit decisions use the native question header "Workit decision: <purp
16356
16878
 
16357
16879
  // packages/workit-opencode/src/shared/assets.ts
16358
16880
  import { existsSync as existsSync6, realpathSync as realpathSync8 } from "node:fs";
16359
- import path13 from "node:path";
16881
+ import path15 from "node:path";
16360
16882
  import { fileURLToPath as fileURLToPath2 } from "node:url";
16361
16883
  var moduleDir = () => {
16362
16884
  try {
16363
- return realpathSync8(path13.dirname(fileURLToPath2(import.meta.url)));
16885
+ return realpathSync8(path15.dirname(fileURLToPath2(import.meta.url)));
16364
16886
  } catch {
16365
- return path13.dirname(fileURLToPath2(import.meta.url));
16887
+ return path15.dirname(fileURLToPath2(import.meta.url));
16366
16888
  }
16367
16889
  };
16368
16890
  var packageRoot2 = () => {
16369
16891
  const dir = moduleDir();
16370
16892
  for (const candidate of [
16371
- path13.resolve(dir, ".."),
16372
- path13.resolve(dir, "..", ".."),
16373
- path13.resolve(dir, "..", "..", "..")
16893
+ path15.resolve(dir, ".."),
16894
+ path15.resolve(dir, "..", ".."),
16895
+ path15.resolve(dir, "..", "..", "..")
16374
16896
  ]) {
16375
- if (existsSync6(path13.join(candidate, "package.json")))
16897
+ if (existsSync6(path15.join(candidate, "package.json")))
16376
16898
  return candidate;
16377
16899
  }
16378
- return path13.resolve(dir, "..");
16900
+ return path15.resolve(dir, "..");
16379
16901
  };
16380
- var assetsRoot = () => path13.join(packageRoot2(), "assets");
16902
+ var assetsRoot = () => path15.join(packageRoot2(), "assets");
16381
16903
 
16382
16904
  // packages/workit-opencode/src/stale-sources.ts
16383
16905
  import { existsSync as existsSync7, readdirSync as readdirSync5 } from "node:fs";
16384
- import path14 from "node:path";
16906
+ import path16 from "node:path";
16385
16907
  import { fileURLToPath as fileURLToPath3 } from "node:url";
16386
16908
  var typescriptFiles = (directory) => {
16387
16909
  if (!existsSync7(directory))
16388
16910
  return [];
16389
16911
  return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
16390
- const file = path14.join(directory, entry.name);
16912
+ const file = path16.join(directory, entry.name);
16391
16913
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
16392
16914
  });
16393
16915
  };
16394
16916
  var pluginSourceFiles = [
16395
16917
  ...new Set([
16396
16918
  fileURLToPath3(import.meta.url),
16397
- ...typescriptFiles(path14.join(packageRoot2(), "src")),
16398
- path14.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
16399
- ...typescriptFiles(path14.join(packageRoot2(), "..", "workit-core", "src", "core"))
16919
+ ...typescriptFiles(path16.join(packageRoot2(), "src")),
16920
+ path16.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
16921
+ ...typescriptFiles(path16.join(packageRoot2(), "..", "workit-core", "src", "core"))
16400
16922
  ])
16401
16923
  ].filter((file) => existsSync7(file)).toSorted();
16402
16924
 
@@ -28721,11 +29243,11 @@ function tool(input) {
28721
29243
  }
28722
29244
  tool.schema = exports_external2;
28723
29245
  // packages/workit-opencode/src/shared/init-apply.ts
28724
- import path20 from "node:path";
29246
+ import path22 from "node:path";
28725
29247
 
28726
29248
  // packages/workit-core/src/core/gitignore.ts
28727
29249
  import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
28728
- import path15 from "node:path";
29250
+ import path17 from "node:path";
28729
29251
  var GITIGNORE_ENTRIES = [
28730
29252
  "# workit: SDD working state (never commit)",
28731
29253
  "docs/*/sdd/",
@@ -28745,7 +29267,7 @@ var GITIGNORE_ENTRIES = [
28745
29267
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
28746
29268
  if (!confirmed)
28747
29269
  return { ok: false, error: "confirmed: true required" };
28748
- const file = path15.join(workspaceRoot, ".gitignore");
29270
+ const file = path17.join(workspaceRoot, ".gitignore");
28749
29271
  const existing = existsSync8(file) ? readFileSync7(file, "utf8") : "";
28750
29272
  const existingLines = new Set(existing.split(`
28751
29273
  `).map((l) => l.trim()).filter(Boolean));
@@ -28773,7 +29295,7 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
28773
29295
 
28774
29296
  // packages/workit-core/src/core/hygiene.ts
28775
29297
  import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
28776
- import path16 from "node:path";
29298
+ import path18 from "node:path";
28777
29299
 
28778
29300
  // packages/workit-core/src/core/scripts.ts
28779
29301
  var PLUGIN_ROOT = packageRoot();
@@ -28782,23 +29304,23 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
28782
29304
 
28783
29305
  // packages/workit-core/src/core/hygiene.ts
28784
29306
  var repoRoot = assetRoot();
28785
- var templatesDir = () => path16.join(repoRoot, "templates", "hygiene");
29307
+ var templatesDir = () => path18.join(repoRoot, "templates", "hygiene");
28786
29308
  var packageJson = (root) => {
28787
- if (!existsSync9(path16.join(root, "package.json")))
29309
+ if (!existsSync9(path18.join(root, "package.json")))
28788
29310
  return null;
28789
29311
  try {
28790
- return JSON.parse(readFileSync8(path16.join(root, "package.json"), "utf8"));
29312
+ return JSON.parse(readFileSync8(path18.join(root, "package.json"), "utf8"));
28791
29313
  } catch {
28792
29314
  return null;
28793
29315
  }
28794
29316
  };
28795
29317
  var isOpenSource = (root) => {
28796
- if (existsSync9(path16.join(root, "LICENSE")))
29318
+ if (existsSync9(path18.join(root, "LICENSE")))
28797
29319
  return true;
28798
29320
  const pkg = packageJson(root);
28799
29321
  if (pkg && !pkg.private)
28800
29322
  return true;
28801
- return path16.basename(root) === "workflow-toolkit";
29323
+ return path18.basename(root) === "workflow-toolkit";
28802
29324
  };
28803
29325
  var licenseHolder = (root) => {
28804
29326
  const pkg = packageJson(root);
@@ -28821,13 +29343,13 @@ var planHygieneFiles = (root, opts = {}) => {
28821
29343
  files.push("LICENSE", "CONTRIBUTING.md");
28822
29344
  const planned = [];
28823
29345
  for (const file of files) {
28824
- if (existsSync9(path16.join(root, file)))
29346
+ if (existsSync9(path18.join(root, file)))
28825
29347
  continue;
28826
- const tpl = path16.join(templatesDir(), file);
29348
+ const tpl = path18.join(templatesDir(), file);
28827
29349
  if (!existsSync9(tpl))
28828
29350
  continue;
28829
- const content = readFileSync8(tpl, "utf8").replace(/<PROJECT>/g, path16.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
28830
- planned.push({ path: path16.join(root, file), content });
29351
+ const content = readFileSync8(tpl, "utf8").replace(/<PROJECT>/g, path18.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
29352
+ planned.push({ path: path18.join(root, file), content });
28831
29353
  }
28832
29354
  return planned;
28833
29355
  };
@@ -28837,15 +29359,15 @@ var ensureHygieneFiles = (root, opts) => {
28837
29359
  const created = [];
28838
29360
  for (const planned of planHygieneFiles(root, opts)) {
28839
29361
  writeFileSync4(planned.path, planned.content, "utf8");
28840
- created.push(path16.basename(planned.path));
29362
+ created.push(path18.basename(planned.path));
28841
29363
  }
28842
29364
  return { ok: true, created };
28843
29365
  };
28844
29366
 
28845
29367
  // packages/workit-core/src/core/init.ts
28846
- import fs10 from "node:fs";
28847
- import path19 from "node:path";
28848
- import { spawnSync as spawnSync6 } from "node:child_process";
29368
+ import fs12 from "node:fs";
29369
+ import path21 from "node:path";
29370
+ import { spawnSync as spawnSync8 } from "node:child_process";
28849
29371
 
28850
29372
  // packages/workit-core/src/core/safe-write.ts
28851
29373
  import {
@@ -28886,11 +29408,11 @@ import {
28886
29408
  statSync as statSync3,
28887
29409
  writeFileSync as writeFileSync6
28888
29410
  } from "node:fs";
28889
- import path17 from "node:path";
29411
+ import path19 from "node:path";
28890
29412
  import { isDeepStrictEqual } from "node:util";
28891
29413
 
28892
29414
  // packages/workit-core/src/core/branch-policy.ts
28893
- import { spawnSync as spawnSync5 } from "node:child_process";
29415
+ import { spawnSync as spawnSync7 } from "node:child_process";
28894
29416
  var PREFIXES = {
28895
29417
  feature: "feature/*",
28896
29418
  bugfix: "bugfix/*",
@@ -28899,7 +29421,7 @@ var PREFIXES = {
28899
29421
  };
28900
29422
  var detectBranchPolicy = (workspaceRoot) => {
28901
29423
  const branchExists = (name) => {
28902
- const r = spawnSync5("git", ["branch", "--list", name], {
29424
+ const r = spawnSync7("git", ["branch", "--list", name], {
28903
29425
  cwd: workspaceRoot,
28904
29426
  encoding: "utf8"
28905
29427
  });
@@ -29000,7 +29522,7 @@ var readFileSafe = (p) => {
29000
29522
  };
29001
29523
  function applyWorkspaceBranchPolicy(opts) {
29002
29524
  const { workspace_root, env = process.env } = opts;
29003
- const dir = path17.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29525
+ const dir = path19.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29004
29526
  const {
29005
29527
  status,
29006
29528
  path: wsPath,
@@ -29012,7 +29534,7 @@ function applyWorkspaceBranchPolicy(opts) {
29012
29534
  if (status === "malformed" || status === "invalid")
29013
29535
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
29014
29536
  const detection = detectBranchPolicy(workspace_root);
29015
- const name = String(env.WORKFLOW_BP_NAME ?? path17.basename(workspace_root));
29537
+ const name = String(env.WORKFLOW_BP_NAME ?? path19.basename(workspace_root));
29016
29538
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
29017
29539
  const policy = {
29018
29540
  preset: detection.preset,
@@ -29061,7 +29583,7 @@ function applyWorkspaceBranchPolicy(opts) {
29061
29583
  ok: false,
29062
29584
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
29063
29585
  };
29064
- mkdirSync5(path17.dirname(wsPath), { recursive: true });
29586
+ mkdirSync5(path19.dirname(wsPath), { recursive: true });
29065
29587
  const tmp = `${wsPath}.${process.pid}.tmp`;
29066
29588
  try {
29067
29589
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -29093,8 +29615,8 @@ function applyWorkspaceBranchPolicy(opts) {
29093
29615
  }
29094
29616
 
29095
29617
  // packages/workit-core/src/core/youtrack.ts
29096
- import fs9 from "node:fs";
29097
- import path18 from "node:path";
29618
+ import fs11 from "node:fs";
29619
+ import path20 from "node:path";
29098
29620
 
29099
29621
  // packages/workit-core/src/core/templates.ts
29100
29622
  var repoRoot2 = assetRoot();
@@ -29102,22 +29624,22 @@ var repoRoot2 = assetRoot();
29102
29624
  // packages/workit-core/src/core/youtrack.ts
29103
29625
  var ISSUE_RE = /^[A-Z]+-\d+$/;
29104
29626
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
29105
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
29106
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
29627
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
29628
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
29107
29629
  var youTrackTokenModeOk = (p) => {
29108
29630
  if (process.platform === "win32")
29109
29631
  return true;
29110
- const mode = fs9.statSync(p).mode & 511;
29632
+ const mode = fs11.statSync(p).mode & 511;
29111
29633
  return mode === 384;
29112
29634
  };
29113
29635
  function readYouTrackConfig(required) {
29114
29636
  const cfgPath = youTrackConfigPath();
29115
- if (!fs9.existsSync(cfgPath)) {
29637
+ if (!fs11.existsSync(cfgPath)) {
29116
29638
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
29117
29639
  }
29118
29640
  let parsed;
29119
29641
  try {
29120
- parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
29642
+ parsed = JSON.parse(fs11.readFileSync(cfgPath, "utf8"));
29121
29643
  } catch {
29122
29644
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
29123
29645
  }
@@ -29128,11 +29650,11 @@ function readYouTrackConfig(required) {
29128
29650
  }
29129
29651
  var readYouTrackContextConfig = () => {
29130
29652
  const cfgPath = youTrackReadOnlyConfigPath();
29131
- if (!fs9.existsSync(cfgPath))
29653
+ if (!fs11.existsSync(cfgPath))
29132
29654
  return { data: {} };
29133
29655
  let parsed;
29134
29656
  try {
29135
- parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
29657
+ parsed = JSON.parse(fs11.readFileSync(cfgPath, "utf8"));
29136
29658
  } catch {
29137
29659
  return { error: "YouTrack configuration is unavailable" };
29138
29660
  }
@@ -29171,12 +29693,12 @@ var youTrackToken = () => {
29171
29693
  if ("error" in loaded)
29172
29694
  return loaded;
29173
29695
  const tokenFile = String(loaded.config.tokenFile ?? "");
29174
- const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
29175
- if (!tokenPath || !fs9.existsSync(tokenPath))
29696
+ const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
29697
+ if (!tokenPath || !fs11.existsSync(tokenPath))
29176
29698
  return { error: "missing youtrack.token" };
29177
29699
  if (!youTrackTokenModeOk(tokenPath))
29178
29700
  return { error: "youtrack.token mode must be 0600" };
29179
- const token = fs9.readFileSync(tokenPath, "utf8").trim();
29701
+ const token = fs11.readFileSync(tokenPath, "utf8").trim();
29180
29702
  if (!token)
29181
29703
  return { error: "empty token file" };
29182
29704
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -29222,7 +29744,7 @@ function youTrackTokenCreateUrl() {
29222
29744
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
29223
29745
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
29224
29746
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
29225
- const tokenFile = String(config.tokenFile ?? path18.join(path18.dirname(loaded.path), "youtrack.token"));
29747
+ const tokenFile = String(config.tokenFile ?? path20.join(path20.dirname(loaded.path), "youtrack.token"));
29226
29748
  const tab = String(defaults.profileTab ?? "account-security");
29227
29749
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
29228
29750
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -29231,7 +29753,7 @@ function youTrackTokenCreateUrl() {
29231
29753
  tokenName: name,
29232
29754
  tokenDescription: desc,
29233
29755
  scopes,
29234
- tokenFile: path18.resolve(tokenFile),
29756
+ tokenFile: path20.resolve(tokenFile),
29235
29757
  createUrl,
29236
29758
  docsUrl,
29237
29759
  prefillSupported: false,
@@ -29277,9 +29799,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
29277
29799
  } catch {
29278
29800
  continue;
29279
29801
  }
29280
- if (!fs9.existsSync(full))
29802
+ if (!fs11.existsSync(full))
29281
29803
  continue;
29282
- const text = fs9.readFileSync(full, "utf8");
29804
+ const text = fs11.readFileSync(full, "utf8");
29283
29805
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
29284
29806
  if (m)
29285
29807
  return m[1];
@@ -29430,7 +29952,7 @@ var fetchYouTrackIssueBody = async (issue, creds = youTrackToken, request = youT
29430
29952
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
29431
29953
  var readJson = (p) => {
29432
29954
  try {
29433
- const parsed = JSON.parse(fs10.readFileSync(p, "utf8"));
29955
+ const parsed = JSON.parse(fs12.readFileSync(p, "utf8"));
29434
29956
  return isConfigObject(parsed) ? parsed : null;
29435
29957
  } catch {
29436
29958
  return null;
@@ -29442,7 +29964,7 @@ var resolveInitProvider = (cwd) => {
29442
29964
  return env.toLowerCase();
29443
29965
  const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
29444
29966
  try {
29445
- const out = spawnSync6("git", ["remote", "get-url", "origin"], {
29967
+ const out = spawnSync8("git", ["remote", "get-url", "origin"], {
29446
29968
  cwd: root,
29447
29969
  encoding: "utf8"
29448
29970
  });
@@ -29460,7 +29982,7 @@ var youtrackJsonContent = (dir) => {
29460
29982
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
29461
29983
  return {
29462
29984
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
29463
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path19.join(dir, "youtrack.token"),
29985
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path21.join(dir, "youtrack.token"),
29464
29986
  ...meetingIssue ? {
29465
29987
  meetingIssue,
29466
29988
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -29490,19 +30012,19 @@ var vcsJsonContent = () => {
29490
30012
  };
29491
30013
  function initApplyData(action, env = process.env) {
29492
30014
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29493
- fs10.mkdirSync(dir, { recursive: true });
30015
+ fs12.mkdirSync(dir, { recursive: true });
29494
30016
  switch (action) {
29495
30017
  case "youtrack_json": {
29496
- const out = path19.join(dir, "youtrack.json");
29497
- fs10.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
30018
+ const out = path21.join(dir, "youtrack.json");
30019
+ fs12.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
29498
30020
  `, "utf8");
29499
30021
  return { action, ok: true, path: out };
29500
30022
  }
29501
30023
  case "youtrack_token_placeholder": {
29502
- const p = path19.join(dir, "youtrack.token");
30024
+ const p = path21.join(dir, "youtrack.token");
29503
30025
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
29504
30026
  `, 384) === "preserved";
29505
- const abs = path19.resolve(p);
30027
+ const abs = path21.resolve(p);
29506
30028
  return {
29507
30029
  action,
29508
30030
  ok: true,
@@ -29514,14 +30036,14 @@ function initApplyData(action, env = process.env) {
29514
30036
  };
29515
30037
  }
29516
30038
  case "youtrack_scaffold": {
29517
- const jsonOut = path19.join(dir, "youtrack.json");
29518
- const tokenOut = path19.join(dir, "youtrack.token");
29519
- fs10.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
30039
+ const jsonOut = path21.join(dir, "youtrack.json");
30040
+ const tokenOut = path21.join(dir, "youtrack.token");
30041
+ fs12.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
29520
30042
  `, "utf8");
29521
30043
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
29522
30044
  `, 384) === "preserved";
29523
- const configPath = path19.resolve(jsonOut);
29524
- const tokenPath = path19.resolve(tokenOut);
30045
+ const configPath = path21.resolve(jsonOut);
30046
+ const tokenPath = path21.resolve(tokenOut);
29525
30047
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
29526
30048
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
29527
30049
  let tokenCreate = {};
@@ -29573,10 +30095,10 @@ function initApplyData(action, env = process.env) {
29573
30095
  }
29574
30096
  }
29575
30097
  case "vcs_scaffold": {
29576
- const jsonOut = path19.join(dir, "vcs.json");
29577
- fs10.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
30098
+ const jsonOut = path21.join(dir, "vcs.json");
30099
+ fs12.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
29578
30100
  `, "utf8");
29579
- const configPath = path19.resolve(jsonOut);
30101
+ const configPath = path21.resolve(jsonOut);
29580
30102
  const prev = process.env.WORKFLOW_VCS_CONFIG;
29581
30103
  process.env.WORKFLOW_VCS_CONFIG = configPath;
29582
30104
  try {
@@ -29695,7 +30217,7 @@ var executeInitApply = (args, directory, runtime) => {
29695
30217
  protectedNames: args.branch_policy_protected
29696
30218
  }, current);
29697
30219
  writeConfig(next);
29698
- return output(ok({ action: "config", path: path20.join(configDir(), "config.json"), ...next }));
30220
+ return output(ok({ action: "config", path: path22.join(configDir(), "config.json"), ...next }));
29699
30221
  }
29700
30222
  const env = Object.fromEntries(Object.entries({
29701
30223
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -29748,12 +30270,12 @@ function createRepoTools(runtime = initApplyRuntime) {
29748
30270
 
29749
30271
  // packages/workit-opencode/src/tools/context.ts
29750
30272
  import { realpathSync as realpathSync10 } from "node:fs";
29751
- import path23 from "node:path";
30273
+ import path25 from "node:path";
29752
30274
 
29753
30275
  // packages/workit-core/src/core/pr-create.ts
29754
- import fs11 from "node:fs";
29755
- import path21 from "node:path";
29756
- import { spawnSync as spawnSync7 } from "node:child_process";
30276
+ import fs13 from "node:fs";
30277
+ import path23 from "node:path";
30278
+ import { spawnSync as spawnSync9 } from "node:child_process";
29757
30279
  // packages/workit-core/src/forge/pr-body.ts
29758
30280
  function parseGhIssue(value) {
29759
30281
  const m = /issues\/(\d+)/.exec(value);
@@ -29763,13 +30285,13 @@ function parseGhIssue(value) {
29763
30285
  // packages/workit-core/src/core/pr-create.ts
29764
30286
  function whichOnPath(tool) {
29765
30287
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
29766
- for (const dir of (process.env.PATH ?? "").split(path21.delimiter)) {
30288
+ for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
29767
30289
  if (!dir)
29768
30290
  continue;
29769
30291
  for (const name of names) {
29770
- const candidate = path21.join(dir, name);
30292
+ const candidate = path23.join(dir, name);
29771
30293
  try {
29772
- fs11.accessSync(candidate, fs11.constants.X_OK);
30294
+ fs13.accessSync(candidate, fs13.constants.X_OK);
29773
30295
  return candidate;
29774
30296
  } catch {}
29775
30297
  }
@@ -29778,7 +30300,7 @@ function whichOnPath(tool) {
29778
30300
  }
29779
30301
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
29780
30302
  var pushRemote = (cwd) => {
29781
- const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
30303
+ const result = spawnSync9("git", ["remote", "get-url", "--push", "--all", "origin"], {
29782
30304
  cwd,
29783
30305
  encoding: "utf8"
29784
30306
  });
@@ -29789,9 +30311,9 @@ var pushRemote = (cwd) => {
29789
30311
  };
29790
30312
 
29791
30313
  // packages/workit-core/src/core/repo-context.ts
29792
- import { spawnSync as spawnSync8 } from "node:child_process";
30314
+ import { spawnSync as spawnSync10 } from "node:child_process";
29793
30315
  import { existsSync as existsSync11, readFileSync as readFileSync10, readdirSync as readdirSync7 } from "node:fs";
29794
- import path22 from "node:path";
30316
+ import path24 from "node:path";
29795
30317
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
29796
30318
  try {
29797
30319
  gitRevisionParts(value);
@@ -29818,7 +30340,7 @@ var invalidRange = (cwd) => ({
29818
30340
  cwd
29819
30341
  });
29820
30342
  var runGit = (cwd, args) => {
29821
- const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
30343
+ const result = spawnSync10("git", args, { cwd, encoding: "utf8" });
29822
30344
  return {
29823
30345
  stdout: (result.stdout ?? "").trimEnd(),
29824
30346
  stderr: (result.stderr ?? "").trimEnd(),
@@ -29925,7 +30447,7 @@ var findPrTemplate = (cwd) => {
29925
30447
  ]) {
29926
30448
  const parts = rel.split("/");
29927
30449
  const base = parts[parts.length - 1];
29928
- const dir = parts.length > 1 ? path22.join(cwd, ...parts.slice(0, -1)) : cwd;
30450
+ const dir = parts.length > 1 ? path24.join(cwd, ...parts.slice(0, -1)) : cwd;
29929
30451
  const actual = probeCaseInsensitive(dir, base);
29930
30452
  if (actual)
29931
30453
  return [...parts.slice(0, -1), actual].join("/");
@@ -29956,7 +30478,7 @@ var diffStatForRange = (cwd, range) => {
29956
30478
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
29957
30479
  };
29958
30480
  var packageScripts = (cwd, keys) => {
29959
- const pkgPath = path22.join(cwd, "package.json");
30481
+ const pkgPath = path24.join(cwd, "package.json");
29960
30482
  if (!existsSync11(pkgPath))
29961
30483
  return [];
29962
30484
  try {
@@ -29982,9 +30504,9 @@ var documentationFiles = (cwd) => {
29982
30504
  if (entry.isDirectory()) {
29983
30505
  if (excluded.has(entry.name))
29984
30506
  continue;
29985
- walk(path22.join(dir, entry.name), depth + 1);
30507
+ walk(path24.join(dir, entry.name), depth + 1);
29986
30508
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
29987
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
30509
+ matches.push("./" + path24.relative(cwd, path24.join(dir, entry.name)).replaceAll("\\", "/"));
29988
30510
  }
29989
30511
  }
29990
30512
  };
@@ -30000,7 +30522,7 @@ var readTrimmed2 = (file, maxLines) => {
30000
30522
  `);
30001
30523
  };
30002
30524
  function prReadyContext(root, range) {
30003
- const cwd = path22.resolve(repoRoot3(root));
30525
+ const cwd = path24.resolve(repoRoot3(root));
30004
30526
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30005
30527
  return invalidRange(cwd);
30006
30528
  let stdout = "";
@@ -30058,7 +30580,7 @@ ${status.stderr}` : "");
30058
30580
  stdout += `template_path: ${template}
30059
30581
 
30060
30582
  `;
30061
- stdout += readTrimmed2(path22.join(cwd, template), 220);
30583
+ stdout += readTrimmed2(path24.join(cwd, template), 220);
30062
30584
  } else {
30063
30585
  stdout += `template_path: none
30064
30586
 
@@ -30068,7 +30590,7 @@ ${status.stderr}` : "");
30068
30590
  stdout += `
30069
30591
  `;
30070
30592
  stdout += printSection("Recent Validation Signals");
30071
- if (existsSync11(path22.join(cwd, "package.json"))) {
30593
+ if (existsSync11(path24.join(cwd, "package.json"))) {
30072
30594
  stdout += `package.json detected. Common scripts:
30073
30595
  `;
30074
30596
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -30077,7 +30599,7 @@ ${status.stderr}` : "");
30077
30599
  `) + `
30078
30600
  `;
30079
30601
  }
30080
- if (existsSync11(path22.join(cwd, "Cargo.toml")) || existsSync11(path22.join(cwd, "src-tauri/Cargo.toml"))) {
30602
+ if (existsSync11(path24.join(cwd, "Cargo.toml")) || existsSync11(path24.join(cwd, "src-tauri/Cargo.toml"))) {
30081
30603
  stdout += `Rust project detected.
30082
30604
  `;
30083
30605
  }
@@ -30110,7 +30632,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
30110
30632
  - Apply with the host-native editor, preserving the repository changelog conventions.
30111
30633
  - If Unreleased already has duplicate category headings, normalize_only first.`;
30112
30634
  function changelogContext(root, range) {
30113
- const cwd = path22.resolve(repoRoot3(root));
30635
+ const cwd = path24.resolve(repoRoot3(root));
30114
30636
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30115
30637
  return invalidRange(cwd);
30116
30638
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -30128,7 +30650,7 @@ function changelogContext(root, range) {
30128
30650
  stdout += CHANGELOG_RULES + `
30129
30651
  `;
30130
30652
  stdout += printSection("Existing CHANGELOG.md");
30131
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
30653
+ const changelogPath = path24.join(cwd, "CHANGELOG.md");
30132
30654
  if (existsSync11(changelogPath)) {
30133
30655
  stdout += readTrimmed2(changelogPath, 260) + `
30134
30656
  `;
@@ -30148,7 +30670,7 @@ function changelogContext(root, range) {
30148
30670
  return { stdout, stderr: "", exitCode: 0, cwd };
30149
30671
  }
30150
30672
  function docsRefreshContext(root, range) {
30151
- const cwd = path22.resolve(repoRoot3(root));
30673
+ const cwd = path24.resolve(repoRoot3(root));
30152
30674
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30153
30675
  return invalidRange(cwd);
30154
30676
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -30170,7 +30692,7 @@ function docsRefreshContext(root, range) {
30170
30692
  `) + `
30171
30693
  `;
30172
30694
  stdout += printSection("README Preview");
30173
- const readme = path22.join(cwd, "README.md");
30695
+ const readme = path24.join(cwd, "README.md");
30174
30696
  if (existsSync11(readme)) {
30175
30697
  stdout += readTrimmed2(readme, 220) + `
30176
30698
  `;
@@ -30179,7 +30701,7 @@ function docsRefreshContext(root, range) {
30179
30701
  `;
30180
30702
  }
30181
30703
  stdout += printSection("Package Scripts");
30182
- const pkgPath = path22.join(cwd, "package.json");
30704
+ const pkgPath = path24.join(cwd, "package.json");
30183
30705
  if (existsSync11(pkgPath)) {
30184
30706
  try {
30185
30707
  const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
@@ -30193,7 +30715,7 @@ function docsRefreshContext(root, range) {
30193
30715
  return { stdout, stderr: "", exitCode: 0, cwd };
30194
30716
  }
30195
30717
  function releaseNotesContext(root, rangeOrTag) {
30196
- const cwd = path22.resolve(repoRoot3(root));
30718
+ const cwd = path24.resolve(repoRoot3(root));
30197
30719
  if (!rangeOrTag) {
30198
30720
  return { stdout: "", stderr: `ERROR: release tag or range required
30199
30721
  `, exitCode: 1, cwd };
@@ -30231,7 +30753,7 @@ function releaseNotesContext(root, rangeOrTag) {
30231
30753
  `;
30232
30754
  stdout += printSection("Existing Release Files");
30233
30755
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
30234
- if (existsSync11(path22.join(cwd, rel)))
30756
+ if (existsSync11(path24.join(cwd, rel)))
30235
30757
  stdout += rel + `
30236
30758
  `;
30237
30759
  }
@@ -30259,7 +30781,7 @@ function releaseNotesContext(root, rangeOrTag) {
30259
30781
  }
30260
30782
 
30261
30783
  // packages/workit-core/src/core/tracker-issues.ts
30262
- import { spawnSync as spawnSync9 } from "node:child_process";
30784
+ import { spawnSync as spawnSync11 } from "node:child_process";
30263
30785
  var repoPathFromRemote = (remote) => {
30264
30786
  let rest = (remote || "").trim().replace(/\/+$/, "");
30265
30787
  if (!rest)
@@ -30280,7 +30802,7 @@ var defaultCli = (bin, available) => {
30280
30802
  if (!available)
30281
30803
  return null;
30282
30804
  return (args, root) => {
30283
- const out = spawnSync9(bin, args, {
30805
+ const out = spawnSync11(bin, args, {
30284
30806
  cwd: root,
30285
30807
  encoding: "utf8",
30286
30808
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -30363,10 +30885,6 @@ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\
30363
30885
  var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
30364
30886
  var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
30365
30887
 
30366
- // packages/workit-core/src/git/rev.ts
30367
- var MAX_BUFFER = 256 * 1024 * 1024;
30368
- var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
30369
-
30370
30888
  // packages/workit-core/src/ledger.ts
30371
30889
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
30372
30890
  var FAILING = new Set(["failed", "blocked"]);
@@ -30478,7 +30996,7 @@ var createContextTool = () => tool({
30478
30996
  try {
30479
30997
  const payload = parsed.data.payload;
30480
30998
  const cwd = payload.cwd;
30481
- root = realpathSync10(cwd ? path23.resolve(context.directory, cwd) : context.directory);
30999
+ root = realpathSync10(cwd ? path25.resolve(context.directory, cwd) : context.directory);
30482
31000
  } catch {
30483
31001
  return JSON.stringify(failure2("invalid_input", "action target directory cannot be resolved", {
30484
31002
  outcome: "not_started"
@@ -30490,14 +31008,14 @@ var createContextTool = () => tool({
30490
31008
 
30491
31009
  // packages/workit-opencode/src/shared/session.ts
30492
31010
  import { realpathSync as realpathSync11 } from "node:fs";
30493
- import path24 from "node:path";
31011
+ import path26 from "node:path";
30494
31012
  var sameWorkspace = (expected, observed) => {
30495
31013
  if (typeof observed !== "string" || !observed)
30496
31014
  return false;
30497
31015
  try {
30498
31016
  return realpathSync11(expected) === realpathSync11(observed);
30499
31017
  } catch {
30500
- return path24.resolve(expected) === path24.resolve(observed);
31018
+ return path26.resolve(expected) === path26.resolve(observed);
30501
31019
  }
30502
31020
  };
30503
31021
 
@@ -30999,7 +31517,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
30999
31517
  };
31000
31518
  });
31001
31519
  // packages/workit-core/src/hooks/context.ts
31002
- import path25 from "node:path";
31520
+ import path27 from "node:path";
31003
31521
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
31004
31522
  var newestFirst = (left, right) => right.updatedAt.localeCompare(left.updatedAt);
31005
31523
  var sessionTaskEntry = (entries, session) => entries.filter((entry) => entry.status !== "closed" && boundTo(entry, session)).toSorted(newestFirst)[0] ?? null;
@@ -31024,6 +31542,19 @@ var filesKey = (files) => {
31024
31542
  return signatures.some((item) => item !== "-" && racySignature(item)) ? null : signatures.join("\x00");
31025
31543
  };
31026
31544
  var CACHE_LIMIT = 64;
31545
+ var SIGNAL_TTL_MS = 1500;
31546
+ var signals = new Map;
31547
+ var recentSignal = (root) => {
31548
+ const now = Date.now();
31549
+ const cached = signals.get(root);
31550
+ if (cached && now - cached.at < SIGNAL_TTL_MS)
31551
+ return cached.value;
31552
+ const value = worktreeSignal(root);
31553
+ signals.set(root, { at: now, value });
31554
+ if (signals.size > CACHE_LIMIT)
31555
+ signals.delete(signals.keys().next().value);
31556
+ return value;
31557
+ };
31027
31558
  var cache = new Map;
31028
31559
  function sessionCompactContext(store, session, context, selection = "session-bound") {
31029
31560
  const listed = store.listTaskIndex();
@@ -31047,18 +31578,26 @@ function entryCompactContext(store, entry, session, context) {
31047
31578
  capabilities: context.capabilities
31048
31579
  });
31049
31580
  const hit = cache.get(slot);
31050
- if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files))
31581
+ if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
31051
31582
  return hit.value;
31052
31583
  const task = store.readTask(entry.id);
31053
31584
  if (!task.ok)
31054
31585
  return null;
31055
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path25.resolve(store.root, ref.path)] : []));
31056
- const observed = filesKey(files);
31057
- const compact = new WorkitCore(store, context).compactContext(entry.id);
31586
+ const freshness = { mode: "signal", current: () => recentSignal(store.root) };
31587
+ const observed = task.data.evidence.some(isObservedCheck);
31588
+ const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path27.resolve(store.root, ref.path)] : []));
31589
+ const observedFiles = filesKey(files);
31590
+ const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
31058
31591
  if (!compact.ok)
31059
31592
  return null;
31060
31593
  cache.delete(slot);
31061
- cache.set(slot, { key, files, filesKey: observed, value: compact.data });
31594
+ cache.set(slot, {
31595
+ key,
31596
+ files,
31597
+ filesKey: observedFiles,
31598
+ signal: observed ? freshness.current() : null,
31599
+ value: compact.data
31600
+ });
31062
31601
  if (cache.size > CACHE_LIMIT)
31063
31602
  cache.delete(cache.keys().next().value);
31064
31603
  return compact.data;
@@ -31230,7 +31769,7 @@ var loadProvenance = (logger, pkgUrl) => {
31230
31769
 
31231
31770
  // packages/workit-opencode/src/v1/server.ts
31232
31771
  var root = assetsRoot();
31233
- var skillsPath = path26.join(root, "skills");
31772
+ var skillsPath = path28.join(root, "skills");
31234
31773
  var logger = createLogger({
31235
31774
  appLog: () => {
31236
31775
  return;
@@ -31249,7 +31788,7 @@ var warnStaleSources = () => {
31249
31788
  logger.warn(EVENT.hooks, {
31250
31789
  boundary: "stale_sources",
31251
31790
  reason: "workit sources changed after plugin load; restart the session for latest behavior",
31252
- files: changed.map((file) => path26.basename(file))
31791
+ files: changed.map((file) => path28.basename(file))
31253
31792
  });
31254
31793
  } catch {}
31255
31794
  };
@@ -31279,7 +31818,7 @@ var plugin = async ({ client, directory }) => {
31279
31818
  const dispatches = new Map;
31280
31819
  try {
31281
31820
  logger.info(EVENT.initialization, { host: "opencode", plugin_root: root });
31282
- logger.info(EVENT.provenance, loadProvenance(logger, path26.join(packageRoot2(), "package.json")));
31821
+ logger.info(EVENT.provenance, loadProvenance(logger, path28.join(packageRoot2(), "package.json")));
31283
31822
  } catch (error) {
31284
31823
  logger.warn(EVENT.hooks, { boundary: "initialization", ...errorDetail(error) });
31285
31824
  }
@@ -32213,7 +32752,7 @@ var normalizeQuestionAnswers = (answers) => {
32213
32752
 
32214
32753
  // packages/workit-opencode/src/v2/registry.ts
32215
32754
  import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
32216
- import path27 from "node:path";
32755
+ import path29 from "node:path";
32217
32756
  var parseFrontmatter = (markdown) => {
32218
32757
  const match = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
32219
32758
  if (!match)
@@ -32233,7 +32772,7 @@ var parseFrontmatter = (markdown) => {
32233
32772
  };
32234
32773
  };
32235
32774
  var registerSkills = async (ctx) => {
32236
- const skillsDir = path27.join(assetsRoot(), "skills");
32775
+ const skillsDir = path29.join(assetsRoot(), "skills");
32237
32776
  if (!existsSync13(skillsDir))
32238
32777
  return new Set;
32239
32778
  const registered = new Set;
@@ -32242,7 +32781,7 @@ var registerSkills = async (ctx) => {
32242
32781
  for (const id of WORKIT_METHOD_SKILLS) {
32243
32782
  if (existing.has(id))
32244
32783
  continue;
32245
- const file = path27.join(skillsDir, id, "SKILL.md");
32784
+ const file = path29.join(skillsDir, id, "SKILL.md");
32246
32785
  if (!existsSync13(file))
32247
32786
  continue;
32248
32787
  const parsed = parseFrontmatter(readFileSync12(file, "utf8"));