@brainervirus/workit-opencode 2.5.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
  });
@@ -28959,6 +29481,11 @@ function cursorHooksEntry(_packageDir) {
28959
29481
  };
28960
29482
  }
28961
29483
 
29484
+ // packages/workit-core/src/core/host-install.ts
29485
+ var CLAUDE_MARKETPLACE_NAME = "workit";
29486
+ var CLAUDE_PLUGIN_ID = `workit@${CLAUDE_MARKETPLACE_NAME}`;
29487
+ var CLAUDE_SCOPES = new Set(["user", "project", "local"]);
29488
+
28962
29489
  // packages/workit-core/src/core/doctor.ts
28963
29490
  var canonicalCursorHook = cursorHooksEntry("").command;
28964
29491
  var INSTALLER_REQUIRED = new Set([
@@ -28995,7 +29522,7 @@ var readFileSafe = (p) => {
28995
29522
  };
28996
29523
  function applyWorkspaceBranchPolicy(opts) {
28997
29524
  const { workspace_root, env = process.env } = opts;
28998
- const dir = path17.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29525
+ const dir = path19.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
28999
29526
  const {
29000
29527
  status,
29001
29528
  path: wsPath,
@@ -29007,7 +29534,7 @@ function applyWorkspaceBranchPolicy(opts) {
29007
29534
  if (status === "malformed" || status === "invalid")
29008
29535
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
29009
29536
  const detection = detectBranchPolicy(workspace_root);
29010
- const name = String(env.WORKFLOW_BP_NAME ?? path17.basename(workspace_root));
29537
+ const name = String(env.WORKFLOW_BP_NAME ?? path19.basename(workspace_root));
29011
29538
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
29012
29539
  const policy = {
29013
29540
  preset: detection.preset,
@@ -29056,7 +29583,7 @@ function applyWorkspaceBranchPolicy(opts) {
29056
29583
  ok: false,
29057
29584
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
29058
29585
  };
29059
- mkdirSync5(path17.dirname(wsPath), { recursive: true });
29586
+ mkdirSync5(path19.dirname(wsPath), { recursive: true });
29060
29587
  const tmp = `${wsPath}.${process.pid}.tmp`;
29061
29588
  try {
29062
29589
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -29088,8 +29615,8 @@ function applyWorkspaceBranchPolicy(opts) {
29088
29615
  }
29089
29616
 
29090
29617
  // packages/workit-core/src/core/youtrack.ts
29091
- import fs9 from "node:fs";
29092
- import path18 from "node:path";
29618
+ import fs11 from "node:fs";
29619
+ import path20 from "node:path";
29093
29620
 
29094
29621
  // packages/workit-core/src/core/templates.ts
29095
29622
  var repoRoot2 = assetRoot();
@@ -29097,22 +29624,22 @@ var repoRoot2 = assetRoot();
29097
29624
  // packages/workit-core/src/core/youtrack.ts
29098
29625
  var ISSUE_RE = /^[A-Z]+-\d+$/;
29099
29626
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
29100
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
29101
- 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");
29102
29629
  var youTrackTokenModeOk = (p) => {
29103
29630
  if (process.platform === "win32")
29104
29631
  return true;
29105
- const mode = fs9.statSync(p).mode & 511;
29632
+ const mode = fs11.statSync(p).mode & 511;
29106
29633
  return mode === 384;
29107
29634
  };
29108
29635
  function readYouTrackConfig(required) {
29109
29636
  const cfgPath = youTrackConfigPath();
29110
- if (!fs9.existsSync(cfgPath)) {
29637
+ if (!fs11.existsSync(cfgPath)) {
29111
29638
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
29112
29639
  }
29113
29640
  let parsed;
29114
29641
  try {
29115
- parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
29642
+ parsed = JSON.parse(fs11.readFileSync(cfgPath, "utf8"));
29116
29643
  } catch {
29117
29644
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
29118
29645
  }
@@ -29123,11 +29650,11 @@ function readYouTrackConfig(required) {
29123
29650
  }
29124
29651
  var readYouTrackContextConfig = () => {
29125
29652
  const cfgPath = youTrackReadOnlyConfigPath();
29126
- if (!fs9.existsSync(cfgPath))
29653
+ if (!fs11.existsSync(cfgPath))
29127
29654
  return { data: {} };
29128
29655
  let parsed;
29129
29656
  try {
29130
- parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
29657
+ parsed = JSON.parse(fs11.readFileSync(cfgPath, "utf8"));
29131
29658
  } catch {
29132
29659
  return { error: "YouTrack configuration is unavailable" };
29133
29660
  }
@@ -29166,12 +29693,12 @@ var youTrackToken = () => {
29166
29693
  if ("error" in loaded)
29167
29694
  return loaded;
29168
29695
  const tokenFile = String(loaded.config.tokenFile ?? "");
29169
- const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
29170
- 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))
29171
29698
  return { error: "missing youtrack.token" };
29172
29699
  if (!youTrackTokenModeOk(tokenPath))
29173
29700
  return { error: "youtrack.token mode must be 0600" };
29174
- const token = fs9.readFileSync(tokenPath, "utf8").trim();
29701
+ const token = fs11.readFileSync(tokenPath, "utf8").trim();
29175
29702
  if (!token)
29176
29703
  return { error: "empty token file" };
29177
29704
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -29217,7 +29744,7 @@ function youTrackTokenCreateUrl() {
29217
29744
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
29218
29745
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
29219
29746
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
29220
- 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"));
29221
29748
  const tab = String(defaults.profileTab ?? "account-security");
29222
29749
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
29223
29750
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -29226,7 +29753,7 @@ function youTrackTokenCreateUrl() {
29226
29753
  tokenName: name,
29227
29754
  tokenDescription: desc,
29228
29755
  scopes,
29229
- tokenFile: path18.resolve(tokenFile),
29756
+ tokenFile: path20.resolve(tokenFile),
29230
29757
  createUrl,
29231
29758
  docsUrl,
29232
29759
  prefillSupported: false,
@@ -29272,9 +29799,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
29272
29799
  } catch {
29273
29800
  continue;
29274
29801
  }
29275
- if (!fs9.existsSync(full))
29802
+ if (!fs11.existsSync(full))
29276
29803
  continue;
29277
- const text = fs9.readFileSync(full, "utf8");
29804
+ const text = fs11.readFileSync(full, "utf8");
29278
29805
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
29279
29806
  if (m)
29280
29807
  return m[1];
@@ -29425,7 +29952,7 @@ var fetchYouTrackIssueBody = async (issue, creds = youTrackToken, request = youT
29425
29952
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
29426
29953
  var readJson = (p) => {
29427
29954
  try {
29428
- const parsed = JSON.parse(fs10.readFileSync(p, "utf8"));
29955
+ const parsed = JSON.parse(fs12.readFileSync(p, "utf8"));
29429
29956
  return isConfigObject(parsed) ? parsed : null;
29430
29957
  } catch {
29431
29958
  return null;
@@ -29437,7 +29964,7 @@ var resolveInitProvider = (cwd) => {
29437
29964
  return env.toLowerCase();
29438
29965
  const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
29439
29966
  try {
29440
- const out = spawnSync6("git", ["remote", "get-url", "origin"], {
29967
+ const out = spawnSync8("git", ["remote", "get-url", "origin"], {
29441
29968
  cwd: root,
29442
29969
  encoding: "utf8"
29443
29970
  });
@@ -29455,7 +29982,7 @@ var youtrackJsonContent = (dir) => {
29455
29982
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
29456
29983
  return {
29457
29984
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
29458
- 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"),
29459
29986
  ...meetingIssue ? {
29460
29987
  meetingIssue,
29461
29988
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -29485,19 +30012,19 @@ var vcsJsonContent = () => {
29485
30012
  };
29486
30013
  function initApplyData(action, env = process.env) {
29487
30014
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29488
- fs10.mkdirSync(dir, { recursive: true });
30015
+ fs12.mkdirSync(dir, { recursive: true });
29489
30016
  switch (action) {
29490
30017
  case "youtrack_json": {
29491
- const out = path19.join(dir, "youtrack.json");
29492
- 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) + `
29493
30020
  `, "utf8");
29494
30021
  return { action, ok: true, path: out };
29495
30022
  }
29496
30023
  case "youtrack_token_placeholder": {
29497
- const p = path19.join(dir, "youtrack.token");
30024
+ const p = path21.join(dir, "youtrack.token");
29498
30025
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
29499
30026
  `, 384) === "preserved";
29500
- const abs = path19.resolve(p);
30027
+ const abs = path21.resolve(p);
29501
30028
  return {
29502
30029
  action,
29503
30030
  ok: true,
@@ -29509,14 +30036,14 @@ function initApplyData(action, env = process.env) {
29509
30036
  };
29510
30037
  }
29511
30038
  case "youtrack_scaffold": {
29512
- const jsonOut = path19.join(dir, "youtrack.json");
29513
- const tokenOut = path19.join(dir, "youtrack.token");
29514
- 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) + `
29515
30042
  `, "utf8");
29516
30043
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
29517
30044
  `, 384) === "preserved";
29518
- const configPath = path19.resolve(jsonOut);
29519
- const tokenPath = path19.resolve(tokenOut);
30045
+ const configPath = path21.resolve(jsonOut);
30046
+ const tokenPath = path21.resolve(tokenOut);
29520
30047
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
29521
30048
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
29522
30049
  let tokenCreate = {};
@@ -29568,10 +30095,10 @@ function initApplyData(action, env = process.env) {
29568
30095
  }
29569
30096
  }
29570
30097
  case "vcs_scaffold": {
29571
- const jsonOut = path19.join(dir, "vcs.json");
29572
- 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) + `
29573
30100
  `, "utf8");
29574
- const configPath = path19.resolve(jsonOut);
30101
+ const configPath = path21.resolve(jsonOut);
29575
30102
  const prev = process.env.WORKFLOW_VCS_CONFIG;
29576
30103
  process.env.WORKFLOW_VCS_CONFIG = configPath;
29577
30104
  try {
@@ -29690,7 +30217,7 @@ var executeInitApply = (args, directory, runtime) => {
29690
30217
  protectedNames: args.branch_policy_protected
29691
30218
  }, current);
29692
30219
  writeConfig(next);
29693
- 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 }));
29694
30221
  }
29695
30222
  const env = Object.fromEntries(Object.entries({
29696
30223
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -29743,12 +30270,12 @@ function createRepoTools(runtime = initApplyRuntime) {
29743
30270
 
29744
30271
  // packages/workit-opencode/src/tools/context.ts
29745
30272
  import { realpathSync as realpathSync10 } from "node:fs";
29746
- import path23 from "node:path";
30273
+ import path25 from "node:path";
29747
30274
 
29748
30275
  // packages/workit-core/src/core/pr-create.ts
29749
- import fs11 from "node:fs";
29750
- import path21 from "node:path";
29751
- 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";
29752
30279
  // packages/workit-core/src/forge/pr-body.ts
29753
30280
  function parseGhIssue(value) {
29754
30281
  const m = /issues\/(\d+)/.exec(value);
@@ -29758,13 +30285,13 @@ function parseGhIssue(value) {
29758
30285
  // packages/workit-core/src/core/pr-create.ts
29759
30286
  function whichOnPath(tool) {
29760
30287
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
29761
- for (const dir of (process.env.PATH ?? "").split(path21.delimiter)) {
30288
+ for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
29762
30289
  if (!dir)
29763
30290
  continue;
29764
30291
  for (const name of names) {
29765
- const candidate = path21.join(dir, name);
30292
+ const candidate = path23.join(dir, name);
29766
30293
  try {
29767
- fs11.accessSync(candidate, fs11.constants.X_OK);
30294
+ fs13.accessSync(candidate, fs13.constants.X_OK);
29768
30295
  return candidate;
29769
30296
  } catch {}
29770
30297
  }
@@ -29773,7 +30300,7 @@ function whichOnPath(tool) {
29773
30300
  }
29774
30301
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
29775
30302
  var pushRemote = (cwd) => {
29776
- const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
30303
+ const result = spawnSync9("git", ["remote", "get-url", "--push", "--all", "origin"], {
29777
30304
  cwd,
29778
30305
  encoding: "utf8"
29779
30306
  });
@@ -29784,9 +30311,9 @@ var pushRemote = (cwd) => {
29784
30311
  };
29785
30312
 
29786
30313
  // packages/workit-core/src/core/repo-context.ts
29787
- import { spawnSync as spawnSync8 } from "node:child_process";
30314
+ import { spawnSync as spawnSync10 } from "node:child_process";
29788
30315
  import { existsSync as existsSync11, readFileSync as readFileSync10, readdirSync as readdirSync7 } from "node:fs";
29789
- import path22 from "node:path";
30316
+ import path24 from "node:path";
29790
30317
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
29791
30318
  try {
29792
30319
  gitRevisionParts(value);
@@ -29813,7 +30340,7 @@ var invalidRange = (cwd) => ({
29813
30340
  cwd
29814
30341
  });
29815
30342
  var runGit = (cwd, args) => {
29816
- const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
30343
+ const result = spawnSync10("git", args, { cwd, encoding: "utf8" });
29817
30344
  return {
29818
30345
  stdout: (result.stdout ?? "").trimEnd(),
29819
30346
  stderr: (result.stderr ?? "").trimEnd(),
@@ -29920,7 +30447,7 @@ var findPrTemplate = (cwd) => {
29920
30447
  ]) {
29921
30448
  const parts = rel.split("/");
29922
30449
  const base = parts[parts.length - 1];
29923
- 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;
29924
30451
  const actual = probeCaseInsensitive(dir, base);
29925
30452
  if (actual)
29926
30453
  return [...parts.slice(0, -1), actual].join("/");
@@ -29951,7 +30478,7 @@ var diffStatForRange = (cwd, range) => {
29951
30478
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
29952
30479
  };
29953
30480
  var packageScripts = (cwd, keys) => {
29954
- const pkgPath = path22.join(cwd, "package.json");
30481
+ const pkgPath = path24.join(cwd, "package.json");
29955
30482
  if (!existsSync11(pkgPath))
29956
30483
  return [];
29957
30484
  try {
@@ -29977,9 +30504,9 @@ var documentationFiles = (cwd) => {
29977
30504
  if (entry.isDirectory()) {
29978
30505
  if (excluded.has(entry.name))
29979
30506
  continue;
29980
- walk(path22.join(dir, entry.name), depth + 1);
30507
+ walk(path24.join(dir, entry.name), depth + 1);
29981
30508
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
29982
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
30509
+ matches.push("./" + path24.relative(cwd, path24.join(dir, entry.name)).replaceAll("\\", "/"));
29983
30510
  }
29984
30511
  }
29985
30512
  };
@@ -29995,7 +30522,7 @@ var readTrimmed2 = (file, maxLines) => {
29995
30522
  `);
29996
30523
  };
29997
30524
  function prReadyContext(root, range) {
29998
- const cwd = path22.resolve(repoRoot3(root));
30525
+ const cwd = path24.resolve(repoRoot3(root));
29999
30526
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30000
30527
  return invalidRange(cwd);
30001
30528
  let stdout = "";
@@ -30053,7 +30580,7 @@ ${status.stderr}` : "");
30053
30580
  stdout += `template_path: ${template}
30054
30581
 
30055
30582
  `;
30056
- stdout += readTrimmed2(path22.join(cwd, template), 220);
30583
+ stdout += readTrimmed2(path24.join(cwd, template), 220);
30057
30584
  } else {
30058
30585
  stdout += `template_path: none
30059
30586
 
@@ -30063,7 +30590,7 @@ ${status.stderr}` : "");
30063
30590
  stdout += `
30064
30591
  `;
30065
30592
  stdout += printSection("Recent Validation Signals");
30066
- if (existsSync11(path22.join(cwd, "package.json"))) {
30593
+ if (existsSync11(path24.join(cwd, "package.json"))) {
30067
30594
  stdout += `package.json detected. Common scripts:
30068
30595
  `;
30069
30596
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -30072,7 +30599,7 @@ ${status.stderr}` : "");
30072
30599
  `) + `
30073
30600
  `;
30074
30601
  }
30075
- 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"))) {
30076
30603
  stdout += `Rust project detected.
30077
30604
  `;
30078
30605
  }
@@ -30105,7 +30632,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
30105
30632
  - Apply with the host-native editor, preserving the repository changelog conventions.
30106
30633
  - If Unreleased already has duplicate category headings, normalize_only first.`;
30107
30634
  function changelogContext(root, range) {
30108
- const cwd = path22.resolve(repoRoot3(root));
30635
+ const cwd = path24.resolve(repoRoot3(root));
30109
30636
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30110
30637
  return invalidRange(cwd);
30111
30638
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -30123,7 +30650,7 @@ function changelogContext(root, range) {
30123
30650
  stdout += CHANGELOG_RULES + `
30124
30651
  `;
30125
30652
  stdout += printSection("Existing CHANGELOG.md");
30126
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
30653
+ const changelogPath = path24.join(cwd, "CHANGELOG.md");
30127
30654
  if (existsSync11(changelogPath)) {
30128
30655
  stdout += readTrimmed2(changelogPath, 260) + `
30129
30656
  `;
@@ -30143,7 +30670,7 @@ function changelogContext(root, range) {
30143
30670
  return { stdout, stderr: "", exitCode: 0, cwd };
30144
30671
  }
30145
30672
  function docsRefreshContext(root, range) {
30146
- const cwd = path22.resolve(repoRoot3(root));
30673
+ const cwd = path24.resolve(repoRoot3(root));
30147
30674
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30148
30675
  return invalidRange(cwd);
30149
30676
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -30165,7 +30692,7 @@ function docsRefreshContext(root, range) {
30165
30692
  `) + `
30166
30693
  `;
30167
30694
  stdout += printSection("README Preview");
30168
- const readme = path22.join(cwd, "README.md");
30695
+ const readme = path24.join(cwd, "README.md");
30169
30696
  if (existsSync11(readme)) {
30170
30697
  stdout += readTrimmed2(readme, 220) + `
30171
30698
  `;
@@ -30174,7 +30701,7 @@ function docsRefreshContext(root, range) {
30174
30701
  `;
30175
30702
  }
30176
30703
  stdout += printSection("Package Scripts");
30177
- const pkgPath = path22.join(cwd, "package.json");
30704
+ const pkgPath = path24.join(cwd, "package.json");
30178
30705
  if (existsSync11(pkgPath)) {
30179
30706
  try {
30180
30707
  const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
@@ -30188,7 +30715,7 @@ function docsRefreshContext(root, range) {
30188
30715
  return { stdout, stderr: "", exitCode: 0, cwd };
30189
30716
  }
30190
30717
  function releaseNotesContext(root, rangeOrTag) {
30191
- const cwd = path22.resolve(repoRoot3(root));
30718
+ const cwd = path24.resolve(repoRoot3(root));
30192
30719
  if (!rangeOrTag) {
30193
30720
  return { stdout: "", stderr: `ERROR: release tag or range required
30194
30721
  `, exitCode: 1, cwd };
@@ -30226,7 +30753,7 @@ function releaseNotesContext(root, rangeOrTag) {
30226
30753
  `;
30227
30754
  stdout += printSection("Existing Release Files");
30228
30755
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
30229
- if (existsSync11(path22.join(cwd, rel)))
30756
+ if (existsSync11(path24.join(cwd, rel)))
30230
30757
  stdout += rel + `
30231
30758
  `;
30232
30759
  }
@@ -30254,7 +30781,7 @@ function releaseNotesContext(root, rangeOrTag) {
30254
30781
  }
30255
30782
 
30256
30783
  // packages/workit-core/src/core/tracker-issues.ts
30257
- import { spawnSync as spawnSync9 } from "node:child_process";
30784
+ import { spawnSync as spawnSync11 } from "node:child_process";
30258
30785
  var repoPathFromRemote = (remote) => {
30259
30786
  let rest = (remote || "").trim().replace(/\/+$/, "");
30260
30787
  if (!rest)
@@ -30275,7 +30802,7 @@ var defaultCli = (bin, available) => {
30275
30802
  if (!available)
30276
30803
  return null;
30277
30804
  return (args, root) => {
30278
- const out = spawnSync9(bin, args, {
30805
+ const out = spawnSync11(bin, args, {
30279
30806
  cwd: root,
30280
30807
  encoding: "utf8",
30281
30808
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -30358,10 +30885,6 @@ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\
30358
30885
  var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
30359
30886
  var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
30360
30887
 
30361
- // packages/workit-core/src/git/rev.ts
30362
- var MAX_BUFFER = 256 * 1024 * 1024;
30363
- var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
30364
-
30365
30888
  // packages/workit-core/src/ledger.ts
30366
30889
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
30367
30890
  var FAILING = new Set(["failed", "blocked"]);
@@ -30473,7 +30996,7 @@ var createContextTool = () => tool({
30473
30996
  try {
30474
30997
  const payload = parsed.data.payload;
30475
30998
  const cwd = payload.cwd;
30476
- root = realpathSync10(cwd ? path23.resolve(context.directory, cwd) : context.directory);
30999
+ root = realpathSync10(cwd ? path25.resolve(context.directory, cwd) : context.directory);
30477
31000
  } catch {
30478
31001
  return JSON.stringify(failure2("invalid_input", "action target directory cannot be resolved", {
30479
31002
  outcome: "not_started"
@@ -30485,14 +31008,14 @@ var createContextTool = () => tool({
30485
31008
 
30486
31009
  // packages/workit-opencode/src/shared/session.ts
30487
31010
  import { realpathSync as realpathSync11 } from "node:fs";
30488
- import path24 from "node:path";
31011
+ import path26 from "node:path";
30489
31012
  var sameWorkspace = (expected, observed) => {
30490
31013
  if (typeof observed !== "string" || !observed)
30491
31014
  return false;
30492
31015
  try {
30493
31016
  return realpathSync11(expected) === realpathSync11(observed);
30494
31017
  } catch {
30495
- return path24.resolve(expected) === path24.resolve(observed);
31018
+ return path26.resolve(expected) === path26.resolve(observed);
30496
31019
  }
30497
31020
  };
30498
31021
 
@@ -30994,7 +31517,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
30994
31517
  };
30995
31518
  });
30996
31519
  // packages/workit-core/src/hooks/context.ts
30997
- import path25 from "node:path";
31520
+ import path27 from "node:path";
30998
31521
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
30999
31522
  var newestFirst = (left, right) => right.updatedAt.localeCompare(left.updatedAt);
31000
31523
  var sessionTaskEntry = (entries, session) => entries.filter((entry) => entry.status !== "closed" && boundTo(entry, session)).toSorted(newestFirst)[0] ?? null;
@@ -31019,6 +31542,19 @@ var filesKey = (files) => {
31019
31542
  return signatures.some((item) => item !== "-" && racySignature(item)) ? null : signatures.join("\x00");
31020
31543
  };
31021
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
+ };
31022
31558
  var cache = new Map;
31023
31559
  function sessionCompactContext(store, session, context, selection = "session-bound") {
31024
31560
  const listed = store.listTaskIndex();
@@ -31042,18 +31578,26 @@ function entryCompactContext(store, entry, session, context) {
31042
31578
  capabilities: context.capabilities
31043
31579
  });
31044
31580
  const hit = cache.get(slot);
31045
- 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)))
31046
31582
  return hit.value;
31047
31583
  const task = store.readTask(entry.id);
31048
31584
  if (!task.ok)
31049
31585
  return null;
31050
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path25.resolve(store.root, ref.path)] : []));
31051
- const observed = filesKey(files);
31052
- 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);
31053
31591
  if (!compact.ok)
31054
31592
  return null;
31055
31593
  cache.delete(slot);
31056
- 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
+ });
31057
31601
  if (cache.size > CACHE_LIMIT)
31058
31602
  cache.delete(cache.keys().next().value);
31059
31603
  return compact.data;
@@ -31225,7 +31769,7 @@ var loadProvenance = (logger, pkgUrl) => {
31225
31769
 
31226
31770
  // packages/workit-opencode/src/v1/server.ts
31227
31771
  var root = assetsRoot();
31228
- var skillsPath = path26.join(root, "skills");
31772
+ var skillsPath = path28.join(root, "skills");
31229
31773
  var logger = createLogger({
31230
31774
  appLog: () => {
31231
31775
  return;
@@ -31244,7 +31788,7 @@ var warnStaleSources = () => {
31244
31788
  logger.warn(EVENT.hooks, {
31245
31789
  boundary: "stale_sources",
31246
31790
  reason: "workit sources changed after plugin load; restart the session for latest behavior",
31247
- files: changed.map((file) => path26.basename(file))
31791
+ files: changed.map((file) => path28.basename(file))
31248
31792
  });
31249
31793
  } catch {}
31250
31794
  };
@@ -31274,7 +31818,7 @@ var plugin = async ({ client, directory }) => {
31274
31818
  const dispatches = new Map;
31275
31819
  try {
31276
31820
  logger.info(EVENT.initialization, { host: "opencode", plugin_root: root });
31277
- logger.info(EVENT.provenance, loadProvenance(logger, path26.join(packageRoot2(), "package.json")));
31821
+ logger.info(EVENT.provenance, loadProvenance(logger, path28.join(packageRoot2(), "package.json")));
31278
31822
  } catch (error) {
31279
31823
  logger.warn(EVENT.hooks, { boundary: "initialization", ...errorDetail(error) });
31280
31824
  }
@@ -32208,7 +32752,7 @@ var normalizeQuestionAnswers = (answers) => {
32208
32752
 
32209
32753
  // packages/workit-opencode/src/v2/registry.ts
32210
32754
  import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
32211
- import path27 from "node:path";
32755
+ import path29 from "node:path";
32212
32756
  var parseFrontmatter = (markdown) => {
32213
32757
  const match = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
32214
32758
  if (!match)
@@ -32228,7 +32772,7 @@ var parseFrontmatter = (markdown) => {
32228
32772
  };
32229
32773
  };
32230
32774
  var registerSkills = async (ctx) => {
32231
- const skillsDir = path27.join(assetsRoot(), "skills");
32775
+ const skillsDir = path29.join(assetsRoot(), "skills");
32232
32776
  if (!existsSync13(skillsDir))
32233
32777
  return new Set;
32234
32778
  const registered = new Set;
@@ -32237,7 +32781,7 @@ var registerSkills = async (ctx) => {
32237
32781
  for (const id of WORKIT_METHOD_SKILLS) {
32238
32782
  if (existing.has(id))
32239
32783
  continue;
32240
- const file = path27.join(skillsDir, id, "SKILL.md");
32784
+ const file = path29.join(skillsDir, id, "SKILL.md");
32241
32785
  if (!existsSync13(file))
32242
32786
  continue;
32243
32787
  const parsed = parseFrontmatter(readFileSync12(file, "utf8"));