@brainervirus/workit-cursor 8.23.0 → 8.25.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 +1521 -400
- package/dist/mcp-server.js +1122 -140
- package/dist/workit-hook.js +1516 -400
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +1 -1
- package/skills/workit-fanout/references/brief.md +4 -4
- package/skills/workit-implement/SKILL.md +5 -5
- package/skills/workit-review/SKILL.md +5 -0
- package/skills/workit-ship/SKILL.md +1 -2
|
@@ -6981,6 +6981,130 @@ function resolveRef(cwd, ref) {
|
|
|
6981
6981
|
return null;
|
|
6982
6982
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
6983
6983
|
}
|
|
6984
|
+
function originHeadRef(cwd) {
|
|
6985
|
+
return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
|
|
6986
|
+
}
|
|
6987
|
+
function ownChangeDrift(cwd, range) {
|
|
6988
|
+
const before = diffRaw(cwd, range.fromFork, range.from);
|
|
6989
|
+
const after = diffRaw(cwd, range.toFork, range.to);
|
|
6990
|
+
if (!before || !after)
|
|
6991
|
+
return null;
|
|
6992
|
+
const judged = new Map(before.map((entry) => [entry.path, entry]));
|
|
6993
|
+
const current = new Map(after.map((entry) => [entry.path, entry]));
|
|
6994
|
+
const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
|
|
6995
|
+
return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
|
|
6996
|
+
}
|
|
6997
|
+
function sameOwnChange(cwd, range) {
|
|
6998
|
+
const fromFork = mergeBase(cwd, range.fromBase, range.from);
|
|
6999
|
+
const toFork = mergeBase(cwd, range.toBase, range.to);
|
|
7000
|
+
if (!fromFork || !toFork)
|
|
7001
|
+
return false;
|
|
7002
|
+
return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
|
|
7003
|
+
}
|
|
7004
|
+
function diffNumstat(cwd, fork, head) {
|
|
7005
|
+
const range = laneRange(fork, head);
|
|
7006
|
+
if (!range)
|
|
7007
|
+
return null;
|
|
7008
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
7009
|
+
if (!run.ok)
|
|
7010
|
+
return null;
|
|
7011
|
+
const out = [];
|
|
7012
|
+
for (const record of run.stdout.split("\x00")) {
|
|
7013
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
7014
|
+
if (!match)
|
|
7015
|
+
continue;
|
|
7016
|
+
const binary = match[1] === "-";
|
|
7017
|
+
out.push({
|
|
7018
|
+
path: match[3],
|
|
7019
|
+
added: binary ? 0 : Number(match[1]),
|
|
7020
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
7021
|
+
binary
|
|
7022
|
+
});
|
|
7023
|
+
}
|
|
7024
|
+
return out;
|
|
7025
|
+
}
|
|
7026
|
+
function diffRaw(cwd, fork, head) {
|
|
7027
|
+
const range = laneRange(fork, head);
|
|
7028
|
+
if (!range)
|
|
7029
|
+
return null;
|
|
7030
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
7031
|
+
if (!run.ok)
|
|
7032
|
+
return null;
|
|
7033
|
+
const fields = run.stdout.split("\x00");
|
|
7034
|
+
const out = [];
|
|
7035
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
7036
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
7037
|
+
if (!meta)
|
|
7038
|
+
return null;
|
|
7039
|
+
out.push({
|
|
7040
|
+
path: fields[index + 1],
|
|
7041
|
+
oldMode: meta[1],
|
|
7042
|
+
newMode: meta[2],
|
|
7043
|
+
oldBlob: meta[3],
|
|
7044
|
+
newBlob: meta[4]
|
|
7045
|
+
});
|
|
7046
|
+
}
|
|
7047
|
+
return out;
|
|
7048
|
+
}
|
|
7049
|
+
function readBlobs(cwd, ids) {
|
|
7050
|
+
const out = new Map;
|
|
7051
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
7052
|
+
if (!wanted.length)
|
|
7053
|
+
return out;
|
|
7054
|
+
const run = spawnSync("git", ["cat-file", "--batch"], {
|
|
7055
|
+
cwd,
|
|
7056
|
+
input: `${wanted.join(`
|
|
7057
|
+
`)}
|
|
7058
|
+
`,
|
|
7059
|
+
maxBuffer: MAX_BUFFER,
|
|
7060
|
+
timeout: GIT_TIMEOUTS.local,
|
|
7061
|
+
killSignal: "SIGKILL",
|
|
7062
|
+
windowsHide: true
|
|
7063
|
+
});
|
|
7064
|
+
if (run.status !== 0 || !run.stdout)
|
|
7065
|
+
return null;
|
|
7066
|
+
const bytes = run.stdout;
|
|
7067
|
+
let at = 0;
|
|
7068
|
+
for (const id of wanted) {
|
|
7069
|
+
const end = bytes.indexOf(10, at);
|
|
7070
|
+
if (end < 0)
|
|
7071
|
+
return null;
|
|
7072
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
7073
|
+
at = end + 1;
|
|
7074
|
+
if (header[1] === "missing")
|
|
7075
|
+
continue;
|
|
7076
|
+
const size = Number(header[2]);
|
|
7077
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
7078
|
+
return null;
|
|
7079
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
7080
|
+
at += size + 1;
|
|
7081
|
+
}
|
|
7082
|
+
return out;
|
|
7083
|
+
}
|
|
7084
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
7085
|
+
if (!safeArg(rev))
|
|
7086
|
+
return null;
|
|
7087
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
7088
|
+
if (!run.ok)
|
|
7089
|
+
return null;
|
|
7090
|
+
const out = new Map;
|
|
7091
|
+
for (const record of run.stdout.split("\x00")) {
|
|
7092
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
7093
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
7094
|
+
out.set(match[2], match[1]);
|
|
7095
|
+
}
|
|
7096
|
+
return out;
|
|
7097
|
+
}
|
|
7098
|
+
function fileAt(cwd, rev, file) {
|
|
7099
|
+
if (!safeArg(rev))
|
|
7100
|
+
return null;
|
|
7101
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
7102
|
+
return run.ok ? run.stdout : null;
|
|
7103
|
+
}
|
|
7104
|
+
function untrackedFiles(cwd) {
|
|
7105
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
7106
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
7107
|
+
}
|
|
6984
7108
|
function currentBranch(cwd) {
|
|
6985
7109
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
6986
7110
|
}
|
|
@@ -7027,11 +7151,24 @@ var MAX_BUFFER, GIT_TIMEOUTS, git = (cwd, args, options = {}) => {
|
|
|
7027
7151
|
const { atimeNs, mtimeNs } = fs.statSync(from, { bigint: true });
|
|
7028
7152
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
7029
7153
|
fs.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
7154
|
+
}, LANE_DIFF, laneRange = (fork, head) => {
|
|
7155
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
7156
|
+
return null;
|
|
7157
|
+
return head === null ? [fork] : [fork, head];
|
|
7030
7158
|
}, config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
7031
7159
|
var init_rev = __esm(() => {
|
|
7032
7160
|
MAX_BUFFER = 256 * 1024 * 1024;
|
|
7033
7161
|
GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
7034
7162
|
MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
7163
|
+
LANE_DIFF = [
|
|
7164
|
+
"-c",
|
|
7165
|
+
"core.quotePath=false",
|
|
7166
|
+
"diff",
|
|
7167
|
+
"--no-color",
|
|
7168
|
+
"--no-ext-diff",
|
|
7169
|
+
"--no-textconv",
|
|
7170
|
+
"--no-renames"
|
|
7171
|
+
];
|
|
7035
7172
|
});
|
|
7036
7173
|
|
|
7037
7174
|
// packages/workit-core/src/store/paths.ts
|
|
@@ -8094,7 +8231,7 @@ var init_workspaces = __esm(() => {
|
|
|
8094
8231
|
}).passthrough().optional(),
|
|
8095
8232
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
8096
8233
|
defaultEndpoint: unknown().optional(),
|
|
8097
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
8234
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
8098
8235
|
delegation: unknown().optional(),
|
|
8099
8236
|
trailers: boolean2().optional(),
|
|
8100
8237
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -8715,6 +8852,751 @@ var init_vcs_config = __esm(() => {
|
|
|
8715
8852
|
TRACKERS = new Set(["github", "gitlab", "youtrack"]);
|
|
8716
8853
|
});
|
|
8717
8854
|
|
|
8855
|
+
// packages/workit-core/src/git/default-base.ts
|
|
8856
|
+
function defaultBase(cwd) {
|
|
8857
|
+
const trunk = trackTrunk(cwd);
|
|
8858
|
+
if (trunk) {
|
|
8859
|
+
for (const candidate of [`origin/${trunk}`, trunk])
|
|
8860
|
+
if (resolveRef(cwd, candidate))
|
|
8861
|
+
return candidate;
|
|
8862
|
+
}
|
|
8863
|
+
const originHead = originHeadRef(cwd);
|
|
8864
|
+
if (originHead && resolveRef(cwd, originHead))
|
|
8865
|
+
return originHead;
|
|
8866
|
+
for (const candidate of [
|
|
8867
|
+
"origin/main",
|
|
8868
|
+
"origin/master",
|
|
8869
|
+
"main",
|
|
8870
|
+
"master",
|
|
8871
|
+
"origin/develop",
|
|
8872
|
+
"develop"
|
|
8873
|
+
])
|
|
8874
|
+
if (resolveRef(cwd, candidate))
|
|
8875
|
+
return candidate;
|
|
8876
|
+
return null;
|
|
8877
|
+
}
|
|
8878
|
+
var safeRef = (value) => value.length > 0 && !value.startsWith("-"), trackTrunk = (cwd) => {
|
|
8879
|
+
try {
|
|
8880
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
8881
|
+
if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
|
|
8882
|
+
return null;
|
|
8883
|
+
const target = String(resolved.defaultTargetBranch ?? "").trim();
|
|
8884
|
+
return target && safeRef(target) ? target : null;
|
|
8885
|
+
} catch {
|
|
8886
|
+
return null;
|
|
8887
|
+
}
|
|
8888
|
+
};
|
|
8889
|
+
var init_default_base = __esm(() => {
|
|
8890
|
+
init_vcs_config();
|
|
8891
|
+
init_rev();
|
|
8892
|
+
});
|
|
8893
|
+
|
|
8894
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
8895
|
+
function patternRegex(dir, pattern) {
|
|
8896
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
8897
|
+
return null;
|
|
8898
|
+
const anchored = pattern.includes("/");
|
|
8899
|
+
const body = pattern.replace(/^\//u, "");
|
|
8900
|
+
let source = "";
|
|
8901
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
8902
|
+
const char = body[index];
|
|
8903
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
8904
|
+
const slash = body[index + 2] === "/";
|
|
8905
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
8906
|
+
index += slash ? 2 : 1;
|
|
8907
|
+
} else if (char === "*")
|
|
8908
|
+
source += "[^/]*";
|
|
8909
|
+
else if (char === "?")
|
|
8910
|
+
source += "[^/]";
|
|
8911
|
+
else if (char === "[") {
|
|
8912
|
+
const end = body.indexOf("]", index + 2);
|
|
8913
|
+
if (end < 0)
|
|
8914
|
+
return null;
|
|
8915
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
8916
|
+
source += `[${set}]`;
|
|
8917
|
+
index = end;
|
|
8918
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
8919
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
8920
|
+
index += 1;
|
|
8921
|
+
} else
|
|
8922
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
8923
|
+
}
|
|
8924
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
8925
|
+
try {
|
|
8926
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
8927
|
+
} catch {
|
|
8928
|
+
return null;
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
function rulesOf(dir, text) {
|
|
8932
|
+
const out = [];
|
|
8933
|
+
for (const raw of text.split(`
|
|
8934
|
+
`)) {
|
|
8935
|
+
const line = raw.trim();
|
|
8936
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
8937
|
+
continue;
|
|
8938
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
8939
|
+
let generated = null;
|
|
8940
|
+
for (const attribute of attributes) {
|
|
8941
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
8942
|
+
generated = true;
|
|
8943
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
8944
|
+
generated = false;
|
|
8945
|
+
}
|
|
8946
|
+
if (generated === null)
|
|
8947
|
+
continue;
|
|
8948
|
+
const regex = patternRegex(dir, pattern);
|
|
8949
|
+
if (regex)
|
|
8950
|
+
out.push({ regex, generated });
|
|
8951
|
+
}
|
|
8952
|
+
return out;
|
|
8953
|
+
}
|
|
8954
|
+
function generatedByAttributes(files, paths) {
|
|
8955
|
+
const rules = [...files].map(([file, text]) => {
|
|
8956
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
8957
|
+
return {
|
|
8958
|
+
depth: dir ? dir.split("/").length : 0,
|
|
8959
|
+
rules: rulesOf(dir, text)
|
|
8960
|
+
};
|
|
8961
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
8962
|
+
const out = new Set;
|
|
8963
|
+
for (const file of paths) {
|
|
8964
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
8965
|
+
if (last?.generated)
|
|
8966
|
+
out.add(file);
|
|
8967
|
+
}
|
|
8968
|
+
return out;
|
|
8969
|
+
}
|
|
8970
|
+
|
|
8971
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
8972
|
+
function substanceOf(file, text) {
|
|
8973
|
+
const syntax = syntaxOf(file);
|
|
8974
|
+
if (!syntax)
|
|
8975
|
+
return plainSubstance(text);
|
|
8976
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
8977
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
8978
|
+
return plainSubstance(text);
|
|
8979
|
+
return lex(syntax, text);
|
|
8980
|
+
}
|
|
8981
|
+
function plainSubstance(text) {
|
|
8982
|
+
return text.split(`
|
|
8983
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
8984
|
+
`);
|
|
8985
|
+
}
|
|
8986
|
+
function lex(syntax, text) {
|
|
8987
|
+
const collapse = !syntax.indented;
|
|
8988
|
+
let out = "";
|
|
8989
|
+
let lineStart = 0;
|
|
8990
|
+
let kept = 0;
|
|
8991
|
+
let pendingSpace = false;
|
|
8992
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
8993
|
+
let index = 0;
|
|
8994
|
+
if (text.startsWith("#!")) {
|
|
8995
|
+
const end = text.indexOf(`
|
|
8996
|
+
`);
|
|
8997
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
8998
|
+
kept = out.length;
|
|
8999
|
+
index = end < 0 ? text.length : end;
|
|
9000
|
+
}
|
|
9001
|
+
const emitCode = (chunk) => {
|
|
9002
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
9003
|
+
const before = out[out.length - 1];
|
|
9004
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
9005
|
+
out += " ";
|
|
9006
|
+
}
|
|
9007
|
+
pendingSpace = false;
|
|
9008
|
+
out += chunk;
|
|
9009
|
+
};
|
|
9010
|
+
const emitVerbatim = (chunk) => {
|
|
9011
|
+
emitCode(chunk);
|
|
9012
|
+
kept = out.length;
|
|
9013
|
+
};
|
|
9014
|
+
const newline = () => {
|
|
9015
|
+
if (!collapse) {
|
|
9016
|
+
let end = out.length;
|
|
9017
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
9018
|
+
end -= 1;
|
|
9019
|
+
out = out.slice(0, end);
|
|
9020
|
+
}
|
|
9021
|
+
pendingSpace = false;
|
|
9022
|
+
if (out.length > lineStart) {
|
|
9023
|
+
out += `
|
|
9024
|
+
`;
|
|
9025
|
+
lineStart = out.length;
|
|
9026
|
+
}
|
|
9027
|
+
};
|
|
9028
|
+
const regexAllowed = () => {
|
|
9029
|
+
let end = out.length;
|
|
9030
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
9031
|
+
end -= 1;
|
|
9032
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
9033
|
+
return true;
|
|
9034
|
+
let start = end;
|
|
9035
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
9036
|
+
start -= 1;
|
|
9037
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
9038
|
+
};
|
|
9039
|
+
const scanString = (quote, escapes, multiline) => {
|
|
9040
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
9041
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
9042
|
+
let end = index + quote.length;
|
|
9043
|
+
while (end < text.length) {
|
|
9044
|
+
const char = text[end];
|
|
9045
|
+
if (escapes && char === "\\") {
|
|
9046
|
+
end += 2;
|
|
9047
|
+
continue;
|
|
9048
|
+
}
|
|
9049
|
+
if (text.startsWith(quote, end)) {
|
|
9050
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
9051
|
+
return false;
|
|
9052
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
9053
|
+
index = end + quote.length;
|
|
9054
|
+
return true;
|
|
9055
|
+
}
|
|
9056
|
+
if (char === `
|
|
9057
|
+
` && !multiline)
|
|
9058
|
+
return false;
|
|
9059
|
+
end += 1;
|
|
9060
|
+
}
|
|
9061
|
+
return false;
|
|
9062
|
+
};
|
|
9063
|
+
while (index < text.length) {
|
|
9064
|
+
const frame = frames[frames.length - 1];
|
|
9065
|
+
const char = text[index];
|
|
9066
|
+
if (frame.kind === "template") {
|
|
9067
|
+
if (char === "\\") {
|
|
9068
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
9069
|
+
index += 2;
|
|
9070
|
+
} else if (char === "`") {
|
|
9071
|
+
emitVerbatim(char);
|
|
9072
|
+
frames.pop();
|
|
9073
|
+
index += 1;
|
|
9074
|
+
} else if (text.startsWith("${", index)) {
|
|
9075
|
+
emitVerbatim("${");
|
|
9076
|
+
frames.push({ kind: "code", braces: 0 });
|
|
9077
|
+
index += 2;
|
|
9078
|
+
} else {
|
|
9079
|
+
out += char;
|
|
9080
|
+
kept = out.length;
|
|
9081
|
+
index += 1;
|
|
9082
|
+
}
|
|
9083
|
+
continue;
|
|
9084
|
+
}
|
|
9085
|
+
if (char === `
|
|
9086
|
+
`) {
|
|
9087
|
+
newline();
|
|
9088
|
+
index += 1;
|
|
9089
|
+
continue;
|
|
9090
|
+
}
|
|
9091
|
+
if (SPACE.test(char)) {
|
|
9092
|
+
if (collapse)
|
|
9093
|
+
pendingSpace = true;
|
|
9094
|
+
else
|
|
9095
|
+
out += char;
|
|
9096
|
+
index += 1;
|
|
9097
|
+
continue;
|
|
9098
|
+
}
|
|
9099
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
9100
|
+
if (lineOpener) {
|
|
9101
|
+
const end = text.indexOf(`
|
|
9102
|
+
`, index);
|
|
9103
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
9104
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
9105
|
+
emitVerbatim(comment);
|
|
9106
|
+
else
|
|
9107
|
+
pendingSpace = true;
|
|
9108
|
+
index = end < 0 ? text.length : end;
|
|
9109
|
+
continue;
|
|
9110
|
+
}
|
|
9111
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
9112
|
+
if (block) {
|
|
9113
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
9114
|
+
if (end < 0)
|
|
9115
|
+
return null;
|
|
9116
|
+
const comment = text.slice(index, end + block[1].length);
|
|
9117
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
9118
|
+
emitVerbatim(comment);
|
|
9119
|
+
else
|
|
9120
|
+
pendingSpace = true;
|
|
9121
|
+
if (comment.includes(`
|
|
9122
|
+
`))
|
|
9123
|
+
newline();
|
|
9124
|
+
index = end + block[1].length;
|
|
9125
|
+
continue;
|
|
9126
|
+
}
|
|
9127
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
9128
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
9129
|
+
return null;
|
|
9130
|
+
continue;
|
|
9131
|
+
}
|
|
9132
|
+
if (syntax.quotes.includes(char)) {
|
|
9133
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
9134
|
+
return null;
|
|
9135
|
+
continue;
|
|
9136
|
+
}
|
|
9137
|
+
if (syntax.literal?.includes(char)) {
|
|
9138
|
+
if (!scanString(char, false, false))
|
|
9139
|
+
return null;
|
|
9140
|
+
continue;
|
|
9141
|
+
}
|
|
9142
|
+
if (syntax.rawBacktick && char === "`") {
|
|
9143
|
+
if (!scanString("`", false, true))
|
|
9144
|
+
return null;
|
|
9145
|
+
continue;
|
|
9146
|
+
}
|
|
9147
|
+
if (syntax.js && char === "`") {
|
|
9148
|
+
emitVerbatim(char);
|
|
9149
|
+
frames.push({ kind: "template" });
|
|
9150
|
+
index += 1;
|
|
9151
|
+
continue;
|
|
9152
|
+
}
|
|
9153
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
9154
|
+
return null;
|
|
9155
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
9156
|
+
const lineEnd = text.indexOf(`
|
|
9157
|
+
`, index);
|
|
9158
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
9159
|
+
return null;
|
|
9160
|
+
}
|
|
9161
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
9162
|
+
let end = index + 1;
|
|
9163
|
+
let inClass = false;
|
|
9164
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
9165
|
+
if (text[end] === `
|
|
9166
|
+
`)
|
|
9167
|
+
return null;
|
|
9168
|
+
if (text[end] === "\\")
|
|
9169
|
+
end += 1;
|
|
9170
|
+
else if (text[end] === "[")
|
|
9171
|
+
inClass = true;
|
|
9172
|
+
else if (text[end] === "]")
|
|
9173
|
+
inClass = false;
|
|
9174
|
+
end += 1;
|
|
9175
|
+
}
|
|
9176
|
+
if (end >= text.length)
|
|
9177
|
+
return null;
|
|
9178
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
9179
|
+
index = end + 1;
|
|
9180
|
+
continue;
|
|
9181
|
+
}
|
|
9182
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
9183
|
+
if (char === "{")
|
|
9184
|
+
frame.braces += 1;
|
|
9185
|
+
else if (char === "}") {
|
|
9186
|
+
if (frame.braces === 0) {
|
|
9187
|
+
emitVerbatim(char);
|
|
9188
|
+
frames.pop();
|
|
9189
|
+
index += 1;
|
|
9190
|
+
continue;
|
|
9191
|
+
}
|
|
9192
|
+
frame.braces -= 1;
|
|
9193
|
+
}
|
|
9194
|
+
}
|
|
9195
|
+
emitCode(char);
|
|
9196
|
+
index += 1;
|
|
9197
|
+
}
|
|
9198
|
+
if (frames.length !== 1)
|
|
9199
|
+
return null;
|
|
9200
|
+
newline();
|
|
9201
|
+
return out;
|
|
9202
|
+
}
|
|
9203
|
+
function unbalanced(body) {
|
|
9204
|
+
let depth = 0;
|
|
9205
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
9206
|
+
if (part === "{")
|
|
9207
|
+
depth += 1;
|
|
9208
|
+
else if (part === "}")
|
|
9209
|
+
depth = Math.max(0, depth - 1);
|
|
9210
|
+
}
|
|
9211
|
+
return depth > 0;
|
|
9212
|
+
}
|
|
9213
|
+
function hasOpaqueLineBreak(text) {
|
|
9214
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
9215
|
+
}
|
|
9216
|
+
function textOf(bytes) {
|
|
9217
|
+
if (bytes.includes(0))
|
|
9218
|
+
return null;
|
|
9219
|
+
try {
|
|
9220
|
+
return decoder.decode(bytes);
|
|
9221
|
+
} catch {
|
|
9222
|
+
return null;
|
|
9223
|
+
}
|
|
9224
|
+
}
|
|
9225
|
+
function cosmeticChange(file, before, after) {
|
|
9226
|
+
const left = textOf(before);
|
|
9227
|
+
const right = textOf(after);
|
|
9228
|
+
if (left === null || right === null)
|
|
9229
|
+
return false;
|
|
9230
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
9231
|
+
return false;
|
|
9232
|
+
const a = substanceOf(file, left);
|
|
9233
|
+
return a !== null && a === substanceOf(file, right);
|
|
9234
|
+
}
|
|
9235
|
+
var SLASH_BLOCK, C_LIKE, SYNTAXES, DIRECTIVE, extensionOf = (file) => {
|
|
9236
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
9237
|
+
const dot = name.lastIndexOf(".");
|
|
9238
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
9239
|
+
}, CI_PATH, syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null, SPACE, WORD, NO_SPACE_AFTER = "([{,;", NO_SPACE_BEFORE = ")]},;", REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^", REGEX_KEYWORDS, decoder;
|
|
9240
|
+
var init_substance = __esm(() => {
|
|
9241
|
+
SLASH_BLOCK = [["/*", "*/"]];
|
|
9242
|
+
C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
9243
|
+
SYNTAXES = Object.fromEntries([
|
|
9244
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
9245
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
9246
|
+
ext,
|
|
9247
|
+
{ ...C_LIKE, triple: true }
|
|
9248
|
+
]),
|
|
9249
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
9250
|
+
...["css", "scss", "less"].map((ext) => [
|
|
9251
|
+
ext,
|
|
9252
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
9253
|
+
]),
|
|
9254
|
+
...["py", "pyi"].map((ext) => [
|
|
9255
|
+
ext,
|
|
9256
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
9257
|
+
]),
|
|
9258
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
9259
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
9260
|
+
ext,
|
|
9261
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
9262
|
+
])
|
|
9263
|
+
]);
|
|
9264
|
+
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;
|
|
9265
|
+
CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
9266
|
+
SPACE = /[ \t\r\f\v]/u;
|
|
9267
|
+
WORD = /[\w$]/u;
|
|
9268
|
+
REGEX_KEYWORDS = new Set([
|
|
9269
|
+
"return",
|
|
9270
|
+
"typeof",
|
|
9271
|
+
"instanceof",
|
|
9272
|
+
"case",
|
|
9273
|
+
"do",
|
|
9274
|
+
"else",
|
|
9275
|
+
"in",
|
|
9276
|
+
"of",
|
|
9277
|
+
"new",
|
|
9278
|
+
"delete",
|
|
9279
|
+
"void",
|
|
9280
|
+
"throw",
|
|
9281
|
+
"yield",
|
|
9282
|
+
"await"
|
|
9283
|
+
]);
|
|
9284
|
+
decoder = new TextDecoder("utf-8", { fatal: true });
|
|
9285
|
+
});
|
|
9286
|
+
|
|
9287
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
9288
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
9289
|
+
import path6 from "node:path";
|
|
9290
|
+
function kindOf(file) {
|
|
9291
|
+
if (file.binary)
|
|
9292
|
+
return "behavior";
|
|
9293
|
+
if (file.generated)
|
|
9294
|
+
return "generated";
|
|
9295
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
9296
|
+
return "lockfile";
|
|
9297
|
+
if (isDoc(file.path))
|
|
9298
|
+
return "doc";
|
|
9299
|
+
if (file.cosmetic)
|
|
9300
|
+
return "cosmetic";
|
|
9301
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
9302
|
+
}
|
|
9303
|
+
function globMatch(pattern, file) {
|
|
9304
|
+
let regex = globCache.get(pattern);
|
|
9305
|
+
if (!regex) {
|
|
9306
|
+
let source = "";
|
|
9307
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
9308
|
+
const char = pattern[index];
|
|
9309
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
9310
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
9311
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
9312
|
+
} else if (char === "*")
|
|
9313
|
+
source += "[^/]*";
|
|
9314
|
+
else if (char === "?")
|
|
9315
|
+
source += "[^/]";
|
|
9316
|
+
else
|
|
9317
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
9318
|
+
}
|
|
9319
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
9320
|
+
globCache.set(pattern, regex);
|
|
9321
|
+
}
|
|
9322
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
9323
|
+
}
|
|
9324
|
+
function isRiskPath(file, extra = []) {
|
|
9325
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
9326
|
+
return true;
|
|
9327
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
9328
|
+
return true;
|
|
9329
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
9330
|
+
return false;
|
|
9331
|
+
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)));
|
|
9332
|
+
}
|
|
9333
|
+
function computeLane(input) {
|
|
9334
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
9335
|
+
const risky = new Set(riskFiles);
|
|
9336
|
+
const kinds = input.files.map((file) => ({
|
|
9337
|
+
file,
|
|
9338
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
9339
|
+
}));
|
|
9340
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
9341
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
9342
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
9343
|
+
const result = (lane, reasons) => ({
|
|
9344
|
+
lane,
|
|
9345
|
+
reasons,
|
|
9346
|
+
lines,
|
|
9347
|
+
files: behavior.length,
|
|
9348
|
+
riskFiles
|
|
9349
|
+
});
|
|
9350
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
9351
|
+
if (input.riskTier === "high")
|
|
9352
|
+
return result("full", ["the task judgment records high risk"]);
|
|
9353
|
+
if (riskFiles.length)
|
|
9354
|
+
return result("full", [
|
|
9355
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
9356
|
+
]);
|
|
9357
|
+
if (!behavior.length)
|
|
9358
|
+
return result("none", [
|
|
9359
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
9360
|
+
]);
|
|
9361
|
+
if (lines >= fullMinLines)
|
|
9362
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
9363
|
+
if (lines > selfMaxLines)
|
|
9364
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
9365
|
+
if (behavior.length > selfMaxFiles)
|
|
9366
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
9367
|
+
if (opaque.length)
|
|
9368
|
+
return result("focused", [
|
|
9369
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
9370
|
+
]);
|
|
9371
|
+
return result("self", [
|
|
9372
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
9373
|
+
]);
|
|
9374
|
+
}
|
|
9375
|
+
function untrackedStat(cwd, file) {
|
|
9376
|
+
try {
|
|
9377
|
+
const bytes = readFileSync3(path6.join(cwd, file));
|
|
9378
|
+
if (bytes.includes(0))
|
|
9379
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
9380
|
+
const text = bytes.toString("utf8");
|
|
9381
|
+
if (hasOpaqueLineBreak(text))
|
|
9382
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
9383
|
+
const lines = text.split(`
|
|
9384
|
+
`).length - (text.endsWith(`
|
|
9385
|
+
`) ? 1 : 0);
|
|
9386
|
+
return { path: file, added: lines, deleted: 0 };
|
|
9387
|
+
} catch {
|
|
9388
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
9392
|
+
const raw = diffRaw(cwd, fork, head);
|
|
9393
|
+
if (!raw)
|
|
9394
|
+
return null;
|
|
9395
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
9396
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
9397
|
+
if (!blobs)
|
|
9398
|
+
return null;
|
|
9399
|
+
const out = new Set;
|
|
9400
|
+
const opaque = new Set;
|
|
9401
|
+
for (const entry of candidates) {
|
|
9402
|
+
const before = blobs.get(entry.oldBlob);
|
|
9403
|
+
let after = blobs.get(entry.newBlob);
|
|
9404
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
9405
|
+
try {
|
|
9406
|
+
after = readFileSync3(path6.join(cwd, entry.path));
|
|
9407
|
+
} catch {
|
|
9408
|
+
after = undefined;
|
|
9409
|
+
}
|
|
9410
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
9411
|
+
out.add(entry.path);
|
|
9412
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
9413
|
+
opaque.add(entry.path);
|
|
9414
|
+
}
|
|
9415
|
+
return { cosmetic: out, opaque };
|
|
9416
|
+
}
|
|
9417
|
+
function generatedAt(cwd, fork, paths) {
|
|
9418
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
9419
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
9420
|
+
if (!files || !blobs)
|
|
9421
|
+
return new Set;
|
|
9422
|
+
const texts = new Map;
|
|
9423
|
+
for (const [file, id] of files) {
|
|
9424
|
+
const bytes = blobs.get(id);
|
|
9425
|
+
if (bytes)
|
|
9426
|
+
texts.set(file, bytes.toString("utf8"));
|
|
9427
|
+
}
|
|
9428
|
+
return generatedByAttributes(texts, paths);
|
|
9429
|
+
}
|
|
9430
|
+
function laneFiles(cwd, fork, head, attributesAt = fork) {
|
|
9431
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
9432
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
9433
|
+
if (!stats || !cosmetic)
|
|
9434
|
+
return null;
|
|
9435
|
+
const files = [
|
|
9436
|
+
...stats.map((entry) => ({
|
|
9437
|
+
...entry,
|
|
9438
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
9439
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
9440
|
+
})),
|
|
9441
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
9442
|
+
];
|
|
9443
|
+
const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
|
|
9444
|
+
return files.map((entry) => ({
|
|
9445
|
+
...entry,
|
|
9446
|
+
generated: generated.has(entry.path)
|
|
9447
|
+
}));
|
|
9448
|
+
}
|
|
9449
|
+
function driftFiles(cwd, range) {
|
|
9450
|
+
if (range.fromFork === range.toFork)
|
|
9451
|
+
return laneFiles(cwd, range.from, range.to, range.toFork);
|
|
9452
|
+
const drifted = ownChangeDrift(cwd, range);
|
|
9453
|
+
const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
|
|
9454
|
+
if (!drifted || !stats)
|
|
9455
|
+
return null;
|
|
9456
|
+
const byPath = new Map(stats.map((entry) => [entry.path, entry]));
|
|
9457
|
+
const generated = generatedAt(cwd, range.toFork, drifted);
|
|
9458
|
+
return drifted.map((file) => ({
|
|
9459
|
+
path: file,
|
|
9460
|
+
added: byPath.get(file)?.added ?? 0,
|
|
9461
|
+
deleted: byPath.get(file)?.deleted ?? 0,
|
|
9462
|
+
...byPath.get(file)?.binary ? { binary: true } : {},
|
|
9463
|
+
cosmetic: false,
|
|
9464
|
+
generated: generated.has(file)
|
|
9465
|
+
}));
|
|
9466
|
+
}
|
|
9467
|
+
function branchLane(cwd, options) {
|
|
9468
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
9469
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
9470
|
+
const head = options.workingTree ? null : options.head;
|
|
9471
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
9472
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
9473
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
9474
|
+
if (!fork || !files) {
|
|
9475
|
+
const high = riskTier === "high";
|
|
9476
|
+
return {
|
|
9477
|
+
lane: high ? "full" : "focused",
|
|
9478
|
+
reasons: [
|
|
9479
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
9480
|
+
...high ? ["the task judgment records high risk"] : []
|
|
9481
|
+
],
|
|
9482
|
+
lines: 0,
|
|
9483
|
+
files: 0,
|
|
9484
|
+
riskFiles: [],
|
|
9485
|
+
base,
|
|
9486
|
+
head: options.head
|
|
9487
|
+
};
|
|
9488
|
+
}
|
|
9489
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
9490
|
+
return {
|
|
9491
|
+
...computeLane({
|
|
9492
|
+
files,
|
|
9493
|
+
riskTier,
|
|
9494
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
9495
|
+
}),
|
|
9496
|
+
base,
|
|
9497
|
+
head: options.head
|
|
9498
|
+
};
|
|
9499
|
+
}
|
|
9500
|
+
var LANE_THRESHOLDS, RISK_TOKENS, RISK_FILES, RISK_NAMES, RISK_PATHS_FILE = ".workit/risk-paths", LOCKFILES, DOC_EXT, DOC_NAMES, INSTRUCTIONS, TEST_PATH, basename = (file) => file.slice(file.lastIndexOf("/") + 1), isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file))), globCache, parseRiskPaths = (text) => text.split(`
|
|
9501
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, "")), REGULAR, ABSENT, forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD"), laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
9502
|
+
var init_lane = __esm(() => {
|
|
9503
|
+
init_rev();
|
|
9504
|
+
init_default_base();
|
|
9505
|
+
init_substance();
|
|
9506
|
+
LANE_THRESHOLDS = {
|
|
9507
|
+
selfMaxLines: 100,
|
|
9508
|
+
selfMaxFiles: 5,
|
|
9509
|
+
fullMinLines: 500
|
|
9510
|
+
};
|
|
9511
|
+
RISK_TOKENS = new Set([
|
|
9512
|
+
"auth",
|
|
9513
|
+
"authn",
|
|
9514
|
+
"authz",
|
|
9515
|
+
"oauth",
|
|
9516
|
+
"security",
|
|
9517
|
+
"permission",
|
|
9518
|
+
"permissions",
|
|
9519
|
+
"secret",
|
|
9520
|
+
"secrets",
|
|
9521
|
+
"crypto",
|
|
9522
|
+
"gate",
|
|
9523
|
+
"gates",
|
|
9524
|
+
"policy",
|
|
9525
|
+
"policies",
|
|
9526
|
+
"hook",
|
|
9527
|
+
"hooks",
|
|
9528
|
+
"husky",
|
|
9529
|
+
"lefthook",
|
|
9530
|
+
"release",
|
|
9531
|
+
"releases",
|
|
9532
|
+
"migration",
|
|
9533
|
+
"migrations",
|
|
9534
|
+
"migrate",
|
|
9535
|
+
"workflows"
|
|
9536
|
+
]);
|
|
9537
|
+
RISK_FILES = new Set([
|
|
9538
|
+
".gitlab-ci.yml",
|
|
9539
|
+
"Jenkinsfile",
|
|
9540
|
+
"azure-pipelines.yml",
|
|
9541
|
+
".pre-commit-config.yaml",
|
|
9542
|
+
".workit/risk-paths"
|
|
9543
|
+
]);
|
|
9544
|
+
RISK_NAMES = new Set([".gitattributes"]);
|
|
9545
|
+
LOCKFILES = new Set([
|
|
9546
|
+
"bun.lock",
|
|
9547
|
+
"bun.lockb",
|
|
9548
|
+
"package-lock.json",
|
|
9549
|
+
"npm-shrinkwrap.json",
|
|
9550
|
+
"yarn.lock",
|
|
9551
|
+
"pnpm-lock.yaml",
|
|
9552
|
+
"Cargo.lock",
|
|
9553
|
+
"go.sum",
|
|
9554
|
+
"poetry.lock",
|
|
9555
|
+
"uv.lock",
|
|
9556
|
+
"Pipfile.lock",
|
|
9557
|
+
"Gemfile.lock",
|
|
9558
|
+
"composer.lock"
|
|
9559
|
+
]);
|
|
9560
|
+
DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
|
|
9561
|
+
DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
9562
|
+
INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
9563
|
+
TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
|
|
9564
|
+
globCache = new Map;
|
|
9565
|
+
REGULAR = new Set(["100644", "100755"]);
|
|
9566
|
+
ABSENT = /^0+$/u;
|
|
9567
|
+
});
|
|
9568
|
+
|
|
9569
|
+
// packages/workit-core/src/core/policy/carry.ts
|
|
9570
|
+
function verdictDrift(cwd, input) {
|
|
9571
|
+
const since = input.from;
|
|
9572
|
+
const stale = (reason, files = null, focused = false) => ({
|
|
9573
|
+
carries: false,
|
|
9574
|
+
since,
|
|
9575
|
+
files,
|
|
9576
|
+
focused,
|
|
9577
|
+
reason
|
|
9578
|
+
});
|
|
9579
|
+
const fromFork = mergeBase(cwd, input.fromBase, input.from);
|
|
9580
|
+
const toFork = mergeBase(cwd, input.toBase, input.to);
|
|
9581
|
+
const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
|
|
9582
|
+
if (!fromFork || !toFork || !files)
|
|
9583
|
+
return stale(`the change since ${since.slice(0, 12)} cannot be read`);
|
|
9584
|
+
const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
|
|
9585
|
+
const lane = computeLane({ files, riskPaths });
|
|
9586
|
+
const paths = files.map((file) => file.path);
|
|
9587
|
+
if (lane.lane === "none")
|
|
9588
|
+
return { carries: true, rule: "lane-none", since, files: paths };
|
|
9589
|
+
if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
|
|
9590
|
+
return { carries: true, rule: "tests-only", since, files: paths };
|
|
9591
|
+
return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
|
|
9592
|
+
}
|
|
9593
|
+
var TEST_ONLY_KINDS;
|
|
9594
|
+
var init_carry = __esm(() => {
|
|
9595
|
+
init_rev();
|
|
9596
|
+
init_lane();
|
|
9597
|
+
TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
|
|
9598
|
+
});
|
|
9599
|
+
|
|
8718
9600
|
// packages/workit-core/src/core/commit-flavors.ts
|
|
8719
9601
|
var init_commit_flavors = () => {};
|
|
8720
9602
|
|
|
@@ -8932,21 +9814,21 @@ var init_branch = __esm(() => {
|
|
|
8932
9814
|
|
|
8933
9815
|
// packages/workit-core/src/core/logger.ts
|
|
8934
9816
|
import os3 from "node:os";
|
|
8935
|
-
import
|
|
9817
|
+
import path7 from "node:path";
|
|
8936
9818
|
var SENSITIVE_WORDS, STACK_WORDS, escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), resolveStateDir = () => {
|
|
8937
9819
|
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
8938
9820
|
if (override)
|
|
8939
9821
|
return override;
|
|
8940
9822
|
const home = os3.homedir();
|
|
8941
9823
|
if (process.env.XDG_STATE_HOME)
|
|
8942
|
-
return
|
|
9824
|
+
return path7.join(process.env.XDG_STATE_HOME, "workit");
|
|
8943
9825
|
switch (os3.platform()) {
|
|
8944
9826
|
case "darwin":
|
|
8945
|
-
return
|
|
9827
|
+
return path7.join(home, "Library", "Application Support", "workit");
|
|
8946
9828
|
case "win32":
|
|
8947
|
-
return
|
|
9829
|
+
return path7.join(process.env.LOCALAPPDATA ?? path7.join(home, "AppData", "Local"), "workit");
|
|
8948
9830
|
default:
|
|
8949
|
-
return
|
|
9831
|
+
return path7.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
8950
9832
|
}
|
|
8951
9833
|
}, homePattern;
|
|
8952
9834
|
var init_logger = __esm(() => {
|
|
@@ -8993,7 +9875,7 @@ var init_logger = __esm(() => {
|
|
|
8993
9875
|
// packages/workit-core/src/host-session.ts
|
|
8994
9876
|
import { createHash as createHash7, randomBytes, timingSafeEqual } from "node:crypto";
|
|
8995
9877
|
import fs5 from "node:fs";
|
|
8996
|
-
import
|
|
9878
|
+
import path8 from "node:path";
|
|
8997
9879
|
function hostSessionFromEnv(env) {
|
|
8998
9880
|
const value = (key) => env[key]?.trim() || null;
|
|
8999
9881
|
let inner = null;
|
|
@@ -9020,7 +9902,7 @@ function migrateRepoIssued(cwd) {
|
|
|
9020
9902
|
const location = resolveStore(cwd);
|
|
9021
9903
|
if (location instanceof Error)
|
|
9022
9904
|
return;
|
|
9023
|
-
old =
|
|
9905
|
+
old = path8.join(location.dir, "hooks", "issued");
|
|
9024
9906
|
if (!fs5.existsSync(old))
|
|
9025
9907
|
return;
|
|
9026
9908
|
} catch {
|
|
@@ -9028,19 +9910,19 @@ function migrateRepoIssued(cwd) {
|
|
|
9028
9910
|
}
|
|
9029
9911
|
for (const lead of safeList(old)) {
|
|
9030
9912
|
try {
|
|
9031
|
-
const from =
|
|
9913
|
+
const from = path8.join(old, lead);
|
|
9032
9914
|
if (!fs5.statSync(from).isDirectory())
|
|
9033
9915
|
continue;
|
|
9034
|
-
const to =
|
|
9916
|
+
const to = path8.join(userIssuedRoot(), lead);
|
|
9035
9917
|
fs5.mkdirSync(to, { recursive: true, mode: 448 });
|
|
9036
9918
|
for (const id of fs5.readdirSync(from)) {
|
|
9037
9919
|
try {
|
|
9038
|
-
fs5.copyFileSync(
|
|
9920
|
+
fs5.copyFileSync(path8.join(from, id), path8.join(to, id), fs5.constants.COPYFILE_EXCL);
|
|
9039
9921
|
} catch (error) {
|
|
9040
9922
|
if (error.code !== "EEXIST")
|
|
9041
9923
|
throw error;
|
|
9042
9924
|
}
|
|
9043
|
-
fs5.rmSync(
|
|
9925
|
+
fs5.rmSync(path8.join(from, id), { force: true });
|
|
9044
9926
|
}
|
|
9045
9927
|
removeIfEmpty(from);
|
|
9046
9928
|
} catch {}
|
|
@@ -9052,13 +9934,13 @@ function recordIssuedSession(cwd, lead, issued) {
|
|
|
9052
9934
|
try {
|
|
9053
9935
|
migrateRepoIssued(cwd);
|
|
9054
9936
|
const own = hash(issued);
|
|
9055
|
-
const tokens =
|
|
9937
|
+
const tokens = path8.join(userTokenRoot(), hash(lead));
|
|
9056
9938
|
fs5.mkdirSync(tokens, { recursive: true, mode: 448 });
|
|
9057
|
-
fs5.writeFileSync(
|
|
9939
|
+
fs5.writeFileSync(path8.join(tokens, own), `${tokenHash(token)}
|
|
9058
9940
|
`, { mode: 384, flag: "wx" });
|
|
9059
|
-
const dir =
|
|
9941
|
+
const dir = path8.join(userIssuedRoot(), hash(lead));
|
|
9060
9942
|
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
9061
|
-
fs5.writeFileSync(
|
|
9943
|
+
fs5.writeFileSync(path8.join(dir, own), `${ISSUED_SESSIONS_MARKER}
|
|
9062
9944
|
`, { mode: 384 });
|
|
9063
9945
|
pruneIssued(dir, own, tokens);
|
|
9064
9946
|
return token;
|
|
@@ -9086,10 +9968,10 @@ function pruneIssued(dir, own, tokens) {
|
|
|
9086
9968
|
const entries = fs5.readdirSync(dir);
|
|
9087
9969
|
if (entries.length <= ISSUED_PER_LEAD)
|
|
9088
9970
|
return;
|
|
9089
|
-
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs5.statSync(
|
|
9971
|
+
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs5.statSync(path8.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
|
|
9090
9972
|
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD)) {
|
|
9091
|
-
fs5.rmSync(
|
|
9092
|
-
fs5.rmSync(
|
|
9973
|
+
fs5.rmSync(path8.join(dir, name), { force: true });
|
|
9974
|
+
fs5.rmSync(path8.join(tokens, name), { force: true });
|
|
9093
9975
|
}
|
|
9094
9976
|
} catch {}
|
|
9095
9977
|
}
|
|
@@ -9102,7 +9984,7 @@ function issuedBinding(cwd, claimed) {
|
|
|
9102
9984
|
return null;
|
|
9103
9985
|
return leadBound ? "token" : "legacy";
|
|
9104
9986
|
}
|
|
9105
|
-
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, ISSUED_PER_LEAD = 64, ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3", hash = (value) => createHash7("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () =>
|
|
9987
|
+
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, ISSUED_PER_LEAD = 64, ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3", hash = (value) => createHash7("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () => path8.join(resolveStateDir(), "issued"), userTokenRoot = () => path8.join(resolveStateDir(), "issued-tokens"), safeList = (dir) => {
|
|
9106
9988
|
try {
|
|
9107
9989
|
return fs5.readdirSync(dir);
|
|
9108
9990
|
} catch {
|
|
@@ -9118,7 +10000,7 @@ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "")
|
|
|
9118
10000
|
return null;
|
|
9119
10001
|
migrateRepoIssued(cwd);
|
|
9120
10002
|
try {
|
|
9121
|
-
return fs5.readFileSync(
|
|
10003
|
+
return fs5.readFileSync(path8.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
9122
10004
|
} catch {
|
|
9123
10005
|
return null;
|
|
9124
10006
|
}
|
|
@@ -9126,9 +10008,9 @@ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "")
|
|
|
9126
10008
|
const cut = claimed.lastIndexOf(":");
|
|
9127
10009
|
if (cut <= 0)
|
|
9128
10010
|
return { stored: null, leadBound: false };
|
|
9129
|
-
const dir =
|
|
10011
|
+
const dir = path8.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
9130
10012
|
try {
|
|
9131
|
-
const text = fs5.readFileSync(
|
|
10013
|
+
const text = fs5.readFileSync(path8.join(dir, hash(claimed)), "utf8");
|
|
9132
10014
|
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
9133
10015
|
} catch {
|
|
9134
10016
|
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
@@ -9154,31 +10036,31 @@ var init_host_session = __esm(() => {
|
|
|
9154
10036
|
// packages/workit-core/src/hooks/standing-signal.ts
|
|
9155
10037
|
import { createHash as createHash8 } from "node:crypto";
|
|
9156
10038
|
import fs6 from "node:fs";
|
|
9157
|
-
import
|
|
10039
|
+
import path9 from "node:path";
|
|
9158
10040
|
var gitStoreDir = (cwd) => {
|
|
9159
10041
|
try {
|
|
9160
|
-
for (let dir =
|
|
9161
|
-
const dotGit =
|
|
10042
|
+
for (let dir = path9.resolve(cwd);; dir = path9.dirname(dir)) {
|
|
10043
|
+
const dotGit = path9.join(dir, ".git");
|
|
9162
10044
|
if (fs6.existsSync(dotGit)) {
|
|
9163
10045
|
let gitDir = dotGit;
|
|
9164
10046
|
if (fs6.statSync(dotGit).isFile()) {
|
|
9165
10047
|
const target = /^gitdir:\s*(.+)$/mu.exec(fs6.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
9166
10048
|
if (!target)
|
|
9167
10049
|
return null;
|
|
9168
|
-
gitDir =
|
|
9169
|
-
const common =
|
|
10050
|
+
gitDir = path9.resolve(dir, target);
|
|
10051
|
+
const common = path9.join(gitDir, "commondir");
|
|
9170
10052
|
if (fs6.existsSync(common))
|
|
9171
|
-
gitDir =
|
|
10053
|
+
gitDir = path9.resolve(gitDir, fs6.readFileSync(common, "utf8").trim());
|
|
9172
10054
|
}
|
|
9173
|
-
return
|
|
10055
|
+
return path9.join(gitDir, "workit");
|
|
9174
10056
|
}
|
|
9175
|
-
if (
|
|
10057
|
+
if (path9.dirname(dir) === dir)
|
|
9176
10058
|
return null;
|
|
9177
10059
|
}
|
|
9178
10060
|
} catch {
|
|
9179
10061
|
return null;
|
|
9180
10062
|
}
|
|
9181
|
-
}, standingRevFile = (store) =>
|
|
10063
|
+
}, standingRevFile = (store) => path9.join(store, "standing", "rev"), standingSeenFile = (store, session, agentId) => path9.join(store, "standing", "seen", `${createHash8("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`), readStandingRev = (store) => {
|
|
9182
10064
|
try {
|
|
9183
10065
|
return fs6.readFileSync(standingRevFile(store), "utf8").trim() || null;
|
|
9184
10066
|
} catch {
|
|
@@ -9196,7 +10078,7 @@ var gitStoreDir = (cwd) => {
|
|
|
9196
10078
|
}
|
|
9197
10079
|
}, writeStandingSeen = (file, seen) => {
|
|
9198
10080
|
try {
|
|
9199
|
-
fs6.mkdirSync(
|
|
10081
|
+
fs6.mkdirSync(path9.dirname(file), { recursive: true });
|
|
9200
10082
|
fs6.writeFileSync(file, JSON.stringify(seen));
|
|
9201
10083
|
} catch {}
|
|
9202
10084
|
};
|
|
@@ -9206,41 +10088,19 @@ var init_standing_signal = () => {};
|
|
|
9206
10088
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
9207
10089
|
import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
|
|
9208
10090
|
import fs7 from "node:fs";
|
|
9209
|
-
import
|
|
10091
|
+
import path10 from "node:path";
|
|
9210
10092
|
function resolveCommit(cwd, ref) {
|
|
9211
|
-
if (!
|
|
10093
|
+
if (!safeRef2(ref))
|
|
9212
10094
|
return null;
|
|
9213
10095
|
return git3(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
9214
10096
|
}
|
|
9215
10097
|
function branchHead(cwd, branch) {
|
|
9216
|
-
if (!
|
|
10098
|
+
if (!safeRef2(branch))
|
|
9217
10099
|
return null;
|
|
9218
10100
|
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
9219
10101
|
}
|
|
9220
|
-
function defaultBase(cwd) {
|
|
9221
|
-
const trunk = trackTrunk(cwd);
|
|
9222
|
-
if (trunk) {
|
|
9223
|
-
for (const candidate of [`origin/${trunk}`, trunk])
|
|
9224
|
-
if (resolveCommit(cwd, candidate))
|
|
9225
|
-
return candidate;
|
|
9226
|
-
}
|
|
9227
|
-
const originHead = git3(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
9228
|
-
if (originHead && resolveCommit(cwd, originHead))
|
|
9229
|
-
return originHead;
|
|
9230
|
-
for (const candidate of [
|
|
9231
|
-
"origin/main",
|
|
9232
|
-
"origin/master",
|
|
9233
|
-
"main",
|
|
9234
|
-
"master",
|
|
9235
|
-
"origin/develop",
|
|
9236
|
-
"develop"
|
|
9237
|
-
])
|
|
9238
|
-
if (resolveCommit(cwd, candidate))
|
|
9239
|
-
return candidate;
|
|
9240
|
-
return null;
|
|
9241
|
-
}
|
|
9242
10102
|
function diffHash(cwd, base, head) {
|
|
9243
|
-
if (!
|
|
10103
|
+
if (!safeRef2(base) || !safeRef2(head))
|
|
9244
10104
|
return null;
|
|
9245
10105
|
const diff = gitRun2(cwd, [
|
|
9246
10106
|
"diff",
|
|
@@ -9272,14 +10132,14 @@ function diffHash(cwd, base, head) {
|
|
|
9272
10132
|
return `sha256:${hash.digest("hex")}`;
|
|
9273
10133
|
}
|
|
9274
10134
|
function storeRoot(cwd) {
|
|
9275
|
-
const location = resolveStore(
|
|
10135
|
+
const location = resolveStore(path10.resolve(cwd));
|
|
9276
10136
|
if (location instanceof Error)
|
|
9277
10137
|
return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
|
|
9278
10138
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
9279
10139
|
}
|
|
9280
10140
|
function ledgerPath(cwd) {
|
|
9281
10141
|
const root = storeRoot(cwd);
|
|
9282
|
-
return root.ok ? { ok: true, value:
|
|
10142
|
+
return root.ok ? { ok: true, value: path10.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
9283
10143
|
}
|
|
9284
10144
|
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
9285
10145
|
const lock = `${file}.lock`;
|
|
@@ -9331,8 +10191,8 @@ function appendLine(file, build, options) {
|
|
|
9331
10191
|
}
|
|
9332
10192
|
};
|
|
9333
10193
|
try {
|
|
9334
|
-
fs7.mkdirSync(
|
|
9335
|
-
if (!options.forceLock && !onNetworkFs(
|
|
10194
|
+
fs7.mkdirSync(path10.dirname(file), { recursive: true });
|
|
10195
|
+
if (!options.forceLock && !onNetworkFs(path10.dirname(file)))
|
|
9336
10196
|
return write();
|
|
9337
10197
|
const locked = withLedgerLock(file, write);
|
|
9338
10198
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -9543,7 +10403,7 @@ function verdictBasis(verdict, current) {
|
|
|
9543
10403
|
return "stale";
|
|
9544
10404
|
}
|
|
9545
10405
|
function trailerSessions(cwd, base, head) {
|
|
9546
|
-
if (!base || !head || !
|
|
10406
|
+
if (!base || !head || !safeRef2(base) || !safeRef2(head))
|
|
9547
10407
|
return [];
|
|
9548
10408
|
const out = git3(cwd, [
|
|
9549
10409
|
"log",
|
|
@@ -9555,7 +10415,7 @@ function trailerSessions(cwd, base, head) {
|
|
|
9555
10415
|
function branchScope(cwd, branch) {
|
|
9556
10416
|
const renamedAt = new Map;
|
|
9557
10417
|
let since = Number.NEGATIVE_INFINITY;
|
|
9558
|
-
if (
|
|
10418
|
+
if (safeRef2(branch)) {
|
|
9559
10419
|
const log = git3(cwd, [
|
|
9560
10420
|
"log",
|
|
9561
10421
|
"-g",
|
|
@@ -9654,12 +10514,28 @@ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
|
9654
10514
|
}
|
|
9655
10515
|
return effective;
|
|
9656
10516
|
}
|
|
9657
|
-
function restackCarries(rows, branch, from, to) {
|
|
10517
|
+
function restackCarries(exactIn, rows, branch, from, to) {
|
|
9658
10518
|
if (!from || !to)
|
|
9659
10519
|
return false;
|
|
10520
|
+
const exact = (row, fromHead, head) => {
|
|
10521
|
+
if (exactIn === null)
|
|
10522
|
+
return true;
|
|
10523
|
+
const cwd = exactIn;
|
|
10524
|
+
let known = restackExact.get(row);
|
|
10525
|
+
if (known === undefined) {
|
|
10526
|
+
known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
|
|
10527
|
+
from: fromHead,
|
|
10528
|
+
to: head,
|
|
10529
|
+
fromBase: row.fromBase,
|
|
10530
|
+
toBase: row.toBase
|
|
10531
|
+
});
|
|
10532
|
+
restackExact.set(row, known);
|
|
10533
|
+
}
|
|
10534
|
+
return known;
|
|
10535
|
+
};
|
|
9660
10536
|
const edges = new Map;
|
|
9661
10537
|
for (const row of rows)
|
|
9662
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
10538
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head && exact(row, row.fromHead, row.head))
|
|
9663
10539
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
9664
10540
|
const seen = new Set([from]);
|
|
9665
10541
|
const queue = [from];
|
|
@@ -9693,9 +10569,57 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
9693
10569
|
return key;
|
|
9694
10570
|
};
|
|
9695
10571
|
const keyFor = (base) => keysAt(base, head);
|
|
9696
|
-
const
|
|
10572
|
+
const driftCache = new Map;
|
|
10573
|
+
const driftOf = (row) => {
|
|
10574
|
+
if (driftCache.has(row))
|
|
10575
|
+
return driftCache.get(row) ?? null;
|
|
10576
|
+
let drift = null;
|
|
10577
|
+
const toBase = row.base ?? fallbackBase;
|
|
10578
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
10579
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
10580
|
+
if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
|
|
10581
|
+
drift = verdictDrift(cwd, {
|
|
10582
|
+
from: row.head,
|
|
10583
|
+
to: head,
|
|
10584
|
+
fromBase,
|
|
10585
|
+
toBase: toBaseSha,
|
|
10586
|
+
kind: typeof row.kind === "string" ? row.kind : "review"
|
|
10587
|
+
});
|
|
10588
|
+
driftCache.set(row, drift);
|
|
10589
|
+
return drift;
|
|
10590
|
+
};
|
|
10591
|
+
const exactCache = new Map;
|
|
10592
|
+
const exactCarry = (row) => {
|
|
10593
|
+
if (FAILING.has(String(row.result)))
|
|
10594
|
+
return true;
|
|
10595
|
+
let exact = exactCache.get(row);
|
|
10596
|
+
if (exact === undefined) {
|
|
10597
|
+
const toBase = row.base ?? fallbackBase;
|
|
10598
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
10599
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
10600
|
+
exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
|
|
10601
|
+
exactCache.set(row, exact);
|
|
10602
|
+
}
|
|
10603
|
+
return exact;
|
|
10604
|
+
};
|
|
10605
|
+
const keyBasis = (row) => {
|
|
9697
10606
|
const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
9698
|
-
return basis === "
|
|
10607
|
+
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
10608
|
+
};
|
|
10609
|
+
const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
|
|
10610
|
+
const carryOf = (row) => {
|
|
10611
|
+
if (keyBasis(row) !== "stale")
|
|
10612
|
+
return null;
|
|
10613
|
+
if (restacked(row))
|
|
10614
|
+
return null;
|
|
10615
|
+
const drift = driftOf(row);
|
|
10616
|
+
return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
|
|
10617
|
+
};
|
|
10618
|
+
const basisOf = (row) => {
|
|
10619
|
+
const basis = keyBasis(row);
|
|
10620
|
+
if (basis !== "stale")
|
|
10621
|
+
return basis;
|
|
10622
|
+
return restacked(row) || carryOf(row) ? "carried" : basis;
|
|
9699
10623
|
};
|
|
9700
10624
|
const sameCode = (failed, row) => {
|
|
9701
10625
|
if (failed.head && failed.head === row.head)
|
|
@@ -9703,7 +10627,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
9703
10627
|
const base = failed.base ?? fallbackBase;
|
|
9704
10628
|
const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
|
|
9705
10629
|
const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
|
|
9706
|
-
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
10630
|
+
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
|
|
9707
10631
|
};
|
|
9708
10632
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
9709
10633
|
const failureAuthorCache = new Map;
|
|
@@ -9759,6 +10683,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
9759
10683
|
independent: independence.length === 0,
|
|
9760
10684
|
accepted: reasons.length === 0,
|
|
9761
10685
|
reasons,
|
|
10686
|
+
carry: basis === "carried" ? carryOf(row) : null,
|
|
9762
10687
|
verdict: row
|
|
9763
10688
|
});
|
|
9764
10689
|
if (reasons.length === 0) {
|
|
@@ -9787,7 +10712,12 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
9787
10712
|
verdict: failing?.verdict ?? null,
|
|
9788
10713
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
9789
10714
|
};
|
|
9790
|
-
const
|
|
10715
|
+
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))));
|
|
10716
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
10717
|
+
const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
|
|
10718
|
+
const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
10719
|
+
const drift = lastPass ? driftOf(lastPass.verdict) : null;
|
|
10720
|
+
const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
|
|
9791
10721
|
return {
|
|
9792
10722
|
branch,
|
|
9793
10723
|
head,
|
|
@@ -9796,6 +10726,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
9796
10726
|
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
9797
10727
|
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
9798
10728
|
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
10729
|
+
carry: carryEntry?.carry ?? null,
|
|
10730
|
+
reverify,
|
|
9799
10731
|
accepted,
|
|
9800
10732
|
verdicts,
|
|
9801
10733
|
authors: [...authors].toSorted(),
|
|
@@ -9853,17 +10785,7 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, PASSING, FAILING, STRONG_RESULTS,
|
|
|
9853
10785
|
const run = gitRun2(cwd, args);
|
|
9854
10786
|
const value = run.ok ? run.stdout.trim() : "";
|
|
9855
10787
|
return value ? value : null;
|
|
9856
|
-
},
|
|
9857
|
-
try {
|
|
9858
|
-
const resolved = vcsConfig("resolve", cwd);
|
|
9859
|
-
if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
|
|
9860
|
-
return null;
|
|
9861
|
-
const target = String(resolved.defaultTargetBranch ?? "").trim();
|
|
9862
|
-
return target && safeRef(target) ? target : null;
|
|
9863
|
-
} catch {
|
|
9864
|
-
return null;
|
|
9865
|
-
}
|
|
9866
|
-
}, newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`, NETWORK_FS_TYPES, isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0), onNetworkFs = (dir) => {
|
|
10788
|
+
}, safeRef2 = (value) => value.length > 0 && !value.startsWith("-"), newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`, NETWORK_FS_TYPES, isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0), onNetworkFs = (dir) => {
|
|
9867
10789
|
if (process.platform !== "linux")
|
|
9868
10790
|
return false;
|
|
9869
10791
|
try {
|
|
@@ -9873,12 +10795,12 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, PASSING, FAILING, STRONG_RESULTS,
|
|
|
9873
10795
|
}
|
|
9874
10796
|
}, LOCK_WAIT_MS = 5000, LOCK_STALE_MS = 30000, LOCK_OWNER = "owner", lockToken = (lock) => {
|
|
9875
10797
|
try {
|
|
9876
|
-
return fs7.readFileSync(
|
|
10798
|
+
return fs7.readFileSync(path10.join(lock, LOCK_OWNER), "utf8");
|
|
9877
10799
|
} catch {
|
|
9878
10800
|
return null;
|
|
9879
10801
|
}
|
|
9880
10802
|
}, lockAgeMs = (lock) => {
|
|
9881
|
-
for (const target of [
|
|
10803
|
+
for (const target of [path10.join(lock, LOCK_OWNER), lock]) {
|
|
9882
10804
|
try {
|
|
9883
10805
|
return Date.now() - fs7.statSync(target).mtimeMs;
|
|
9884
10806
|
} catch {}
|
|
@@ -9961,10 +10883,11 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
9961
10883
|
if (row.self === true && !reasons.includes("no_session"))
|
|
9962
10884
|
reasons.push("self");
|
|
9963
10885
|
return reasons;
|
|
9964
|
-
}, fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session), OBSERVED_ONLY;
|
|
10886
|
+
}, restackExact, fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session), OBSERVED_ONLY;
|
|
9965
10887
|
var init_ledger = __esm(() => {
|
|
9966
10888
|
init_store_lock();
|
|
9967
|
-
|
|
10889
|
+
init_carry();
|
|
10890
|
+
init_default_base();
|
|
9968
10891
|
init_rev();
|
|
9969
10892
|
init_host_session();
|
|
9970
10893
|
init_standing_signal();
|
|
@@ -10000,7 +10923,7 @@ var init_ledger = __esm(() => {
|
|
|
10000
10923
|
throw error;
|
|
10001
10924
|
}
|
|
10002
10925
|
const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
|
|
10003
|
-
fs7.writeFileSync(
|
|
10926
|
+
fs7.writeFileSync(path10.join(lock, LOCK_OWNER), token);
|
|
10004
10927
|
return token;
|
|
10005
10928
|
},
|
|
10006
10929
|
release(lock, token) {
|
|
@@ -10060,6 +10983,7 @@ var init_ledger = __esm(() => {
|
|
|
10060
10983
|
"codex_cli",
|
|
10061
10984
|
"codex_desktop"
|
|
10062
10985
|
]);
|
|
10986
|
+
restackExact = new WeakMap;
|
|
10063
10987
|
OBSERVED_ONLY = new Set([
|
|
10064
10988
|
"check",
|
|
10065
10989
|
"merge.unverified",
|
|
@@ -10069,11 +10993,12 @@ var init_ledger = __esm(() => {
|
|
|
10069
10993
|
});
|
|
10070
10994
|
|
|
10071
10995
|
// packages/workit-core/src/core/policy/derive.ts
|
|
10072
|
-
import { existsSync as
|
|
10073
|
-
import
|
|
10996
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
10997
|
+
import path11 from "node:path";
|
|
10074
10998
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
10075
10999
|
const out = [];
|
|
10076
11000
|
const base = { scope, dependentAction: null };
|
|
11001
|
+
const proportional = verification === "proportional";
|
|
10077
11002
|
if (judgment.behaviorChange)
|
|
10078
11003
|
out.push(derived2({
|
|
10079
11004
|
...base,
|
|
@@ -10084,7 +11009,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
|
|
|
10084
11009
|
before: "close",
|
|
10085
11010
|
acceptanceAllowed: false
|
|
10086
11011
|
}));
|
|
10087
|
-
if (
|
|
11012
|
+
if (proportional && judgment.riskTier === "normal")
|
|
11013
|
+
out.push(derived2({
|
|
11014
|
+
...base,
|
|
11015
|
+
ruleId: RULES.lane,
|
|
11016
|
+
dimension: "review",
|
|
11017
|
+
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.",
|
|
11018
|
+
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.",
|
|
11019
|
+
before: "close",
|
|
11020
|
+
acceptanceAllowed: false
|
|
11021
|
+
}));
|
|
11022
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
10088
11023
|
out.push(verification === "independent" ? derived2({
|
|
10089
11024
|
...base,
|
|
10090
11025
|
ruleId: RULES.nonAuthor,
|
|
@@ -10163,7 +11098,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
10163
11098
|
case RULES.self:
|
|
10164
11099
|
case RULES.nonAuthor:
|
|
10165
11100
|
case RULES.verified:
|
|
10166
|
-
return verdict(requirement, root, ledger);
|
|
11101
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
11102
|
+
case RULES.lane:
|
|
11103
|
+
return laneVerdict(task, root, ledger);
|
|
10167
11104
|
default:
|
|
10168
11105
|
return null;
|
|
10169
11106
|
}
|
|
@@ -10222,7 +11159,7 @@ var RULES, derived2 = (spec) => ({
|
|
|
10222
11159
|
decisionIds: []
|
|
10223
11160
|
};
|
|
10224
11161
|
}, planRecorded = (task, root) => {
|
|
10225
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" &&
|
|
11162
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path11.resolve(root, ref.path)));
|
|
10226
11163
|
if (cited)
|
|
10227
11164
|
return {
|
|
10228
11165
|
met: true,
|
|
@@ -10235,14 +11172,28 @@ var RULES, derived2 = (spec) => ({
|
|
|
10235
11172
|
reason: "no plan recorded: write it (docs/ and Markdown writes stay allowed), then `workit policy assess --ref <path>`",
|
|
10236
11173
|
decisionIds: []
|
|
10237
11174
|
};
|
|
10238
|
-
},
|
|
11175
|
+
}, workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) }), laneVerdict = (task, root, ledger) => {
|
|
11176
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
11177
|
+
return {
|
|
11178
|
+
met: true,
|
|
11179
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
11180
|
+
decisionIds: []
|
|
11181
|
+
};
|
|
11182
|
+
const lane = workingLane(task, root);
|
|
11183
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
11184
|
+
const need = laneNeed(lane.lane);
|
|
11185
|
+
if (need === "none")
|
|
11186
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
11187
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
11188
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
11189
|
+
}, verdict = (ruleId, root, ledger) => {
|
|
10239
11190
|
const branch = ledger.branch();
|
|
10240
11191
|
if (!branch)
|
|
10241
11192
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
10242
11193
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
10243
11194
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
10244
11195
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
10245
|
-
const met = !failing && (
|
|
11196
|
+
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)));
|
|
10246
11197
|
if (met)
|
|
10247
11198
|
return {
|
|
10248
11199
|
met: true,
|
|
@@ -10250,18 +11201,20 @@ var RULES, derived2 = (spec) => ({
|
|
|
10250
11201
|
decisionIds: []
|
|
10251
11202
|
};
|
|
10252
11203
|
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, " ");
|
|
10253
|
-
const unblock =
|
|
11204
|
+
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>\``;
|
|
10254
11205
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
10255
11206
|
};
|
|
10256
11207
|
var init_derive = __esm(() => {
|
|
10257
11208
|
init_rev();
|
|
10258
11209
|
init_ledger();
|
|
11210
|
+
init_lane();
|
|
10259
11211
|
init_task_contract();
|
|
10260
11212
|
RULES = {
|
|
10261
11213
|
test: "check:test",
|
|
10262
11214
|
self: "verdict:self",
|
|
10263
11215
|
nonAuthor: "verdict:non-author",
|
|
10264
11216
|
verified: "verdict:verified",
|
|
11217
|
+
lane: "verdict:lane",
|
|
10265
11218
|
product: "decision:product",
|
|
10266
11219
|
plan: "plan"
|
|
10267
11220
|
};
|
|
@@ -10819,7 +11772,7 @@ var init_safe_write = () => {};
|
|
|
10819
11772
|
|
|
10820
11773
|
// packages/workit-core/src/autonomy.ts
|
|
10821
11774
|
import os4 from "node:os";
|
|
10822
|
-
import
|
|
11775
|
+
import path12 from "node:path";
|
|
10823
11776
|
function endpointOf(entry) {
|
|
10824
11777
|
const raw = entry?.defaultEndpoint;
|
|
10825
11778
|
if (raw === undefined)
|
|
@@ -10916,10 +11869,13 @@ var accountHome = () => {
|
|
|
10916
11869
|
} catch {
|
|
10917
11870
|
return os4.homedir();
|
|
10918
11871
|
}
|
|
10919
|
-
}, grantsHome, grantsDir = () =>
|
|
10920
|
-
const configured =
|
|
10921
|
-
return configured ===
|
|
10922
|
-
}, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), DEFAULT_VERIFICATION = "
|
|
11872
|
+
}, grantsHome, grantsDir = () => path12.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
|
|
11873
|
+
const configured = path12.resolve(resolveConfigDir());
|
|
11874
|
+
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`;
|
|
11875
|
+
}, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), VERIFICATION_MODES, DEFAULT_VERIFICATION = "proportional", verificationOf = (entry) => {
|
|
11876
|
+
const value = entry?.verification;
|
|
11877
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
11878
|
+
}, isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
|
|
10923
11879
|
if (value === true)
|
|
10924
11880
|
return { push: true, pr: true, merge: "verified" };
|
|
10925
11881
|
if (!Array.isArray(value))
|
|
@@ -10946,6 +11902,11 @@ var init_autonomy = __esm(() => {
|
|
|
10946
11902
|
rerun: true
|
|
10947
11903
|
});
|
|
10948
11904
|
DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
|
|
11905
|
+
VERIFICATION_MODES = [
|
|
11906
|
+
"proportional",
|
|
11907
|
+
"self",
|
|
11908
|
+
"independent"
|
|
11909
|
+
];
|
|
10949
11910
|
});
|
|
10950
11911
|
|
|
10951
11912
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
@@ -11058,7 +12019,7 @@ var init_read_open_flags = () => {};
|
|
|
11058
12019
|
// node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
11059
12020
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
11060
12021
|
import fsSync from "node:fs";
|
|
11061
|
-
import
|
|
12022
|
+
import path13 from "node:path";
|
|
11062
12023
|
function parseSidecarLockSnapshot(snapshot, parser) {
|
|
11063
12024
|
return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
|
|
11064
12025
|
}
|
|
@@ -11086,13 +12047,13 @@ ${ownershipToken}
|
|
|
11086
12047
|
return { raw, ownershipToken };
|
|
11087
12048
|
}
|
|
11088
12049
|
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
11089
|
-
const resolved =
|
|
11090
|
-
const lexicalRelative =
|
|
11091
|
-
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${
|
|
11092
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
12050
|
+
const resolved = path13.resolve(lockPath);
|
|
12051
|
+
const lexicalRelative = path13.relative(lockRoot.rootDir, resolved);
|
|
12052
|
+
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path13.sep}`) && !path13.isAbsolute(lexicalRelative) ? lexicalRelative : path13.relative(lockRoot.rootReal, resolved);
|
|
12053
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
|
|
11093
12054
|
throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
11094
12055
|
}
|
|
11095
|
-
return relative.split(
|
|
12056
|
+
return relative.split(path13.sep).join(path13.posix.sep);
|
|
11096
12057
|
}
|
|
11097
12058
|
function parseSidecarLockPayload(raw, parser) {
|
|
11098
12059
|
if (parser) {
|
|
@@ -11262,7 +12223,7 @@ function sleepSync(ms) {
|
|
|
11262
12223
|
|
|
11263
12224
|
// node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
|
|
11264
12225
|
import fs10 from "node:fs";
|
|
11265
|
-
import
|
|
12226
|
+
import path14 from "node:path";
|
|
11266
12227
|
function getSyncHeldLocks() {
|
|
11267
12228
|
const globalWithState = globalThis;
|
|
11268
12229
|
if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
|
|
@@ -11340,26 +12301,26 @@ function canonicalLockParentSync(parent) {
|
|
|
11340
12301
|
return process.platform === "win32" ? fs10.realpathSync.native(parent) : fs10.realpathSync(parent);
|
|
11341
12302
|
}
|
|
11342
12303
|
function normalizeTargetPath(targetPath) {
|
|
11343
|
-
const resolved =
|
|
11344
|
-
fs10.mkdirSync(
|
|
12304
|
+
const resolved = path14.resolve(targetPath);
|
|
12305
|
+
fs10.mkdirSync(path14.dirname(resolved), { recursive: true });
|
|
11345
12306
|
try {
|
|
11346
|
-
return
|
|
12307
|
+
return path14.join(canonicalLockParentSync(path14.dirname(resolved)), path14.basename(resolved));
|
|
11347
12308
|
} catch {
|
|
11348
12309
|
return resolved;
|
|
11349
12310
|
}
|
|
11350
12311
|
}
|
|
11351
12312
|
function boundedLockPath(lockPath, lockRoot) {
|
|
11352
|
-
const resolved =
|
|
12313
|
+
const resolved = path14.resolve(lockPath);
|
|
11353
12314
|
if (!lockRoot)
|
|
11354
12315
|
return resolved;
|
|
11355
12316
|
relativeSidecarLockPath(lockRoot, resolved);
|
|
11356
|
-
const parent =
|
|
12317
|
+
const parent = path14.dirname(resolved);
|
|
11357
12318
|
const parentReal = canonicalLockParentSync(parent);
|
|
11358
|
-
const parentRelative =
|
|
11359
|
-
if (parentRelative === ".." || parentRelative.startsWith(`..${
|
|
12319
|
+
const parentRelative = path14.relative(lockRoot.rootReal, parentReal);
|
|
12320
|
+
if (parentRelative === ".." || parentRelative.startsWith(`..${path14.sep}`) || path14.isAbsolute(parentRelative)) {
|
|
11360
12321
|
throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
|
|
11361
12322
|
}
|
|
11362
|
-
return
|
|
12323
|
+
return path14.join(parentReal, path14.basename(resolved));
|
|
11363
12324
|
}
|
|
11364
12325
|
function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
11365
12326
|
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
@@ -11575,14 +12536,14 @@ var init_file_lock = __esm(() => {
|
|
|
11575
12536
|
});
|
|
11576
12537
|
|
|
11577
12538
|
// packages/workit-core/src/core/package-root.ts
|
|
11578
|
-
import { existsSync as
|
|
11579
|
-
import
|
|
12539
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
12540
|
+
import path15 from "node:path";
|
|
11580
12541
|
import { fileURLToPath } from "node:url";
|
|
11581
12542
|
var packageRoot = () => {
|
|
11582
|
-
let dir =
|
|
12543
|
+
let dir = path15.dirname(fileURLToPath(import.meta.url));
|
|
11583
12544
|
while (true) {
|
|
11584
|
-
const parent =
|
|
11585
|
-
if (
|
|
12545
|
+
const parent = path15.dirname(dir);
|
|
12546
|
+
if (existsSync4(path15.join(dir, "package.json")) || parent === dir)
|
|
11586
12547
|
return dir;
|
|
11587
12548
|
dir = parent;
|
|
11588
12549
|
}
|
|
@@ -11592,7 +12553,7 @@ var init_package_root = () => {};
|
|
|
11592
12553
|
// packages/workit-core/src/store/event-log.ts
|
|
11593
12554
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11594
12555
|
import fs11 from "node:fs";
|
|
11595
|
-
import
|
|
12556
|
+
import path16 from "node:path";
|
|
11596
12557
|
function readLog(file, from = 0, expectSeq = null) {
|
|
11597
12558
|
let fd;
|
|
11598
12559
|
try {
|
|
@@ -11665,7 +12626,7 @@ function appendEvent(file, event, validEnd) {
|
|
|
11665
12626
|
fs11.closeSync(fd);
|
|
11666
12627
|
}
|
|
11667
12628
|
if (created)
|
|
11668
|
-
fsyncDirectory(
|
|
12629
|
+
fsyncDirectory(path16.dirname(file));
|
|
11669
12630
|
return validEnd;
|
|
11670
12631
|
}
|
|
11671
12632
|
function replaceFile(file, bytes, durable) {
|
|
@@ -11681,7 +12642,7 @@ function replaceFile(file, bytes, durable) {
|
|
|
11681
12642
|
}
|
|
11682
12643
|
renameWithRetry(temporary, file);
|
|
11683
12644
|
if (durable)
|
|
11684
|
-
fsyncDirectory(
|
|
12645
|
+
fsyncDirectory(path16.dirname(file));
|
|
11685
12646
|
} catch (error) {
|
|
11686
12647
|
try {
|
|
11687
12648
|
fs11.unlinkSync(temporary);
|
|
@@ -11991,7 +12952,7 @@ var init_notes = __esm(() => {
|
|
|
11991
12952
|
// packages/workit-core/src/core/task-store.ts
|
|
11992
12953
|
import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
|
|
11993
12954
|
import * as fs12 from "node:fs";
|
|
11994
|
-
import
|
|
12955
|
+
import path17 from "node:path";
|
|
11995
12956
|
|
|
11996
12957
|
class TaskStore {
|
|
11997
12958
|
root;
|
|
@@ -12003,7 +12964,7 @@ class TaskStore {
|
|
|
12003
12964
|
migrationChecked = false;
|
|
12004
12965
|
compactDue = new Set;
|
|
12005
12966
|
constructor(root, options = {}) {
|
|
12006
|
-
const resolved =
|
|
12967
|
+
const resolved = path17.resolve(root);
|
|
12007
12968
|
this.origin = fs12.existsSync(resolved) ? fs12.realpathSync(resolved) : resolved;
|
|
12008
12969
|
this.located = resolveStore(this.origin);
|
|
12009
12970
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
@@ -12012,7 +12973,7 @@ class TaskStore {
|
|
|
12012
12973
|
this.migrateOnRead = options.migrateOnRead;
|
|
12013
12974
|
}
|
|
12014
12975
|
location() {
|
|
12015
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(
|
|
12976
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path17.join(this.root, ".git"))) {
|
|
12016
12977
|
this.located = undefined;
|
|
12017
12978
|
this.migrationChecked = false;
|
|
12018
12979
|
}
|
|
@@ -12168,7 +13129,7 @@ class TaskStore {
|
|
|
12168
13129
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
12169
13130
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
12170
13131
|
}
|
|
12171
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
13132
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path17.basename(this.root) || this.root;
|
|
12172
13133
|
const intent = {
|
|
12173
13134
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
12174
13135
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -12234,14 +13195,14 @@ class TaskStore {
|
|
|
12234
13195
|
const branch = key.ok ? key.data.branch : null;
|
|
12235
13196
|
if (!branch || !location.ok)
|
|
12236
13197
|
return success(null, null, null);
|
|
12237
|
-
const common =
|
|
12238
|
-
const heads =
|
|
12239
|
-
const headLog = location.data.git ?
|
|
13198
|
+
const common = path17.dirname(location.data.dir);
|
|
13199
|
+
const heads = path17.join(common, "logs", "refs", "heads");
|
|
13200
|
+
const headLog = location.data.git ? path17.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
12240
13201
|
const signatures = [
|
|
12241
|
-
|
|
13202
|
+
path17.join(heads, branch),
|
|
12242
13203
|
headLog,
|
|
12243
13204
|
heads,
|
|
12244
|
-
|
|
13205
|
+
path17.join(common, "packed-refs")
|
|
12245
13206
|
].map((file) => fileSignature(file) ?? "-");
|
|
12246
13207
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
12247
13208
|
let resolved = origins.get(slot);
|
|
@@ -12253,7 +13214,7 @@ class TaskStore {
|
|
|
12253
13214
|
aliases.push(rename.from);
|
|
12254
13215
|
const start = branchStart(location.data, branch, aliases);
|
|
12255
13216
|
const slash = start?.indexOf("/") ?? -1;
|
|
12256
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
13217
|
+
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)}"]`));
|
|
12257
13218
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
12258
13219
|
resolved = {
|
|
12259
13220
|
signature: signatures.join("|"),
|
|
@@ -12491,13 +13452,13 @@ class TaskStore {
|
|
|
12491
13452
|
if (!ready.ok)
|
|
12492
13453
|
return ready;
|
|
12493
13454
|
const dirs = this.paths();
|
|
12494
|
-
const recovery =
|
|
13455
|
+
const recovery = path17.join(this.root, ".workit", "recovery");
|
|
12495
13456
|
if (fs12.existsSync(recovery)) {
|
|
12496
13457
|
let files = 0;
|
|
12497
13458
|
let bytes = 0;
|
|
12498
13459
|
try {
|
|
12499
13460
|
for (const name of fs12.readdirSync(recovery)) {
|
|
12500
|
-
const stat = fs12.lstatSync(
|
|
13461
|
+
const stat = fs12.lstatSync(path17.join(recovery, name));
|
|
12501
13462
|
if (!stat.isFile())
|
|
12502
13463
|
continue;
|
|
12503
13464
|
files += 1;
|
|
@@ -12550,7 +13511,7 @@ class TaskStore {
|
|
|
12550
13511
|
if (fs12.existsSync(dirs.blobs))
|
|
12551
13512
|
for (const name of fs12.readdirSync(dirs.blobs)) {
|
|
12552
13513
|
const digest = name.replace(/\.json$/, "");
|
|
12553
|
-
const file =
|
|
13514
|
+
const file = path17.join(dirs.blobs, name);
|
|
12554
13515
|
let stat;
|
|
12555
13516
|
try {
|
|
12556
13517
|
stat = fs12.lstatSync(file);
|
|
@@ -12568,7 +13529,7 @@ class TaskStore {
|
|
|
12568
13529
|
}
|
|
12569
13530
|
return success(null, null, null);
|
|
12570
13531
|
};
|
|
12571
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
13532
|
+
const swept = dryRun ? sweep() : this.withLock(path17.join(dirs.dir, "gc.lock"), sweep);
|
|
12572
13533
|
if (!swept.ok)
|
|
12573
13534
|
return swept;
|
|
12574
13535
|
const temporaryDirs = [
|
|
@@ -12576,7 +13537,7 @@ class TaskStore {
|
|
|
12576
13537
|
dirs.tasks,
|
|
12577
13538
|
dirs.blobs,
|
|
12578
13539
|
dirs.checkout,
|
|
12579
|
-
...names.map((name) =>
|
|
13540
|
+
...names.map((name) => path17.join(dirs.tasks, name))
|
|
12580
13541
|
];
|
|
12581
13542
|
for (const directory of temporaryDirs) {
|
|
12582
13543
|
if (!fs12.existsSync(directory))
|
|
@@ -12584,7 +13545,7 @@ class TaskStore {
|
|
|
12584
13545
|
for (const name of fs12.readdirSync(directory)) {
|
|
12585
13546
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
12586
13547
|
continue;
|
|
12587
|
-
const file =
|
|
13548
|
+
const file = path17.join(directory, name);
|
|
12588
13549
|
try {
|
|
12589
13550
|
const stat = fs12.lstatSync(file);
|
|
12590
13551
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
@@ -12601,30 +13562,30 @@ class TaskStore {
|
|
|
12601
13562
|
}
|
|
12602
13563
|
paths() {
|
|
12603
13564
|
const location = this.location();
|
|
12604
|
-
const dir = location.ok ? location.data.dir :
|
|
12605
|
-
const checkout =
|
|
13565
|
+
const dir = location.ok ? location.data.dir : path17.join(this.root, ".workit");
|
|
13566
|
+
const checkout = path17.join(dir, "checkouts", checkoutSlug(this.root));
|
|
12606
13567
|
return {
|
|
12607
13568
|
dir,
|
|
12608
13569
|
shared: location.ok ? location.data.shared : false,
|
|
12609
|
-
marker:
|
|
13570
|
+
marker: path17.join(dir, "store.json"),
|
|
12610
13571
|
checkout,
|
|
12611
|
-
workspace:
|
|
12612
|
-
checkoutLock:
|
|
12613
|
-
tasks:
|
|
12614
|
-
index:
|
|
12615
|
-
blobs:
|
|
12616
|
-
legacy:
|
|
12617
|
-
v2:
|
|
13572
|
+
workspace: path17.join(checkout, "workspace.json"),
|
|
13573
|
+
checkoutLock: path17.join(checkout, "metadata.lock"),
|
|
13574
|
+
tasks: path17.join(dir, "tasks"),
|
|
13575
|
+
index: path17.join(dir, "task-index.json"),
|
|
13576
|
+
blobs: path17.join(dir, "blobs", "candidates"),
|
|
13577
|
+
legacy: path17.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
13578
|
+
v2: path17.join(this.root, ".workit")
|
|
12618
13579
|
};
|
|
12619
13580
|
}
|
|
12620
13581
|
eventsPath(taskId) {
|
|
12621
|
-
return
|
|
13582
|
+
return path17.join(this.paths().tasks, taskId, "events.jsonl");
|
|
12622
13583
|
}
|
|
12623
13584
|
snapshotPath(taskId) {
|
|
12624
|
-
return
|
|
13585
|
+
return path17.join(this.paths().tasks, taskId, "snapshot.json");
|
|
12625
13586
|
}
|
|
12626
13587
|
taskLockPath(taskId) {
|
|
12627
|
-
return
|
|
13588
|
+
return path17.join(this.paths().tasks, taskId, "lock");
|
|
12628
13589
|
}
|
|
12629
13590
|
ready(write = false) {
|
|
12630
13591
|
const location = this.location();
|
|
@@ -12663,13 +13624,13 @@ class TaskStore {
|
|
|
12663
13624
|
return {
|
|
12664
13625
|
read: (digest) => {
|
|
12665
13626
|
try {
|
|
12666
|
-
return JSON.parse(fs12.readFileSync(
|
|
13627
|
+
return JSON.parse(fs12.readFileSync(path17.join(dir, `${digest}.json`), "utf8"));
|
|
12667
13628
|
} catch (error) {
|
|
12668
13629
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
12669
13630
|
}
|
|
12670
13631
|
},
|
|
12671
13632
|
write: (digest, value) => {
|
|
12672
|
-
const file =
|
|
13633
|
+
const file = path17.join(dir, `${digest}.json`);
|
|
12673
13634
|
if (fs12.existsSync(file)) {
|
|
12674
13635
|
const stamp = new Date;
|
|
12675
13636
|
try {
|
|
@@ -12861,7 +13822,7 @@ class TaskStore {
|
|
|
12861
13822
|
}
|
|
12862
13823
|
}
|
|
12863
13824
|
openTask(record, type, key, legacy = null) {
|
|
12864
|
-
const dir =
|
|
13825
|
+
const dir = path17.join(this.paths().tasks, record.id);
|
|
12865
13826
|
fs12.mkdirSync(dir, { recursive: true });
|
|
12866
13827
|
const blobs = this.blobStore();
|
|
12867
13828
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
@@ -12954,7 +13915,7 @@ class TaskStore {
|
|
|
12954
13915
|
}
|
|
12955
13916
|
withKeyLock(key, operation) {
|
|
12956
13917
|
const digest = createHash11("sha256").update(key).digest("hex").slice(0, 32);
|
|
12957
|
-
return this.withLock(
|
|
13918
|
+
return this.withLock(path17.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
12958
13919
|
}
|
|
12959
13920
|
bindLocked(taskId, key, timestamp) {
|
|
12960
13921
|
const current = this.readWorkspace();
|
|
@@ -13095,7 +14056,7 @@ class TaskStore {
|
|
|
13095
14056
|
writeWorkspace(record) {
|
|
13096
14057
|
const file = this.paths().workspace;
|
|
13097
14058
|
try {
|
|
13098
|
-
fs12.mkdirSync(
|
|
14059
|
+
fs12.mkdirSync(path17.dirname(file), { recursive: true });
|
|
13099
14060
|
replaceFile(file, `${canonicalJson(record)}
|
|
13100
14061
|
`, true);
|
|
13101
14062
|
return success(null, null, null);
|
|
@@ -13173,23 +14134,23 @@ class TaskStore {
|
|
|
13173
14134
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
13174
14135
|
}
|
|
13175
14136
|
v2Dirs() {
|
|
13176
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
14137
|
+
return [...new Set([this.root, this.origin])].map((dir) => path17.join(dir, ".workit"));
|
|
13177
14138
|
}
|
|
13178
14139
|
pendingMigrations() {
|
|
13179
14140
|
return this.v2Dirs().filter((dir) => {
|
|
13180
|
-
const raw = this.readV2Workspace(
|
|
14141
|
+
const raw = this.readV2Workspace(path17.join(dir, "workspace.json"));
|
|
13181
14142
|
return raw !== null && raw !== TOMBSTONE;
|
|
13182
14143
|
});
|
|
13183
14144
|
}
|
|
13184
14145
|
migrateV2(v2) {
|
|
13185
14146
|
const dirs = this.paths();
|
|
13186
|
-
const v2Workspace =
|
|
13187
|
-
const v2Tasks =
|
|
13188
|
-
const legacy =
|
|
14147
|
+
const v2Workspace = path17.join(v2, "workspace.json");
|
|
14148
|
+
const v2Tasks = path17.join(v2, "tasks");
|
|
14149
|
+
const legacy = path17.join(dirs.dir, "legacy", checkoutSlug(path17.dirname(v2)), "v2");
|
|
13189
14150
|
const raw = this.readV2Workspace(v2Workspace);
|
|
13190
14151
|
if (raw === null || raw === TOMBSTONE)
|
|
13191
14152
|
return success(null, null, null);
|
|
13192
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
14153
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path17.join(v2, "metadata.lock"), () => {
|
|
13193
14154
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
13194
14155
|
if (workspace === null || workspace === TOMBSTONE)
|
|
13195
14156
|
return success(null, null, null);
|
|
@@ -13198,7 +14159,7 @@ class TaskStore {
|
|
|
13198
14159
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
13199
14160
|
let files = [];
|
|
13200
14161
|
try {
|
|
13201
|
-
files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(
|
|
14162
|
+
files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path17.join(v2Tasks, name)));
|
|
13202
14163
|
} catch {}
|
|
13203
14164
|
const current = this.readWorkspace();
|
|
13204
14165
|
if (!current.ok)
|
|
@@ -13216,21 +14177,21 @@ class TaskStore {
|
|
|
13216
14177
|
let skipped = 0;
|
|
13217
14178
|
const done = new Map;
|
|
13218
14179
|
try {
|
|
13219
|
-
fs12.mkdirSync(
|
|
14180
|
+
fs12.mkdirSync(path17.join(legacy, "tasks"), { recursive: true });
|
|
13220
14181
|
for (const name of ["workspace.json", "index.json"])
|
|
13221
|
-
if (fs12.existsSync(
|
|
13222
|
-
fs12.copyFileSync(
|
|
14182
|
+
if (fs12.existsSync(path17.join(v2, name)))
|
|
14183
|
+
fs12.copyFileSync(path17.join(v2, name), path17.join(legacy, name));
|
|
13223
14184
|
for (const name of files) {
|
|
13224
14185
|
const id = name.slice(0, -5);
|
|
13225
|
-
const file =
|
|
14186
|
+
const file = path17.join(v2Tasks, name);
|
|
13226
14187
|
const bytes = fs12.readFileSync(file, "utf8");
|
|
13227
14188
|
const digest = sha256(bytes);
|
|
13228
|
-
fs12.writeFileSync(
|
|
14189
|
+
fs12.writeFileSync(path17.join(legacy, "tasks", name), bytes);
|
|
13229
14190
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
13230
14191
|
if (!parsed.ok || parsed.data.id !== id)
|
|
13231
14192
|
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 });
|
|
13232
14193
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
13233
|
-
const origin = { path:
|
|
14194
|
+
const origin = { path: path17.join(legacy, "tasks", name), digest };
|
|
13234
14195
|
if (!fs12.existsSync(this.eventsPath(id))) {
|
|
13235
14196
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
13236
14197
|
migrated += 1;
|
|
@@ -13266,7 +14227,7 @@ class TaskStore {
|
|
|
13266
14227
|
try {
|
|
13267
14228
|
let complete = true;
|
|
13268
14229
|
for (const [name, digest] of done) {
|
|
13269
|
-
const file =
|
|
14230
|
+
const file = path17.join(v2Tasks, name);
|
|
13270
14231
|
if (sha256(fs12.readFileSync(file, "utf8")) !== digest) {
|
|
13271
14232
|
complete = false;
|
|
13272
14233
|
continue;
|
|
@@ -13277,7 +14238,7 @@ class TaskStore {
|
|
|
13277
14238
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
13278
14239
|
path: v2
|
|
13279
14240
|
});
|
|
13280
|
-
fs12.rmSync(
|
|
14241
|
+
fs12.rmSync(path17.join(v2, "index.json"), { force: true });
|
|
13281
14242
|
this.writeTombstone(v2, target.id);
|
|
13282
14243
|
} catch (error) {
|
|
13283
14244
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -13297,7 +14258,7 @@ class TaskStore {
|
|
|
13297
14258
|
}
|
|
13298
14259
|
adoptLocalStore() {
|
|
13299
14260
|
const dirs = this.paths();
|
|
13300
|
-
const marker =
|
|
14261
|
+
const marker = path17.join(dirs.v2, "store.json");
|
|
13301
14262
|
if (!dirs.shared || !fs12.existsSync(marker))
|
|
13302
14263
|
return success(null, null, null);
|
|
13303
14264
|
return this.withLock(dirs.checkoutLock, () => {
|
|
@@ -13307,7 +14268,7 @@ class TaskStore {
|
|
|
13307
14268
|
try {
|
|
13308
14269
|
const merge = (from, to) => {
|
|
13309
14270
|
if (!fs12.existsSync(to)) {
|
|
13310
|
-
fs12.mkdirSync(
|
|
14271
|
+
fs12.mkdirSync(path17.dirname(to), { recursive: true });
|
|
13311
14272
|
try {
|
|
13312
14273
|
fs12.renameSync(from, to);
|
|
13313
14274
|
} catch {
|
|
@@ -13318,24 +14279,24 @@ class TaskStore {
|
|
|
13318
14279
|
}
|
|
13319
14280
|
if (fs12.lstatSync(from).isDirectory() && fs12.lstatSync(to).isDirectory())
|
|
13320
14281
|
for (const name of fs12.readdirSync(from))
|
|
13321
|
-
merge(
|
|
14282
|
+
merge(path17.join(from, name), path17.join(to, name));
|
|
13322
14283
|
};
|
|
13323
14284
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
13324
|
-
const from =
|
|
14285
|
+
const from = path17.join(dirs.v2, sub);
|
|
13325
14286
|
if (!fs12.existsSync(from))
|
|
13326
14287
|
continue;
|
|
13327
14288
|
if (sub === "tasks") {
|
|
13328
14289
|
for (const name of fs12.readdirSync(from))
|
|
13329
|
-
if (validId(name) && !fs12.existsSync(
|
|
14290
|
+
if (validId(name) && !fs12.existsSync(path17.join(dirs.dir, sub, name)))
|
|
13330
14291
|
tasks += 1;
|
|
13331
14292
|
}
|
|
13332
|
-
merge(from,
|
|
14293
|
+
merge(from, path17.join(dirs.dir, sub));
|
|
13333
14294
|
}
|
|
13334
|
-
const ledger =
|
|
13335
|
-
if (fs12.existsSync(ledger) && !fs12.existsSync(
|
|
13336
|
-
merge(ledger,
|
|
13337
|
-
fs12.rmSync(
|
|
13338
|
-
fs12.renameSync(marker,
|
|
14295
|
+
const ledger = path17.join(dirs.v2, "ledger");
|
|
14296
|
+
if (fs12.existsSync(ledger) && !fs12.existsSync(path17.join(dirs.dir, "ledger")))
|
|
14297
|
+
merge(ledger, path17.join(dirs.dir, "ledger"));
|
|
14298
|
+
fs12.rmSync(path17.join(dirs.v2, "task-index.json"), { force: true });
|
|
14299
|
+
fs12.renameSync(marker, path17.join(dirs.v2, "store.moved.json"));
|
|
13339
14300
|
} catch (error) {
|
|
13340
14301
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
13341
14302
|
path: dirs.v2
|
|
@@ -13373,7 +14334,7 @@ class TaskStore {
|
|
|
13373
14334
|
schemaVersion: SCHEMA_VERSION,
|
|
13374
14335
|
id: workspaceId ?? newId(),
|
|
13375
14336
|
revision: newRevision(),
|
|
13376
|
-
root:
|
|
14337
|
+
root: path17.dirname(v2),
|
|
13377
14338
|
runtime: { createdWith: version, updatedWith: version },
|
|
13378
14339
|
writer: null,
|
|
13379
14340
|
store: {
|
|
@@ -13385,11 +14346,11 @@ class TaskStore {
|
|
|
13385
14346
|
critical: ["store"]
|
|
13386
14347
|
};
|
|
13387
14348
|
fs12.mkdirSync(v2, { recursive: true });
|
|
13388
|
-
const ignore =
|
|
14349
|
+
const ignore = path17.join(v2, ".gitignore");
|
|
13389
14350
|
if (!fs12.existsSync(ignore))
|
|
13390
14351
|
fs12.writeFileSync(ignore, `*
|
|
13391
14352
|
`);
|
|
13392
|
-
replaceFile(
|
|
14353
|
+
replaceFile(path17.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
13393
14354
|
`, true);
|
|
13394
14355
|
}
|
|
13395
14356
|
stubFor(taskId) {
|
|
@@ -13413,8 +14374,8 @@ class TaskStore {
|
|
|
13413
14374
|
}
|
|
13414
14375
|
writeTombstoneIfMissing() {
|
|
13415
14376
|
try {
|
|
13416
|
-
const v2 =
|
|
13417
|
-
if (fs12.existsSync(
|
|
14377
|
+
const v2 = path17.join(this.root, ".workit");
|
|
14378
|
+
if (fs12.existsSync(path17.join(v2, "workspace.json")))
|
|
13418
14379
|
return;
|
|
13419
14380
|
let id = null;
|
|
13420
14381
|
try {
|
|
@@ -13431,7 +14392,7 @@ class TaskStore {
|
|
|
13431
14392
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
13432
14393
|
`, false);
|
|
13433
14394
|
if (!dirs.shared) {
|
|
13434
|
-
const ignore =
|
|
14395
|
+
const ignore = path17.join(dirs.dir, ".gitignore");
|
|
13435
14396
|
let current = null;
|
|
13436
14397
|
try {
|
|
13437
14398
|
current = fs12.readFileSync(ignore, "utf8");
|
|
@@ -13491,7 +14452,7 @@ class TaskStore {
|
|
|
13491
14452
|
withLock(lockPath, operation) {
|
|
13492
14453
|
try {
|
|
13493
14454
|
this.initializeStorage();
|
|
13494
|
-
fs12.mkdirSync(
|
|
14455
|
+
fs12.mkdirSync(path17.dirname(lockPath), { recursive: true });
|
|
13495
14456
|
} catch (error) {
|
|
13496
14457
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
13497
14458
|
path: this.paths().dir
|
|
@@ -13554,7 +14515,7 @@ class TaskStore {
|
|
|
13554
14515
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
13555
14516
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
13556
14517
|
path: lockPath,
|
|
13557
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
14518
|
+
guidance: `Check permissions and the read-only attribute on ${path17.dirname(lockPath)}.`
|
|
13558
14519
|
});
|
|
13559
14520
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
13560
14521
|
let lock = null;
|
|
@@ -13612,7 +14573,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13612
14573
|
}, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), oldestFirst = (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id), cachedRuntimeVersion = null, runtimeVersion = () => {
|
|
13613
14574
|
if (cachedRuntimeVersion === null) {
|
|
13614
14575
|
try {
|
|
13615
|
-
const pkg = JSON.parse(fs12.readFileSync(
|
|
14576
|
+
const pkg = JSON.parse(fs12.readFileSync(path17.join(packageRoot(), "package.json"), "utf8"));
|
|
13616
14577
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
13617
14578
|
} catch {
|
|
13618
14579
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -13639,7 +14600,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13639
14600
|
return "";
|
|
13640
14601
|
}
|
|
13641
14602
|
}, REFLOG_LINE, branchStart = (location, branch, aliases) => {
|
|
13642
|
-
const first = readText(
|
|
14603
|
+
const first = readText(path17.join(path17.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
13643
14604
|
`, 1)[0];
|
|
13644
14605
|
const created = REFLOG_LINE.exec(first);
|
|
13645
14606
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -13649,7 +14610,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13649
14610
|
return start;
|
|
13650
14611
|
if (!location.git)
|
|
13651
14612
|
return null;
|
|
13652
|
-
for (const line of readText(
|
|
14613
|
+
for (const line of readText(path17.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
13653
14614
|
`)) {
|
|
13654
14615
|
const entry = REFLOG_LINE.exec(line);
|
|
13655
14616
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -13658,12 +14619,12 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13658
14619
|
return moved[1];
|
|
13659
14620
|
}
|
|
13660
14621
|
return null;
|
|
13661
|
-
}, refExists = (common, ref) => fs12.existsSync(
|
|
14622
|
+
}, refExists = (common, ref) => fs12.existsSync(path17.join(common, ref)) || readText(path17.join(common, "packed-refs")).includes(` ${ref}
|
|
13662
14623
|
`), origins, sameDirectoryIdentity = (left, right) => {
|
|
13663
|
-
if (!
|
|
14624
|
+
if (!path17.isAbsolute(left) || !path17.isAbsolute(right))
|
|
13664
14625
|
return false;
|
|
13665
|
-
const normalizedLeft =
|
|
13666
|
-
const normalizedRight =
|
|
14626
|
+
const normalizedLeft = path17.resolve(left);
|
|
14627
|
+
const normalizedRight = path17.resolve(right);
|
|
13667
14628
|
try {
|
|
13668
14629
|
const a = fs12.statSync(normalizedLeft, { bigint: true });
|
|
13669
14630
|
const b = fs12.statSync(normalizedRight, { bigint: true });
|
|
@@ -13721,10 +14682,10 @@ var init_workers = __esm(() => {
|
|
|
13721
14682
|
// packages/workit-core/src/check-config.ts
|
|
13722
14683
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13723
14684
|
import fs13 from "node:fs";
|
|
13724
|
-
import
|
|
14685
|
+
import path18 from "node:path";
|
|
13725
14686
|
function checksConfigFile(root) {
|
|
13726
|
-
const current =
|
|
13727
|
-
const legacy =
|
|
14687
|
+
const current = path18.join(root, CHECKS_FILE);
|
|
14688
|
+
const legacy = path18.join(root, LEGACY_CHECKS_FILE);
|
|
13728
14689
|
return !fs13.existsSync(current) && fs13.existsSync(legacy) ? legacy : current;
|
|
13729
14690
|
}
|
|
13730
14691
|
function repoHeavyCommands(root) {
|
|
@@ -13774,12 +14735,12 @@ function splitCommand(command) {
|
|
|
13774
14735
|
return argv.length ? argv : new Error("empty command");
|
|
13775
14736
|
}
|
|
13776
14737
|
function checkRoot(cwd) {
|
|
13777
|
-
return
|
|
14738
|
+
return path18.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
13778
14739
|
}
|
|
13779
14740
|
function detectedChecks(root) {
|
|
13780
14741
|
const read = (file) => {
|
|
13781
14742
|
try {
|
|
13782
|
-
return fs13.readFileSync(
|
|
14743
|
+
return fs13.readFileSync(path18.join(root, file), "utf8");
|
|
13783
14744
|
} catch {
|
|
13784
14745
|
return null;
|
|
13785
14746
|
}
|
|
@@ -13818,7 +14779,7 @@ function detectedChecks(root) {
|
|
|
13818
14779
|
function loadCheckConfig(cwd) {
|
|
13819
14780
|
const root = checkRoot(cwd);
|
|
13820
14781
|
const file = checksConfigFile(root);
|
|
13821
|
-
const legacy =
|
|
14782
|
+
const legacy = path18.basename(file) === LEGACY_CHECKS_FILE;
|
|
13822
14783
|
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
13823
14784
|
const note = legacy ? {
|
|
13824
14785
|
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
@@ -13894,10 +14855,10 @@ function workitAvailable(env = process.env) {
|
|
|
13894
14855
|
return workitOnPath;
|
|
13895
14856
|
const windows = process.platform === "win32";
|
|
13896
14857
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
13897
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
14858
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path18.delimiter).filter(Boolean);
|
|
13898
14859
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
13899
14860
|
try {
|
|
13900
|
-
return fs13.statSync(
|
|
14861
|
+
return fs13.statSync(path18.join(dir, `workit${ext}`)).isFile();
|
|
13901
14862
|
} catch {
|
|
13902
14863
|
return false;
|
|
13903
14864
|
}
|
|
@@ -13918,7 +14879,7 @@ var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "wo
|
|
|
13918
14879
|
const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
|
|
13919
14880
|
return value || null;
|
|
13920
14881
|
}, packageManager = (root) => {
|
|
13921
|
-
const has = (file) => fs13.existsSync(
|
|
14882
|
+
const has = (file) => fs13.existsSync(path18.join(root, file));
|
|
13922
14883
|
if (has("bun.lock") || has("bun.lockb"))
|
|
13923
14884
|
return "bun";
|
|
13924
14885
|
if (has("pnpm-lock.yaml"))
|
|
@@ -13944,7 +14905,7 @@ var init_check_config = __esm(() => {
|
|
|
13944
14905
|
import { createHash as createHash12 } from "node:crypto";
|
|
13945
14906
|
import * as fs14 from "node:fs";
|
|
13946
14907
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
13947
|
-
import
|
|
14908
|
+
import path19 from "node:path";
|
|
13948
14909
|
function captureCandidate(root, scope, environment = []) {
|
|
13949
14910
|
let checkout;
|
|
13950
14911
|
try {
|
|
@@ -13986,7 +14947,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13986
14947
|
for (const item of [...names].toSorted()) {
|
|
13987
14948
|
if (!scopeMatches(item, normalizedScope))
|
|
13988
14949
|
continue;
|
|
13989
|
-
const target =
|
|
14950
|
+
const target = path19.join(checkout, item);
|
|
13990
14951
|
try {
|
|
13991
14952
|
const stat = fs14.lstatSync(target);
|
|
13992
14953
|
if (stat.isSymbolicLink()) {
|
|
@@ -14395,12 +15356,12 @@ function boundTaskSummary(summary) {
|
|
|
14395
15356
|
brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
|
|
14396
15357
|
return brief;
|
|
14397
15358
|
}
|
|
14398
|
-
var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
15359
|
+
var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path19.sep}`), relative = (root, candidate) => path19.relative(root, candidate).split(path19.sep).join("/") || ".", canonicalPath = (value) => {
|
|
14399
15360
|
if (typeof value !== "string" || !value)
|
|
14400
15361
|
return null;
|
|
14401
15362
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14402
15363
|
return null;
|
|
14403
|
-
const normalized =
|
|
15364
|
+
const normalized = path19.posix.normalize(value);
|
|
14404
15365
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14405
15366
|
return null;
|
|
14406
15367
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14442,14 +15403,14 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14442
15403
|
git: !/not a git repository/i.test(stderr)
|
|
14443
15404
|
};
|
|
14444
15405
|
}
|
|
14445
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15406
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")) : [];
|
|
14446
15407
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14447
15408
|
if (stagedDeleted.status !== 0) {
|
|
14448
15409
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14449
15410
|
if (!/not a git repository/i.test(stderr))
|
|
14450
15411
|
return { paths, uncertain: true, git: true };
|
|
14451
15412
|
} else if (stagedDeleted.stdout) {
|
|
14452
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15413
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")));
|
|
14453
15414
|
}
|
|
14454
15415
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14455
15416
|
}, walk = (root, directory, output) => {
|
|
@@ -14463,7 +15424,7 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14463
15424
|
for (const name of names) {
|
|
14464
15425
|
if (name === ".git" || name === ".workit")
|
|
14465
15426
|
continue;
|
|
14466
|
-
const target =
|
|
15427
|
+
const target = path19.join(directory, name);
|
|
14467
15428
|
let stat;
|
|
14468
15429
|
try {
|
|
14469
15430
|
stat = fs14.lstatSync(target);
|
|
@@ -14480,12 +15441,12 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14480
15441
|
}, scopeRoots = (root, scope) => {
|
|
14481
15442
|
const roots = [];
|
|
14482
15443
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14483
|
-
const target =
|
|
15444
|
+
const target = path19.resolve(root, item);
|
|
14484
15445
|
if (!inside(root, target))
|
|
14485
15446
|
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` }] });
|
|
14486
15447
|
let ancestor = target;
|
|
14487
15448
|
while (!fs14.existsSync(ancestor) && ancestor !== root)
|
|
14488
|
-
ancestor =
|
|
15449
|
+
ancestor = path19.dirname(ancestor);
|
|
14489
15450
|
try {
|
|
14490
15451
|
if (!inside(root, fs14.realpathSync(ancestor)))
|
|
14491
15452
|
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` }] });
|
|
@@ -14549,23 +15510,23 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14549
15510
|
continue;
|
|
14550
15511
|
if (!reference.digest)
|
|
14551
15512
|
return failure2("invalid_input", "document references require a byte digest");
|
|
14552
|
-
const target =
|
|
14553
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15513
|
+
const target = path19.resolve(root, reference.path);
|
|
15514
|
+
if (target !== root && !target.startsWith(`${root}${path19.sep}`))
|
|
14554
15515
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14555
15516
|
fields: [
|
|
14556
15517
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
14557
15518
|
]
|
|
14558
15519
|
});
|
|
14559
15520
|
try {
|
|
14560
|
-
const below =
|
|
15521
|
+
const below = path19.relative(root, target);
|
|
14561
15522
|
let current = root;
|
|
14562
|
-
for (const segment of below.split(
|
|
15523
|
+
for (const segment of below.split(path19.sep)) {
|
|
14563
15524
|
if (!segment)
|
|
14564
15525
|
continue;
|
|
14565
|
-
current =
|
|
15526
|
+
current = path19.join(current, segment);
|
|
14566
15527
|
const stat = fs14.lstatSync(current);
|
|
14567
15528
|
const real = fs14.realpathSync(current);
|
|
14568
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15529
|
+
if (real !== root && !real.startsWith(`${root}${path19.sep}`))
|
|
14569
15530
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14570
15531
|
fields: [
|
|
14571
15532
|
{
|
|
@@ -14693,9 +15654,9 @@ Autonomy contract:
|
|
|
14693
15654
|
Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
|
|
14694
15655
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
14695
15656
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
14696
|
-
self-reviewed, never verified. High risk,
|
|
14697
|
-
merge need a verifier with its
|
|
14698
|
-
\`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
15657
|
+
self-reviewed, never verified. High risk, a focused or full lane and
|
|
15658
|
+
merge under \`verification: self|independent\` need a verifier with its
|
|
15659
|
+
own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
14699
15660
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
14700
15661
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
14701
15662
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -14703,7 +15664,7 @@ Autonomy contract:
|
|
|
14703
15664
|
CLI first: if a step has one right answer, use the \`workit\` verb instead of
|
|
14704
15665
|
hand-running git, gh or glab (\`workit git|pr|ci|check|help\`;
|
|
14705
15666
|
else \`npx -y @brainervirus/workit-cli\`).
|
|
14706
|
-
Other checkout: \`workit --cwd <dir>
|
|
15667
|
+
Other checkout: \`workit <verb> --cwd <dir> …\` (verb first: host rules match it); one workit
|
|
14707
15668
|
verb per shell call, never \`cd <dir> && workit\`. zsh-safe: no \`echo =====\`
|
|
14708
15669
|
(\`printf '%s\\n' ---\`), quote globs and URLs, no variable named \`status\` or \`path\`.
|
|
14709
15670
|
Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
|
|
@@ -14715,7 +15676,7 @@ authorizes an effect. Judge work once with
|
|
|
14715
15676
|
\`workit policy assess --judge risk= behavior= product-choice= plan=\`.
|
|
14716
15677
|
Across repositories, bind items to checkout, branch and endpoint, resolve
|
|
14717
15678
|
competing targets before a mutation, and reconcile an uncertain
|
|
14718
|
-
|
|
15679
|
+
effect before a retry.
|
|
14719
15680
|
|
|
14720
15681
|
Mid-task: never silently drop or resume an objective (workit-continue).
|
|
14721
15682
|
|
|
@@ -16566,7 +17527,7 @@ var support = (descriptor, axis) => {
|
|
|
16566
17527
|
// packages/workit-core/src/hooks/context.ts
|
|
16567
17528
|
import { createHash as createHash13 } from "node:crypto";
|
|
16568
17529
|
import fs15 from "node:fs";
|
|
16569
|
-
import
|
|
17530
|
+
import path20 from "node:path";
|
|
16570
17531
|
function sessionCompactContext(store, session, context, selection = "session-bound") {
|
|
16571
17532
|
const listed = store.listTaskIndex();
|
|
16572
17533
|
if (!listed.ok)
|
|
@@ -16601,7 +17562,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
16601
17562
|
return null;
|
|
16602
17563
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
16603
17564
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
16604
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
17565
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
|
|
16605
17566
|
const observedFiles = filesKey(files);
|
|
16606
17567
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
16607
17568
|
if (!compact.ok || !compact.data)
|
|
@@ -16707,13 +17668,13 @@ ${options.addendum}` : ""}
|
|
|
16707
17668
|
if (location instanceof Error)
|
|
16708
17669
|
return null;
|
|
16709
17670
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
16710
|
-
return
|
|
17671
|
+
return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
16711
17672
|
} catch {
|
|
16712
17673
|
return null;
|
|
16713
17674
|
}
|
|
16714
17675
|
}, writeTurnDigest = (file, digest) => {
|
|
16715
17676
|
try {
|
|
16716
|
-
fs15.mkdirSync(
|
|
17677
|
+
fs15.mkdirSync(path20.dirname(file), { recursive: true });
|
|
16717
17678
|
fs15.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
16718
17679
|
`);
|
|
16719
17680
|
} catch {}
|
|
@@ -16726,7 +17687,7 @@ ${options.addendum}` : ""}
|
|
|
16726
17687
|
for (const name of fs15.readdirSync(dir)) {
|
|
16727
17688
|
if (!name.startsWith("turn-"))
|
|
16728
17689
|
continue;
|
|
16729
|
-
const file =
|
|
17690
|
+
const file = path20.join(dir, name);
|
|
16730
17691
|
try {
|
|
16731
17692
|
if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
16732
17693
|
fs15.rmSync(file, { force: true });
|
|
@@ -16754,7 +17715,7 @@ ${options.addendum}` : ""}
|
|
|
16754
17715
|
const file = turnMarker(input);
|
|
16755
17716
|
if (!file)
|
|
16756
17717
|
return;
|
|
16757
|
-
pruneTurnDigests(
|
|
17718
|
+
pruneTurnDigests(path20.dirname(file), Date.now());
|
|
16758
17719
|
if (text)
|
|
16759
17720
|
writeTurnDigest(file, digestOf(text));
|
|
16760
17721
|
else
|
|
@@ -16776,7 +17737,7 @@ var init_context = __esm(() => {
|
|
|
16776
17737
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
16777
17738
|
import { createHash as createHash14 } from "node:crypto";
|
|
16778
17739
|
import fs16 from "node:fs";
|
|
16779
|
-
import
|
|
17740
|
+
import path21 from "node:path";
|
|
16780
17741
|
var sessionMarkerFile = (input, session, store) => {
|
|
16781
17742
|
let dir = store;
|
|
16782
17743
|
if (dir === undefined) {
|
|
@@ -16786,7 +17747,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16786
17747
|
dir = location.dir;
|
|
16787
17748
|
}
|
|
16788
17749
|
const name = createHash14("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
16789
|
-
return
|
|
17750
|
+
return path21.join(dir, "hooks", `skills-${name}.json`);
|
|
16790
17751
|
}, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], EMPTY, readRaw = (file) => {
|
|
16791
17752
|
try {
|
|
16792
17753
|
return fs16.readFileSync(file, "utf8");
|
|
@@ -16807,7 +17768,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16807
17768
|
return { ...EMPTY };
|
|
16808
17769
|
}
|
|
16809
17770
|
}, readSessionMarker = (file) => parseMarker(readRaw(file)), writeAtomic = (file, marker) => {
|
|
16810
|
-
fs16.mkdirSync(
|
|
17771
|
+
fs16.mkdirSync(path21.dirname(file), { recursive: true });
|
|
16811
17772
|
const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
16812
17773
|
try {
|
|
16813
17774
|
fs16.writeFileSync(temp, JSON.stringify(marker));
|
|
@@ -17060,7 +18021,17 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17060
18021
|
}
|
|
17061
18022
|
}
|
|
17062
18023
|
return found;
|
|
17063
|
-
}, PLAIN_POSIX, PLAIN_POWERSHELL,
|
|
18024
|
+
}, PLAIN_POSIX, PLAIN_POWERSHELL, expandsInQuotes = (body, dialect) => {
|
|
18025
|
+
const escape = dialect === "posix" ? "\\" : "`";
|
|
18026
|
+
for (let index = 0;index < body.length; index++) {
|
|
18027
|
+
const char = body[index];
|
|
18028
|
+
if (char === escape)
|
|
18029
|
+
index++;
|
|
18030
|
+
else if (char === "$" || dialect === "posix" && char === "`")
|
|
18031
|
+
return true;
|
|
18032
|
+
}
|
|
18033
|
+
return false;
|
|
18034
|
+
}, EXPANDING_RUN, arithmeticEnd = (command, start) => {
|
|
17064
18035
|
const end = substitutionEnd(command, start);
|
|
17065
18036
|
return command[end - 1] === ")" && end > start + 2 ? end : 0;
|
|
17066
18037
|
}, segmentsOf = (command, dialect = "posix") => {
|
|
@@ -17076,6 +18047,7 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17076
18047
|
let current = { words: [], redirects: [] };
|
|
17077
18048
|
let word = "";
|
|
17078
18049
|
let inWord = false;
|
|
18050
|
+
let expanding = false;
|
|
17079
18051
|
let pending = null;
|
|
17080
18052
|
const heredocs = [];
|
|
17081
18053
|
const escape = posix ? "\\" : "`";
|
|
@@ -17090,11 +18062,16 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17090
18062
|
heredocs.push({ delimiter: text.replace(/^-/, ""), owner: current });
|
|
17091
18063
|
else if (pending === "herestring")
|
|
17092
18064
|
(current.stdin ??= []).push(text);
|
|
17093
|
-
else if (pending !== "input")
|
|
18065
|
+
else if (pending !== "input") {
|
|
18066
|
+
const at = current.words.length;
|
|
17094
18067
|
current.words.push(...MARKERS.test(word) ? expandBraces(word) : [word]);
|
|
18068
|
+
if (expanding || !posix && word.startsWith("@"))
|
|
18069
|
+
(current.expands ??= []).push(...current.words.slice(at).map((_, i) => at + i));
|
|
18070
|
+
}
|
|
17095
18071
|
pending = null;
|
|
17096
18072
|
word = "";
|
|
17097
18073
|
inWord = false;
|
|
18074
|
+
expanding = false;
|
|
17098
18075
|
};
|
|
17099
18076
|
const flushSegment = () => {
|
|
17100
18077
|
flushWord();
|
|
@@ -17109,6 +18086,7 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17109
18086
|
};
|
|
17110
18087
|
const substitution = (inner, text) => {
|
|
17111
18088
|
(current.subs ??= []).push(inner);
|
|
18089
|
+
expanding = true;
|
|
17112
18090
|
word += text;
|
|
17113
18091
|
inWord = true;
|
|
17114
18092
|
};
|
|
@@ -17119,6 +18097,8 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17119
18097
|
if (run) {
|
|
17120
18098
|
word += run[0];
|
|
17121
18099
|
inWord = true;
|
|
18100
|
+
if (EXPANDING_RUN.test(run[0]))
|
|
18101
|
+
expanding = true;
|
|
17122
18102
|
index += run[0].length - 1;
|
|
17123
18103
|
continue;
|
|
17124
18104
|
}
|
|
@@ -17147,6 +18127,8 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17147
18127
|
const body = command.slice(index + 1, end < 0 ? undefined : end);
|
|
17148
18128
|
if (posix && char === '"' && /\$\(|`/.test(body))
|
|
17149
18129
|
(current.subs ??= []).push(...substitutions(body));
|
|
18130
|
+
if (char === '"' && expandsInQuotes(body, dialect))
|
|
18131
|
+
expanding = true;
|
|
17150
18132
|
word += unquote(body, char, dialect);
|
|
17151
18133
|
inWord = true;
|
|
17152
18134
|
index = end < 0 ? command.length : end;
|
|
@@ -17192,6 +18174,8 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17192
18174
|
if (char === "(" && inWord && word.endsWith("$")) {
|
|
17193
18175
|
word = word.slice(0, -1);
|
|
17194
18176
|
inWord = word !== "";
|
|
18177
|
+
if (!inWord)
|
|
18178
|
+
expanding = false;
|
|
17195
18179
|
}
|
|
17196
18180
|
group(char === "(" ? "open" : "close");
|
|
17197
18181
|
} else if (!posix && (char === "{" || char === "}")) {
|
|
@@ -17245,6 +18229,8 @@ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "
|
|
|
17245
18229
|
else if (posix && char === "}" && word.includes(OPEN))
|
|
17246
18230
|
word += CLOSE;
|
|
17247
18231
|
else {
|
|
18232
|
+
if (char === "$")
|
|
18233
|
+
expanding = true;
|
|
17248
18234
|
word += char;
|
|
17249
18235
|
inWord = true;
|
|
17250
18236
|
}
|
|
@@ -17270,6 +18256,7 @@ var init_shell_words = __esm(() => {
|
|
|
17270
18256
|
};
|
|
17271
18257
|
PLAIN_POSIX = /[^\s'"\\`$(){},<>|;&#]+/uy;
|
|
17272
18258
|
PLAIN_POWERSHELL = /[^\s'"`(){}<>|;&#]+/uy;
|
|
18259
|
+
EXPANDING_RUN = /[*?[]/u;
|
|
17273
18260
|
});
|
|
17274
18261
|
|
|
17275
18262
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
@@ -17277,7 +18264,7 @@ import { spawnSync as spawnSync11 } from "node:child_process";
|
|
|
17277
18264
|
import { createHash as createHash15 } from "node:crypto";
|
|
17278
18265
|
import fs17 from "node:fs";
|
|
17279
18266
|
import os5 from "node:os";
|
|
17280
|
-
import
|
|
18267
|
+
import path22 from "node:path";
|
|
17281
18268
|
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
17282
18269
|
if (depth > 0)
|
|
17283
18270
|
return walk2(command, dialect, depth);
|
|
@@ -17338,8 +18325,17 @@ function walk2(command, dialect, depth, seen) {
|
|
|
17338
18325
|
visit(next, undefined, hops + 1);
|
|
17339
18326
|
};
|
|
17340
18327
|
const wrapped = seen ? null : workitCheckTail(words);
|
|
17341
|
-
if (wrapped)
|
|
17342
|
-
|
|
18328
|
+
if (wrapped) {
|
|
18329
|
+
const outer = dir;
|
|
18330
|
+
if (wrapped.cwd !== null)
|
|
18331
|
+
dir = joinDir(dir, wrapped.cwd);
|
|
18332
|
+
if ("script" in wrapped)
|
|
18333
|
+
nested(wrapped.script, "posix");
|
|
18334
|
+
else
|
|
18335
|
+
runs([wrapped.argv]);
|
|
18336
|
+
dir = outer;
|
|
18337
|
+
return;
|
|
18338
|
+
}
|
|
17343
18339
|
if (CD.has(name)) {
|
|
17344
18340
|
const target = cdTarget(words);
|
|
17345
18341
|
dir = target === null ? os5.homedir() : joinDir(dir, target);
|
|
@@ -17424,14 +18420,14 @@ function workitCheckTail(words) {
|
|
|
17424
18420
|
return null;
|
|
17425
18421
|
} else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
|
|
17426
18422
|
return null;
|
|
17427
|
-
const args = words.slice(at + 1);
|
|
18423
|
+
const { rest: args, cwd } = skipWorkitGlobals(words.slice(at + 1));
|
|
17428
18424
|
if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
|
|
17429
18425
|
return null;
|
|
17430
18426
|
const split = args.indexOf("--");
|
|
17431
18427
|
const tail = split < 0 ? [] : args.slice(split + 1);
|
|
17432
18428
|
if (!tail.length)
|
|
17433
18429
|
return null;
|
|
17434
|
-
return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
|
|
18430
|
+
return args.slice(0, split).includes("--shell") ? { script: tail.join(" "), cwd } : { argv: tail, cwd };
|
|
17435
18431
|
}
|
|
17436
18432
|
function simpleCommand(command, dialect = "posix", depth = 0) {
|
|
17437
18433
|
let segments;
|
|
@@ -17491,7 +18487,21 @@ function skipWorkitGlobals(args) {
|
|
|
17491
18487
|
} else if (args[at] !== "--json")
|
|
17492
18488
|
break;
|
|
17493
18489
|
}
|
|
17494
|
-
|
|
18490
|
+
const rest = [];
|
|
18491
|
+
for (;at < args.length; at++) {
|
|
18492
|
+
const arg = args[at];
|
|
18493
|
+
if (arg === "--") {
|
|
18494
|
+
rest.push(...args.slice(at));
|
|
18495
|
+
break;
|
|
18496
|
+
}
|
|
18497
|
+
if (arg.startsWith("--cwd="))
|
|
18498
|
+
cwd = arg.slice("--cwd=".length);
|
|
18499
|
+
else if (arg === "--cwd" && args[at + 1] !== undefined)
|
|
18500
|
+
cwd = args[++at];
|
|
18501
|
+
else
|
|
18502
|
+
rest.push(arg);
|
|
18503
|
+
}
|
|
18504
|
+
return { rest, cwd };
|
|
17495
18505
|
}
|
|
17496
18506
|
function rawAction(invocation) {
|
|
17497
18507
|
const args = invocation.args;
|
|
@@ -17576,7 +18586,7 @@ function noteRawCommit(input, command, key) {
|
|
|
17576
18586
|
if (action.kind !== "commit")
|
|
17577
18587
|
continue;
|
|
17578
18588
|
const file = markerFile(repo, input.session.id, key);
|
|
17579
|
-
fs17.mkdirSync(
|
|
18589
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
17580
18590
|
fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17581
18591
|
`);
|
|
17582
18592
|
}
|
|
@@ -17603,12 +18613,12 @@ function settlePendingCommit(input) {
|
|
|
17603
18613
|
settle2(input, repo, file);
|
|
17604
18614
|
} catch {}
|
|
17605
18615
|
}
|
|
17606
|
-
var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLARATIONS, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, GIT_CONFIG_OPTIONS, GIT_CONFIG_ENV, ASSIGNMENT, MAX_DEPTH = 4, MAX_EXPANSIONS = 16, isAbsolute = (value) =>
|
|
18616
|
+
var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLARATIONS, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, GIT_CONFIG_OPTIONS, GIT_CONFIG_ENV, ASSIGNMENT, MAX_DEPTH = 4, MAX_EXPANSIONS = 16, isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), HERE, joinDir = (base, raw) => {
|
|
17607
18617
|
const next = raw.replace(HERE, ".");
|
|
17608
18618
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17609
18619
|
return;
|
|
17610
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
17611
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18620
|
+
const expanded = next === "~" || next.startsWith("~/") ? path22.join(os5.homedir(), next.slice(1)) : next;
|
|
18621
|
+
return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
|
|
17612
18622
|
}, rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner), cdTarget = (words) => {
|
|
17613
18623
|
const args = words.slice(1);
|
|
17614
18624
|
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
@@ -18096,17 +19106,17 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18096
19106
|
if (!git || git.reftable)
|
|
18097
19107
|
return null;
|
|
18098
19108
|
try {
|
|
18099
|
-
const head = fs17.readFileSync(
|
|
19109
|
+
const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18100
19110
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18101
19111
|
return head;
|
|
18102
19112
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18103
19113
|
if (!ref)
|
|
18104
19114
|
return null;
|
|
18105
|
-
const common =
|
|
19115
|
+
const common = path22.dirname(repo.location.dir);
|
|
18106
19116
|
try {
|
|
18107
|
-
return fs17.readFileSync(
|
|
19117
|
+
return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
|
|
18108
19118
|
} catch {}
|
|
18109
|
-
const packed = fs17.readFileSync(
|
|
19119
|
+
const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
|
|
18110
19120
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18111
19121
|
} catch {
|
|
18112
19122
|
return null;
|
|
@@ -18172,7 +19182,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18172
19182
|
const sub = invocation.args[0];
|
|
18173
19183
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
18174
19184
|
return [invocation];
|
|
18175
|
-
const dir =
|
|
19185
|
+
const dir = path22.resolve(root, invocation.dir ?? ".");
|
|
18176
19186
|
if (!fs17.existsSync(dir))
|
|
18177
19187
|
return [invocation];
|
|
18178
19188
|
const config = gitConfig(dir, invocation, cache);
|
|
@@ -18194,11 +19204,11 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18194
19204
|
}, normalizedUrl = (url, dir) => {
|
|
18195
19205
|
const trimmed = url.trim();
|
|
18196
19206
|
if (/^file:\/\//i.test(trimmed))
|
|
18197
|
-
return
|
|
19207
|
+
return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18198
19208
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
18199
19209
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
18200
19210
|
if (!scheme && !scp)
|
|
18201
|
-
return
|
|
19211
|
+
return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18202
19212
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
18203
19213
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18204
19214
|
}, rewrittenUrl = (url, config) => {
|
|
@@ -18404,7 +19414,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18404
19414
|
const out = [];
|
|
18405
19415
|
const repos = new Map;
|
|
18406
19416
|
const repoFor = (relative) => {
|
|
18407
|
-
const dir =
|
|
19417
|
+
const dir = path22.resolve(cwd, relative ?? ".");
|
|
18408
19418
|
if (!repos.has(dir)) {
|
|
18409
19419
|
const repo = repoAt(dir);
|
|
18410
19420
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18448,7 +19458,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18448
19458
|
kind: "deny",
|
|
18449
19459
|
reason: `workit: the shell hook could not analyze this command (${error.message}), so it does not run. ${SPLIT_IT}`,
|
|
18450
19460
|
unblock: SPLIT_IT
|
|
18451
|
-
}), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) =>
|
|
19461
|
+
}), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash15("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
|
|
18452
19462
|
const run = spawnSync11("git", [
|
|
18453
19463
|
"log",
|
|
18454
19464
|
"-1",
|
|
@@ -18647,8 +19657,8 @@ var init_raw_git = __esm(() => {
|
|
|
18647
19657
|
|
|
18648
19658
|
// packages/workit-core/src/core/route-intent.ts
|
|
18649
19659
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18650
|
-
import { existsSync as
|
|
18651
|
-
import
|
|
19660
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
19661
|
+
import path23 from "node:path";
|
|
18652
19662
|
var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, SUBTREE_CREATE, BRANCH_NO_CREATE, BRANCH_RENAME, longOption = (name, flags) => flags.has(name) || name.length >= 3 && [...flags].some((flag) => flag.startsWith("--") && flag.startsWith(name)), shortsOf = (flags) => [...flags].filter((flag) => /^-[^-]$/u.test(flag)).join("").replaceAll("-", ""), flagValue = (args, flags) => {
|
|
18653
19663
|
const shorts = shortsOf(flags);
|
|
18654
19664
|
for (let index = 1;index < args.length; index++) {
|
|
@@ -18774,8 +19784,8 @@ var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, SUBTREE_CREATE,
|
|
|
18774
19784
|
return null;
|
|
18775
19785
|
if (dir === null)
|
|
18776
19786
|
return root;
|
|
18777
|
-
const resolved =
|
|
18778
|
-
return
|
|
19787
|
+
const resolved = path23.resolve(root, dir);
|
|
19788
|
+
return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18779
19789
|
}, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18780
19790
|
let judged = false;
|
|
18781
19791
|
const cache = new Map;
|
|
@@ -18826,10 +19836,57 @@ var init_policy = __esm(() => {
|
|
|
18826
19836
|
init_shell_words();
|
|
18827
19837
|
});
|
|
18828
19838
|
|
|
19839
|
+
// packages/workit-core/src/hooks/delivery-verbs.ts
|
|
19840
|
+
var WORKIT_ALLOWED_VERBS, WORKIT_BYPASS_FLAGS, ALLOWED, isBypass = (word) => WORKIT_BYPASS_FLAGS.some((flag) => word === flag || word.startsWith(`${flag}=`)), isWorkitWord = (word) => /^(?:.*[\\/])?workit(?:-cli)?$/u.test(word), bypassReason = (flag) => `Workit: \`${flag}\` overrides a Workit gate, so the user decides this call (no allow rule covers it).`, EXPANSION_REASON = "Workit: an expansion before `--` could become a bypass flag, so the user decides this call (no allow rule covers it).", workitBypassFlag = (command, dialect = "posix") => {
|
|
19841
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
19842
|
+
if (segment.unanalyzable) {
|
|
19843
|
+
const text = command.replaceAll(/['"\\`]/gu, "");
|
|
19844
|
+
if (!/\bworkit\b/u.test(text))
|
|
19845
|
+
return null;
|
|
19846
|
+
const found = WORKIT_BYPASS_FLAGS.find((flag) => text.includes(flag));
|
|
19847
|
+
return found ? bypassReason(found) : null;
|
|
19848
|
+
}
|
|
19849
|
+
const start = segment.words.findIndex(isWorkitWord);
|
|
19850
|
+
if (start < 0)
|
|
19851
|
+
continue;
|
|
19852
|
+
const words = segment.words.slice(start + 1);
|
|
19853
|
+
const end = words.indexOf("--");
|
|
19854
|
+
const flag = (end < 0 ? words : words.slice(0, end)).find(isBypass);
|
|
19855
|
+
if (flag)
|
|
19856
|
+
return bypassReason(flag.split("=")[0] ?? flag);
|
|
19857
|
+
const limit = start + 1 + (end < 0 ? words.length : end);
|
|
19858
|
+
if (segment.expands?.some((index) => index > start && index < limit))
|
|
19859
|
+
return EXPANSION_REASON;
|
|
19860
|
+
}
|
|
19861
|
+
return null;
|
|
19862
|
+
};
|
|
19863
|
+
var init_delivery_verbs = __esm(() => {
|
|
19864
|
+
init_shell_words();
|
|
19865
|
+
WORKIT_ALLOWED_VERBS = [
|
|
19866
|
+
"git",
|
|
19867
|
+
"pr",
|
|
19868
|
+
"ci",
|
|
19869
|
+
"stack",
|
|
19870
|
+
"release",
|
|
19871
|
+
"task",
|
|
19872
|
+
"ledger",
|
|
19873
|
+
"evidence",
|
|
19874
|
+
"finding",
|
|
19875
|
+
"decision",
|
|
19876
|
+
"worker",
|
|
19877
|
+
"verify-delivery",
|
|
19878
|
+
"policy",
|
|
19879
|
+
"doctor",
|
|
19880
|
+
"help"
|
|
19881
|
+
];
|
|
19882
|
+
WORKIT_BYPASS_FLAGS = ["--unverified", "--overwrite-unintegrated"];
|
|
19883
|
+
ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
|
|
19884
|
+
});
|
|
19885
|
+
|
|
18829
19886
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
18830
19887
|
import fs18 from "node:fs";
|
|
18831
19888
|
import os6 from "node:os";
|
|
18832
|
-
import
|
|
19889
|
+
import path24 from "node:path";
|
|
18833
19890
|
function parseJsonc(text) {
|
|
18834
19891
|
let out = "";
|
|
18835
19892
|
for (let index = 0;index < text.length; index += 1) {
|
|
@@ -18859,21 +19916,21 @@ function parseJsonc(text) {
|
|
|
18859
19916
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
18860
19917
|
}
|
|
18861
19918
|
function gitTop(cwd) {
|
|
18862
|
-
for (let dir =
|
|
18863
|
-
if (fs18.existsSync(
|
|
19919
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
19920
|
+
if (fs18.existsSync(path24.join(dir, ".git")))
|
|
18864
19921
|
return dir;
|
|
18865
|
-
if (
|
|
19922
|
+
if (path24.dirname(dir) === dir)
|
|
18866
19923
|
return null;
|
|
18867
19924
|
}
|
|
18868
19925
|
}
|
|
18869
19926
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
18870
19927
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
18871
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
19928
|
+
const user = env.CLAUDE_CONFIG_DIR || path24.join(homeOf(env), ".claude");
|
|
18872
19929
|
return [
|
|
18873
19930
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
18874
|
-
|
|
18875
|
-
|
|
18876
|
-
|
|
19931
|
+
path24.join(user, "settings.json"),
|
|
19932
|
+
path24.join(project, ".claude", "settings.json"),
|
|
19933
|
+
path24.join(project, ".claude", "settings.local.json")
|
|
18877
19934
|
];
|
|
18878
19935
|
}
|
|
18879
19936
|
function claudeRules(cwd, env = process.env) {
|
|
@@ -18918,10 +19975,10 @@ function claudeRules(cwd, env = process.env) {
|
|
|
18918
19975
|
function opencodeRules(cwd, env = process.env) {
|
|
18919
19976
|
const rules = EMPTY2();
|
|
18920
19977
|
try {
|
|
18921
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
19978
|
+
const config = env.XDG_CONFIG_HOME || path24.join(homeOf(env), ".config");
|
|
18922
19979
|
const files = [
|
|
18923
|
-
...OPENCODE_NAMES.map((name) =>
|
|
18924
|
-
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [
|
|
19980
|
+
...OPENCODE_NAMES.map((name) => path24.join(config, "opencode", name)),
|
|
19981
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path24.join(dir, name), path24.join(dir, ".opencode", name)])),
|
|
18925
19982
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
18926
19983
|
];
|
|
18927
19984
|
for (const file of files) {
|
|
@@ -18939,9 +19996,9 @@ function opencodeRules(cwd, env = process.env) {
|
|
|
18939
19996
|
function codexRules(cwd, env = process.env) {
|
|
18940
19997
|
const rules = EMPTY2();
|
|
18941
19998
|
try {
|
|
18942
|
-
const home = env.CODEX_HOME ||
|
|
19999
|
+
const home = env.CODEX_HOME || path24.join(homeOf(env), ".codex");
|
|
18943
20000
|
const top = gitTop(cwd) ?? cwd;
|
|
18944
|
-
for (const dir of [
|
|
20001
|
+
for (const dir of [path24.join(home, "rules"), path24.join(top, ".codex", "rules")]) {
|
|
18945
20002
|
let files;
|
|
18946
20003
|
try {
|
|
18947
20004
|
files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -18951,7 +20008,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
18951
20008
|
return "unreadable";
|
|
18952
20009
|
}
|
|
18953
20010
|
for (const name of files) {
|
|
18954
|
-
const text = readText2(
|
|
20011
|
+
const text = readText2(path24.join(dir, name)) ?? "";
|
|
18955
20012
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
18956
20013
|
const rule = codexRule(part);
|
|
18957
20014
|
if (!rule)
|
|
@@ -18970,8 +20027,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
18970
20027
|
try {
|
|
18971
20028
|
const top = gitTop(cwd) ?? cwd;
|
|
18972
20029
|
for (const file of [
|
|
18973
|
-
|
|
18974
|
-
|
|
20030
|
+
path24.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
20031
|
+
path24.join(top, ".cursor", "cli.json")
|
|
18975
20032
|
]) {
|
|
18976
20033
|
const text = readText2(file);
|
|
18977
20034
|
if (text === null)
|
|
@@ -19033,9 +20090,9 @@ var EMPTY2 = () => ({ deny: [], allow: [] }), readText2 = (file) => {
|
|
|
19033
20090
|
}, isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), projectDirs = (cwd) => {
|
|
19034
20091
|
const top = gitTop(cwd);
|
|
19035
20092
|
const dirs = [];
|
|
19036
|
-
for (let dir =
|
|
20093
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
19037
20094
|
dirs.push(dir);
|
|
19038
|
-
if (!top || dir === top ||
|
|
20095
|
+
if (!top || dir === top || path24.dirname(dir) === dir)
|
|
19039
20096
|
return dirs;
|
|
19040
20097
|
}
|
|
19041
20098
|
}, homeOf = (env) => env.HOME || os6.homedir(), CLAUDE_MANAGED, bashPattern = (entry) => {
|
|
@@ -19133,18 +20190,18 @@ var init_heavy_rules = __esm(() => {
|
|
|
19133
20190
|
// packages/workit-core/src/core/heavy.ts
|
|
19134
20191
|
import fs19 from "node:fs";
|
|
19135
20192
|
import os7 from "node:os";
|
|
19136
|
-
import
|
|
20193
|
+
import path25 from "node:path";
|
|
19137
20194
|
function stateDir(env, platform = process.platform) {
|
|
19138
20195
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19139
20196
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19140
20197
|
if (env.XDG_STATE_HOME)
|
|
19141
|
-
return
|
|
20198
|
+
return path25.join(env.XDG_STATE_HOME, "workit");
|
|
19142
20199
|
const home = env.HOME || os7.homedir();
|
|
19143
20200
|
if (platform === "darwin")
|
|
19144
|
-
return
|
|
20201
|
+
return path25.join(home, "Library", "Application Support", "workit");
|
|
19145
20202
|
if (platform === "win32")
|
|
19146
|
-
return
|
|
19147
|
-
return
|
|
20203
|
+
return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
|
|
20204
|
+
return path25.join(home, ".local", "state", "workit");
|
|
19148
20205
|
}
|
|
19149
20206
|
function positional2(words, from, values = PM_VALUES) {
|
|
19150
20207
|
let index = from;
|
|
@@ -19284,7 +20341,7 @@ function machinePatterns(env) {
|
|
|
19284
20341
|
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
19285
20342
|
}
|
|
19286
20343
|
function repoPatterns(cwd) {
|
|
19287
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
20344
|
+
return repoHeavyCommands(gitTop(cwd) ?? path25.resolve(cwd));
|
|
19288
20345
|
}
|
|
19289
20346
|
function heavyLines(lines, options) {
|
|
19290
20347
|
const found = [];
|
|
@@ -19353,12 +20410,12 @@ function heavyPlan(command, options) {
|
|
|
19353
20410
|
}
|
|
19354
20411
|
function workitOnPath2(env = process.env) {
|
|
19355
20412
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
19356
|
-
for (const dir of (env.PATH ?? "").split(
|
|
20413
|
+
for (const dir of (env.PATH ?? "").split(path25.delimiter))
|
|
19357
20414
|
for (const name of names) {
|
|
19358
20415
|
if (!dir)
|
|
19359
20416
|
continue;
|
|
19360
20417
|
try {
|
|
19361
|
-
fs19.accessSync(
|
|
20418
|
+
fs19.accessSync(path25.join(dir, name), fs19.constants.X_OK);
|
|
19362
20419
|
return true;
|
|
19363
20420
|
} catch {}
|
|
19364
20421
|
}
|
|
@@ -19372,7 +20429,7 @@ function leaseRewrite(command) {
|
|
|
19372
20429
|
return null;
|
|
19373
20430
|
return `${prefix}workit check --heavy -- ${rest}`;
|
|
19374
20431
|
}
|
|
19375
|
-
var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) =>
|
|
20432
|
+
var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path25.join(stateDir(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index])), readJson = (file) => {
|
|
19376
20433
|
try {
|
|
19377
20434
|
return JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
19378
20435
|
} catch {
|
|
@@ -19518,7 +20575,7 @@ var init_heavy = __esm(() => {
|
|
|
19518
20575
|
// packages/workit-core/src/core/heavy-words.ts
|
|
19519
20576
|
import fs20 from "node:fs";
|
|
19520
20577
|
import os8 from "node:os";
|
|
19521
|
-
import
|
|
20578
|
+
import path26 from "node:path";
|
|
19522
20579
|
function maybeHeavy(command, cwd, env = process.env) {
|
|
19523
20580
|
try {
|
|
19524
20581
|
const words = wordsOf(command);
|
|
@@ -19534,13 +20591,13 @@ var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((w
|
|
|
19534
20591
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19535
20592
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19536
20593
|
if (env.XDG_STATE_HOME)
|
|
19537
|
-
return
|
|
20594
|
+
return path26.join(env.XDG_STATE_HOME, "workit");
|
|
19538
20595
|
const home = env.HOME || os8.homedir();
|
|
19539
20596
|
if (process.platform === "darwin")
|
|
19540
|
-
return
|
|
20597
|
+
return path26.join(home, "Library", "Application Support", "workit");
|
|
19541
20598
|
if (process.platform === "win32")
|
|
19542
|
-
return
|
|
19543
|
-
return
|
|
20599
|
+
return path26.join(env.LOCALAPPDATA ?? path26.join(home, "AppData", "Local"), "workit");
|
|
20600
|
+
return path26.join(home, ".local", "state", "workit");
|
|
19544
20601
|
}, readJson2 = (file) => {
|
|
19545
20602
|
try {
|
|
19546
20603
|
return JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
@@ -19549,15 +20606,15 @@ var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((w
|
|
|
19549
20606
|
}
|
|
19550
20607
|
}, patternWords = (cwd, env) => {
|
|
19551
20608
|
const patterns = [];
|
|
19552
|
-
const machine = readJson2(
|
|
20609
|
+
const machine = readJson2(path26.join(stateDir2(env), "heavy-commands.json"));
|
|
19553
20610
|
if (Array.isArray(machine?.patterns))
|
|
19554
20611
|
patterns.push(...machine.patterns);
|
|
19555
|
-
for (let dir =
|
|
19556
|
-
if (fs20.existsSync(
|
|
20612
|
+
for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
|
|
20613
|
+
if (fs20.existsSync(path26.join(dir, ".git"))) {
|
|
19557
20614
|
patterns.push(...repoHeavyCommands(dir));
|
|
19558
20615
|
break;
|
|
19559
20616
|
}
|
|
19560
|
-
if (
|
|
20617
|
+
if (path26.dirname(dir) === dir)
|
|
19561
20618
|
break;
|
|
19562
20619
|
}
|
|
19563
20620
|
const words = new Set;
|
|
@@ -19627,7 +20684,7 @@ import { spawnSync as spawnSync13 } from "node:child_process";
|
|
|
19627
20684
|
import { createHash as createHash16 } from "node:crypto";
|
|
19628
20685
|
import fs21 from "node:fs";
|
|
19629
20686
|
import os9 from "node:os";
|
|
19630
|
-
import
|
|
20687
|
+
import path27 from "node:path";
|
|
19631
20688
|
var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session, familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, ""), processAlive = (pid) => {
|
|
19632
20689
|
try {
|
|
19633
20690
|
process.kill(pid, 0);
|
|
@@ -19685,15 +20742,15 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
19685
20742
|
pid: () => hostPid(transport)
|
|
19686
20743
|
};
|
|
19687
20744
|
}, checkoutOf = (target) => {
|
|
19688
|
-
let dir =
|
|
19689
|
-
while (!fs21.existsSync(dir) &&
|
|
19690
|
-
dir =
|
|
20745
|
+
let dir = path27.resolve(target);
|
|
20746
|
+
while (!fs21.existsSync(dir) && path27.dirname(dir) !== dir)
|
|
20747
|
+
dir = path27.dirname(dir);
|
|
19691
20748
|
if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
|
|
19692
|
-
dir =
|
|
19693
|
-
const parts = dir.split(
|
|
20749
|
+
dir = path27.dirname(dir);
|
|
20750
|
+
const parts = dir.split(path27.sep);
|
|
19694
20751
|
const dotGit = parts.lastIndexOf(".git");
|
|
19695
20752
|
if (dotGit > 0)
|
|
19696
|
-
dir = parts.slice(0, dotGit).join(
|
|
20753
|
+
dir = parts.slice(0, dotGit).join(path27.sep) || path27.sep;
|
|
19697
20754
|
const location = resolveStore(dir);
|
|
19698
20755
|
if (location instanceof Error || !location.shared || !location.top)
|
|
19699
20756
|
return null;
|
|
@@ -19702,8 +20759,8 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
19702
20759
|
top = fs21.realpathSync.native(top);
|
|
19703
20760
|
} catch {}
|
|
19704
20761
|
return top;
|
|
19705
|
-
}, markerFile2 = (top) =>
|
|
19706
|
-
let key =
|
|
20762
|
+
}, markerFile2 = (top) => path27.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
|
|
20763
|
+
let key = path27.resolve(top);
|
|
19707
20764
|
try {
|
|
19708
20765
|
key = fs21.realpathSync.native(key);
|
|
19709
20766
|
} catch {}
|
|
@@ -19791,7 +20848,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
19791
20848
|
checkout,
|
|
19792
20849
|
heartbeatAt: now.toISOString()
|
|
19793
20850
|
};
|
|
19794
|
-
fs21.mkdirSync(
|
|
20851
|
+
fs21.mkdirSync(path27.dirname(file), { recursive: true });
|
|
19795
20852
|
const temp = `${file}.${process.pid}.tmp`;
|
|
19796
20853
|
fs21.writeFileSync(temp, JSON.stringify(marker));
|
|
19797
20854
|
fs21.renameSync(temp, file);
|
|
@@ -19812,9 +20869,9 @@ var init_live_session = __esm(() => {
|
|
|
19812
20869
|
});
|
|
19813
20870
|
|
|
19814
20871
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19815
|
-
import { existsSync as
|
|
20872
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
19816
20873
|
import os10 from "node:os";
|
|
19817
|
-
import
|
|
20874
|
+
import path28 from "node:path";
|
|
19818
20875
|
function writeGate(cwd, paths, owner, deps) {
|
|
19819
20876
|
const forged = paths?.length ? issuedTarget(cwd, paths) : null;
|
|
19820
20877
|
if (forged)
|
|
@@ -19872,21 +20929,21 @@ function beforeWriteGate(cwd, paths) {
|
|
|
19872
20929
|
}
|
|
19873
20930
|
var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
19874
20931
|
let rest = "";
|
|
19875
|
-
for (let dir =
|
|
20932
|
+
for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
|
|
19876
20933
|
try {
|
|
19877
|
-
return
|
|
20934
|
+
return path28.join(realpathSync5.native(dir), rest);
|
|
19878
20935
|
} catch {
|
|
19879
|
-
if (
|
|
19880
|
-
return
|
|
19881
|
-
rest = rest ?
|
|
20936
|
+
if (path28.dirname(dir) === dir)
|
|
20937
|
+
return path28.resolve(file);
|
|
20938
|
+
rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
|
|
19882
20939
|
}
|
|
19883
20940
|
}
|
|
19884
20941
|
}, exempt = (cwd, root, file, plans) => {
|
|
19885
20942
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
19886
|
-
const relative =
|
|
19887
|
-
if (relative.startsWith("..") ||
|
|
20943
|
+
const relative = path28.relative(fold(canonical2(root)), fold(canonical2(path28.resolve(cwd, file))));
|
|
20944
|
+
if (relative.startsWith("..") || path28.isAbsolute(relative))
|
|
19888
20945
|
return true;
|
|
19889
|
-
const posix = relative.split(
|
|
20946
|
+
const posix = relative.split(path28.sep).join("/");
|
|
19890
20947
|
const segments = posix.split("/");
|
|
19891
20948
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
19892
20949
|
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), SCRIPT_SHELLS, wrappedScript = (input) => {
|
|
@@ -20053,10 +21110,10 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20053
21110
|
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
20054
21111
|
const target = dest === undefined ? null : at(dest);
|
|
20055
21112
|
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
20056
|
-
const base = target === null ? null : into >= 0 || sources.length > 1 ||
|
|
21113
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path28.dirname(target);
|
|
20057
21114
|
return sources.flatMap((source) => {
|
|
20058
|
-
if (symbolic && base !== null && !
|
|
20059
|
-
return [
|
|
21115
|
+
if (symbolic && base !== null && !path28.isAbsolute(homeExpanded(source)))
|
|
21116
|
+
return [path28.resolve(base, source)];
|
|
20060
21117
|
const resolved = at(source);
|
|
20061
21118
|
return resolved ? [resolved] : [];
|
|
20062
21119
|
});
|
|
@@ -20082,7 +21139,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20082
21139
|
if (!unknown && targets.length === 0)
|
|
20083
21140
|
return { writes: false, targets: null };
|
|
20084
21141
|
return { writes: true, targets: unknown ? null : targets };
|
|
20085
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
21142
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path28.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
20086
21143
|
const store = new TaskStore(cwd);
|
|
20087
21144
|
const listed = store.listTaskIndex();
|
|
20088
21145
|
if (!listed.ok)
|
|
@@ -20135,7 +21192,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20135
21192
|
return [];
|
|
20136
21193
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20137
21194
|
return [];
|
|
20138
|
-
return [
|
|
21195
|
+
return [path28.resolve(cwd, invocation.dir ?? ".")];
|
|
20139
21196
|
});
|
|
20140
21197
|
}, CD2, PUSHD2, POPD2, UNKNOWN_DIR, cdInto = (dir, words, strict = false) => {
|
|
20141
21198
|
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
@@ -20145,7 +21202,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20145
21202
|
return lost;
|
|
20146
21203
|
if (dir === undefined)
|
|
20147
21204
|
return lost;
|
|
20148
|
-
return target === undefined ? os10.homedir() :
|
|
21205
|
+
return target === undefined ? os10.homedir() : path28.resolve(dir, target);
|
|
20149
21206
|
}, quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, homeExpanded = (file) => {
|
|
20150
21207
|
const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
|
|
20151
21208
|
return match ? (process.env.HOME || os10.homedir()) + file.slice(match[0].length) : file;
|
|
@@ -20156,7 +21213,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20156
21213
|
const groups = [];
|
|
20157
21214
|
const at = (file) => {
|
|
20158
21215
|
const home = homeExpanded(file);
|
|
20159
|
-
return
|
|
21216
|
+
return path28.isAbsolute(home) ? home : dir === undefined ? null : path28.resolve(dir, home);
|
|
20160
21217
|
};
|
|
20161
21218
|
for (const segment of segmentsOf(command, dialect)) {
|
|
20162
21219
|
if (segment.group === "open") {
|
|
@@ -20187,7 +21244,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20187
21244
|
}
|
|
20188
21245
|
const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
|
|
20189
21246
|
if (inner !== null && dir !== undefined)
|
|
20190
|
-
targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
|
|
21247
|
+
targets.push(...segmentTargets(workitRunDir(dir, words), inner, "posix", depth + 1, strict));
|
|
20191
21248
|
if (strict && dir !== undefined) {
|
|
20192
21249
|
const runIn = dir;
|
|
20193
21250
|
for (const sub of depth < 4 ? segment.subs ?? [] : [])
|
|
@@ -20196,7 +21253,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20196
21253
|
for (const file of opaque ?? []) {
|
|
20197
21254
|
const resolved = at(file);
|
|
20198
21255
|
if (resolved)
|
|
20199
|
-
targets.push(file === "." ?
|
|
21256
|
+
targets.push(file === "." ? path28.join(resolved, DIR_TARGET) : resolved);
|
|
20200
21257
|
}
|
|
20201
21258
|
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
20202
21259
|
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
@@ -20211,7 +21268,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20211
21268
|
const edits = editsOf(segment.words);
|
|
20212
21269
|
if (edits === null) {
|
|
20213
21270
|
if (dir !== undefined)
|
|
20214
|
-
targets.push(
|
|
21271
|
+
targets.push(path28.join(dir, DIR_TARGET));
|
|
20215
21272
|
} else
|
|
20216
21273
|
for (const file of edits) {
|
|
20217
21274
|
const resolved = at(file);
|
|
@@ -20222,18 +21279,25 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20222
21279
|
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
20223
21280
|
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
20224
21281
|
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
20225
|
-
targets.push(
|
|
21282
|
+
targets.push(path28.join(runsIn, DIR_TARGET));
|
|
20226
21283
|
}
|
|
20227
21284
|
}
|
|
20228
21285
|
return targets;
|
|
21286
|
+
}, workitRunDir = (dir, words) => {
|
|
21287
|
+
for (const invocation of workitInvocations(words.map(quoteWord2).join(" "), "posix")) {
|
|
21288
|
+
const { at, args } = workitCwd(dir, invocation);
|
|
21289
|
+
if (args[0] === "check" || args[0] === "lock" && args[1] === "run")
|
|
21290
|
+
return at;
|
|
21291
|
+
}
|
|
21292
|
+
return dir;
|
|
20229
21293
|
}, workitCwd = (dir, args) => {
|
|
20230
21294
|
const { rest, cwd } = skipWorkitGlobals(args);
|
|
20231
|
-
return { at: cwd === null ? dir :
|
|
21295
|
+
return { at: cwd === null ? dir : path28.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
20232
21296
|
}, shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
20233
21297
|
try {
|
|
20234
21298
|
return [
|
|
20235
21299
|
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
20236
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
21300
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path28.join(dir, DIR_TARGET))
|
|
20237
21301
|
];
|
|
20238
21302
|
} catch {
|
|
20239
21303
|
return [];
|
|
@@ -20258,7 +21322,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20258
21322
|
}, guarded = (targets) => {
|
|
20259
21323
|
const seen = new Set;
|
|
20260
21324
|
return targets.filter((target) => {
|
|
20261
|
-
const dir =
|
|
21325
|
+
const dir = path28.dirname(target);
|
|
20262
21326
|
if (seen.has(dir))
|
|
20263
21327
|
return false;
|
|
20264
21328
|
seen.add(dir);
|
|
@@ -20276,13 +21340,13 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20276
21340
|
}, claimAll = (owner, targets, deps) => {
|
|
20277
21341
|
for (const target of guarded(targets))
|
|
20278
21342
|
claimLiveSession(target, owner, deps);
|
|
20279
|
-
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
21343
|
+
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path28.resolve(cwd, file)) : [path28.join(cwd, DIR_TARGET)], issuedTarget = (cwd, targets) => {
|
|
20280
21344
|
const state = resolveStateDir();
|
|
20281
21345
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
20282
21346
|
return found ?? null;
|
|
20283
21347
|
}, underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
20284
21348
|
const win = platform === "win32";
|
|
20285
|
-
const p = win ?
|
|
21349
|
+
const p = win ? path28.win32 : path28.posix;
|
|
20286
21350
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
20287
21351
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
20288
21352
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -20353,7 +21417,7 @@ var init_write_gate = __esm(() => {
|
|
|
20353
21417
|
CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20354
21418
|
PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20355
21419
|
POPD2 = new Set(["popd", "pop-location"]);
|
|
20356
|
-
UNKNOWN_DIR =
|
|
21420
|
+
UNKNOWN_DIR = path28.resolve(path28.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
20357
21421
|
HOST_LABELS = {
|
|
20358
21422
|
claude_code: "Claude Code",
|
|
20359
21423
|
opencode: "OpenCode",
|
|
@@ -20364,6 +21428,26 @@ var init_write_gate = __esm(() => {
|
|
|
20364
21428
|
};
|
|
20365
21429
|
});
|
|
20366
21430
|
|
|
21431
|
+
// packages/workit-core/src/core/policy/current-judgment.ts
|
|
21432
|
+
function currentJudgment(cwd) {
|
|
21433
|
+
try {
|
|
21434
|
+
const store = new TaskStore(cwd);
|
|
21435
|
+
const listed = store.listTaskIndex();
|
|
21436
|
+
const key = store.currentKey();
|
|
21437
|
+
if (!listed.ok || !key.ok)
|
|
21438
|
+
return null;
|
|
21439
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
21440
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
21441
|
+
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
21442
|
+
} catch {
|
|
21443
|
+
return null;
|
|
21444
|
+
}
|
|
21445
|
+
}
|
|
21446
|
+
var init_current_judgment = __esm(() => {
|
|
21447
|
+
init_task_store();
|
|
21448
|
+
init_derive();
|
|
21449
|
+
});
|
|
21450
|
+
|
|
20367
21451
|
// packages/workit-core/src/hooks/stop.ts
|
|
20368
21452
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20369
21453
|
function stopObligation(input, descriptor) {
|
|
@@ -20402,7 +21486,7 @@ function stopObligation(input, descriptor) {
|
|
|
20402
21486
|
}
|
|
20403
21487
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
20404
21488
|
return null;
|
|
20405
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
21489
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
20406
21490
|
return left ? { ...left, head } : null;
|
|
20407
21491
|
} catch {
|
|
20408
21492
|
return null;
|
|
@@ -20505,28 +21589,31 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
20505
21589
|
kind: "checks",
|
|
20506
21590
|
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)" : ""}.`
|
|
20507
21591
|
};
|
|
20508
|
-
},
|
|
20509
|
-
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
const key = store.currentKey();
|
|
20513
|
-
if (!listed.ok || !key.ok)
|
|
20514
|
-
return null;
|
|
20515
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
20516
|
-
const task = entry ? store.readTask(entry.id) : null;
|
|
20517
|
-
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
20518
|
-
} catch {
|
|
20519
|
-
return null;
|
|
20520
|
-
}
|
|
20521
|
-
}, unverified = (cwd, branch, all, descriptor, verification) => {
|
|
21592
|
+
}, prBase = (rows) => {
|
|
21593
|
+
const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
|
|
21594
|
+
return typeof seen?.base === "string" ? seen.base : null;
|
|
21595
|
+
}, unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
20522
21596
|
const check = checkVerdicts(cwd, branch, all);
|
|
20523
21597
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
20524
21598
|
return null;
|
|
20525
|
-
const judgment =
|
|
21599
|
+
const judgment = currentJudgment(cwd);
|
|
20526
21600
|
const high = judgment?.riskTier === "high";
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
21601
|
+
let independent;
|
|
21602
|
+
if (verification === "proportional") {
|
|
21603
|
+
const lane = branchLane(cwd, {
|
|
21604
|
+
base: prBase(all.filter((row) => row.branch === branch)),
|
|
21605
|
+
head,
|
|
21606
|
+
judgment
|
|
21607
|
+
});
|
|
21608
|
+
const need = laneNeed(lane.lane);
|
|
21609
|
+
if (need === "none")
|
|
21610
|
+
return null;
|
|
21611
|
+
independent = need === "non-author";
|
|
21612
|
+
} else {
|
|
21613
|
+
if (judgment && !judgment.behaviorChange && !high)
|
|
21614
|
+
return null;
|
|
21615
|
+
independent = verification === "independent" || high;
|
|
21616
|
+
}
|
|
20530
21617
|
if (!independent && check.review === "self-reviewed")
|
|
20531
21618
|
return null;
|
|
20532
21619
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -20539,8 +21626,8 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
20539
21626
|
};
|
|
20540
21627
|
var init_stop = __esm(() => {
|
|
20541
21628
|
init_autonomy();
|
|
20542
|
-
|
|
20543
|
-
|
|
21629
|
+
init_current_judgment();
|
|
21630
|
+
init_lane();
|
|
20544
21631
|
init_rev();
|
|
20545
21632
|
init_ledger();
|
|
20546
21633
|
init_raw_git();
|
|
@@ -20781,10 +21868,10 @@ var init_skill_nudge = __esm(() => {
|
|
|
20781
21868
|
});
|
|
20782
21869
|
|
|
20783
21870
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20784
|
-
import { existsSync as
|
|
20785
|
-
import
|
|
21871
|
+
import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21872
|
+
import path29 from "node:path";
|
|
20786
21873
|
var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
20787
|
-
if (!nonEmpty2(value) || !
|
|
21874
|
+
if (!nonEmpty2(value) || !path29.isAbsolute(value) || !existsSync9(value))
|
|
20788
21875
|
return null;
|
|
20789
21876
|
try {
|
|
20790
21877
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20850,9 +21937,9 @@ var init_fields = __esm(() => {
|
|
|
20850
21937
|
|
|
20851
21938
|
// packages/workit-core/src/hooks/standing.ts
|
|
20852
21939
|
import fs22 from "node:fs";
|
|
20853
|
-
import
|
|
21940
|
+
import path30 from "node:path";
|
|
20854
21941
|
var readPlans = (store) => {
|
|
20855
|
-
const dir =
|
|
21942
|
+
const dir = path30.join(store, "fanouts");
|
|
20856
21943
|
let names;
|
|
20857
21944
|
try {
|
|
20858
21945
|
names = fs22.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -20862,7 +21949,7 @@ var readPlans = (store) => {
|
|
|
20862
21949
|
const plans = [];
|
|
20863
21950
|
for (const name of names) {
|
|
20864
21951
|
try {
|
|
20865
|
-
const raw = JSON.parse(fs22.readFileSync(
|
|
21952
|
+
const raw = JSON.parse(fs22.readFileSync(path30.join(dir, name), "utf8"));
|
|
20866
21953
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
20867
21954
|
continue;
|
|
20868
21955
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -20950,7 +22037,7 @@ var init_standing = __esm(() => {
|
|
|
20950
22037
|
});
|
|
20951
22038
|
|
|
20952
22039
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
20953
|
-
var COMPOUND, FILTERS, stdinOnly = (words) => {
|
|
22040
|
+
var DIR, COMPOUND, GLOBALS_FIRST, VERB_FIRST, VERBS, FILTERS, clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length, stdinOnly = (words) => {
|
|
20954
22041
|
const [name, ...args] = words;
|
|
20955
22042
|
const operands = args.filter((w) => !w.startsWith("-"));
|
|
20956
22043
|
if (name === "grep")
|
|
@@ -20970,30 +22057,48 @@ var COMPOUND, FILTERS, stdinOnly = (words) => {
|
|
|
20970
22057
|
return null;
|
|
20971
22058
|
if (head.words.some((w) => w === "--cwd" || w.startsWith("--cwd=")))
|
|
20972
22059
|
return null;
|
|
20973
|
-
const clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length;
|
|
20974
22060
|
if (!segments.every(clean))
|
|
20975
22061
|
return null;
|
|
20976
22062
|
if (!segments.slice(0, -1).every((s) => s.pipe))
|
|
20977
22063
|
return null;
|
|
20978
22064
|
if (!filters.every((s) => FILTERS.has(s.words[0] ?? "") && stdinOnly(s.words)))
|
|
20979
22065
|
return null;
|
|
20980
|
-
|
|
22066
|
+
const args = rest.slice("workit".length).trimStart();
|
|
22067
|
+
const verb = VERB_FIRST.exec(args);
|
|
22068
|
+
if (!verb?.[2] || !VERBS.has(verb[2]))
|
|
22069
|
+
return `workit --cwd ${dir} ${args}`.trimEnd();
|
|
22070
|
+
const json = verb[1] ? " --json" : "";
|
|
22071
|
+
return `workit ${verb[2]} --cwd ${dir}${json}${args.slice(verb[0].length)}`.trimEnd();
|
|
22072
|
+
}, verbFirst = (command) => {
|
|
22073
|
+
const match = GLOBALS_FIRST.exec(command);
|
|
22074
|
+
const [whole, lead, globals, verb] = match ?? [];
|
|
22075
|
+
if (!whole || !verb || !VERBS.has(verb) || !globals?.includes("--cwd"))
|
|
22076
|
+
return null;
|
|
22077
|
+
const segments = segmentsOf(command);
|
|
22078
|
+
if (segments.length !== 1 || !segments.every(clean))
|
|
22079
|
+
return null;
|
|
22080
|
+
return `${lead} ${verb}${globals}${command.slice(whole.length)}`;
|
|
20981
22081
|
};
|
|
20982
22082
|
var init_cwd_normalize = __esm(() => {
|
|
22083
|
+
init_delivery_verbs();
|
|
20983
22084
|
init_shell_words();
|
|
20984
|
-
|
|
22085
|
+
DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
22086
|
+
COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
|
22087
|
+
GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
|
|
22088
|
+
VERB_FIRST = /^((?:--json\s+)*)([a-z][a-z-]*)(?=\s|$)/u;
|
|
22089
|
+
VERBS = new Set(WORKIT_ALLOWED_VERBS);
|
|
20985
22090
|
FILTERS = new Set(["tail", "head", "grep"]);
|
|
20986
22091
|
});
|
|
20987
22092
|
|
|
20988
22093
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
20989
22094
|
import { createHash as createHash17 } from "node:crypto";
|
|
20990
22095
|
import fs23 from "node:fs";
|
|
20991
|
-
import
|
|
22096
|
+
import path31 from "node:path";
|
|
20992
22097
|
function confineImplementer(input, targets) {
|
|
20993
22098
|
try {
|
|
20994
22099
|
if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
|
|
20995
22100
|
return NONE5;
|
|
20996
|
-
if (targets.some((target) => within(
|
|
22101
|
+
if (targets.some((target) => within(path31.resolve(target), UNKNOWN_DIR)))
|
|
20997
22102
|
return unknownDirDeny(input, boundWorktree(input));
|
|
20998
22103
|
const bindings = canonical3(bindingDir());
|
|
20999
22104
|
const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
|
|
@@ -21028,32 +22133,32 @@ function confineImplementerShell(input, command, dialect = "posix") {
|
|
|
21028
22133
|
}
|
|
21029
22134
|
var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonical3 = (file) => {
|
|
21030
22135
|
let rest = "";
|
|
21031
|
-
for (let dir =
|
|
22136
|
+
for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
|
|
21032
22137
|
try {
|
|
21033
|
-
return
|
|
22138
|
+
return path31.join(fs23.realpathSync.native(dir), rest);
|
|
21034
22139
|
} catch {
|
|
21035
|
-
if (
|
|
21036
|
-
return
|
|
21037
|
-
rest = rest ?
|
|
22140
|
+
if (path31.dirname(dir) === dir)
|
|
22141
|
+
return path31.resolve(file);
|
|
22142
|
+
rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
|
|
21038
22143
|
}
|
|
21039
22144
|
}
|
|
21040
22145
|
}, within = (child, parent) => {
|
|
21041
22146
|
const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
|
|
21042
|
-
const relative =
|
|
21043
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
22147
|
+
const relative = path31.relative(fold(parent), fold(child));
|
|
22148
|
+
return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
|
|
21044
22149
|
}, checkoutOf2 = (file) => {
|
|
21045
|
-
for (let dir = canonical3(file);; dir =
|
|
21046
|
-
const dotGit =
|
|
22150
|
+
for (let dir = canonical3(file);; dir = path31.dirname(dir)) {
|
|
22151
|
+
const dotGit = path31.join(dir, ".git");
|
|
21047
22152
|
if (fs23.existsSync(dotGit)) {
|
|
21048
22153
|
if (!fs23.statSync(dotGit).isFile())
|
|
21049
22154
|
return { root: dir, workit: false };
|
|
21050
22155
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21051
22156
|
return {
|
|
21052
22157
|
root: dir,
|
|
21053
|
-
workit: Boolean(admin) && fs23.existsSync(
|
|
22158
|
+
workit: Boolean(admin) && fs23.existsSync(path31.join(path31.resolve(dir, admin), FANOUT_TOKEN_FILE))
|
|
21054
22159
|
};
|
|
21055
22160
|
}
|
|
21056
|
-
if (
|
|
22161
|
+
if (path31.dirname(dir) === dir)
|
|
21057
22162
|
return null;
|
|
21058
22163
|
}
|
|
21059
22164
|
}, workitWorktreeOf = (file) => {
|
|
@@ -21063,7 +22168,7 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21063
22168
|
} catch {
|
|
21064
22169
|
return null;
|
|
21065
22170
|
}
|
|
21066
|
-
}, bindingDir = () =>
|
|
22171
|
+
}, bindingDir = () => path31.join(resolveStateDir(), "worktree-bind"), bindingFile = (session, agentId) => path31.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`), readBinding = (file) => {
|
|
21067
22172
|
try {
|
|
21068
22173
|
const value = JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
21069
22174
|
return typeof value.worktree === "string" ? value : null;
|
|
@@ -21085,7 +22190,7 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21085
22190
|
agentId,
|
|
21086
22191
|
at: new Date().toISOString()
|
|
21087
22192
|
};
|
|
21088
|
-
fs23.mkdirSync(
|
|
22193
|
+
fs23.mkdirSync(path31.dirname(file), { recursive: true });
|
|
21089
22194
|
fs23.writeFileSync(file, JSON.stringify(binding));
|
|
21090
22195
|
try {
|
|
21091
22196
|
if (inWorkitWorkspace(input.cwd))
|
|
@@ -21103,32 +22208,32 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21103
22208
|
const checkout = checkoutOf2(cwd);
|
|
21104
22209
|
if (!checkout)
|
|
21105
22210
|
return false;
|
|
21106
|
-
const dotGit =
|
|
22211
|
+
const dotGit = path31.join(checkout.root, ".git");
|
|
21107
22212
|
if (!fs23.statSync(dotGit).isFile())
|
|
21108
22213
|
return false;
|
|
21109
22214
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21110
22215
|
if (!admin)
|
|
21111
22216
|
return false;
|
|
21112
|
-
const adminDir =
|
|
21113
|
-
const back = fs23.readFileSync(
|
|
21114
|
-
if (
|
|
22217
|
+
const adminDir = path31.resolve(checkout.root, admin);
|
|
22218
|
+
const back = fs23.readFileSync(path31.join(adminDir, "gitdir"), "utf8").trim();
|
|
22219
|
+
if (path31.relative(canonical3(path31.resolve(adminDir, back)), canonical3(dotGit)) !== "")
|
|
21115
22220
|
return false;
|
|
21116
|
-
const common =
|
|
21117
|
-
const gitDir = fs23.existsSync(common) ?
|
|
21118
|
-
const expected =
|
|
21119
|
-
return
|
|
22221
|
+
const common = path31.join(adminDir, "commondir");
|
|
22222
|
+
const gitDir = fs23.existsSync(common) ? path31.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
|
|
22223
|
+
const expected = path31.join(path31.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
|
|
22224
|
+
return path31.relative(canonical3(expected), checkout.root) === "";
|
|
21120
22225
|
} catch {
|
|
21121
22226
|
return false;
|
|
21122
22227
|
}
|
|
21123
|
-
}, shown = (target) =>
|
|
21124
|
-
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>
|
|
22228
|
+
}, shown = (target) => path31.basename(target) === ".workit-live-target" ? path31.dirname(target) : target, unboundDeny = (input, target) => {
|
|
22229
|
+
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 git --cwd <worktree> commit -m … -- <paths>`, `workit check --cwd <worktree> <name>`). If your brief names no worktree, stop and report that to the lead.";
|
|
21125
22230
|
return {
|
|
21126
22231
|
kind: "deny",
|
|
21127
22232
|
reason: `workit worktree confinement: implementer ${input.session.agentId} is not working in a Workit worktree yet, and ${shown(target)} is outside every worktree \`workit fanout worktree create\` made, so it would change the lead's checkout. ${unblock}`,
|
|
21128
22233
|
unblock
|
|
21129
22234
|
};
|
|
21130
22235
|
}, outsideDeny = (input, worktree, target) => {
|
|
21131
|
-
const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit --cwd ${worktree}
|
|
22236
|
+
const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit git --cwd ${worktree} commit -m … -- <paths>\`, \`workit check --cwd ${worktree} <name>\` (or \`git -C ${worktree} …\`); a command whose writes Workit cannot see is judged by the directory it runs in. Anything outside it is a follow-up for your report.`;
|
|
21132
22237
|
return {
|
|
21133
22238
|
kind: "deny",
|
|
21134
22239
|
reason: `workit worktree confinement: implementer ${input.session.agentId} is bound to the worktree ${worktree}, and ${shown(target)} is outside it. ${unblock}`,
|
|
@@ -21136,7 +22241,7 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21136
22241
|
};
|
|
21137
22242
|
}, unknownDirDeny = (input, worktree) => {
|
|
21138
22243
|
const wt = worktree ?? "<worktree>";
|
|
21139
|
-
const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit --cwd ${wt}\`.`;
|
|
22244
|
+
const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit <verb> --cwd ${wt}\`.`;
|
|
21140
22245
|
return {
|
|
21141
22246
|
kind: "deny",
|
|
21142
22247
|
reason: `workit worktree confinement: implementer ${input.session.agentId} writes after an unknown directory change. ${unblock}`,
|
|
@@ -21234,7 +22339,7 @@ var init_worktree_confine = __esm(() => {
|
|
|
21234
22339
|
// packages/workit-core/src/hooks/handle.ts
|
|
21235
22340
|
function handleHook(input, deps) {
|
|
21236
22341
|
const decision = decide(input, deps);
|
|
21237
|
-
if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny")
|
|
22342
|
+
if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny" || decision.kind === "ask")
|
|
21238
22343
|
return decision;
|
|
21239
22344
|
const delta = standingDelta(input);
|
|
21240
22345
|
if (!delta)
|
|
@@ -21301,9 +22406,12 @@ function decide(input, deps) {
|
|
|
21301
22406
|
if (plan.kind === "rewrite" && !rewrites && canDeny)
|
|
21302
22407
|
return heavyDenial(event.command, plan);
|
|
21303
22408
|
const lease = plan.kind === "rewrite" && rewrites ? plan : null;
|
|
21304
|
-
const cwdCommand = !lease && rewrites && input.host === "claude_code" ? cwdRewrite(event.command) : null;
|
|
22409
|
+
const cwdCommand = !lease && rewrites && input.host === "claude_code" ? cwdRewrite(event.command) ?? verbFirst(event.command) : null;
|
|
21305
22410
|
const aside = plan.kind === "passthrough" ? plan.note : null;
|
|
21306
22411
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
22412
|
+
const bypass = !lease && input.host === "claude_code" ? workitBypassFlag(event.command, event.dialect) : null;
|
|
22413
|
+
if (bypass)
|
|
22414
|
+
return { kind: "ask", reason: bypass };
|
|
21307
22415
|
const rewrittenTo = lease?.command ?? cwdCommand;
|
|
21308
22416
|
const rewritten = (text) => rewrittenTo ? {
|
|
21309
22417
|
kind: "rewrite",
|
|
@@ -21375,7 +22483,7 @@ var usable2 = (value) => value === "native" || value === "partial", NONE6, resen
|
|
|
21375
22483
|
return `${isolatedGit(input.cwd)}
|
|
21376
22484
|
${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
21377
22485
|
if (role === "implementer")
|
|
21378
|
-
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree, the one its brief names (the lead makes it with \`workit fanout worktree create <slice>\` before spawning), never in the lead's checkout, within its brief's scope. Workit binds it to the first Workit worktree it changes: from then on an edit or a mutating git command outside that worktree is denied, and before then so is one outside every Workit worktree. Run Workit against the worktree one verb per call, never \`cd … &&\` chains or \`$VAR\` paths: \`workit --cwd <worktree>
|
|
22486
|
+
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree, the one its brief names (the lead makes it with \`workit fanout worktree create <slice>\` before spawning), never in the lead's checkout, within its brief's scope. Workit binds it to the first Workit worktree it changes: from then on an edit or a mutating git command outside that worktree is denied, and before then so is one outside every Workit worktree. Run Workit against the worktree one verb per call, never \`cd … &&\` chains or \`$VAR\` paths: \`workit git --cwd <worktree> commit -m "<message>" -- <paths>\`, \`workit check --cwd <worktree> <name>\` (verb first, so host allow rules match). The worktree is on the brief's branch once the lead made it; otherwise switch with \`workit git --cwd <worktree> branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
21379
22487
|
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
21380
22488
|
if (role !== "judge" || !event.agentId)
|
|
21381
22489
|
return readOnly;
|
|
@@ -21422,6 +22530,7 @@ var init_handle = __esm(() => {
|
|
|
21422
22530
|
init_workspaces();
|
|
21423
22531
|
init_host_session();
|
|
21424
22532
|
init_cwd_normalize();
|
|
22533
|
+
init_delivery_verbs();
|
|
21425
22534
|
init_heavy_gate();
|
|
21426
22535
|
init_policy();
|
|
21427
22536
|
init_raw_git();
|
|
@@ -21445,14 +22554,14 @@ var init_handle = __esm(() => {
|
|
|
21445
22554
|
|
|
21446
22555
|
// packages/workit-core/src/hooks/deadline.ts
|
|
21447
22556
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
|
|
21448
|
-
import
|
|
22557
|
+
import path32 from "node:path";
|
|
21449
22558
|
var HOOK_SKIP_TEXT = "workit context skipped: host under load", HOOK_DEADLINE_MS = 8000, HOST_DEADLINES, HOOK_SKIPS_FILE = "hook-skips.jsonl", override = (env) => {
|
|
21450
22559
|
const value = Number(env.WORKIT_HOOK_DEADLINE_MS);
|
|
21451
22560
|
return Number.isFinite(value) && value > 0 ? value : null;
|
|
21452
22561
|
}, hookDeadlineMs = (env, host = "", native) => {
|
|
21453
22562
|
const row = HOST_DEADLINES[host];
|
|
21454
22563
|
return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
|
|
21455
|
-
}, skipsFile = (stateDir) =>
|
|
22564
|
+
}, skipsFile = (stateDir) => path32.join(stateDir, HOOK_SKIPS_FILE), recordHookSkip = (skip, stateDir = resolveStateDir()) => {
|
|
21456
22565
|
try {
|
|
21457
22566
|
mkdirSync3(stateDir, { recursive: true });
|
|
21458
22567
|
appendFileSync(skipsFile(stateDir), `${JSON.stringify(skip)}
|
|
@@ -21660,6 +22769,17 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
21660
22769
|
},
|
|
21661
22770
|
exitCode: 0
|
|
21662
22771
|
};
|
|
22772
|
+
if (decision.kind === "ask" && native === "PreToolUse")
|
|
22773
|
+
return {
|
|
22774
|
+
json: {
|
|
22775
|
+
hookSpecificOutput: {
|
|
22776
|
+
hookEventName: "PreToolUse",
|
|
22777
|
+
permissionDecision: "ask",
|
|
22778
|
+
permissionDecisionReason: decision.reason
|
|
22779
|
+
}
|
|
22780
|
+
},
|
|
22781
|
+
exitCode: 0
|
|
22782
|
+
};
|
|
21663
22783
|
if (decision.kind === "context" && native !== null && CONTEXT_EVENTS.has(native))
|
|
21664
22784
|
return {
|
|
21665
22785
|
json: { hookSpecificOutput: { hookEventName: native, additionalContext: decision.text } },
|
|
@@ -22097,7 +23217,7 @@ var init_codex = __esm(() => {
|
|
|
22097
23217
|
|
|
22098
23218
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
22099
23219
|
import fs24 from "node:fs";
|
|
22100
|
-
import
|
|
23220
|
+
import path33 from "node:path";
|
|
22101
23221
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
22102
23222
|
if (!isRecord6(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
22103
23223
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -22178,7 +23298,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
22178
23298
|
};
|
|
22179
23299
|
}
|
|
22180
23300
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
22181
|
-
if (!file || !
|
|
23301
|
+
if (!file || !path33.isAbsolute(file))
|
|
22182
23302
|
return null;
|
|
22183
23303
|
try {
|
|
22184
23304
|
const size = fs24.statSync(file).size;
|
|
@@ -22214,7 +23334,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
22214
23334
|
} catch {
|
|
22215
23335
|
return null;
|
|
22216
23336
|
}
|
|
22217
|
-
}, within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
23337
|
+
}, within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path33.sep) ? root : `${root}${path33.sep}`), hookCwd = (input) => {
|
|
22218
23338
|
const cwd = existingDirectory(input.cwd);
|
|
22219
23339
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
22220
23340
|
return cwd;
|
|
@@ -22441,6 +23561,7 @@ var init_pi = () => {};
|
|
|
22441
23561
|
var init_hooks = __esm(async () => {
|
|
22442
23562
|
init_context();
|
|
22443
23563
|
init_policy();
|
|
23564
|
+
init_delivery_verbs();
|
|
22444
23565
|
init_heavy_gate();
|
|
22445
23566
|
init_write_gate();
|
|
22446
23567
|
init_live_session();
|