@brainervirus/workit-cursor 8.23.0 → 8.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +1177 -303
- package/dist/mcp-server.js +972 -147
- package/dist/workit-hook.js +1151 -304
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +1 -1
- package/skills/workit-implement/SKILL.md +5 -5
- package/skills/workit-review/SKILL.md +3 -0
- package/skills/workit-ship/SKILL.md +1 -2
package/dist/workit-hook.js
CHANGED
|
@@ -6739,8 +6739,8 @@ function requirementId(input) {
|
|
|
6739
6739
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
6740
6740
|
|
|
6741
6741
|
// packages/workit-core/src/core/policy/derive.ts
|
|
6742
|
-
import { existsSync as
|
|
6743
|
-
import
|
|
6742
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
6743
|
+
import path11 from "node:path";
|
|
6744
6744
|
|
|
6745
6745
|
// packages/workit-core/src/git/rev.ts
|
|
6746
6746
|
import { spawnSync } from "node:child_process";
|
|
@@ -6903,6 +6903,124 @@ function resolveRef(cwd, ref) {
|
|
|
6903
6903
|
return null;
|
|
6904
6904
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
6905
6905
|
}
|
|
6906
|
+
var LANE_DIFF = [
|
|
6907
|
+
"-c",
|
|
6908
|
+
"core.quotePath=false",
|
|
6909
|
+
"diff",
|
|
6910
|
+
"--no-color",
|
|
6911
|
+
"--no-ext-diff",
|
|
6912
|
+
"--no-textconv",
|
|
6913
|
+
"--no-renames"
|
|
6914
|
+
];
|
|
6915
|
+
var laneRange = (fork, head) => {
|
|
6916
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
6917
|
+
return null;
|
|
6918
|
+
return head === null ? [fork] : [fork, head];
|
|
6919
|
+
};
|
|
6920
|
+
function diffNumstat(cwd, fork, head) {
|
|
6921
|
+
const range = laneRange(fork, head);
|
|
6922
|
+
if (!range)
|
|
6923
|
+
return null;
|
|
6924
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
6925
|
+
if (!run.ok)
|
|
6926
|
+
return null;
|
|
6927
|
+
const out = [];
|
|
6928
|
+
for (const record of run.stdout.split("\x00")) {
|
|
6929
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
6930
|
+
if (!match)
|
|
6931
|
+
continue;
|
|
6932
|
+
const binary = match[1] === "-";
|
|
6933
|
+
out.push({
|
|
6934
|
+
path: match[3],
|
|
6935
|
+
added: binary ? 0 : Number(match[1]),
|
|
6936
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
6937
|
+
binary
|
|
6938
|
+
});
|
|
6939
|
+
}
|
|
6940
|
+
return out;
|
|
6941
|
+
}
|
|
6942
|
+
function diffRaw(cwd, fork, head) {
|
|
6943
|
+
const range = laneRange(fork, head);
|
|
6944
|
+
if (!range)
|
|
6945
|
+
return null;
|
|
6946
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
6947
|
+
if (!run.ok)
|
|
6948
|
+
return null;
|
|
6949
|
+
const fields = run.stdout.split("\x00");
|
|
6950
|
+
const out = [];
|
|
6951
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
6952
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
6953
|
+
if (!meta)
|
|
6954
|
+
return null;
|
|
6955
|
+
out.push({
|
|
6956
|
+
path: fields[index + 1],
|
|
6957
|
+
oldMode: meta[1],
|
|
6958
|
+
newMode: meta[2],
|
|
6959
|
+
oldBlob: meta[3],
|
|
6960
|
+
newBlob: meta[4]
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6963
|
+
return out;
|
|
6964
|
+
}
|
|
6965
|
+
function readBlobs(cwd, ids) {
|
|
6966
|
+
const out = new Map;
|
|
6967
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
6968
|
+
if (!wanted.length)
|
|
6969
|
+
return out;
|
|
6970
|
+
const run = spawnSync("git", ["cat-file", "--batch"], {
|
|
6971
|
+
cwd,
|
|
6972
|
+
input: `${wanted.join(`
|
|
6973
|
+
`)}
|
|
6974
|
+
`,
|
|
6975
|
+
maxBuffer: MAX_BUFFER,
|
|
6976
|
+
timeout: GIT_TIMEOUTS.local,
|
|
6977
|
+
killSignal: "SIGKILL",
|
|
6978
|
+
windowsHide: true
|
|
6979
|
+
});
|
|
6980
|
+
if (run.status !== 0 || !run.stdout)
|
|
6981
|
+
return null;
|
|
6982
|
+
const bytes = run.stdout;
|
|
6983
|
+
let at = 0;
|
|
6984
|
+
for (const id of wanted) {
|
|
6985
|
+
const end = bytes.indexOf(10, at);
|
|
6986
|
+
if (end < 0)
|
|
6987
|
+
return null;
|
|
6988
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
6989
|
+
at = end + 1;
|
|
6990
|
+
if (header[1] === "missing")
|
|
6991
|
+
continue;
|
|
6992
|
+
const size = Number(header[2]);
|
|
6993
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
6994
|
+
return null;
|
|
6995
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
6996
|
+
at += size + 1;
|
|
6997
|
+
}
|
|
6998
|
+
return out;
|
|
6999
|
+
}
|
|
7000
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
7001
|
+
if (!safeArg(rev))
|
|
7002
|
+
return null;
|
|
7003
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
7004
|
+
if (!run.ok)
|
|
7005
|
+
return null;
|
|
7006
|
+
const out = new Map;
|
|
7007
|
+
for (const record of run.stdout.split("\x00")) {
|
|
7008
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
7009
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
7010
|
+
out.set(match[2], match[1]);
|
|
7011
|
+
}
|
|
7012
|
+
return out;
|
|
7013
|
+
}
|
|
7014
|
+
function fileAt(cwd, rev, file) {
|
|
7015
|
+
if (!safeArg(rev))
|
|
7016
|
+
return null;
|
|
7017
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
7018
|
+
return run.ok ? run.stdout : null;
|
|
7019
|
+
}
|
|
7020
|
+
function untrackedFiles(cwd) {
|
|
7021
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
7022
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
7023
|
+
}
|
|
6906
7024
|
var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
6907
7025
|
function currentBranch(cwd) {
|
|
6908
7026
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
@@ -7716,7 +7834,7 @@ var workspaceConfigSchema = object({
|
|
|
7716
7834
|
}).passthrough().optional(),
|
|
7717
7835
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
7718
7836
|
defaultEndpoint: unknown().optional(),
|
|
7719
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
7837
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
7720
7838
|
delegation: unknown().optional(),
|
|
7721
7839
|
trailers: boolean2().optional(),
|
|
7722
7840
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -10011,7 +10129,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
10011
10129
|
verdict: failing?.verdict ?? null,
|
|
10012
10130
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
10013
10131
|
};
|
|
10014
|
-
const
|
|
10132
|
+
const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
|
|
10133
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
10015
10134
|
return {
|
|
10016
10135
|
branch,
|
|
10017
10136
|
head,
|
|
@@ -10055,12 +10174,682 @@ var OBSERVED_ONLY = new Set([
|
|
|
10055
10174
|
...PR_ROW_TYPES
|
|
10056
10175
|
]);
|
|
10057
10176
|
|
|
10177
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
10178
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
10179
|
+
import path10 from "node:path";
|
|
10180
|
+
// packages/workit-core/src/forge/redact.ts
|
|
10181
|
+
var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
|
|
10182
|
+
var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
|
|
10183
|
+
var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
10184
|
+
var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
10185
|
+
|
|
10186
|
+
// packages/workit-core/src/git/ops.ts
|
|
10187
|
+
var COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
10188
|
+
|
|
10189
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
10190
|
+
function patternRegex(dir, pattern) {
|
|
10191
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
10192
|
+
return null;
|
|
10193
|
+
const anchored = pattern.includes("/");
|
|
10194
|
+
const body = pattern.replace(/^\//u, "");
|
|
10195
|
+
let source = "";
|
|
10196
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
10197
|
+
const char = body[index];
|
|
10198
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
10199
|
+
const slash = body[index + 2] === "/";
|
|
10200
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
10201
|
+
index += slash ? 2 : 1;
|
|
10202
|
+
} else if (char === "*")
|
|
10203
|
+
source += "[^/]*";
|
|
10204
|
+
else if (char === "?")
|
|
10205
|
+
source += "[^/]";
|
|
10206
|
+
else if (char === "[") {
|
|
10207
|
+
const end = body.indexOf("]", index + 2);
|
|
10208
|
+
if (end < 0)
|
|
10209
|
+
return null;
|
|
10210
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
10211
|
+
source += `[${set}]`;
|
|
10212
|
+
index = end;
|
|
10213
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
10214
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
10215
|
+
index += 1;
|
|
10216
|
+
} else
|
|
10217
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
10218
|
+
}
|
|
10219
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
10220
|
+
try {
|
|
10221
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
10222
|
+
} catch {
|
|
10223
|
+
return null;
|
|
10224
|
+
}
|
|
10225
|
+
}
|
|
10226
|
+
function rulesOf(dir, text) {
|
|
10227
|
+
const out = [];
|
|
10228
|
+
for (const raw of text.split(`
|
|
10229
|
+
`)) {
|
|
10230
|
+
const line = raw.trim();
|
|
10231
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
10232
|
+
continue;
|
|
10233
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
10234
|
+
let generated = null;
|
|
10235
|
+
for (const attribute of attributes) {
|
|
10236
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
10237
|
+
generated = true;
|
|
10238
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
10239
|
+
generated = false;
|
|
10240
|
+
}
|
|
10241
|
+
if (generated === null)
|
|
10242
|
+
continue;
|
|
10243
|
+
const regex = patternRegex(dir, pattern);
|
|
10244
|
+
if (regex)
|
|
10245
|
+
out.push({ regex, generated });
|
|
10246
|
+
}
|
|
10247
|
+
return out;
|
|
10248
|
+
}
|
|
10249
|
+
function generatedByAttributes(files, paths) {
|
|
10250
|
+
const rules = [...files].map(([file, text]) => {
|
|
10251
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
10252
|
+
return {
|
|
10253
|
+
depth: dir ? dir.split("/").length : 0,
|
|
10254
|
+
rules: rulesOf(dir, text)
|
|
10255
|
+
};
|
|
10256
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
10257
|
+
const out = new Set;
|
|
10258
|
+
for (const file of paths) {
|
|
10259
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
10260
|
+
if (last?.generated)
|
|
10261
|
+
out.add(file);
|
|
10262
|
+
}
|
|
10263
|
+
return out;
|
|
10264
|
+
}
|
|
10265
|
+
|
|
10266
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
10267
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
10268
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
10269
|
+
var SYNTAXES = Object.fromEntries([
|
|
10270
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
10271
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
10272
|
+
ext,
|
|
10273
|
+
{ ...C_LIKE, triple: true }
|
|
10274
|
+
]),
|
|
10275
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
10276
|
+
...["css", "scss", "less"].map((ext) => [
|
|
10277
|
+
ext,
|
|
10278
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
10279
|
+
]),
|
|
10280
|
+
...["py", "pyi"].map((ext) => [
|
|
10281
|
+
ext,
|
|
10282
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
10283
|
+
]),
|
|
10284
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
10285
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
10286
|
+
ext,
|
|
10287
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
10288
|
+
])
|
|
10289
|
+
]);
|
|
10290
|
+
var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
|
|
10291
|
+
var extensionOf = (file) => {
|
|
10292
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
10293
|
+
const dot = name.lastIndexOf(".");
|
|
10294
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
10295
|
+
};
|
|
10296
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
10297
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
10298
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
10299
|
+
var WORD = /[\w$]/u;
|
|
10300
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
10301
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
10302
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
10303
|
+
var REGEX_KEYWORDS = new Set([
|
|
10304
|
+
"return",
|
|
10305
|
+
"typeof",
|
|
10306
|
+
"instanceof",
|
|
10307
|
+
"case",
|
|
10308
|
+
"do",
|
|
10309
|
+
"else",
|
|
10310
|
+
"in",
|
|
10311
|
+
"of",
|
|
10312
|
+
"new",
|
|
10313
|
+
"delete",
|
|
10314
|
+
"void",
|
|
10315
|
+
"throw",
|
|
10316
|
+
"yield",
|
|
10317
|
+
"await"
|
|
10318
|
+
]);
|
|
10319
|
+
function substanceOf(file, text) {
|
|
10320
|
+
const syntax = syntaxOf(file);
|
|
10321
|
+
if (!syntax)
|
|
10322
|
+
return plainSubstance(text);
|
|
10323
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
10324
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
10325
|
+
return plainSubstance(text);
|
|
10326
|
+
return lex(syntax, text);
|
|
10327
|
+
}
|
|
10328
|
+
function plainSubstance(text) {
|
|
10329
|
+
return text.split(`
|
|
10330
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
10331
|
+
`);
|
|
10332
|
+
}
|
|
10333
|
+
function lex(syntax, text) {
|
|
10334
|
+
const collapse = !syntax.indented;
|
|
10335
|
+
let out = "";
|
|
10336
|
+
let lineStart = 0;
|
|
10337
|
+
let kept = 0;
|
|
10338
|
+
let pendingSpace = false;
|
|
10339
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
10340
|
+
let index = 0;
|
|
10341
|
+
if (text.startsWith("#!")) {
|
|
10342
|
+
const end = text.indexOf(`
|
|
10343
|
+
`);
|
|
10344
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
10345
|
+
kept = out.length;
|
|
10346
|
+
index = end < 0 ? text.length : end;
|
|
10347
|
+
}
|
|
10348
|
+
const emitCode = (chunk) => {
|
|
10349
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
10350
|
+
const before = out[out.length - 1];
|
|
10351
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
10352
|
+
out += " ";
|
|
10353
|
+
}
|
|
10354
|
+
pendingSpace = false;
|
|
10355
|
+
out += chunk;
|
|
10356
|
+
};
|
|
10357
|
+
const emitVerbatim = (chunk) => {
|
|
10358
|
+
emitCode(chunk);
|
|
10359
|
+
kept = out.length;
|
|
10360
|
+
};
|
|
10361
|
+
const newline = () => {
|
|
10362
|
+
if (!collapse) {
|
|
10363
|
+
let end = out.length;
|
|
10364
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
10365
|
+
end -= 1;
|
|
10366
|
+
out = out.slice(0, end);
|
|
10367
|
+
}
|
|
10368
|
+
pendingSpace = false;
|
|
10369
|
+
if (out.length > lineStart) {
|
|
10370
|
+
out += `
|
|
10371
|
+
`;
|
|
10372
|
+
lineStart = out.length;
|
|
10373
|
+
}
|
|
10374
|
+
};
|
|
10375
|
+
const regexAllowed = () => {
|
|
10376
|
+
let end = out.length;
|
|
10377
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
10378
|
+
end -= 1;
|
|
10379
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
10380
|
+
return true;
|
|
10381
|
+
let start = end;
|
|
10382
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
10383
|
+
start -= 1;
|
|
10384
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
10385
|
+
};
|
|
10386
|
+
const scanString = (quote, escapes, multiline) => {
|
|
10387
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
10388
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
10389
|
+
let end = index + quote.length;
|
|
10390
|
+
while (end < text.length) {
|
|
10391
|
+
const char = text[end];
|
|
10392
|
+
if (escapes && char === "\\") {
|
|
10393
|
+
end += 2;
|
|
10394
|
+
continue;
|
|
10395
|
+
}
|
|
10396
|
+
if (text.startsWith(quote, end)) {
|
|
10397
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
10398
|
+
return false;
|
|
10399
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
10400
|
+
index = end + quote.length;
|
|
10401
|
+
return true;
|
|
10402
|
+
}
|
|
10403
|
+
if (char === `
|
|
10404
|
+
` && !multiline)
|
|
10405
|
+
return false;
|
|
10406
|
+
end += 1;
|
|
10407
|
+
}
|
|
10408
|
+
return false;
|
|
10409
|
+
};
|
|
10410
|
+
while (index < text.length) {
|
|
10411
|
+
const frame = frames[frames.length - 1];
|
|
10412
|
+
const char = text[index];
|
|
10413
|
+
if (frame.kind === "template") {
|
|
10414
|
+
if (char === "\\") {
|
|
10415
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
10416
|
+
index += 2;
|
|
10417
|
+
} else if (char === "`") {
|
|
10418
|
+
emitVerbatim(char);
|
|
10419
|
+
frames.pop();
|
|
10420
|
+
index += 1;
|
|
10421
|
+
} else if (text.startsWith("${", index)) {
|
|
10422
|
+
emitVerbatim("${");
|
|
10423
|
+
frames.push({ kind: "code", braces: 0 });
|
|
10424
|
+
index += 2;
|
|
10425
|
+
} else {
|
|
10426
|
+
out += char;
|
|
10427
|
+
kept = out.length;
|
|
10428
|
+
index += 1;
|
|
10429
|
+
}
|
|
10430
|
+
continue;
|
|
10431
|
+
}
|
|
10432
|
+
if (char === `
|
|
10433
|
+
`) {
|
|
10434
|
+
newline();
|
|
10435
|
+
index += 1;
|
|
10436
|
+
continue;
|
|
10437
|
+
}
|
|
10438
|
+
if (SPACE.test(char)) {
|
|
10439
|
+
if (collapse)
|
|
10440
|
+
pendingSpace = true;
|
|
10441
|
+
else
|
|
10442
|
+
out += char;
|
|
10443
|
+
index += 1;
|
|
10444
|
+
continue;
|
|
10445
|
+
}
|
|
10446
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
10447
|
+
if (lineOpener) {
|
|
10448
|
+
const end = text.indexOf(`
|
|
10449
|
+
`, index);
|
|
10450
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
10451
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
10452
|
+
emitVerbatim(comment);
|
|
10453
|
+
else
|
|
10454
|
+
pendingSpace = true;
|
|
10455
|
+
index = end < 0 ? text.length : end;
|
|
10456
|
+
continue;
|
|
10457
|
+
}
|
|
10458
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
10459
|
+
if (block) {
|
|
10460
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
10461
|
+
if (end < 0)
|
|
10462
|
+
return null;
|
|
10463
|
+
const comment = text.slice(index, end + block[1].length);
|
|
10464
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
10465
|
+
emitVerbatim(comment);
|
|
10466
|
+
else
|
|
10467
|
+
pendingSpace = true;
|
|
10468
|
+
if (comment.includes(`
|
|
10469
|
+
`))
|
|
10470
|
+
newline();
|
|
10471
|
+
index = end + block[1].length;
|
|
10472
|
+
continue;
|
|
10473
|
+
}
|
|
10474
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
10475
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
10476
|
+
return null;
|
|
10477
|
+
continue;
|
|
10478
|
+
}
|
|
10479
|
+
if (syntax.quotes.includes(char)) {
|
|
10480
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
10481
|
+
return null;
|
|
10482
|
+
continue;
|
|
10483
|
+
}
|
|
10484
|
+
if (syntax.literal?.includes(char)) {
|
|
10485
|
+
if (!scanString(char, false, false))
|
|
10486
|
+
return null;
|
|
10487
|
+
continue;
|
|
10488
|
+
}
|
|
10489
|
+
if (syntax.rawBacktick && char === "`") {
|
|
10490
|
+
if (!scanString("`", false, true))
|
|
10491
|
+
return null;
|
|
10492
|
+
continue;
|
|
10493
|
+
}
|
|
10494
|
+
if (syntax.js && char === "`") {
|
|
10495
|
+
emitVerbatim(char);
|
|
10496
|
+
frames.push({ kind: "template" });
|
|
10497
|
+
index += 1;
|
|
10498
|
+
continue;
|
|
10499
|
+
}
|
|
10500
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
10501
|
+
return null;
|
|
10502
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
10503
|
+
const lineEnd = text.indexOf(`
|
|
10504
|
+
`, index);
|
|
10505
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
10506
|
+
return null;
|
|
10507
|
+
}
|
|
10508
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
10509
|
+
let end = index + 1;
|
|
10510
|
+
let inClass = false;
|
|
10511
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
10512
|
+
if (text[end] === `
|
|
10513
|
+
`)
|
|
10514
|
+
return null;
|
|
10515
|
+
if (text[end] === "\\")
|
|
10516
|
+
end += 1;
|
|
10517
|
+
else if (text[end] === "[")
|
|
10518
|
+
inClass = true;
|
|
10519
|
+
else if (text[end] === "]")
|
|
10520
|
+
inClass = false;
|
|
10521
|
+
end += 1;
|
|
10522
|
+
}
|
|
10523
|
+
if (end >= text.length)
|
|
10524
|
+
return null;
|
|
10525
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
10526
|
+
index = end + 1;
|
|
10527
|
+
continue;
|
|
10528
|
+
}
|
|
10529
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
10530
|
+
if (char === "{")
|
|
10531
|
+
frame.braces += 1;
|
|
10532
|
+
else if (char === "}") {
|
|
10533
|
+
if (frame.braces === 0) {
|
|
10534
|
+
emitVerbatim(char);
|
|
10535
|
+
frames.pop();
|
|
10536
|
+
index += 1;
|
|
10537
|
+
continue;
|
|
10538
|
+
}
|
|
10539
|
+
frame.braces -= 1;
|
|
10540
|
+
}
|
|
10541
|
+
}
|
|
10542
|
+
emitCode(char);
|
|
10543
|
+
index += 1;
|
|
10544
|
+
}
|
|
10545
|
+
if (frames.length !== 1)
|
|
10546
|
+
return null;
|
|
10547
|
+
newline();
|
|
10548
|
+
return out;
|
|
10549
|
+
}
|
|
10550
|
+
function unbalanced(body) {
|
|
10551
|
+
let depth = 0;
|
|
10552
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
10553
|
+
if (part === "{")
|
|
10554
|
+
depth += 1;
|
|
10555
|
+
else if (part === "}")
|
|
10556
|
+
depth = Math.max(0, depth - 1);
|
|
10557
|
+
}
|
|
10558
|
+
return depth > 0;
|
|
10559
|
+
}
|
|
10560
|
+
function hasOpaqueLineBreak(text) {
|
|
10561
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
10562
|
+
}
|
|
10563
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
10564
|
+
function textOf(bytes) {
|
|
10565
|
+
if (bytes.includes(0))
|
|
10566
|
+
return null;
|
|
10567
|
+
try {
|
|
10568
|
+
return decoder.decode(bytes);
|
|
10569
|
+
} catch {
|
|
10570
|
+
return null;
|
|
10571
|
+
}
|
|
10572
|
+
}
|
|
10573
|
+
function cosmeticChange(file, before, after) {
|
|
10574
|
+
const left = textOf(before);
|
|
10575
|
+
const right = textOf(after);
|
|
10576
|
+
if (left === null || right === null)
|
|
10577
|
+
return false;
|
|
10578
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
10579
|
+
return false;
|
|
10580
|
+
const a = substanceOf(file, left);
|
|
10581
|
+
return a !== null && a === substanceOf(file, right);
|
|
10582
|
+
}
|
|
10583
|
+
|
|
10584
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
10585
|
+
var LANE_THRESHOLDS = {
|
|
10586
|
+
selfMaxLines: 100,
|
|
10587
|
+
selfMaxFiles: 5,
|
|
10588
|
+
fullMinLines: 500
|
|
10589
|
+
};
|
|
10590
|
+
var RISK_TOKENS = new Set([
|
|
10591
|
+
"auth",
|
|
10592
|
+
"authn",
|
|
10593
|
+
"authz",
|
|
10594
|
+
"oauth",
|
|
10595
|
+
"security",
|
|
10596
|
+
"permission",
|
|
10597
|
+
"permissions",
|
|
10598
|
+
"secret",
|
|
10599
|
+
"secrets",
|
|
10600
|
+
"crypto",
|
|
10601
|
+
"gate",
|
|
10602
|
+
"gates",
|
|
10603
|
+
"policy",
|
|
10604
|
+
"policies",
|
|
10605
|
+
"hook",
|
|
10606
|
+
"hooks",
|
|
10607
|
+
"husky",
|
|
10608
|
+
"lefthook",
|
|
10609
|
+
"release",
|
|
10610
|
+
"releases",
|
|
10611
|
+
"migration",
|
|
10612
|
+
"migrations",
|
|
10613
|
+
"migrate",
|
|
10614
|
+
"workflows"
|
|
10615
|
+
]);
|
|
10616
|
+
var RISK_FILES = new Set([
|
|
10617
|
+
".gitlab-ci.yml",
|
|
10618
|
+
"Jenkinsfile",
|
|
10619
|
+
"azure-pipelines.yml",
|
|
10620
|
+
".pre-commit-config.yaml",
|
|
10621
|
+
".workit/risk-paths"
|
|
10622
|
+
]);
|
|
10623
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
10624
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
10625
|
+
var LOCKFILES = new Set([
|
|
10626
|
+
"bun.lock",
|
|
10627
|
+
"bun.lockb",
|
|
10628
|
+
"package-lock.json",
|
|
10629
|
+
"npm-shrinkwrap.json",
|
|
10630
|
+
"yarn.lock",
|
|
10631
|
+
"pnpm-lock.yaml",
|
|
10632
|
+
"Cargo.lock",
|
|
10633
|
+
"go.sum",
|
|
10634
|
+
"poetry.lock",
|
|
10635
|
+
"uv.lock",
|
|
10636
|
+
"Pipfile.lock",
|
|
10637
|
+
"Gemfile.lock",
|
|
10638
|
+
"composer.lock"
|
|
10639
|
+
]);
|
|
10640
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
|
|
10641
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
10642
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
10643
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
|
|
10644
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
10645
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
10646
|
+
function kindOf(file) {
|
|
10647
|
+
if (file.binary)
|
|
10648
|
+
return "behavior";
|
|
10649
|
+
if (file.generated)
|
|
10650
|
+
return "generated";
|
|
10651
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
10652
|
+
return "lockfile";
|
|
10653
|
+
if (isDoc(file.path))
|
|
10654
|
+
return "doc";
|
|
10655
|
+
if (file.cosmetic)
|
|
10656
|
+
return "cosmetic";
|
|
10657
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
10658
|
+
}
|
|
10659
|
+
var globCache = new Map;
|
|
10660
|
+
function globMatch(pattern, file) {
|
|
10661
|
+
let regex = globCache.get(pattern);
|
|
10662
|
+
if (!regex) {
|
|
10663
|
+
let source = "";
|
|
10664
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
10665
|
+
const char = pattern[index];
|
|
10666
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
10667
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
10668
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
10669
|
+
} else if (char === "*")
|
|
10670
|
+
source += "[^/]*";
|
|
10671
|
+
else if (char === "?")
|
|
10672
|
+
source += "[^/]";
|
|
10673
|
+
else
|
|
10674
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
10675
|
+
}
|
|
10676
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
10677
|
+
globCache.set(pattern, regex);
|
|
10678
|
+
}
|
|
10679
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
10680
|
+
}
|
|
10681
|
+
function isRiskPath(file, extra = []) {
|
|
10682
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
10683
|
+
return true;
|
|
10684
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
10685
|
+
return true;
|
|
10686
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
10687
|
+
return false;
|
|
10688
|
+
return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
|
|
10689
|
+
}
|
|
10690
|
+
var parseRiskPaths = (text) => text.split(`
|
|
10691
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
10692
|
+
function computeLane(input) {
|
|
10693
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
10694
|
+
const risky = new Set(riskFiles);
|
|
10695
|
+
const kinds = input.files.map((file) => ({
|
|
10696
|
+
file,
|
|
10697
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
10698
|
+
}));
|
|
10699
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
10700
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
10701
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
10702
|
+
const result = (lane, reasons) => ({
|
|
10703
|
+
lane,
|
|
10704
|
+
reasons,
|
|
10705
|
+
lines,
|
|
10706
|
+
files: behavior.length,
|
|
10707
|
+
riskFiles
|
|
10708
|
+
});
|
|
10709
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
10710
|
+
if (input.riskTier === "high")
|
|
10711
|
+
return result("full", ["the task judgment records high risk"]);
|
|
10712
|
+
if (riskFiles.length)
|
|
10713
|
+
return result("full", [
|
|
10714
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
10715
|
+
]);
|
|
10716
|
+
if (!behavior.length)
|
|
10717
|
+
return result("none", [
|
|
10718
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
10719
|
+
]);
|
|
10720
|
+
if (lines >= fullMinLines)
|
|
10721
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
10722
|
+
if (lines > selfMaxLines)
|
|
10723
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
10724
|
+
if (behavior.length > selfMaxFiles)
|
|
10725
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
10726
|
+
if (opaque.length)
|
|
10727
|
+
return result("focused", [
|
|
10728
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
10729
|
+
]);
|
|
10730
|
+
return result("self", [
|
|
10731
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
10732
|
+
]);
|
|
10733
|
+
}
|
|
10734
|
+
function untrackedStat(cwd, file) {
|
|
10735
|
+
try {
|
|
10736
|
+
const bytes = readFileSync3(path10.join(cwd, file));
|
|
10737
|
+
if (bytes.includes(0))
|
|
10738
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
10739
|
+
const text = bytes.toString("utf8");
|
|
10740
|
+
if (hasOpaqueLineBreak(text))
|
|
10741
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
10742
|
+
const lines = text.split(`
|
|
10743
|
+
`).length - (text.endsWith(`
|
|
10744
|
+
`) ? 1 : 0);
|
|
10745
|
+
return { path: file, added: lines, deleted: 0 };
|
|
10746
|
+
} catch {
|
|
10747
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
10748
|
+
}
|
|
10749
|
+
}
|
|
10750
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
10751
|
+
var ABSENT = /^0+$/u;
|
|
10752
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
10753
|
+
const raw = diffRaw(cwd, fork, head);
|
|
10754
|
+
if (!raw)
|
|
10755
|
+
return null;
|
|
10756
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
10757
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
10758
|
+
if (!blobs)
|
|
10759
|
+
return null;
|
|
10760
|
+
const out = new Set;
|
|
10761
|
+
const opaque = new Set;
|
|
10762
|
+
for (const entry of candidates) {
|
|
10763
|
+
const before = blobs.get(entry.oldBlob);
|
|
10764
|
+
let after = blobs.get(entry.newBlob);
|
|
10765
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
10766
|
+
try {
|
|
10767
|
+
after = readFileSync3(path10.join(cwd, entry.path));
|
|
10768
|
+
} catch {
|
|
10769
|
+
after = undefined;
|
|
10770
|
+
}
|
|
10771
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
10772
|
+
out.add(entry.path);
|
|
10773
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
10774
|
+
opaque.add(entry.path);
|
|
10775
|
+
}
|
|
10776
|
+
return { cosmetic: out, opaque };
|
|
10777
|
+
}
|
|
10778
|
+
function generatedAt(cwd, fork, paths) {
|
|
10779
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
10780
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
10781
|
+
if (!files || !blobs)
|
|
10782
|
+
return new Set;
|
|
10783
|
+
const texts = new Map;
|
|
10784
|
+
for (const [file, id] of files) {
|
|
10785
|
+
const bytes = blobs.get(id);
|
|
10786
|
+
if (bytes)
|
|
10787
|
+
texts.set(file, bytes.toString("utf8"));
|
|
10788
|
+
}
|
|
10789
|
+
return generatedByAttributes(texts, paths);
|
|
10790
|
+
}
|
|
10791
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
10792
|
+
function laneFiles(cwd, fork, head) {
|
|
10793
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
10794
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
10795
|
+
if (!stats || !cosmetic)
|
|
10796
|
+
return null;
|
|
10797
|
+
const files = [
|
|
10798
|
+
...stats.map((entry) => ({
|
|
10799
|
+
...entry,
|
|
10800
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
10801
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
10802
|
+
})),
|
|
10803
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
10804
|
+
];
|
|
10805
|
+
const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
|
|
10806
|
+
return files.map((entry) => ({
|
|
10807
|
+
...entry,
|
|
10808
|
+
generated: generated.has(entry.path)
|
|
10809
|
+
}));
|
|
10810
|
+
}
|
|
10811
|
+
function branchLane(cwd, options) {
|
|
10812
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
10813
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
10814
|
+
const head = options.workingTree ? null : options.head;
|
|
10815
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
10816
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
10817
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
10818
|
+
if (!fork || !files) {
|
|
10819
|
+
const high = riskTier === "high";
|
|
10820
|
+
return {
|
|
10821
|
+
lane: high ? "full" : "focused",
|
|
10822
|
+
reasons: [
|
|
10823
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
10824
|
+
...high ? ["the task judgment records high risk"] : []
|
|
10825
|
+
],
|
|
10826
|
+
lines: 0,
|
|
10827
|
+
files: 0,
|
|
10828
|
+
riskFiles: [],
|
|
10829
|
+
base,
|
|
10830
|
+
head: options.head
|
|
10831
|
+
};
|
|
10832
|
+
}
|
|
10833
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
10834
|
+
return {
|
|
10835
|
+
...computeLane({
|
|
10836
|
+
files,
|
|
10837
|
+
riskTier,
|
|
10838
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
10839
|
+
}),
|
|
10840
|
+
base,
|
|
10841
|
+
head: options.head
|
|
10842
|
+
};
|
|
10843
|
+
}
|
|
10844
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
10845
|
+
|
|
10058
10846
|
// packages/workit-core/src/core/policy/derive.ts
|
|
10059
10847
|
var RULES = {
|
|
10060
10848
|
test: "check:test",
|
|
10061
10849
|
self: "verdict:self",
|
|
10062
10850
|
nonAuthor: "verdict:non-author",
|
|
10063
10851
|
verified: "verdict:verified",
|
|
10852
|
+
lane: "verdict:lane",
|
|
10064
10853
|
product: "decision:product",
|
|
10065
10854
|
plan: "plan"
|
|
10066
10855
|
};
|
|
@@ -10083,6 +10872,7 @@ var normalizedScope = (scope) => ({
|
|
|
10083
10872
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
10084
10873
|
const out = [];
|
|
10085
10874
|
const base = { scope, dependentAction: null };
|
|
10875
|
+
const proportional = verification === "proportional";
|
|
10086
10876
|
if (judgment.behaviorChange)
|
|
10087
10877
|
out.push(derived2({
|
|
10088
10878
|
...base,
|
|
@@ -10093,7 +10883,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
|
|
|
10093
10883
|
before: "close",
|
|
10094
10884
|
acceptanceAllowed: false
|
|
10095
10885
|
}));
|
|
10096
|
-
if (
|
|
10886
|
+
if (proportional && judgment.riskTier === "normal")
|
|
10887
|
+
out.push(derived2({
|
|
10888
|
+
...base,
|
|
10889
|
+
ruleId: RULES.lane,
|
|
10890
|
+
dimension: "review",
|
|
10891
|
+
reason: "The review lane computed from the diff decides the verdict: none needs none, self the author's own (shown as self-reviewed), focused and full a non-author one.",
|
|
10892
|
+
satisfaction: "Read the lane in `workit policy assess` or `workit pr status`; record what it asks: `workit ledger verdict tests-verified --self --how <what you ran>` for self, or hand focused and full to a verifier.",
|
|
10893
|
+
before: "close",
|
|
10894
|
+
acceptanceAllowed: false
|
|
10895
|
+
}));
|
|
10896
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
10097
10897
|
out.push(verification === "independent" ? derived2({
|
|
10098
10898
|
...base,
|
|
10099
10899
|
ruleId: RULES.nonAuthor,
|
|
@@ -10196,7 +10996,7 @@ var productDecision = (task, ledger) => {
|
|
|
10196
10996
|
};
|
|
10197
10997
|
};
|
|
10198
10998
|
var planRecorded = (task, root) => {
|
|
10199
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" &&
|
|
10999
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path11.resolve(root, ref.path)));
|
|
10200
11000
|
if (cited)
|
|
10201
11001
|
return {
|
|
10202
11002
|
met: true,
|
|
@@ -10210,14 +11010,30 @@ var planRecorded = (task, root) => {
|
|
|
10210
11010
|
decisionIds: []
|
|
10211
11011
|
};
|
|
10212
11012
|
};
|
|
10213
|
-
var
|
|
11013
|
+
var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
|
|
11014
|
+
var laneVerdict = (task, root, ledger) => {
|
|
11015
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
11016
|
+
return {
|
|
11017
|
+
met: true,
|
|
11018
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
11019
|
+
decisionIds: []
|
|
11020
|
+
};
|
|
11021
|
+
const lane = workingLane(task, root);
|
|
11022
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
11023
|
+
const need = laneNeed(lane.lane);
|
|
11024
|
+
if (need === "none")
|
|
11025
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
11026
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
11027
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
11028
|
+
};
|
|
11029
|
+
var verdict = (ruleId, root, ledger) => {
|
|
10214
11030
|
const branch = ledger.branch();
|
|
10215
11031
|
if (!branch)
|
|
10216
11032
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
10217
11033
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
10218
11034
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
10219
11035
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
10220
|
-
const met = !failing && (
|
|
11036
|
+
const met = !failing && (ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
|
|
10221
11037
|
if (met)
|
|
10222
11038
|
return {
|
|
10223
11039
|
met: true,
|
|
@@ -10225,7 +11041,7 @@ var verdict = (requirement, root, ledger) => {
|
|
|
10225
11041
|
decisionIds: []
|
|
10226
11042
|
};
|
|
10227
11043
|
const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
|
|
10228
|
-
const unblock =
|
|
11044
|
+
const unblock = ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
|
|
10229
11045
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
10230
11046
|
};
|
|
10231
11047
|
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
@@ -10237,7 +11053,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
10237
11053
|
case RULES.self:
|
|
10238
11054
|
case RULES.nonAuthor:
|
|
10239
11055
|
case RULES.verified:
|
|
10240
|
-
return verdict(requirement, root, ledger);
|
|
11056
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
11057
|
+
case RULES.lane:
|
|
11058
|
+
return laneVerdict(task, root, ledger);
|
|
10241
11059
|
default:
|
|
10242
11060
|
return null;
|
|
10243
11061
|
}
|
|
@@ -10812,7 +11630,7 @@ var judgeInputSummary = (raw) => {
|
|
|
10812
11630
|
|
|
10813
11631
|
// packages/workit-core/src/autonomy.ts
|
|
10814
11632
|
import os4 from "node:os";
|
|
10815
|
-
import
|
|
11633
|
+
import path12 from "node:path";
|
|
10816
11634
|
var accountHome = () => {
|
|
10817
11635
|
try {
|
|
10818
11636
|
return os4.userInfo().homedir;
|
|
@@ -10821,10 +11639,10 @@ var accountHome = () => {
|
|
|
10821
11639
|
}
|
|
10822
11640
|
};
|
|
10823
11641
|
var grantsHome = { resolve: accountHome };
|
|
10824
|
-
var grantsDir = () =>
|
|
11642
|
+
var grantsDir = () => path12.join(grantsHome.resolve(), ".config", "workit");
|
|
10825
11643
|
var grantsOverride = () => {
|
|
10826
|
-
const configured =
|
|
10827
|
-
return configured ===
|
|
11644
|
+
const configured = path12.resolve(resolveConfigDir());
|
|
11645
|
+
return configured === path12.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path12.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
10828
11646
|
};
|
|
10829
11647
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
10830
11648
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -10877,8 +11695,16 @@ function endpointView(entry, context) {
|
|
|
10877
11695
|
...issue ? { endpointIssue: issue } : {}
|
|
10878
11696
|
};
|
|
10879
11697
|
}
|
|
10880
|
-
var
|
|
10881
|
-
|
|
11698
|
+
var VERIFICATION_MODES = [
|
|
11699
|
+
"proportional",
|
|
11700
|
+
"self",
|
|
11701
|
+
"independent"
|
|
11702
|
+
];
|
|
11703
|
+
var DEFAULT_VERIFICATION = "proportional";
|
|
11704
|
+
var verificationOf = (entry) => {
|
|
11705
|
+
const value = entry?.verification;
|
|
11706
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
11707
|
+
};
|
|
10882
11708
|
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
10883
11709
|
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
10884
11710
|
var fromAutoApprove = (value) => {
|
|
@@ -10950,7 +11776,7 @@ var grantHint = (workspace, kind) => `ask the user to run, in their own terminal
|
|
|
10950
11776
|
// packages/workit-core/src/core/task-store.ts
|
|
10951
11777
|
import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
|
|
10952
11778
|
import * as fs12 from "node:fs";
|
|
10953
|
-
import
|
|
11779
|
+
import path17 from "node:path";
|
|
10954
11780
|
|
|
10955
11781
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
10956
11782
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -11001,7 +11827,7 @@ function sameFileIdentity(left, right, platform = process.platform) {
|
|
|
11001
11827
|
// node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
11002
11828
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
11003
11829
|
import fsSync from "node:fs";
|
|
11004
|
-
import
|
|
11830
|
+
import path13 from "node:path";
|
|
11005
11831
|
|
|
11006
11832
|
// node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
11007
11833
|
import fs8 from "node:fs";
|
|
@@ -11091,13 +11917,13 @@ ${ownershipToken}
|
|
|
11091
11917
|
return { raw, ownershipToken };
|
|
11092
11918
|
}
|
|
11093
11919
|
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
11094
|
-
const resolved =
|
|
11095
|
-
const lexicalRelative =
|
|
11096
|
-
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${
|
|
11097
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
11920
|
+
const resolved = path13.resolve(lockPath);
|
|
11921
|
+
const lexicalRelative = path13.relative(lockRoot.rootDir, resolved);
|
|
11922
|
+
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path13.sep}`) && !path13.isAbsolute(lexicalRelative) ? lexicalRelative : path13.relative(lockRoot.rootReal, resolved);
|
|
11923
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
|
|
11098
11924
|
throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
11099
11925
|
}
|
|
11100
|
-
return relative.split(
|
|
11926
|
+
return relative.split(path13.sep).join(path13.posix.sep);
|
|
11101
11927
|
}
|
|
11102
11928
|
function parseSidecarLockPayload(raw, parser) {
|
|
11103
11929
|
if (parser) {
|
|
@@ -11244,7 +12070,7 @@ function getFsSafeLockConfig() {
|
|
|
11244
12070
|
|
|
11245
12071
|
// node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
|
|
11246
12072
|
import fs10 from "node:fs";
|
|
11247
|
-
import
|
|
12073
|
+
import path14 from "node:path";
|
|
11248
12074
|
|
|
11249
12075
|
// node_modules/@openclaw/fs-safe/dist/timing.js
|
|
11250
12076
|
function sleepSync(ms) {
|
|
@@ -11335,26 +12161,26 @@ function canonicalLockParentSync(parent) {
|
|
|
11335
12161
|
return process.platform === "win32" ? fs10.realpathSync.native(parent) : fs10.realpathSync(parent);
|
|
11336
12162
|
}
|
|
11337
12163
|
function normalizeTargetPath(targetPath) {
|
|
11338
|
-
const resolved =
|
|
11339
|
-
fs10.mkdirSync(
|
|
12164
|
+
const resolved = path14.resolve(targetPath);
|
|
12165
|
+
fs10.mkdirSync(path14.dirname(resolved), { recursive: true });
|
|
11340
12166
|
try {
|
|
11341
|
-
return
|
|
12167
|
+
return path14.join(canonicalLockParentSync(path14.dirname(resolved)), path14.basename(resolved));
|
|
11342
12168
|
} catch {
|
|
11343
12169
|
return resolved;
|
|
11344
12170
|
}
|
|
11345
12171
|
}
|
|
11346
12172
|
function boundedLockPath(lockPath, lockRoot) {
|
|
11347
|
-
const resolved =
|
|
12173
|
+
const resolved = path14.resolve(lockPath);
|
|
11348
12174
|
if (!lockRoot)
|
|
11349
12175
|
return resolved;
|
|
11350
12176
|
relativeSidecarLockPath(lockRoot, resolved);
|
|
11351
|
-
const parent =
|
|
12177
|
+
const parent = path14.dirname(resolved);
|
|
11352
12178
|
const parentReal = canonicalLockParentSync(parent);
|
|
11353
|
-
const parentRelative =
|
|
11354
|
-
if (parentRelative === ".." || parentRelative.startsWith(`..${
|
|
12179
|
+
const parentRelative = path14.relative(lockRoot.rootReal, parentReal);
|
|
12180
|
+
if (parentRelative === ".." || parentRelative.startsWith(`..${path14.sep}`) || path14.isAbsolute(parentRelative)) {
|
|
11355
12181
|
throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
|
|
11356
12182
|
}
|
|
11357
|
-
return
|
|
12183
|
+
return path14.join(parentReal, path14.basename(resolved));
|
|
11358
12184
|
}
|
|
11359
12185
|
function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
11360
12186
|
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
@@ -11554,14 +12380,14 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
11554
12380
|
}
|
|
11555
12381
|
}
|
|
11556
12382
|
// packages/workit-core/src/core/package-root.ts
|
|
11557
|
-
import { existsSync as
|
|
11558
|
-
import
|
|
12383
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
12384
|
+
import path15 from "node:path";
|
|
11559
12385
|
import { fileURLToPath } from "node:url";
|
|
11560
12386
|
var packageRoot = () => {
|
|
11561
|
-
let dir =
|
|
12387
|
+
let dir = path15.dirname(fileURLToPath(import.meta.url));
|
|
11562
12388
|
while (true) {
|
|
11563
|
-
const parent =
|
|
11564
|
-
if (
|
|
12389
|
+
const parent = path15.dirname(dir);
|
|
12390
|
+
if (existsSync4(path15.join(dir, "package.json")) || parent === dir)
|
|
11565
12391
|
return dir;
|
|
11566
12392
|
dir = parent;
|
|
11567
12393
|
}
|
|
@@ -11570,7 +12396,7 @@ var packageRoot = () => {
|
|
|
11570
12396
|
// packages/workit-core/src/store/event-log.ts
|
|
11571
12397
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11572
12398
|
import fs11 from "node:fs";
|
|
11573
|
-
import
|
|
12399
|
+
import path16 from "node:path";
|
|
11574
12400
|
var EVENT_VERSION = 1;
|
|
11575
12401
|
|
|
11576
12402
|
class LogDamage extends Error {
|
|
@@ -11681,7 +12507,7 @@ function appendEvent(file, event, validEnd) {
|
|
|
11681
12507
|
fs11.closeSync(fd);
|
|
11682
12508
|
}
|
|
11683
12509
|
if (created)
|
|
11684
|
-
fsyncDirectory(
|
|
12510
|
+
fsyncDirectory(path16.dirname(file));
|
|
11685
12511
|
return validEnd;
|
|
11686
12512
|
}
|
|
11687
12513
|
function replaceFile(file, bytes, durable) {
|
|
@@ -11697,7 +12523,7 @@ function replaceFile(file, bytes, durable) {
|
|
|
11697
12523
|
}
|
|
11698
12524
|
renameWithRetry(temporary, file);
|
|
11699
12525
|
if (durable)
|
|
11700
|
-
fsyncDirectory(
|
|
12526
|
+
fsyncDirectory(path16.dirname(file));
|
|
11701
12527
|
} catch (error) {
|
|
11702
12528
|
try {
|
|
11703
12529
|
fs11.unlinkSync(temporary);
|
|
@@ -12040,7 +12866,7 @@ var cachedRuntimeVersion = null;
|
|
|
12040
12866
|
var runtimeVersion = () => {
|
|
12041
12867
|
if (cachedRuntimeVersion === null) {
|
|
12042
12868
|
try {
|
|
12043
|
-
const pkg = JSON.parse(fs12.readFileSync(
|
|
12869
|
+
const pkg = JSON.parse(fs12.readFileSync(path17.join(packageRoot(), "package.json"), "utf8"));
|
|
12044
12870
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
12045
12871
|
} catch {
|
|
12046
12872
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -12073,7 +12899,7 @@ var readText = (file) => {
|
|
|
12073
12899
|
};
|
|
12074
12900
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
12075
12901
|
var branchStart = (location, branch, aliases) => {
|
|
12076
|
-
const first = readText(
|
|
12902
|
+
const first = readText(path17.join(path17.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
12077
12903
|
`, 1)[0];
|
|
12078
12904
|
const created = REFLOG_LINE.exec(first);
|
|
12079
12905
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -12083,7 +12909,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
12083
12909
|
return start;
|
|
12084
12910
|
if (!location.git)
|
|
12085
12911
|
return null;
|
|
12086
|
-
for (const line of readText(
|
|
12912
|
+
for (const line of readText(path17.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
12087
12913
|
`)) {
|
|
12088
12914
|
const entry = REFLOG_LINE.exec(line);
|
|
12089
12915
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -12093,14 +12919,14 @@ var branchStart = (location, branch, aliases) => {
|
|
|
12093
12919
|
}
|
|
12094
12920
|
return null;
|
|
12095
12921
|
};
|
|
12096
|
-
var refExists = (common, ref) => fs12.existsSync(
|
|
12922
|
+
var refExists = (common, ref) => fs12.existsSync(path17.join(common, ref)) || readText(path17.join(common, "packed-refs")).includes(` ${ref}
|
|
12097
12923
|
`);
|
|
12098
12924
|
var origins = new Map;
|
|
12099
12925
|
var sameDirectoryIdentity = (left, right) => {
|
|
12100
|
-
if (!
|
|
12926
|
+
if (!path17.isAbsolute(left) || !path17.isAbsolute(right))
|
|
12101
12927
|
return false;
|
|
12102
|
-
const normalizedLeft =
|
|
12103
|
-
const normalizedRight =
|
|
12928
|
+
const normalizedLeft = path17.resolve(left);
|
|
12929
|
+
const normalizedRight = path17.resolve(right);
|
|
12104
12930
|
try {
|
|
12105
12931
|
const a = fs12.statSync(normalizedLeft, { bigint: true });
|
|
12106
12932
|
const b = fs12.statSync(normalizedRight, { bigint: true });
|
|
@@ -12146,7 +12972,7 @@ class TaskStore {
|
|
|
12146
12972
|
migrationChecked = false;
|
|
12147
12973
|
compactDue = new Set;
|
|
12148
12974
|
constructor(root, options = {}) {
|
|
12149
|
-
const resolved =
|
|
12975
|
+
const resolved = path17.resolve(root);
|
|
12150
12976
|
this.origin = fs12.existsSync(resolved) ? fs12.realpathSync(resolved) : resolved;
|
|
12151
12977
|
this.located = resolveStore(this.origin);
|
|
12152
12978
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
@@ -12155,7 +12981,7 @@ class TaskStore {
|
|
|
12155
12981
|
this.migrateOnRead = options.migrateOnRead;
|
|
12156
12982
|
}
|
|
12157
12983
|
location() {
|
|
12158
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(
|
|
12984
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path17.join(this.root, ".git"))) {
|
|
12159
12985
|
this.located = undefined;
|
|
12160
12986
|
this.migrationChecked = false;
|
|
12161
12987
|
}
|
|
@@ -12311,7 +13137,7 @@ class TaskStore {
|
|
|
12311
13137
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
12312
13138
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
12313
13139
|
}
|
|
12314
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
13140
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path17.basename(this.root) || this.root;
|
|
12315
13141
|
const intent = {
|
|
12316
13142
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
12317
13143
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -12377,14 +13203,14 @@ class TaskStore {
|
|
|
12377
13203
|
const branch = key.ok ? key.data.branch : null;
|
|
12378
13204
|
if (!branch || !location.ok)
|
|
12379
13205
|
return success(null, null, null);
|
|
12380
|
-
const common =
|
|
12381
|
-
const heads =
|
|
12382
|
-
const headLog = location.data.git ?
|
|
13206
|
+
const common = path17.dirname(location.data.dir);
|
|
13207
|
+
const heads = path17.join(common, "logs", "refs", "heads");
|
|
13208
|
+
const headLog = location.data.git ? path17.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
12383
13209
|
const signatures = [
|
|
12384
|
-
|
|
13210
|
+
path17.join(heads, branch),
|
|
12385
13211
|
headLog,
|
|
12386
13212
|
heads,
|
|
12387
|
-
|
|
13213
|
+
path17.join(common, "packed-refs")
|
|
12388
13214
|
].map((file) => fileSignature(file) ?? "-");
|
|
12389
13215
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
12390
13216
|
let resolved = origins.get(slot);
|
|
@@ -12396,7 +13222,7 @@ class TaskStore {
|
|
|
12396
13222
|
aliases.push(rename.from);
|
|
12397
13223
|
const start = branchStart(location.data, branch, aliases);
|
|
12398
13224
|
const slash = start?.indexOf("/") ?? -1;
|
|
12399
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
13225
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path17.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
12400
13226
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
12401
13227
|
resolved = {
|
|
12402
13228
|
signature: signatures.join("|"),
|
|
@@ -12634,13 +13460,13 @@ class TaskStore {
|
|
|
12634
13460
|
if (!ready.ok)
|
|
12635
13461
|
return ready;
|
|
12636
13462
|
const dirs = this.paths();
|
|
12637
|
-
const recovery =
|
|
13463
|
+
const recovery = path17.join(this.root, ".workit", "recovery");
|
|
12638
13464
|
if (fs12.existsSync(recovery)) {
|
|
12639
13465
|
let files = 0;
|
|
12640
13466
|
let bytes = 0;
|
|
12641
13467
|
try {
|
|
12642
13468
|
for (const name of fs12.readdirSync(recovery)) {
|
|
12643
|
-
const stat = fs12.lstatSync(
|
|
13469
|
+
const stat = fs12.lstatSync(path17.join(recovery, name));
|
|
12644
13470
|
if (!stat.isFile())
|
|
12645
13471
|
continue;
|
|
12646
13472
|
files += 1;
|
|
@@ -12693,7 +13519,7 @@ class TaskStore {
|
|
|
12693
13519
|
if (fs12.existsSync(dirs.blobs))
|
|
12694
13520
|
for (const name of fs12.readdirSync(dirs.blobs)) {
|
|
12695
13521
|
const digest = name.replace(/\.json$/, "");
|
|
12696
|
-
const file =
|
|
13522
|
+
const file = path17.join(dirs.blobs, name);
|
|
12697
13523
|
let stat;
|
|
12698
13524
|
try {
|
|
12699
13525
|
stat = fs12.lstatSync(file);
|
|
@@ -12711,7 +13537,7 @@ class TaskStore {
|
|
|
12711
13537
|
}
|
|
12712
13538
|
return success(null, null, null);
|
|
12713
13539
|
};
|
|
12714
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
13540
|
+
const swept = dryRun ? sweep() : this.withLock(path17.join(dirs.dir, "gc.lock"), sweep);
|
|
12715
13541
|
if (!swept.ok)
|
|
12716
13542
|
return swept;
|
|
12717
13543
|
const temporaryDirs = [
|
|
@@ -12719,7 +13545,7 @@ class TaskStore {
|
|
|
12719
13545
|
dirs.tasks,
|
|
12720
13546
|
dirs.blobs,
|
|
12721
13547
|
dirs.checkout,
|
|
12722
|
-
...names.map((name) =>
|
|
13548
|
+
...names.map((name) => path17.join(dirs.tasks, name))
|
|
12723
13549
|
];
|
|
12724
13550
|
for (const directory of temporaryDirs) {
|
|
12725
13551
|
if (!fs12.existsSync(directory))
|
|
@@ -12727,7 +13553,7 @@ class TaskStore {
|
|
|
12727
13553
|
for (const name of fs12.readdirSync(directory)) {
|
|
12728
13554
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
12729
13555
|
continue;
|
|
12730
|
-
const file =
|
|
13556
|
+
const file = path17.join(directory, name);
|
|
12731
13557
|
try {
|
|
12732
13558
|
const stat = fs12.lstatSync(file);
|
|
12733
13559
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
@@ -12744,30 +13570,30 @@ class TaskStore {
|
|
|
12744
13570
|
}
|
|
12745
13571
|
paths() {
|
|
12746
13572
|
const location = this.location();
|
|
12747
|
-
const dir = location.ok ? location.data.dir :
|
|
12748
|
-
const checkout =
|
|
13573
|
+
const dir = location.ok ? location.data.dir : path17.join(this.root, ".workit");
|
|
13574
|
+
const checkout = path17.join(dir, "checkouts", checkoutSlug(this.root));
|
|
12749
13575
|
return {
|
|
12750
13576
|
dir,
|
|
12751
13577
|
shared: location.ok ? location.data.shared : false,
|
|
12752
|
-
marker:
|
|
13578
|
+
marker: path17.join(dir, "store.json"),
|
|
12753
13579
|
checkout,
|
|
12754
|
-
workspace:
|
|
12755
|
-
checkoutLock:
|
|
12756
|
-
tasks:
|
|
12757
|
-
index:
|
|
12758
|
-
blobs:
|
|
12759
|
-
legacy:
|
|
12760
|
-
v2:
|
|
13580
|
+
workspace: path17.join(checkout, "workspace.json"),
|
|
13581
|
+
checkoutLock: path17.join(checkout, "metadata.lock"),
|
|
13582
|
+
tasks: path17.join(dir, "tasks"),
|
|
13583
|
+
index: path17.join(dir, "task-index.json"),
|
|
13584
|
+
blobs: path17.join(dir, "blobs", "candidates"),
|
|
13585
|
+
legacy: path17.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
13586
|
+
v2: path17.join(this.root, ".workit")
|
|
12761
13587
|
};
|
|
12762
13588
|
}
|
|
12763
13589
|
eventsPath(taskId) {
|
|
12764
|
-
return
|
|
13590
|
+
return path17.join(this.paths().tasks, taskId, "events.jsonl");
|
|
12765
13591
|
}
|
|
12766
13592
|
snapshotPath(taskId) {
|
|
12767
|
-
return
|
|
13593
|
+
return path17.join(this.paths().tasks, taskId, "snapshot.json");
|
|
12768
13594
|
}
|
|
12769
13595
|
taskLockPath(taskId) {
|
|
12770
|
-
return
|
|
13596
|
+
return path17.join(this.paths().tasks, taskId, "lock");
|
|
12771
13597
|
}
|
|
12772
13598
|
ready(write = false) {
|
|
12773
13599
|
const location = this.location();
|
|
@@ -12806,13 +13632,13 @@ class TaskStore {
|
|
|
12806
13632
|
return {
|
|
12807
13633
|
read: (digest) => {
|
|
12808
13634
|
try {
|
|
12809
|
-
return JSON.parse(fs12.readFileSync(
|
|
13635
|
+
return JSON.parse(fs12.readFileSync(path17.join(dir, `${digest}.json`), "utf8"));
|
|
12810
13636
|
} catch (error) {
|
|
12811
13637
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
12812
13638
|
}
|
|
12813
13639
|
},
|
|
12814
13640
|
write: (digest, value) => {
|
|
12815
|
-
const file =
|
|
13641
|
+
const file = path17.join(dir, `${digest}.json`);
|
|
12816
13642
|
if (fs12.existsSync(file)) {
|
|
12817
13643
|
const stamp = new Date;
|
|
12818
13644
|
try {
|
|
@@ -13004,7 +13830,7 @@ class TaskStore {
|
|
|
13004
13830
|
}
|
|
13005
13831
|
}
|
|
13006
13832
|
openTask(record, type, key, legacy = null) {
|
|
13007
|
-
const dir =
|
|
13833
|
+
const dir = path17.join(this.paths().tasks, record.id);
|
|
13008
13834
|
fs12.mkdirSync(dir, { recursive: true });
|
|
13009
13835
|
const blobs = this.blobStore();
|
|
13010
13836
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
@@ -13097,7 +13923,7 @@ class TaskStore {
|
|
|
13097
13923
|
}
|
|
13098
13924
|
withKeyLock(key, operation) {
|
|
13099
13925
|
const digest = createHash11("sha256").update(key).digest("hex").slice(0, 32);
|
|
13100
|
-
return this.withLock(
|
|
13926
|
+
return this.withLock(path17.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
13101
13927
|
}
|
|
13102
13928
|
bindLocked(taskId, key, timestamp) {
|
|
13103
13929
|
const current = this.readWorkspace();
|
|
@@ -13238,7 +14064,7 @@ class TaskStore {
|
|
|
13238
14064
|
writeWorkspace(record) {
|
|
13239
14065
|
const file = this.paths().workspace;
|
|
13240
14066
|
try {
|
|
13241
|
-
fs12.mkdirSync(
|
|
14067
|
+
fs12.mkdirSync(path17.dirname(file), { recursive: true });
|
|
13242
14068
|
replaceFile(file, `${canonicalJson(record)}
|
|
13243
14069
|
`, true);
|
|
13244
14070
|
return success(null, null, null);
|
|
@@ -13316,23 +14142,23 @@ class TaskStore {
|
|
|
13316
14142
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
13317
14143
|
}
|
|
13318
14144
|
v2Dirs() {
|
|
13319
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
14145
|
+
return [...new Set([this.root, this.origin])].map((dir) => path17.join(dir, ".workit"));
|
|
13320
14146
|
}
|
|
13321
14147
|
pendingMigrations() {
|
|
13322
14148
|
return this.v2Dirs().filter((dir) => {
|
|
13323
|
-
const raw = this.readV2Workspace(
|
|
14149
|
+
const raw = this.readV2Workspace(path17.join(dir, "workspace.json"));
|
|
13324
14150
|
return raw !== null && raw !== TOMBSTONE;
|
|
13325
14151
|
});
|
|
13326
14152
|
}
|
|
13327
14153
|
migrateV2(v2) {
|
|
13328
14154
|
const dirs = this.paths();
|
|
13329
|
-
const v2Workspace =
|
|
13330
|
-
const v2Tasks =
|
|
13331
|
-
const legacy =
|
|
14155
|
+
const v2Workspace = path17.join(v2, "workspace.json");
|
|
14156
|
+
const v2Tasks = path17.join(v2, "tasks");
|
|
14157
|
+
const legacy = path17.join(dirs.dir, "legacy", checkoutSlug(path17.dirname(v2)), "v2");
|
|
13332
14158
|
const raw = this.readV2Workspace(v2Workspace);
|
|
13333
14159
|
if (raw === null || raw === TOMBSTONE)
|
|
13334
14160
|
return success(null, null, null);
|
|
13335
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
14161
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path17.join(v2, "metadata.lock"), () => {
|
|
13336
14162
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
13337
14163
|
if (workspace === null || workspace === TOMBSTONE)
|
|
13338
14164
|
return success(null, null, null);
|
|
@@ -13341,7 +14167,7 @@ class TaskStore {
|
|
|
13341
14167
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
13342
14168
|
let files = [];
|
|
13343
14169
|
try {
|
|
13344
|
-
files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(
|
|
14170
|
+
files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path17.join(v2Tasks, name)));
|
|
13345
14171
|
} catch {}
|
|
13346
14172
|
const current = this.readWorkspace();
|
|
13347
14173
|
if (!current.ok)
|
|
@@ -13359,21 +14185,21 @@ class TaskStore {
|
|
|
13359
14185
|
let skipped = 0;
|
|
13360
14186
|
const done = new Map;
|
|
13361
14187
|
try {
|
|
13362
|
-
fs12.mkdirSync(
|
|
14188
|
+
fs12.mkdirSync(path17.join(legacy, "tasks"), { recursive: true });
|
|
13363
14189
|
for (const name of ["workspace.json", "index.json"])
|
|
13364
|
-
if (fs12.existsSync(
|
|
13365
|
-
fs12.copyFileSync(
|
|
14190
|
+
if (fs12.existsSync(path17.join(v2, name)))
|
|
14191
|
+
fs12.copyFileSync(path17.join(v2, name), path17.join(legacy, name));
|
|
13366
14192
|
for (const name of files) {
|
|
13367
14193
|
const id = name.slice(0, -5);
|
|
13368
|
-
const file =
|
|
14194
|
+
const file = path17.join(v2Tasks, name);
|
|
13369
14195
|
const bytes = fs12.readFileSync(file, "utf8");
|
|
13370
14196
|
const digest = sha256(bytes);
|
|
13371
|
-
fs12.writeFileSync(
|
|
14197
|
+
fs12.writeFileSync(path17.join(legacy, "tasks", name), bytes);
|
|
13372
14198
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
13373
14199
|
if (!parsed.ok || parsed.data.id !== id)
|
|
13374
14200
|
return failure2("recovery_required", `cannot migrate 2.x task ${id}: ${parsed.ok ? "file name and record ID differ" : parsed.error}; move ${file} aside to continue`, { path: file });
|
|
13375
14201
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
13376
|
-
const origin = { path:
|
|
14202
|
+
const origin = { path: path17.join(legacy, "tasks", name), digest };
|
|
13377
14203
|
if (!fs12.existsSync(this.eventsPath(id))) {
|
|
13378
14204
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
13379
14205
|
migrated += 1;
|
|
@@ -13409,7 +14235,7 @@ class TaskStore {
|
|
|
13409
14235
|
try {
|
|
13410
14236
|
let complete = true;
|
|
13411
14237
|
for (const [name, digest] of done) {
|
|
13412
|
-
const file =
|
|
14238
|
+
const file = path17.join(v2Tasks, name);
|
|
13413
14239
|
if (sha256(fs12.readFileSync(file, "utf8")) !== digest) {
|
|
13414
14240
|
complete = false;
|
|
13415
14241
|
continue;
|
|
@@ -13420,7 +14246,7 @@ class TaskStore {
|
|
|
13420
14246
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
13421
14247
|
path: v2
|
|
13422
14248
|
});
|
|
13423
|
-
fs12.rmSync(
|
|
14249
|
+
fs12.rmSync(path17.join(v2, "index.json"), { force: true });
|
|
13424
14250
|
this.writeTombstone(v2, target.id);
|
|
13425
14251
|
} catch (error) {
|
|
13426
14252
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -13440,7 +14266,7 @@ class TaskStore {
|
|
|
13440
14266
|
}
|
|
13441
14267
|
adoptLocalStore() {
|
|
13442
14268
|
const dirs = this.paths();
|
|
13443
|
-
const marker =
|
|
14269
|
+
const marker = path17.join(dirs.v2, "store.json");
|
|
13444
14270
|
if (!dirs.shared || !fs12.existsSync(marker))
|
|
13445
14271
|
return success(null, null, null);
|
|
13446
14272
|
return this.withLock(dirs.checkoutLock, () => {
|
|
@@ -13450,7 +14276,7 @@ class TaskStore {
|
|
|
13450
14276
|
try {
|
|
13451
14277
|
const merge = (from, to) => {
|
|
13452
14278
|
if (!fs12.existsSync(to)) {
|
|
13453
|
-
fs12.mkdirSync(
|
|
14279
|
+
fs12.mkdirSync(path17.dirname(to), { recursive: true });
|
|
13454
14280
|
try {
|
|
13455
14281
|
fs12.renameSync(from, to);
|
|
13456
14282
|
} catch {
|
|
@@ -13461,24 +14287,24 @@ class TaskStore {
|
|
|
13461
14287
|
}
|
|
13462
14288
|
if (fs12.lstatSync(from).isDirectory() && fs12.lstatSync(to).isDirectory())
|
|
13463
14289
|
for (const name of fs12.readdirSync(from))
|
|
13464
|
-
merge(
|
|
14290
|
+
merge(path17.join(from, name), path17.join(to, name));
|
|
13465
14291
|
};
|
|
13466
14292
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
13467
|
-
const from =
|
|
14293
|
+
const from = path17.join(dirs.v2, sub);
|
|
13468
14294
|
if (!fs12.existsSync(from))
|
|
13469
14295
|
continue;
|
|
13470
14296
|
if (sub === "tasks") {
|
|
13471
14297
|
for (const name of fs12.readdirSync(from))
|
|
13472
|
-
if (validId(name) && !fs12.existsSync(
|
|
14298
|
+
if (validId(name) && !fs12.existsSync(path17.join(dirs.dir, sub, name)))
|
|
13473
14299
|
tasks += 1;
|
|
13474
14300
|
}
|
|
13475
|
-
merge(from,
|
|
14301
|
+
merge(from, path17.join(dirs.dir, sub));
|
|
13476
14302
|
}
|
|
13477
|
-
const ledger =
|
|
13478
|
-
if (fs12.existsSync(ledger) && !fs12.existsSync(
|
|
13479
|
-
merge(ledger,
|
|
13480
|
-
fs12.rmSync(
|
|
13481
|
-
fs12.renameSync(marker,
|
|
14303
|
+
const ledger = path17.join(dirs.v2, "ledger");
|
|
14304
|
+
if (fs12.existsSync(ledger) && !fs12.existsSync(path17.join(dirs.dir, "ledger")))
|
|
14305
|
+
merge(ledger, path17.join(dirs.dir, "ledger"));
|
|
14306
|
+
fs12.rmSync(path17.join(dirs.v2, "task-index.json"), { force: true });
|
|
14307
|
+
fs12.renameSync(marker, path17.join(dirs.v2, "store.moved.json"));
|
|
13482
14308
|
} catch (error) {
|
|
13483
14309
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
13484
14310
|
path: dirs.v2
|
|
@@ -13516,7 +14342,7 @@ class TaskStore {
|
|
|
13516
14342
|
schemaVersion: SCHEMA_VERSION,
|
|
13517
14343
|
id: workspaceId ?? newId(),
|
|
13518
14344
|
revision: newRevision(),
|
|
13519
|
-
root:
|
|
14345
|
+
root: path17.dirname(v2),
|
|
13520
14346
|
runtime: { createdWith: version, updatedWith: version },
|
|
13521
14347
|
writer: null,
|
|
13522
14348
|
store: {
|
|
@@ -13528,11 +14354,11 @@ class TaskStore {
|
|
|
13528
14354
|
critical: ["store"]
|
|
13529
14355
|
};
|
|
13530
14356
|
fs12.mkdirSync(v2, { recursive: true });
|
|
13531
|
-
const ignore =
|
|
14357
|
+
const ignore = path17.join(v2, ".gitignore");
|
|
13532
14358
|
if (!fs12.existsSync(ignore))
|
|
13533
14359
|
fs12.writeFileSync(ignore, `*
|
|
13534
14360
|
`);
|
|
13535
|
-
replaceFile(
|
|
14361
|
+
replaceFile(path17.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
13536
14362
|
`, true);
|
|
13537
14363
|
}
|
|
13538
14364
|
stubFor(taskId) {
|
|
@@ -13556,8 +14382,8 @@ class TaskStore {
|
|
|
13556
14382
|
}
|
|
13557
14383
|
writeTombstoneIfMissing() {
|
|
13558
14384
|
try {
|
|
13559
|
-
const v2 =
|
|
13560
|
-
if (fs12.existsSync(
|
|
14385
|
+
const v2 = path17.join(this.root, ".workit");
|
|
14386
|
+
if (fs12.existsSync(path17.join(v2, "workspace.json")))
|
|
13561
14387
|
return;
|
|
13562
14388
|
let id = null;
|
|
13563
14389
|
try {
|
|
@@ -13574,7 +14400,7 @@ class TaskStore {
|
|
|
13574
14400
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
13575
14401
|
`, false);
|
|
13576
14402
|
if (!dirs.shared) {
|
|
13577
|
-
const ignore =
|
|
14403
|
+
const ignore = path17.join(dirs.dir, ".gitignore");
|
|
13578
14404
|
let current = null;
|
|
13579
14405
|
try {
|
|
13580
14406
|
current = fs12.readFileSync(ignore, "utf8");
|
|
@@ -13634,7 +14460,7 @@ class TaskStore {
|
|
|
13634
14460
|
withLock(lockPath, operation) {
|
|
13635
14461
|
try {
|
|
13636
14462
|
this.initializeStorage();
|
|
13637
|
-
fs12.mkdirSync(
|
|
14463
|
+
fs12.mkdirSync(path17.dirname(lockPath), { recursive: true });
|
|
13638
14464
|
} catch (error) {
|
|
13639
14465
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
13640
14466
|
path: this.paths().dir
|
|
@@ -13697,7 +14523,7 @@ class TaskStore {
|
|
|
13697
14523
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
13698
14524
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
13699
14525
|
path: lockPath,
|
|
13700
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
14526
|
+
guidance: `Check permissions and the read-only attribute on ${path17.dirname(lockPath)}.`
|
|
13701
14527
|
});
|
|
13702
14528
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
13703
14529
|
let lock = null;
|
|
@@ -13724,18 +14550,18 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
|
|
|
13724
14550
|
import { createHash as createHash12 } from "node:crypto";
|
|
13725
14551
|
import * as fs14 from "node:fs";
|
|
13726
14552
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
13727
|
-
import
|
|
14553
|
+
import path19 from "node:path";
|
|
13728
14554
|
|
|
13729
14555
|
// packages/workit-core/src/check-config.ts
|
|
13730
14556
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13731
14557
|
import fs13 from "node:fs";
|
|
13732
|
-
import
|
|
14558
|
+
import path18 from "node:path";
|
|
13733
14559
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
13734
14560
|
var CHECKS_FILE = ".workit/checks.json";
|
|
13735
14561
|
var LEGACY_CHECKS_FILE = "workit.checks.json";
|
|
13736
14562
|
function checksConfigFile(root) {
|
|
13737
|
-
const current =
|
|
13738
|
-
const legacy =
|
|
14563
|
+
const current = path18.join(root, CHECKS_FILE);
|
|
14564
|
+
const legacy = path18.join(root, LEGACY_CHECKS_FILE);
|
|
13739
14565
|
return !fs13.existsSync(current) && fs13.existsSync(legacy) ? legacy : current;
|
|
13740
14566
|
}
|
|
13741
14567
|
function repoHeavyCommands(root) {
|
|
@@ -13786,7 +14612,7 @@ function splitCommand(command) {
|
|
|
13786
14612
|
argv.push(current);
|
|
13787
14613
|
return argv.length ? argv : new Error("empty command");
|
|
13788
14614
|
}
|
|
13789
|
-
var
|
|
14615
|
+
var git5 = (cwd, args) => {
|
|
13790
14616
|
const run = spawnSync9("git", args, {
|
|
13791
14617
|
cwd,
|
|
13792
14618
|
encoding: "utf8",
|
|
@@ -13798,10 +14624,10 @@ var git4 = (cwd, args) => {
|
|
|
13798
14624
|
return value || null;
|
|
13799
14625
|
};
|
|
13800
14626
|
function checkRoot(cwd) {
|
|
13801
|
-
return
|
|
14627
|
+
return path18.resolve(git5(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
13802
14628
|
}
|
|
13803
14629
|
var packageManager = (root) => {
|
|
13804
|
-
const has = (file) => fs13.existsSync(
|
|
14630
|
+
const has = (file) => fs13.existsSync(path18.join(root, file));
|
|
13805
14631
|
if (has("bun.lock") || has("bun.lockb"))
|
|
13806
14632
|
return "bun";
|
|
13807
14633
|
if (has("pnpm-lock.yaml"))
|
|
@@ -13821,7 +14647,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
13821
14647
|
function detectedChecks(root) {
|
|
13822
14648
|
const read = (file) => {
|
|
13823
14649
|
try {
|
|
13824
|
-
return fs13.readFileSync(
|
|
14650
|
+
return fs13.readFileSync(path18.join(root, file), "utf8");
|
|
13825
14651
|
} catch {
|
|
13826
14652
|
return null;
|
|
13827
14653
|
}
|
|
@@ -13861,7 +14687,7 @@ var GATES = ["testing", "verification"];
|
|
|
13861
14687
|
function loadCheckConfig(cwd) {
|
|
13862
14688
|
const root = checkRoot(cwd);
|
|
13863
14689
|
const file = checksConfigFile(root);
|
|
13864
|
-
const legacy =
|
|
14690
|
+
const legacy = path18.basename(file) === LEGACY_CHECKS_FILE;
|
|
13865
14691
|
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
13866
14692
|
const note = legacy ? {
|
|
13867
14693
|
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
@@ -13939,10 +14765,10 @@ function workitAvailable(env = process.env) {
|
|
|
13939
14765
|
return workitOnPath;
|
|
13940
14766
|
const windows = process.platform === "win32";
|
|
13941
14767
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
13942
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
14768
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path18.delimiter).filter(Boolean);
|
|
13943
14769
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
13944
14770
|
try {
|
|
13945
|
-
return fs13.statSync(
|
|
14771
|
+
return fs13.statSync(path18.join(dir, `workit${ext}`)).isFile();
|
|
13946
14772
|
} catch {
|
|
13947
14773
|
return false;
|
|
13948
14774
|
}
|
|
@@ -13955,14 +14781,14 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
13955
14781
|
|
|
13956
14782
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
13957
14783
|
var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
|
|
13958
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
13959
|
-
var relative = (root, candidate) =>
|
|
14784
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path19.sep}`);
|
|
14785
|
+
var relative = (root, candidate) => path19.relative(root, candidate).split(path19.sep).join("/") || ".";
|
|
13960
14786
|
var canonicalPath = (value) => {
|
|
13961
14787
|
if (typeof value !== "string" || !value)
|
|
13962
14788
|
return null;
|
|
13963
14789
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
13964
14790
|
return null;
|
|
13965
|
-
const normalized =
|
|
14791
|
+
const normalized = path19.posix.normalize(value);
|
|
13966
14792
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
13967
14793
|
return null;
|
|
13968
14794
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14010,14 +14836,14 @@ var gitPaths = (root) => {
|
|
|
14010
14836
|
git: !/not a git repository/i.test(stderr)
|
|
14011
14837
|
};
|
|
14012
14838
|
}
|
|
14013
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14839
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")) : [];
|
|
14014
14840
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14015
14841
|
if (stagedDeleted.status !== 0) {
|
|
14016
14842
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14017
14843
|
if (!/not a git repository/i.test(stderr))
|
|
14018
14844
|
return { paths, uncertain: true, git: true };
|
|
14019
14845
|
} else if (stagedDeleted.stdout) {
|
|
14020
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14846
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")));
|
|
14021
14847
|
}
|
|
14022
14848
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14023
14849
|
};
|
|
@@ -14032,7 +14858,7 @@ var walk = (root, directory, output) => {
|
|
|
14032
14858
|
for (const name of names) {
|
|
14033
14859
|
if (name === ".git" || name === ".workit")
|
|
14034
14860
|
continue;
|
|
14035
|
-
const target =
|
|
14861
|
+
const target = path19.join(directory, name);
|
|
14036
14862
|
let stat;
|
|
14037
14863
|
try {
|
|
14038
14864
|
stat = fs14.lstatSync(target);
|
|
@@ -14050,12 +14876,12 @@ var walk = (root, directory, output) => {
|
|
|
14050
14876
|
var scopeRoots = (root, scope) => {
|
|
14051
14877
|
const roots = [];
|
|
14052
14878
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14053
|
-
const target =
|
|
14879
|
+
const target = path19.resolve(root, item);
|
|
14054
14880
|
if (!inside(root, target))
|
|
14055
14881
|
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` }] });
|
|
14056
14882
|
let ancestor = target;
|
|
14057
14883
|
while (!fs14.existsSync(ancestor) && ancestor !== root)
|
|
14058
|
-
ancestor =
|
|
14884
|
+
ancestor = path19.dirname(ancestor);
|
|
14059
14885
|
try {
|
|
14060
14886
|
if (!inside(root, fs14.realpathSync(ancestor)))
|
|
14061
14887
|
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` }] });
|
|
@@ -14107,7 +14933,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14107
14933
|
for (const item of [...names].toSorted()) {
|
|
14108
14934
|
if (!scopeMatches(item, normalizedScope))
|
|
14109
14935
|
continue;
|
|
14110
|
-
const target =
|
|
14936
|
+
const target = path19.join(checkout, item);
|
|
14111
14937
|
try {
|
|
14112
14938
|
const stat = fs14.lstatSync(target);
|
|
14113
14939
|
if (stat.isSymbolicLink()) {
|
|
@@ -14291,23 +15117,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
14291
15117
|
continue;
|
|
14292
15118
|
if (!reference.digest)
|
|
14293
15119
|
return failure2("invalid_input", "document references require a byte digest");
|
|
14294
|
-
const target =
|
|
14295
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15120
|
+
const target = path19.resolve(root, reference.path);
|
|
15121
|
+
if (target !== root && !target.startsWith(`${root}${path19.sep}`))
|
|
14296
15122
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14297
15123
|
fields: [
|
|
14298
15124
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
14299
15125
|
]
|
|
14300
15126
|
});
|
|
14301
15127
|
try {
|
|
14302
|
-
const below =
|
|
15128
|
+
const below = path19.relative(root, target);
|
|
14303
15129
|
let current = root;
|
|
14304
|
-
for (const segment of below.split(
|
|
15130
|
+
for (const segment of below.split(path19.sep)) {
|
|
14305
15131
|
if (!segment)
|
|
14306
15132
|
continue;
|
|
14307
|
-
current =
|
|
15133
|
+
current = path19.join(current, segment);
|
|
14308
15134
|
const stat = fs14.lstatSync(current);
|
|
14309
15135
|
const real = fs14.realpathSync(current);
|
|
14310
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15136
|
+
if (real !== root && !real.startsWith(`${root}${path19.sep}`))
|
|
14311
15137
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14312
15138
|
fields: [
|
|
14313
15139
|
{
|
|
@@ -14740,9 +15566,9 @@ Autonomy contract:
|
|
|
14740
15566
|
Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
|
|
14741
15567
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
14742
15568
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
14743
|
-
self-reviewed, never verified. High risk,
|
|
14744
|
-
merge need a verifier with its
|
|
14745
|
-
\`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
15569
|
+
self-reviewed, never verified. High risk, a focused or full lane and
|
|
15570
|
+
merge under \`verification: self|independent\` need a verifier with its
|
|
15571
|
+
own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
14746
15572
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
14747
15573
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
14748
15574
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -16608,7 +17434,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
16608
17434
|
// packages/workit-core/src/hooks/context.ts
|
|
16609
17435
|
import { createHash as createHash13 } from "node:crypto";
|
|
16610
17436
|
import fs15 from "node:fs";
|
|
16611
|
-
import
|
|
17437
|
+
import path20 from "node:path";
|
|
16612
17438
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
16613
17439
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
16614
17440
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -16693,7 +17519,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
16693
17519
|
return null;
|
|
16694
17520
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
16695
17521
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
16696
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
17522
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
|
|
16697
17523
|
const observedFiles = filesKey(files);
|
|
16698
17524
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
16699
17525
|
if (!compact.ok || !compact.data)
|
|
@@ -16771,14 +17597,14 @@ var turnMarker = (input) => {
|
|
|
16771
17597
|
if (location instanceof Error)
|
|
16772
17598
|
return null;
|
|
16773
17599
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
16774
|
-
return
|
|
17600
|
+
return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
16775
17601
|
} catch {
|
|
16776
17602
|
return null;
|
|
16777
17603
|
}
|
|
16778
17604
|
};
|
|
16779
17605
|
var writeTurnDigest = (file, digest) => {
|
|
16780
17606
|
try {
|
|
16781
|
-
fs15.mkdirSync(
|
|
17607
|
+
fs15.mkdirSync(path20.dirname(file), { recursive: true });
|
|
16782
17608
|
fs15.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
16783
17609
|
`);
|
|
16784
17610
|
} catch {}
|
|
@@ -16793,7 +17619,7 @@ var pruneTurnDigests = (dir, now) => {
|
|
|
16793
17619
|
for (const name of fs15.readdirSync(dir)) {
|
|
16794
17620
|
if (!name.startsWith("turn-"))
|
|
16795
17621
|
continue;
|
|
16796
|
-
const file =
|
|
17622
|
+
const file = path20.join(dir, name);
|
|
16797
17623
|
try {
|
|
16798
17624
|
if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
16799
17625
|
fs15.rmSync(file, { force: true });
|
|
@@ -16823,7 +17649,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
16823
17649
|
const file = turnMarker(input);
|
|
16824
17650
|
if (!file)
|
|
16825
17651
|
return;
|
|
16826
|
-
pruneTurnDigests(
|
|
17652
|
+
pruneTurnDigests(path20.dirname(file), Date.now());
|
|
16827
17653
|
if (text)
|
|
16828
17654
|
writeTurnDigest(file, digestOf(text));
|
|
16829
17655
|
else
|
|
@@ -16831,20 +17657,20 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
16831
17657
|
};
|
|
16832
17658
|
// packages/workit-core/src/core/route-intent.ts
|
|
16833
17659
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
16834
|
-
import { existsSync as
|
|
16835
|
-
import
|
|
17660
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
17661
|
+
import path23 from "node:path";
|
|
16836
17662
|
|
|
16837
17663
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
16838
17664
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
16839
17665
|
import { createHash as createHash15 } from "node:crypto";
|
|
16840
17666
|
import fs17 from "node:fs";
|
|
16841
17667
|
import os5 from "node:os";
|
|
16842
|
-
import
|
|
17668
|
+
import path22 from "node:path";
|
|
16843
17669
|
|
|
16844
17670
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
16845
17671
|
import { createHash as createHash14 } from "node:crypto";
|
|
16846
17672
|
import fs16 from "node:fs";
|
|
16847
|
-
import
|
|
17673
|
+
import path21 from "node:path";
|
|
16848
17674
|
var sessionMarkerFile = (input, session, store) => {
|
|
16849
17675
|
let dir = store;
|
|
16850
17676
|
if (dir === undefined) {
|
|
@@ -16854,7 +17680,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16854
17680
|
dir = location.dir;
|
|
16855
17681
|
}
|
|
16856
17682
|
const name = createHash14("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
16857
|
-
return
|
|
17683
|
+
return path21.join(dir, "hooks", `skills-${name}.json`);
|
|
16858
17684
|
};
|
|
16859
17685
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16860
17686
|
var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
|
|
@@ -16881,7 +17707,7 @@ var parseMarker = (text) => {
|
|
|
16881
17707
|
};
|
|
16882
17708
|
var readSessionMarker = (file) => parseMarker(readRaw(file));
|
|
16883
17709
|
var writeAtomic = (file, marker) => {
|
|
16884
|
-
fs16.mkdirSync(
|
|
17710
|
+
fs16.mkdirSync(path21.dirname(file), { recursive: true });
|
|
16885
17711
|
const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
16886
17712
|
try {
|
|
16887
17713
|
fs16.writeFileSync(temp, JSON.stringify(marker));
|
|
@@ -17409,17 +18235,17 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
17409
18235
|
]);
|
|
17410
18236
|
var GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
|
|
17411
18237
|
var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
17412
|
-
var
|
|
18238
|
+
var ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
17413
18239
|
var MAX_DEPTH = 4;
|
|
17414
18240
|
var MAX_EXPANSIONS = 16;
|
|
17415
|
-
var isAbsolute = (value) =>
|
|
18241
|
+
var isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
17416
18242
|
var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
17417
18243
|
var joinDir = (base, raw) => {
|
|
17418
18244
|
const next = raw.replace(HERE, ".");
|
|
17419
18245
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17420
18246
|
return;
|
|
17421
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
17422
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18247
|
+
const expanded = next === "~" || next.startsWith("~/") ? path22.join(os5.homedir(), next.slice(1)) : next;
|
|
18248
|
+
return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
|
|
17423
18249
|
};
|
|
17424
18250
|
var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
|
|
17425
18251
|
var cdTarget = (words) => {
|
|
@@ -17436,7 +18262,7 @@ var stripPrefixes = (input) => {
|
|
|
17436
18262
|
const first = words[0];
|
|
17437
18263
|
if (first === undefined)
|
|
17438
18264
|
return { words, env };
|
|
17439
|
-
const assignment =
|
|
18265
|
+
const assignment = ASSIGNMENT2.exec(first);
|
|
17440
18266
|
if (assignment) {
|
|
17441
18267
|
if (GIT_CONFIG_ENV.test(assignment[1]))
|
|
17442
18268
|
env[assignment[1]] = assignment[2];
|
|
@@ -17495,10 +18321,10 @@ var assignments = (words, vars) => {
|
|
|
17495
18321
|
while (rest[0]?.startsWith("-"))
|
|
17496
18322
|
rest = rest.slice(1);
|
|
17497
18323
|
}
|
|
17498
|
-
if (rest.length === 0 || !rest.every((word) =>
|
|
18324
|
+
if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
|
|
17499
18325
|
return null;
|
|
17500
18326
|
return rest.map((word) => {
|
|
17501
|
-
const [, name, value] =
|
|
18327
|
+
const [, name, value] = ASSIGNMENT2.exec(word);
|
|
17502
18328
|
return [name, expandWord(value, vars)];
|
|
17503
18329
|
});
|
|
17504
18330
|
};
|
|
@@ -17686,7 +18512,7 @@ var splitString = (line) => {
|
|
|
17686
18512
|
return line.slice(index).join(" ").slice(15);
|
|
17687
18513
|
if (/^-S./.test(word))
|
|
17688
18514
|
return [word.slice(2), ...line.slice(index + 1)].join(" ");
|
|
17689
|
-
if (!word.startsWith("-") && !
|
|
18515
|
+
if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
|
|
17690
18516
|
return null;
|
|
17691
18517
|
}
|
|
17692
18518
|
return null;
|
|
@@ -18270,17 +19096,17 @@ var headSha2 = (repo) => {
|
|
|
18270
19096
|
if (!git || git.reftable)
|
|
18271
19097
|
return null;
|
|
18272
19098
|
try {
|
|
18273
|
-
const head = fs17.readFileSync(
|
|
19099
|
+
const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18274
19100
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18275
19101
|
return head;
|
|
18276
19102
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18277
19103
|
if (!ref)
|
|
18278
19104
|
return null;
|
|
18279
|
-
const common =
|
|
19105
|
+
const common = path22.dirname(repo.location.dir);
|
|
18280
19106
|
try {
|
|
18281
|
-
return fs17.readFileSync(
|
|
19107
|
+
return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
|
|
18282
19108
|
} catch {}
|
|
18283
|
-
const packed = fs17.readFileSync(
|
|
19109
|
+
const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
|
|
18284
19110
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18285
19111
|
} catch {
|
|
18286
19112
|
return null;
|
|
@@ -18368,7 +19194,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
18368
19194
|
const sub = invocation.args[0];
|
|
18369
19195
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
18370
19196
|
return [invocation];
|
|
18371
|
-
const dir =
|
|
19197
|
+
const dir = path22.resolve(root, invocation.dir ?? ".");
|
|
18372
19198
|
if (!fs17.existsSync(dir))
|
|
18373
19199
|
return [invocation];
|
|
18374
19200
|
const config = gitConfig(dir, invocation, cache);
|
|
@@ -18391,11 +19217,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
18391
19217
|
var normalizedUrl = (url, dir) => {
|
|
18392
19218
|
const trimmed = url.trim();
|
|
18393
19219
|
if (/^file:\/\//i.test(trimmed))
|
|
18394
|
-
return
|
|
19220
|
+
return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18395
19221
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
18396
19222
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
18397
19223
|
if (!scheme && !scp)
|
|
18398
|
-
return
|
|
19224
|
+
return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18399
19225
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
18400
19226
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18401
19227
|
};
|
|
@@ -18415,7 +19241,7 @@ var rewrittenUrl = (url, config) => {
|
|
|
18415
19241
|
}
|
|
18416
19242
|
return url;
|
|
18417
19243
|
};
|
|
18418
|
-
var
|
|
19244
|
+
var pushUrl2 = (config, remote, dir) => {
|
|
18419
19245
|
const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
|
|
18420
19246
|
return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
|
|
18421
19247
|
};
|
|
@@ -18443,7 +19269,7 @@ var toPushRemote = (repo, remote, config) => {
|
|
|
18443
19269
|
const pushRemote = pushRemoteOf(config, repo.branch);
|
|
18444
19270
|
if (pushRemote === null || pushRemote === remote)
|
|
18445
19271
|
return true;
|
|
18446
|
-
return
|
|
19272
|
+
return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
|
|
18447
19273
|
};
|
|
18448
19274
|
var quote = (text) => `\`${text}\``;
|
|
18449
19275
|
var refusal = (action, branch, dir) => {
|
|
@@ -18619,7 +19445,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
|
18619
19445
|
const out = [];
|
|
18620
19446
|
const repos = new Map;
|
|
18621
19447
|
const repoFor = (relative) => {
|
|
18622
|
-
const dir =
|
|
19448
|
+
const dir = path22.resolve(cwd, relative ?? ".");
|
|
18623
19449
|
if (!repos.has(dir)) {
|
|
18624
19450
|
const repo = repoAt(dir);
|
|
18625
19451
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18705,7 +19531,7 @@ var analysisDenial = (error) => ({
|
|
|
18705
19531
|
});
|
|
18706
19532
|
var NEXT_COMMAND = "next";
|
|
18707
19533
|
var MARKER_TTL_SECONDS = 3600;
|
|
18708
|
-
var markerFile = (repo, session, key) =>
|
|
19534
|
+
var markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18709
19535
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash15("sha256").update(command).digest("hex")}`;
|
|
18710
19536
|
function noteRawCommit(input, command, key) {
|
|
18711
19537
|
try {
|
|
@@ -18716,7 +19542,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18716
19542
|
if (action.kind !== "commit")
|
|
18717
19543
|
continue;
|
|
18718
19544
|
const file = markerFile(repo, input.session.id, key);
|
|
18719
|
-
fs17.mkdirSync(
|
|
19545
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
18720
19546
|
fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18721
19547
|
`);
|
|
18722
19548
|
}
|
|
@@ -18947,8 +19773,8 @@ var judgedDir = (root, dir) => {
|
|
|
18947
19773
|
return null;
|
|
18948
19774
|
if (dir === null)
|
|
18949
19775
|
return root;
|
|
18950
|
-
const resolved =
|
|
18951
|
-
return
|
|
19776
|
+
const resolved = path23.resolve(root, dir);
|
|
19777
|
+
return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18952
19778
|
};
|
|
18953
19779
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18954
19780
|
let judged = false;
|
|
@@ -18984,7 +19810,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
18984
19810
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
18985
19811
|
import fs18 from "node:fs";
|
|
18986
19812
|
import os6 from "node:os";
|
|
18987
|
-
import
|
|
19813
|
+
import path24 from "node:path";
|
|
18988
19814
|
var EMPTY2 = () => ({ deny: [], allow: [] });
|
|
18989
19815
|
var readText2 = (file) => {
|
|
18990
19816
|
try {
|
|
@@ -19025,19 +19851,19 @@ function parseJsonc(text) {
|
|
|
19025
19851
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
19026
19852
|
}
|
|
19027
19853
|
function gitTop(cwd) {
|
|
19028
|
-
for (let dir =
|
|
19029
|
-
if (fs18.existsSync(
|
|
19854
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
19855
|
+
if (fs18.existsSync(path24.join(dir, ".git")))
|
|
19030
19856
|
return dir;
|
|
19031
|
-
if (
|
|
19857
|
+
if (path24.dirname(dir) === dir)
|
|
19032
19858
|
return null;
|
|
19033
19859
|
}
|
|
19034
19860
|
}
|
|
19035
19861
|
var projectDirs = (cwd) => {
|
|
19036
19862
|
const top = gitTop(cwd);
|
|
19037
19863
|
const dirs = [];
|
|
19038
|
-
for (let dir =
|
|
19864
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
19039
19865
|
dirs.push(dir);
|
|
19040
|
-
if (!top || dir === top ||
|
|
19866
|
+
if (!top || dir === top || path24.dirname(dir) === dir)
|
|
19041
19867
|
return dirs;
|
|
19042
19868
|
}
|
|
19043
19869
|
};
|
|
@@ -19049,12 +19875,12 @@ var CLAUDE_MANAGED = {
|
|
|
19049
19875
|
};
|
|
19050
19876
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
19051
19877
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
19052
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
19878
|
+
const user = env.CLAUDE_CONFIG_DIR || path24.join(homeOf(env), ".claude");
|
|
19053
19879
|
return [
|
|
19054
19880
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
19055
|
-
|
|
19056
|
-
|
|
19057
|
-
|
|
19881
|
+
path24.join(user, "settings.json"),
|
|
19882
|
+
path24.join(project, ".claude", "settings.json"),
|
|
19883
|
+
path24.join(project, ".claude", "settings.local.json")
|
|
19058
19884
|
];
|
|
19059
19885
|
}
|
|
19060
19886
|
var bashPattern = (entry) => {
|
|
@@ -19144,10 +19970,10 @@ var opencodeConfig = (value, rules) => {
|
|
|
19144
19970
|
function opencodeRules(cwd, env = process.env) {
|
|
19145
19971
|
const rules = EMPTY2();
|
|
19146
19972
|
try {
|
|
19147
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
19973
|
+
const config = env.XDG_CONFIG_HOME || path24.join(homeOf(env), ".config");
|
|
19148
19974
|
const files = [
|
|
19149
|
-
...OPENCODE_NAMES.map((name) =>
|
|
19150
|
-
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [
|
|
19975
|
+
...OPENCODE_NAMES.map((name) => path24.join(config, "opencode", name)),
|
|
19976
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path24.join(dir, name), path24.join(dir, ".opencode", name)])),
|
|
19151
19977
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
19152
19978
|
];
|
|
19153
19979
|
for (const file of files) {
|
|
@@ -19191,9 +20017,9 @@ var codexRule = (body) => {
|
|
|
19191
20017
|
function codexRules(cwd, env = process.env) {
|
|
19192
20018
|
const rules = EMPTY2();
|
|
19193
20019
|
try {
|
|
19194
|
-
const home = env.CODEX_HOME ||
|
|
20020
|
+
const home = env.CODEX_HOME || path24.join(homeOf(env), ".codex");
|
|
19195
20021
|
const top = gitTop(cwd) ?? cwd;
|
|
19196
|
-
for (const dir of [
|
|
20022
|
+
for (const dir of [path24.join(home, "rules"), path24.join(top, ".codex", "rules")]) {
|
|
19197
20023
|
let files;
|
|
19198
20024
|
try {
|
|
19199
20025
|
files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -19203,7 +20029,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
19203
20029
|
return "unreadable";
|
|
19204
20030
|
}
|
|
19205
20031
|
for (const name of files) {
|
|
19206
|
-
const text = readText2(
|
|
20032
|
+
const text = readText2(path24.join(dir, name)) ?? "";
|
|
19207
20033
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
19208
20034
|
const rule = codexRule(part);
|
|
19209
20035
|
if (!rule)
|
|
@@ -19222,8 +20048,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
19222
20048
|
try {
|
|
19223
20049
|
const top = gitTop(cwd) ?? cwd;
|
|
19224
20050
|
for (const file of [
|
|
19225
|
-
|
|
19226
|
-
|
|
20051
|
+
path24.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
20052
|
+
path24.join(top, ".cursor", "cli.json")
|
|
19227
20053
|
]) {
|
|
19228
20054
|
const text = readText2(file);
|
|
19229
20055
|
if (text === null)
|
|
@@ -19299,21 +20125,21 @@ function wrapAllowed(read, words, command, options = {}) {
|
|
|
19299
20125
|
// packages/workit-core/src/core/heavy.ts
|
|
19300
20126
|
import fs19 from "node:fs";
|
|
19301
20127
|
import os7 from "node:os";
|
|
19302
|
-
import
|
|
20128
|
+
import path25 from "node:path";
|
|
19303
20129
|
function stateDir(env, platform = process.platform) {
|
|
19304
20130
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19305
20131
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19306
20132
|
if (env.XDG_STATE_HOME)
|
|
19307
|
-
return
|
|
20133
|
+
return path25.join(env.XDG_STATE_HOME, "workit");
|
|
19308
20134
|
const home = env.HOME || os7.homedir();
|
|
19309
20135
|
if (platform === "darwin")
|
|
19310
|
-
return
|
|
20136
|
+
return path25.join(home, "Library", "Application Support", "workit");
|
|
19311
20137
|
if (platform === "win32")
|
|
19312
|
-
return
|
|
19313
|
-
return
|
|
20138
|
+
return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
|
|
20139
|
+
return path25.join(home, ".local", "state", "workit");
|
|
19314
20140
|
}
|
|
19315
20141
|
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
19316
|
-
var heavyCommandsFile = (env) =>
|
|
20142
|
+
var heavyCommandsFile = (env) => path25.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
19317
20143
|
var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
|
|
19318
20144
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
19319
20145
|
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
@@ -19588,7 +20414,7 @@ function machinePatterns(env) {
|
|
|
19588
20414
|
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
19589
20415
|
}
|
|
19590
20416
|
function repoPatterns(cwd) {
|
|
19591
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
20417
|
+
return repoHeavyCommands(gitTop(cwd) ?? path25.resolve(cwd));
|
|
19592
20418
|
}
|
|
19593
20419
|
function heavyLines(lines, options) {
|
|
19594
20420
|
const found = [];
|
|
@@ -19666,12 +20492,12 @@ function heavyPlan(command, options) {
|
|
|
19666
20492
|
}
|
|
19667
20493
|
function workitOnPath2(env = process.env) {
|
|
19668
20494
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
19669
|
-
for (const dir of (env.PATH ?? "").split(
|
|
20495
|
+
for (const dir of (env.PATH ?? "").split(path25.delimiter))
|
|
19670
20496
|
for (const name of names) {
|
|
19671
20497
|
if (!dir)
|
|
19672
20498
|
continue;
|
|
19673
20499
|
try {
|
|
19674
|
-
fs19.accessSync(
|
|
20500
|
+
fs19.accessSync(path25.join(dir, name), fs19.constants.X_OK);
|
|
19675
20501
|
return true;
|
|
19676
20502
|
} catch {}
|
|
19677
20503
|
}
|
|
@@ -19690,20 +20516,20 @@ function leaseRewrite(command) {
|
|
|
19690
20516
|
// packages/workit-core/src/core/heavy-words.ts
|
|
19691
20517
|
import fs20 from "node:fs";
|
|
19692
20518
|
import os8 from "node:os";
|
|
19693
|
-
import
|
|
20519
|
+
import path26 from "node:path";
|
|
19694
20520
|
var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
|
|
19695
20521
|
var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
|
|
19696
20522
|
var stateDir2 = (env) => {
|
|
19697
20523
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19698
20524
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19699
20525
|
if (env.XDG_STATE_HOME)
|
|
19700
|
-
return
|
|
20526
|
+
return path26.join(env.XDG_STATE_HOME, "workit");
|
|
19701
20527
|
const home = env.HOME || os8.homedir();
|
|
19702
20528
|
if (process.platform === "darwin")
|
|
19703
|
-
return
|
|
20529
|
+
return path26.join(home, "Library", "Application Support", "workit");
|
|
19704
20530
|
if (process.platform === "win32")
|
|
19705
|
-
return
|
|
19706
|
-
return
|
|
20531
|
+
return path26.join(env.LOCALAPPDATA ?? path26.join(home, "AppData", "Local"), "workit");
|
|
20532
|
+
return path26.join(home, ".local", "state", "workit");
|
|
19707
20533
|
};
|
|
19708
20534
|
var readJson2 = (file) => {
|
|
19709
20535
|
try {
|
|
@@ -19714,15 +20540,15 @@ var readJson2 = (file) => {
|
|
|
19714
20540
|
};
|
|
19715
20541
|
var patternWords = (cwd, env) => {
|
|
19716
20542
|
const patterns = [];
|
|
19717
|
-
const machine = readJson2(
|
|
20543
|
+
const machine = readJson2(path26.join(stateDir2(env), "heavy-commands.json"));
|
|
19718
20544
|
if (Array.isArray(machine?.patterns))
|
|
19719
20545
|
patterns.push(...machine.patterns);
|
|
19720
|
-
for (let dir =
|
|
19721
|
-
if (fs20.existsSync(
|
|
20546
|
+
for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
|
|
20547
|
+
if (fs20.existsSync(path26.join(dir, ".git"))) {
|
|
19722
20548
|
patterns.push(...repoHeavyCommands(dir));
|
|
19723
20549
|
break;
|
|
19724
20550
|
}
|
|
19725
|
-
if (
|
|
20551
|
+
if (path26.dirname(dir) === dir)
|
|
19726
20552
|
break;
|
|
19727
20553
|
}
|
|
19728
20554
|
const words = new Set;
|
|
@@ -19792,16 +20618,16 @@ var splitDenial = (plan) => ({
|
|
|
19792
20618
|
unblock: plan.unblock
|
|
19793
20619
|
});
|
|
19794
20620
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19795
|
-
import { existsSync as
|
|
20621
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
19796
20622
|
import os10 from "node:os";
|
|
19797
|
-
import
|
|
20623
|
+
import path28 from "node:path";
|
|
19798
20624
|
|
|
19799
20625
|
// packages/workit-core/src/core/live-session.ts
|
|
19800
20626
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
19801
20627
|
import { createHash as createHash16 } from "node:crypto";
|
|
19802
20628
|
import fs21 from "node:fs";
|
|
19803
20629
|
import os9 from "node:os";
|
|
19804
|
-
import
|
|
20630
|
+
import path27 from "node:path";
|
|
19805
20631
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
19806
20632
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
19807
20633
|
var liveProbe = {
|
|
@@ -19875,15 +20701,15 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
19875
20701
|
};
|
|
19876
20702
|
};
|
|
19877
20703
|
var checkoutOf = (target) => {
|
|
19878
|
-
let dir =
|
|
19879
|
-
while (!fs21.existsSync(dir) &&
|
|
19880
|
-
dir =
|
|
20704
|
+
let dir = path27.resolve(target);
|
|
20705
|
+
while (!fs21.existsSync(dir) && path27.dirname(dir) !== dir)
|
|
20706
|
+
dir = path27.dirname(dir);
|
|
19881
20707
|
if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
|
|
19882
|
-
dir =
|
|
19883
|
-
const parts = dir.split(
|
|
20708
|
+
dir = path27.dirname(dir);
|
|
20709
|
+
const parts = dir.split(path27.sep);
|
|
19884
20710
|
const dotGit = parts.lastIndexOf(".git");
|
|
19885
20711
|
if (dotGit > 0)
|
|
19886
|
-
dir = parts.slice(0, dotGit).join(
|
|
20712
|
+
dir = parts.slice(0, dotGit).join(path27.sep) || path27.sep;
|
|
19887
20713
|
const location = resolveStore(dir);
|
|
19888
20714
|
if (location instanceof Error || !location.shared || !location.top)
|
|
19889
20715
|
return null;
|
|
@@ -19893,9 +20719,9 @@ var checkoutOf = (target) => {
|
|
|
19893
20719
|
} catch {}
|
|
19894
20720
|
return top;
|
|
19895
20721
|
};
|
|
19896
|
-
var markerFile2 = (top) =>
|
|
20722
|
+
var markerFile2 = (top) => path27.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
19897
20723
|
var checkoutKey = (top, platform = process.platform) => {
|
|
19898
|
-
let key =
|
|
20724
|
+
let key = path27.resolve(top);
|
|
19899
20725
|
try {
|
|
19900
20726
|
key = fs21.realpathSync.native(key);
|
|
19901
20727
|
} catch {}
|
|
@@ -19990,7 +20816,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
19990
20816
|
checkout,
|
|
19991
20817
|
heartbeatAt: now.toISOString()
|
|
19992
20818
|
};
|
|
19993
|
-
fs21.mkdirSync(
|
|
20819
|
+
fs21.mkdirSync(path27.dirname(file), { recursive: true });
|
|
19994
20820
|
const temp = `${file}.${process.pid}.tmp`;
|
|
19995
20821
|
fs21.writeFileSync(temp, JSON.stringify(marker));
|
|
19996
20822
|
fs21.renameSync(temp, file);
|
|
@@ -20002,22 +20828,22 @@ var NONE3 = { kind: "none" };
|
|
|
20002
20828
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20003
20829
|
var canonical2 = (file) => {
|
|
20004
20830
|
let rest = "";
|
|
20005
|
-
for (let dir =
|
|
20831
|
+
for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
|
|
20006
20832
|
try {
|
|
20007
|
-
return
|
|
20833
|
+
return path28.join(realpathSync5.native(dir), rest);
|
|
20008
20834
|
} catch {
|
|
20009
|
-
if (
|
|
20010
|
-
return
|
|
20011
|
-
rest = rest ?
|
|
20835
|
+
if (path28.dirname(dir) === dir)
|
|
20836
|
+
return path28.resolve(file);
|
|
20837
|
+
rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
|
|
20012
20838
|
}
|
|
20013
20839
|
}
|
|
20014
20840
|
};
|
|
20015
20841
|
var exempt = (cwd, root, file, plans) => {
|
|
20016
20842
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20017
|
-
const relative =
|
|
20018
|
-
if (relative.startsWith("..") ||
|
|
20843
|
+
const relative = path28.relative(fold(canonical2(root)), fold(canonical2(path28.resolve(cwd, file))));
|
|
20844
|
+
if (relative.startsWith("..") || path28.isAbsolute(relative))
|
|
20019
20845
|
return true;
|
|
20020
|
-
const posix = relative.split(
|
|
20846
|
+
const posix = relative.split(path28.sep).join("/");
|
|
20021
20847
|
const segments = posix.split("/");
|
|
20022
20848
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20023
20849
|
};
|
|
@@ -20194,10 +21020,10 @@ var linkSources = (args, at) => {
|
|
|
20194
21020
|
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
20195
21021
|
const target = dest === undefined ? null : at(dest);
|
|
20196
21022
|
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
20197
|
-
const base = target === null ? null : into >= 0 || sources.length > 1 ||
|
|
21023
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path28.dirname(target);
|
|
20198
21024
|
return sources.flatMap((source) => {
|
|
20199
|
-
if (symbolic && base !== null && !
|
|
20200
|
-
return [
|
|
21025
|
+
if (symbolic && base !== null && !path28.isAbsolute(homeExpanded(source)))
|
|
21026
|
+
return [path28.resolve(base, source)];
|
|
20201
21027
|
const resolved = at(source);
|
|
20202
21028
|
return resolved ? [resolved] : [];
|
|
20203
21029
|
});
|
|
@@ -20226,7 +21052,7 @@ var shellWrites = (command, depth = 0) => {
|
|
|
20226
21052
|
return { writes: true, targets: unknown ? null : targets };
|
|
20227
21053
|
};
|
|
20228
21054
|
var cache2 = new Map;
|
|
20229
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
21055
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path28.resolve(root, file)) ?? "-").join("\x00");
|
|
20230
21056
|
var gateFor = (cwd) => {
|
|
20231
21057
|
const store = new TaskStore(cwd);
|
|
20232
21058
|
const listed = store.listTaskIndex();
|
|
@@ -20305,13 +21131,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
20305
21131
|
return [];
|
|
20306
21132
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20307
21133
|
return [];
|
|
20308
|
-
return [
|
|
21134
|
+
return [path28.resolve(cwd, invocation.dir ?? ".")];
|
|
20309
21135
|
});
|
|
20310
21136
|
};
|
|
20311
21137
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20312
21138
|
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20313
21139
|
var POPD2 = new Set(["popd", "pop-location"]);
|
|
20314
|
-
var UNKNOWN_DIR =
|
|
21140
|
+
var UNKNOWN_DIR = path28.resolve(path28.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
20315
21141
|
var cdInto = (dir, words, strict = false) => {
|
|
20316
21142
|
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
20317
21143
|
const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
@@ -20320,7 +21146,7 @@ var cdInto = (dir, words, strict = false) => {
|
|
|
20320
21146
|
return lost;
|
|
20321
21147
|
if (dir === undefined)
|
|
20322
21148
|
return lost;
|
|
20323
|
-
return target === undefined ? os10.homedir() :
|
|
21149
|
+
return target === undefined ? os10.homedir() : path28.resolve(dir, target);
|
|
20324
21150
|
};
|
|
20325
21151
|
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
20326
21152
|
var homeExpanded = (file) => {
|
|
@@ -20334,7 +21160,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20334
21160
|
const groups = [];
|
|
20335
21161
|
const at = (file) => {
|
|
20336
21162
|
const home = homeExpanded(file);
|
|
20337
|
-
return
|
|
21163
|
+
return path28.isAbsolute(home) ? home : dir === undefined ? null : path28.resolve(dir, home);
|
|
20338
21164
|
};
|
|
20339
21165
|
for (const segment of segmentsOf(command, dialect)) {
|
|
20340
21166
|
if (segment.group === "open") {
|
|
@@ -20374,7 +21200,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20374
21200
|
for (const file of opaque ?? []) {
|
|
20375
21201
|
const resolved = at(file);
|
|
20376
21202
|
if (resolved)
|
|
20377
|
-
targets.push(file === "." ?
|
|
21203
|
+
targets.push(file === "." ? path28.join(resolved, DIR_TARGET) : resolved);
|
|
20378
21204
|
}
|
|
20379
21205
|
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
20380
21206
|
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
@@ -20389,7 +21215,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20389
21215
|
const edits = editsOf(segment.words);
|
|
20390
21216
|
if (edits === null) {
|
|
20391
21217
|
if (dir !== undefined)
|
|
20392
|
-
targets.push(
|
|
21218
|
+
targets.push(path28.join(dir, DIR_TARGET));
|
|
20393
21219
|
} else
|
|
20394
21220
|
for (const file of edits) {
|
|
20395
21221
|
const resolved = at(file);
|
|
@@ -20400,20 +21226,20 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20400
21226
|
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
20401
21227
|
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
20402
21228
|
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
20403
|
-
targets.push(
|
|
21229
|
+
targets.push(path28.join(runsIn, DIR_TARGET));
|
|
20404
21230
|
}
|
|
20405
21231
|
}
|
|
20406
21232
|
return targets;
|
|
20407
21233
|
};
|
|
20408
21234
|
var workitCwd = (dir, args) => {
|
|
20409
21235
|
const { rest, cwd } = skipWorkitGlobals(args);
|
|
20410
|
-
return { at: cwd === null ? dir :
|
|
21236
|
+
return { at: cwd === null ? dir : path28.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
20411
21237
|
};
|
|
20412
21238
|
var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
20413
21239
|
try {
|
|
20414
21240
|
return [
|
|
20415
21241
|
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
20416
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
21242
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path28.join(dir, DIR_TARGET))
|
|
20417
21243
|
];
|
|
20418
21244
|
} catch {
|
|
20419
21245
|
return [];
|
|
@@ -20450,7 +21276,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
20450
21276
|
var guarded = (targets) => {
|
|
20451
21277
|
const seen = new Set;
|
|
20452
21278
|
return targets.filter((target) => {
|
|
20453
|
-
const dir =
|
|
21279
|
+
const dir = path28.dirname(target);
|
|
20454
21280
|
if (seen.has(dir))
|
|
20455
21281
|
return false;
|
|
20456
21282
|
seen.add(dir);
|
|
@@ -20471,7 +21297,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
20471
21297
|
for (const target of guarded(targets))
|
|
20472
21298
|
claimLiveSession(target, owner, deps);
|
|
20473
21299
|
};
|
|
20474
|
-
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
21300
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path28.resolve(cwd, file)) : [path28.join(cwd, DIR_TARGET)];
|
|
20475
21301
|
var issuedTarget = (cwd, targets) => {
|
|
20476
21302
|
const state = resolveStateDir();
|
|
20477
21303
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
@@ -20479,7 +21305,7 @@ var issuedTarget = (cwd, targets) => {
|
|
|
20479
21305
|
};
|
|
20480
21306
|
var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
20481
21307
|
const win = platform === "win32";
|
|
20482
|
-
const p = win ?
|
|
21308
|
+
const p = win ? path28.win32 : path28.posix;
|
|
20483
21309
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
20484
21310
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
20485
21311
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -20570,6 +21396,24 @@ function beforeWriteGate(cwd, paths) {
|
|
|
20570
21396
|
}
|
|
20571
21397
|
// packages/workit-core/src/hooks/stop.ts
|
|
20572
21398
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
21399
|
+
|
|
21400
|
+
// packages/workit-core/src/core/policy/current-judgment.ts
|
|
21401
|
+
function currentJudgment(cwd) {
|
|
21402
|
+
try {
|
|
21403
|
+
const store = new TaskStore(cwd);
|
|
21404
|
+
const listed = store.listTaskIndex();
|
|
21405
|
+
const key = store.currentKey();
|
|
21406
|
+
if (!listed.ok || !key.ok)
|
|
21407
|
+
return null;
|
|
21408
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
21409
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
21410
|
+
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
21411
|
+
} catch {
|
|
21412
|
+
return null;
|
|
21413
|
+
}
|
|
21414
|
+
}
|
|
21415
|
+
|
|
21416
|
+
// packages/workit-core/src/hooks/stop.ts
|
|
20573
21417
|
var NONE4 = { kind: "none" };
|
|
20574
21418
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
20575
21419
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -20662,29 +21506,32 @@ var redChecks = (rows, pr, head, endpoint) => {
|
|
|
20662
21506
|
reason: `PR #${pr}'s checks were last seen ${state} at ${short(head)}, and this workspace's endpoint is \`${endpoint}\`: run \`workit ci wait --pr ${pr}\`${state === "failing" ? " after fixing the failures `workit pr status` lists (then push)" : ""}.`
|
|
20663
21507
|
};
|
|
20664
21508
|
};
|
|
20665
|
-
var
|
|
20666
|
-
|
|
20667
|
-
|
|
20668
|
-
const listed = store.listTaskIndex();
|
|
20669
|
-
const key = store.currentKey();
|
|
20670
|
-
if (!listed.ok || !key.ok)
|
|
20671
|
-
return null;
|
|
20672
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
20673
|
-
const task = entry ? store.readTask(entry.id) : null;
|
|
20674
|
-
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
20675
|
-
} catch {
|
|
20676
|
-
return null;
|
|
20677
|
-
}
|
|
21509
|
+
var prBase = (rows) => {
|
|
21510
|
+
const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
|
|
21511
|
+
return typeof seen?.base === "string" ? seen.base : null;
|
|
20678
21512
|
};
|
|
20679
|
-
var unverified = (cwd, branch, all, descriptor, verification) => {
|
|
21513
|
+
var unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
20680
21514
|
const check = checkVerdicts(cwd, branch, all);
|
|
20681
21515
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
20682
21516
|
return null;
|
|
20683
|
-
const judgment =
|
|
21517
|
+
const judgment = currentJudgment(cwd);
|
|
20684
21518
|
const high = judgment?.riskTier === "high";
|
|
20685
|
-
|
|
20686
|
-
|
|
20687
|
-
|
|
21519
|
+
let independent;
|
|
21520
|
+
if (verification === "proportional") {
|
|
21521
|
+
const lane = branchLane(cwd, {
|
|
21522
|
+
base: prBase(all.filter((row) => row.branch === branch)),
|
|
21523
|
+
head,
|
|
21524
|
+
judgment
|
|
21525
|
+
});
|
|
21526
|
+
const need = laneNeed(lane.lane);
|
|
21527
|
+
if (need === "none")
|
|
21528
|
+
return null;
|
|
21529
|
+
independent = need === "non-author";
|
|
21530
|
+
} else {
|
|
21531
|
+
if (judgment && !judgment.behaviorChange && !high)
|
|
21532
|
+
return null;
|
|
21533
|
+
independent = verification === "independent" || high;
|
|
21534
|
+
}
|
|
20688
21535
|
if (!independent && check.review === "self-reviewed")
|
|
20689
21536
|
return null;
|
|
20690
21537
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -20731,7 +21578,7 @@ function stopObligation(input, descriptor) {
|
|
|
20731
21578
|
}
|
|
20732
21579
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
20733
21580
|
return null;
|
|
20734
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
21581
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
20735
21582
|
return left ? { ...left, head } : null;
|
|
20736
21583
|
} catch {
|
|
20737
21584
|
return null;
|
|
@@ -20992,13 +21839,13 @@ ${line}` };
|
|
|
20992
21839
|
return decision;
|
|
20993
21840
|
};
|
|
20994
21841
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20995
|
-
import { existsSync as
|
|
20996
|
-
import
|
|
21842
|
+
import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21843
|
+
import path29 from "node:path";
|
|
20997
21844
|
var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20998
21845
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
20999
21846
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
21000
21847
|
var existingDirectory = (value) => {
|
|
21001
|
-
if (!nonEmpty2(value) || !
|
|
21848
|
+
if (!nonEmpty2(value) || !path29.isAbsolute(value) || !existsSync9(value))
|
|
21002
21849
|
return null;
|
|
21003
21850
|
try {
|
|
21004
21851
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -21066,9 +21913,9 @@ var writeTargets = (input) => {
|
|
|
21066
21913
|
};
|
|
21067
21914
|
// packages/workit-core/src/hooks/standing.ts
|
|
21068
21915
|
import fs22 from "node:fs";
|
|
21069
|
-
import
|
|
21916
|
+
import path30 from "node:path";
|
|
21070
21917
|
var readPlans = (store) => {
|
|
21071
|
-
const dir =
|
|
21918
|
+
const dir = path30.join(store, "fanouts");
|
|
21072
21919
|
let names;
|
|
21073
21920
|
try {
|
|
21074
21921
|
names = fs22.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -21078,7 +21925,7 @@ var readPlans = (store) => {
|
|
|
21078
21925
|
const plans = [];
|
|
21079
21926
|
for (const name of names) {
|
|
21080
21927
|
try {
|
|
21081
|
-
const raw = JSON.parse(fs22.readFileSync(
|
|
21928
|
+
const raw = JSON.parse(fs22.readFileSync(path30.join(dir, name), "utf8"));
|
|
21082
21929
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
21083
21930
|
continue;
|
|
21084
21931
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -21204,40 +22051,40 @@ var cwdRewrite = (command) => {
|
|
|
21204
22051
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
21205
22052
|
import { createHash as createHash17 } from "node:crypto";
|
|
21206
22053
|
import fs23 from "node:fs";
|
|
21207
|
-
import
|
|
22054
|
+
import path31 from "node:path";
|
|
21208
22055
|
var FANOUT_TOKEN_FILE = "workit-fanout-token";
|
|
21209
22056
|
var NONE5 = { kind: "none" };
|
|
21210
22057
|
var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
|
|
21211
22058
|
var canonical3 = (file) => {
|
|
21212
22059
|
let rest = "";
|
|
21213
|
-
for (let dir =
|
|
22060
|
+
for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
|
|
21214
22061
|
try {
|
|
21215
|
-
return
|
|
22062
|
+
return path31.join(fs23.realpathSync.native(dir), rest);
|
|
21216
22063
|
} catch {
|
|
21217
|
-
if (
|
|
21218
|
-
return
|
|
21219
|
-
rest = rest ?
|
|
22064
|
+
if (path31.dirname(dir) === dir)
|
|
22065
|
+
return path31.resolve(file);
|
|
22066
|
+
rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
|
|
21220
22067
|
}
|
|
21221
22068
|
}
|
|
21222
22069
|
};
|
|
21223
22070
|
var within = (child, parent) => {
|
|
21224
22071
|
const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
|
|
21225
|
-
const relative =
|
|
21226
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
22072
|
+
const relative = path31.relative(fold(parent), fold(child));
|
|
22073
|
+
return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
|
|
21227
22074
|
};
|
|
21228
22075
|
var checkoutOf2 = (file) => {
|
|
21229
|
-
for (let dir = canonical3(file);; dir =
|
|
21230
|
-
const dotGit =
|
|
22076
|
+
for (let dir = canonical3(file);; dir = path31.dirname(dir)) {
|
|
22077
|
+
const dotGit = path31.join(dir, ".git");
|
|
21231
22078
|
if (fs23.existsSync(dotGit)) {
|
|
21232
22079
|
if (!fs23.statSync(dotGit).isFile())
|
|
21233
22080
|
return { root: dir, workit: false };
|
|
21234
22081
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21235
22082
|
return {
|
|
21236
22083
|
root: dir,
|
|
21237
|
-
workit: Boolean(admin) && fs23.existsSync(
|
|
22084
|
+
workit: Boolean(admin) && fs23.existsSync(path31.join(path31.resolve(dir, admin), FANOUT_TOKEN_FILE))
|
|
21238
22085
|
};
|
|
21239
22086
|
}
|
|
21240
|
-
if (
|
|
22087
|
+
if (path31.dirname(dir) === dir)
|
|
21241
22088
|
return null;
|
|
21242
22089
|
}
|
|
21243
22090
|
};
|
|
@@ -21249,8 +22096,8 @@ var workitWorktreeOf = (file) => {
|
|
|
21249
22096
|
return null;
|
|
21250
22097
|
}
|
|
21251
22098
|
};
|
|
21252
|
-
var bindingDir = () =>
|
|
21253
|
-
var bindingFile = (session, agentId) =>
|
|
22099
|
+
var bindingDir = () => path31.join(resolveStateDir(), "worktree-bind");
|
|
22100
|
+
var bindingFile = (session, agentId) => path31.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
|
|
21254
22101
|
var readBinding = (file) => {
|
|
21255
22102
|
try {
|
|
21256
22103
|
const value = JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
@@ -21275,7 +22122,7 @@ var bind = (input, worktree) => {
|
|
|
21275
22122
|
agentId,
|
|
21276
22123
|
at: new Date().toISOString()
|
|
21277
22124
|
};
|
|
21278
|
-
fs23.mkdirSync(
|
|
22125
|
+
fs23.mkdirSync(path31.dirname(file), { recursive: true });
|
|
21279
22126
|
fs23.writeFileSync(file, JSON.stringify(binding));
|
|
21280
22127
|
try {
|
|
21281
22128
|
if (inWorkitWorkspace(input.cwd))
|
|
@@ -21294,25 +22141,25 @@ var hostIsolated = (cwd, agentId) => {
|
|
|
21294
22141
|
const checkout = checkoutOf2(cwd);
|
|
21295
22142
|
if (!checkout)
|
|
21296
22143
|
return false;
|
|
21297
|
-
const dotGit =
|
|
22144
|
+
const dotGit = path31.join(checkout.root, ".git");
|
|
21298
22145
|
if (!fs23.statSync(dotGit).isFile())
|
|
21299
22146
|
return false;
|
|
21300
22147
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21301
22148
|
if (!admin)
|
|
21302
22149
|
return false;
|
|
21303
|
-
const adminDir =
|
|
21304
|
-
const back = fs23.readFileSync(
|
|
21305
|
-
if (
|
|
22150
|
+
const adminDir = path31.resolve(checkout.root, admin);
|
|
22151
|
+
const back = fs23.readFileSync(path31.join(adminDir, "gitdir"), "utf8").trim();
|
|
22152
|
+
if (path31.relative(canonical3(path31.resolve(adminDir, back)), canonical3(dotGit)) !== "")
|
|
21306
22153
|
return false;
|
|
21307
|
-
const common =
|
|
21308
|
-
const gitDir = fs23.existsSync(common) ?
|
|
21309
|
-
const expected =
|
|
21310
|
-
return
|
|
22154
|
+
const common = path31.join(adminDir, "commondir");
|
|
22155
|
+
const gitDir = fs23.existsSync(common) ? path31.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
|
|
22156
|
+
const expected = path31.join(path31.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
|
|
22157
|
+
return path31.relative(canonical3(expected), checkout.root) === "";
|
|
21311
22158
|
} catch {
|
|
21312
22159
|
return false;
|
|
21313
22160
|
}
|
|
21314
22161
|
};
|
|
21315
|
-
var shown = (target) =>
|
|
22162
|
+
var shown = (target) => path31.basename(target) === ".workit-live-target" ? path31.dirname(target) : target;
|
|
21316
22163
|
var unboundDeny = (input, target) => {
|
|
21317
22164
|
const unblock = "Work only in the worktree your brief names, which the lead made with `workit fanout worktree create <slice>`: edit files under it and run one verb per call against it (`workit --cwd <worktree> git commit -m … -- <paths>`, `workit --cwd <worktree> check <name>`). If your brief names no worktree, stop and report that to the lead.";
|
|
21318
22165
|
return {
|
|
@@ -21342,7 +22189,7 @@ function confineImplementer(input, targets) {
|
|
|
21342
22189
|
try {
|
|
21343
22190
|
if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
|
|
21344
22191
|
return NONE5;
|
|
21345
|
-
if (targets.some((target) => within(
|
|
22192
|
+
if (targets.some((target) => within(path31.resolve(target), UNKNOWN_DIR)))
|
|
21346
22193
|
return unknownDirDeny(input, boundWorktree(input));
|
|
21347
22194
|
const bindings = canonical3(bindingDir());
|
|
21348
22195
|
const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
|
|
@@ -21662,7 +22509,7 @@ var dispatchHook = (adapter, raw, env = process.env) => {
|
|
|
21662
22509
|
};
|
|
21663
22510
|
// packages/workit-core/src/hooks/deadline.ts
|
|
21664
22511
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
|
|
21665
|
-
import
|
|
22512
|
+
import path32 from "node:path";
|
|
21666
22513
|
var HOOK_SKIP_TEXT = "workit context skipped: host under load";
|
|
21667
22514
|
var HOOK_DEADLINE_MS = 8000;
|
|
21668
22515
|
var HOST_DEADLINES = {
|
|
@@ -21680,7 +22527,7 @@ var hookDeadlineMs = (env, host = "", native) => {
|
|
|
21680
22527
|
const row = HOST_DEADLINES[host];
|
|
21681
22528
|
return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
|
|
21682
22529
|
};
|
|
21683
|
-
var skipsFile = (stateDir) =>
|
|
22530
|
+
var skipsFile = (stateDir) => path32.join(stateDir, HOOK_SKIPS_FILE);
|
|
21684
22531
|
var recordHookSkip = (skip, stateDir = resolveStateDir()) => {
|
|
21685
22532
|
try {
|
|
21686
22533
|
mkdirSync3(stateDir, { recursive: true });
|
|
@@ -22325,7 +23172,7 @@ var codexAdapter = {
|
|
|
22325
23172
|
|
|
22326
23173
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
22327
23174
|
import fs24 from "node:fs";
|
|
22328
|
-
import
|
|
23175
|
+
import path33 from "node:path";
|
|
22329
23176
|
var undocumented = { support: "undocumented", native: null };
|
|
22330
23177
|
var none = { support: "none", native: null };
|
|
22331
23178
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -22522,7 +23369,7 @@ var protocolEvent2 = (input) => {
|
|
|
22522
23369
|
};
|
|
22523
23370
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
22524
23371
|
var lastAssistantText = (file) => {
|
|
22525
|
-
if (!file || !
|
|
23372
|
+
if (!file || !path33.isAbsolute(file))
|
|
22526
23373
|
return null;
|
|
22527
23374
|
try {
|
|
22528
23375
|
const size = fs24.statSync(file).size;
|
|
@@ -22559,7 +23406,7 @@ var lastAssistantText = (file) => {
|
|
|
22559
23406
|
return null;
|
|
22560
23407
|
}
|
|
22561
23408
|
};
|
|
22562
|
-
var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
23409
|
+
var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path33.sep) ? root : `${root}${path33.sep}`);
|
|
22563
23410
|
var hookCwd = (input) => {
|
|
22564
23411
|
const cwd = existingDirectory(input.cwd);
|
|
22565
23412
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|