@brainervirus/workit-cursor 8.23.0 → 8.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +1177 -303
- package/dist/mcp-server.js +972 -147
- package/dist/workit-hook.js +1151 -304
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +1 -1
- package/skills/workit-implement/SKILL.md +5 -5
- package/skills/workit-review/SKILL.md +3 -0
- package/skills/workit-ship/SKILL.md +1 -2
|
@@ -6981,6 +6981,110 @@ function resolveRef(cwd, ref) {
|
|
|
6981
6981
|
return null;
|
|
6982
6982
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
6983
6983
|
}
|
|
6984
|
+
function diffNumstat(cwd, fork, head) {
|
|
6985
|
+
const range = laneRange(fork, head);
|
|
6986
|
+
if (!range)
|
|
6987
|
+
return null;
|
|
6988
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
6989
|
+
if (!run.ok)
|
|
6990
|
+
return null;
|
|
6991
|
+
const out = [];
|
|
6992
|
+
for (const record of run.stdout.split("\x00")) {
|
|
6993
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
6994
|
+
if (!match)
|
|
6995
|
+
continue;
|
|
6996
|
+
const binary = match[1] === "-";
|
|
6997
|
+
out.push({
|
|
6998
|
+
path: match[3],
|
|
6999
|
+
added: binary ? 0 : Number(match[1]),
|
|
7000
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
7001
|
+
binary
|
|
7002
|
+
});
|
|
7003
|
+
}
|
|
7004
|
+
return out;
|
|
7005
|
+
}
|
|
7006
|
+
function diffRaw(cwd, fork, head) {
|
|
7007
|
+
const range = laneRange(fork, head);
|
|
7008
|
+
if (!range)
|
|
7009
|
+
return null;
|
|
7010
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
7011
|
+
if (!run.ok)
|
|
7012
|
+
return null;
|
|
7013
|
+
const fields = run.stdout.split("\x00");
|
|
7014
|
+
const out = [];
|
|
7015
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
7016
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
7017
|
+
if (!meta)
|
|
7018
|
+
return null;
|
|
7019
|
+
out.push({
|
|
7020
|
+
path: fields[index + 1],
|
|
7021
|
+
oldMode: meta[1],
|
|
7022
|
+
newMode: meta[2],
|
|
7023
|
+
oldBlob: meta[3],
|
|
7024
|
+
newBlob: meta[4]
|
|
7025
|
+
});
|
|
7026
|
+
}
|
|
7027
|
+
return out;
|
|
7028
|
+
}
|
|
7029
|
+
function readBlobs(cwd, ids) {
|
|
7030
|
+
const out = new Map;
|
|
7031
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
7032
|
+
if (!wanted.length)
|
|
7033
|
+
return out;
|
|
7034
|
+
const run = spawnSync("git", ["cat-file", "--batch"], {
|
|
7035
|
+
cwd,
|
|
7036
|
+
input: `${wanted.join(`
|
|
7037
|
+
`)}
|
|
7038
|
+
`,
|
|
7039
|
+
maxBuffer: MAX_BUFFER,
|
|
7040
|
+
timeout: GIT_TIMEOUTS.local,
|
|
7041
|
+
killSignal: "SIGKILL",
|
|
7042
|
+
windowsHide: true
|
|
7043
|
+
});
|
|
7044
|
+
if (run.status !== 0 || !run.stdout)
|
|
7045
|
+
return null;
|
|
7046
|
+
const bytes = run.stdout;
|
|
7047
|
+
let at = 0;
|
|
7048
|
+
for (const id of wanted) {
|
|
7049
|
+
const end = bytes.indexOf(10, at);
|
|
7050
|
+
if (end < 0)
|
|
7051
|
+
return null;
|
|
7052
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
7053
|
+
at = end + 1;
|
|
7054
|
+
if (header[1] === "missing")
|
|
7055
|
+
continue;
|
|
7056
|
+
const size = Number(header[2]);
|
|
7057
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
7058
|
+
return null;
|
|
7059
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
7060
|
+
at += size + 1;
|
|
7061
|
+
}
|
|
7062
|
+
return out;
|
|
7063
|
+
}
|
|
7064
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
7065
|
+
if (!safeArg(rev))
|
|
7066
|
+
return null;
|
|
7067
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
7068
|
+
if (!run.ok)
|
|
7069
|
+
return null;
|
|
7070
|
+
const out = new Map;
|
|
7071
|
+
for (const record of run.stdout.split("\x00")) {
|
|
7072
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
7073
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
7074
|
+
out.set(match[2], match[1]);
|
|
7075
|
+
}
|
|
7076
|
+
return out;
|
|
7077
|
+
}
|
|
7078
|
+
function fileAt(cwd, rev, file) {
|
|
7079
|
+
if (!safeArg(rev))
|
|
7080
|
+
return null;
|
|
7081
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
7082
|
+
return run.ok ? run.stdout : null;
|
|
7083
|
+
}
|
|
7084
|
+
function untrackedFiles(cwd) {
|
|
7085
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
7086
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
7087
|
+
}
|
|
6984
7088
|
function currentBranch(cwd) {
|
|
6985
7089
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
6986
7090
|
}
|
|
@@ -7027,11 +7131,24 @@ var MAX_BUFFER, GIT_TIMEOUTS, git = (cwd, args, options = {}) => {
|
|
|
7027
7131
|
const { atimeNs, mtimeNs } = fs.statSync(from, { bigint: true });
|
|
7028
7132
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
7029
7133
|
fs.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
7134
|
+
}, LANE_DIFF, laneRange = (fork, head) => {
|
|
7135
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
7136
|
+
return null;
|
|
7137
|
+
return head === null ? [fork] : [fork, head];
|
|
7030
7138
|
}, config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
7031
7139
|
var init_rev = __esm(() => {
|
|
7032
7140
|
MAX_BUFFER = 256 * 1024 * 1024;
|
|
7033
7141
|
GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
7034
7142
|
MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
7143
|
+
LANE_DIFF = [
|
|
7144
|
+
"-c",
|
|
7145
|
+
"core.quotePath=false",
|
|
7146
|
+
"diff",
|
|
7147
|
+
"--no-color",
|
|
7148
|
+
"--no-ext-diff",
|
|
7149
|
+
"--no-textconv",
|
|
7150
|
+
"--no-renames"
|
|
7151
|
+
];
|
|
7035
7152
|
});
|
|
7036
7153
|
|
|
7037
7154
|
// packages/workit-core/src/store/paths.ts
|
|
@@ -8094,7 +8211,7 @@ var init_workspaces = __esm(() => {
|
|
|
8094
8211
|
}).passthrough().optional(),
|
|
8095
8212
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
8096
8213
|
defaultEndpoint: unknown().optional(),
|
|
8097
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
8214
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
8098
8215
|
delegation: unknown().optional(),
|
|
8099
8216
|
trailers: boolean2().optional(),
|
|
8100
8217
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -9787,7 +9904,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
9787
9904
|
verdict: failing?.verdict ?? null,
|
|
9788
9905
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
9789
9906
|
};
|
|
9790
|
-
const
|
|
9907
|
+
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))));
|
|
9908
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
9791
9909
|
return {
|
|
9792
9910
|
branch,
|
|
9793
9911
|
head,
|
|
@@ -10068,12 +10186,709 @@ var init_ledger = __esm(() => {
|
|
|
10068
10186
|
]);
|
|
10069
10187
|
});
|
|
10070
10188
|
|
|
10071
|
-
// packages/workit-core/src/
|
|
10072
|
-
|
|
10189
|
+
// packages/workit-core/src/forge/pr-body.ts
|
|
10190
|
+
var init_pr_body = __esm(() => {
|
|
10191
|
+
init_vcs_config();
|
|
10192
|
+
});
|
|
10193
|
+
|
|
10194
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
10195
|
+
var init_pr_create = __esm(() => {
|
|
10196
|
+
init_vcs_config();
|
|
10197
|
+
init_vcs_config();
|
|
10198
|
+
init_branch();
|
|
10199
|
+
init_pr_body();
|
|
10200
|
+
});
|
|
10201
|
+
|
|
10202
|
+
// packages/workit-core/src/forge/redact.ts
|
|
10203
|
+
var KEY, ASSIGNMENT, QUOTED_ASSIGNMENT, ANSI;
|
|
10204
|
+
var init_redact = __esm(() => {
|
|
10205
|
+
KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
|
|
10206
|
+
ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
|
|
10207
|
+
QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
10208
|
+
ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
10209
|
+
});
|
|
10210
|
+
// packages/workit-core/src/git/ops.ts
|
|
10211
|
+
var COMMIT_TIMEOUT_MS;
|
|
10212
|
+
var init_ops = __esm(() => {
|
|
10213
|
+
init_branch();
|
|
10214
|
+
init_branch_policy();
|
|
10215
|
+
init_commit_flavors();
|
|
10216
|
+
init_pr_create();
|
|
10217
|
+
init_release_tracks();
|
|
10218
|
+
init_vcs_config();
|
|
10219
|
+
init_workspaces();
|
|
10220
|
+
init_redact();
|
|
10221
|
+
init_host_session();
|
|
10222
|
+
init_ledger();
|
|
10223
|
+
init_rev();
|
|
10224
|
+
COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
10225
|
+
});
|
|
10226
|
+
|
|
10227
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
10228
|
+
function patternRegex(dir, pattern) {
|
|
10229
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
10230
|
+
return null;
|
|
10231
|
+
const anchored = pattern.includes("/");
|
|
10232
|
+
const body = pattern.replace(/^\//u, "");
|
|
10233
|
+
let source = "";
|
|
10234
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
10235
|
+
const char = body[index];
|
|
10236
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
10237
|
+
const slash = body[index + 2] === "/";
|
|
10238
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
10239
|
+
index += slash ? 2 : 1;
|
|
10240
|
+
} else if (char === "*")
|
|
10241
|
+
source += "[^/]*";
|
|
10242
|
+
else if (char === "?")
|
|
10243
|
+
source += "[^/]";
|
|
10244
|
+
else if (char === "[") {
|
|
10245
|
+
const end = body.indexOf("]", index + 2);
|
|
10246
|
+
if (end < 0)
|
|
10247
|
+
return null;
|
|
10248
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
10249
|
+
source += `[${set}]`;
|
|
10250
|
+
index = end;
|
|
10251
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
10252
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
10253
|
+
index += 1;
|
|
10254
|
+
} else
|
|
10255
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
10256
|
+
}
|
|
10257
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
10258
|
+
try {
|
|
10259
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
10260
|
+
} catch {
|
|
10261
|
+
return null;
|
|
10262
|
+
}
|
|
10263
|
+
}
|
|
10264
|
+
function rulesOf(dir, text) {
|
|
10265
|
+
const out = [];
|
|
10266
|
+
for (const raw of text.split(`
|
|
10267
|
+
`)) {
|
|
10268
|
+
const line = raw.trim();
|
|
10269
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
10270
|
+
continue;
|
|
10271
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
10272
|
+
let generated = null;
|
|
10273
|
+
for (const attribute of attributes) {
|
|
10274
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
10275
|
+
generated = true;
|
|
10276
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
10277
|
+
generated = false;
|
|
10278
|
+
}
|
|
10279
|
+
if (generated === null)
|
|
10280
|
+
continue;
|
|
10281
|
+
const regex = patternRegex(dir, pattern);
|
|
10282
|
+
if (regex)
|
|
10283
|
+
out.push({ regex, generated });
|
|
10284
|
+
}
|
|
10285
|
+
return out;
|
|
10286
|
+
}
|
|
10287
|
+
function generatedByAttributes(files, paths) {
|
|
10288
|
+
const rules = [...files].map(([file, text]) => {
|
|
10289
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
10290
|
+
return {
|
|
10291
|
+
depth: dir ? dir.split("/").length : 0,
|
|
10292
|
+
rules: rulesOf(dir, text)
|
|
10293
|
+
};
|
|
10294
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
10295
|
+
const out = new Set;
|
|
10296
|
+
for (const file of paths) {
|
|
10297
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
10298
|
+
if (last?.generated)
|
|
10299
|
+
out.add(file);
|
|
10300
|
+
}
|
|
10301
|
+
return out;
|
|
10302
|
+
}
|
|
10303
|
+
|
|
10304
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
10305
|
+
function substanceOf(file, text) {
|
|
10306
|
+
const syntax = syntaxOf(file);
|
|
10307
|
+
if (!syntax)
|
|
10308
|
+
return plainSubstance(text);
|
|
10309
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
10310
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
10311
|
+
return plainSubstance(text);
|
|
10312
|
+
return lex(syntax, text);
|
|
10313
|
+
}
|
|
10314
|
+
function plainSubstance(text) {
|
|
10315
|
+
return text.split(`
|
|
10316
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
10317
|
+
`);
|
|
10318
|
+
}
|
|
10319
|
+
function lex(syntax, text) {
|
|
10320
|
+
const collapse = !syntax.indented;
|
|
10321
|
+
let out = "";
|
|
10322
|
+
let lineStart = 0;
|
|
10323
|
+
let kept = 0;
|
|
10324
|
+
let pendingSpace = false;
|
|
10325
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
10326
|
+
let index = 0;
|
|
10327
|
+
if (text.startsWith("#!")) {
|
|
10328
|
+
const end = text.indexOf(`
|
|
10329
|
+
`);
|
|
10330
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
10331
|
+
kept = out.length;
|
|
10332
|
+
index = end < 0 ? text.length : end;
|
|
10333
|
+
}
|
|
10334
|
+
const emitCode = (chunk) => {
|
|
10335
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
10336
|
+
const before = out[out.length - 1];
|
|
10337
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
10338
|
+
out += " ";
|
|
10339
|
+
}
|
|
10340
|
+
pendingSpace = false;
|
|
10341
|
+
out += chunk;
|
|
10342
|
+
};
|
|
10343
|
+
const emitVerbatim = (chunk) => {
|
|
10344
|
+
emitCode(chunk);
|
|
10345
|
+
kept = out.length;
|
|
10346
|
+
};
|
|
10347
|
+
const newline = () => {
|
|
10348
|
+
if (!collapse) {
|
|
10349
|
+
let end = out.length;
|
|
10350
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
10351
|
+
end -= 1;
|
|
10352
|
+
out = out.slice(0, end);
|
|
10353
|
+
}
|
|
10354
|
+
pendingSpace = false;
|
|
10355
|
+
if (out.length > lineStart) {
|
|
10356
|
+
out += `
|
|
10357
|
+
`;
|
|
10358
|
+
lineStart = out.length;
|
|
10359
|
+
}
|
|
10360
|
+
};
|
|
10361
|
+
const regexAllowed = () => {
|
|
10362
|
+
let end = out.length;
|
|
10363
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
10364
|
+
end -= 1;
|
|
10365
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
10366
|
+
return true;
|
|
10367
|
+
let start = end;
|
|
10368
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
10369
|
+
start -= 1;
|
|
10370
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
10371
|
+
};
|
|
10372
|
+
const scanString = (quote, escapes, multiline) => {
|
|
10373
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
10374
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
10375
|
+
let end = index + quote.length;
|
|
10376
|
+
while (end < text.length) {
|
|
10377
|
+
const char = text[end];
|
|
10378
|
+
if (escapes && char === "\\") {
|
|
10379
|
+
end += 2;
|
|
10380
|
+
continue;
|
|
10381
|
+
}
|
|
10382
|
+
if (text.startsWith(quote, end)) {
|
|
10383
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
10384
|
+
return false;
|
|
10385
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
10386
|
+
index = end + quote.length;
|
|
10387
|
+
return true;
|
|
10388
|
+
}
|
|
10389
|
+
if (char === `
|
|
10390
|
+
` && !multiline)
|
|
10391
|
+
return false;
|
|
10392
|
+
end += 1;
|
|
10393
|
+
}
|
|
10394
|
+
return false;
|
|
10395
|
+
};
|
|
10396
|
+
while (index < text.length) {
|
|
10397
|
+
const frame = frames[frames.length - 1];
|
|
10398
|
+
const char = text[index];
|
|
10399
|
+
if (frame.kind === "template") {
|
|
10400
|
+
if (char === "\\") {
|
|
10401
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
10402
|
+
index += 2;
|
|
10403
|
+
} else if (char === "`") {
|
|
10404
|
+
emitVerbatim(char);
|
|
10405
|
+
frames.pop();
|
|
10406
|
+
index += 1;
|
|
10407
|
+
} else if (text.startsWith("${", index)) {
|
|
10408
|
+
emitVerbatim("${");
|
|
10409
|
+
frames.push({ kind: "code", braces: 0 });
|
|
10410
|
+
index += 2;
|
|
10411
|
+
} else {
|
|
10412
|
+
out += char;
|
|
10413
|
+
kept = out.length;
|
|
10414
|
+
index += 1;
|
|
10415
|
+
}
|
|
10416
|
+
continue;
|
|
10417
|
+
}
|
|
10418
|
+
if (char === `
|
|
10419
|
+
`) {
|
|
10420
|
+
newline();
|
|
10421
|
+
index += 1;
|
|
10422
|
+
continue;
|
|
10423
|
+
}
|
|
10424
|
+
if (SPACE.test(char)) {
|
|
10425
|
+
if (collapse)
|
|
10426
|
+
pendingSpace = true;
|
|
10427
|
+
else
|
|
10428
|
+
out += char;
|
|
10429
|
+
index += 1;
|
|
10430
|
+
continue;
|
|
10431
|
+
}
|
|
10432
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
10433
|
+
if (lineOpener) {
|
|
10434
|
+
const end = text.indexOf(`
|
|
10435
|
+
`, index);
|
|
10436
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
10437
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
10438
|
+
emitVerbatim(comment);
|
|
10439
|
+
else
|
|
10440
|
+
pendingSpace = true;
|
|
10441
|
+
index = end < 0 ? text.length : end;
|
|
10442
|
+
continue;
|
|
10443
|
+
}
|
|
10444
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
10445
|
+
if (block) {
|
|
10446
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
10447
|
+
if (end < 0)
|
|
10448
|
+
return null;
|
|
10449
|
+
const comment = text.slice(index, end + block[1].length);
|
|
10450
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
10451
|
+
emitVerbatim(comment);
|
|
10452
|
+
else
|
|
10453
|
+
pendingSpace = true;
|
|
10454
|
+
if (comment.includes(`
|
|
10455
|
+
`))
|
|
10456
|
+
newline();
|
|
10457
|
+
index = end + block[1].length;
|
|
10458
|
+
continue;
|
|
10459
|
+
}
|
|
10460
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
10461
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
10462
|
+
return null;
|
|
10463
|
+
continue;
|
|
10464
|
+
}
|
|
10465
|
+
if (syntax.quotes.includes(char)) {
|
|
10466
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
10467
|
+
return null;
|
|
10468
|
+
continue;
|
|
10469
|
+
}
|
|
10470
|
+
if (syntax.literal?.includes(char)) {
|
|
10471
|
+
if (!scanString(char, false, false))
|
|
10472
|
+
return null;
|
|
10473
|
+
continue;
|
|
10474
|
+
}
|
|
10475
|
+
if (syntax.rawBacktick && char === "`") {
|
|
10476
|
+
if (!scanString("`", false, true))
|
|
10477
|
+
return null;
|
|
10478
|
+
continue;
|
|
10479
|
+
}
|
|
10480
|
+
if (syntax.js && char === "`") {
|
|
10481
|
+
emitVerbatim(char);
|
|
10482
|
+
frames.push({ kind: "template" });
|
|
10483
|
+
index += 1;
|
|
10484
|
+
continue;
|
|
10485
|
+
}
|
|
10486
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
10487
|
+
return null;
|
|
10488
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
10489
|
+
const lineEnd = text.indexOf(`
|
|
10490
|
+
`, index);
|
|
10491
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
10492
|
+
return null;
|
|
10493
|
+
}
|
|
10494
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
10495
|
+
let end = index + 1;
|
|
10496
|
+
let inClass = false;
|
|
10497
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
10498
|
+
if (text[end] === `
|
|
10499
|
+
`)
|
|
10500
|
+
return null;
|
|
10501
|
+
if (text[end] === "\\")
|
|
10502
|
+
end += 1;
|
|
10503
|
+
else if (text[end] === "[")
|
|
10504
|
+
inClass = true;
|
|
10505
|
+
else if (text[end] === "]")
|
|
10506
|
+
inClass = false;
|
|
10507
|
+
end += 1;
|
|
10508
|
+
}
|
|
10509
|
+
if (end >= text.length)
|
|
10510
|
+
return null;
|
|
10511
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
10512
|
+
index = end + 1;
|
|
10513
|
+
continue;
|
|
10514
|
+
}
|
|
10515
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
10516
|
+
if (char === "{")
|
|
10517
|
+
frame.braces += 1;
|
|
10518
|
+
else if (char === "}") {
|
|
10519
|
+
if (frame.braces === 0) {
|
|
10520
|
+
emitVerbatim(char);
|
|
10521
|
+
frames.pop();
|
|
10522
|
+
index += 1;
|
|
10523
|
+
continue;
|
|
10524
|
+
}
|
|
10525
|
+
frame.braces -= 1;
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
emitCode(char);
|
|
10529
|
+
index += 1;
|
|
10530
|
+
}
|
|
10531
|
+
if (frames.length !== 1)
|
|
10532
|
+
return null;
|
|
10533
|
+
newline();
|
|
10534
|
+
return out;
|
|
10535
|
+
}
|
|
10536
|
+
function unbalanced(body) {
|
|
10537
|
+
let depth = 0;
|
|
10538
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
10539
|
+
if (part === "{")
|
|
10540
|
+
depth += 1;
|
|
10541
|
+
else if (part === "}")
|
|
10542
|
+
depth = Math.max(0, depth - 1);
|
|
10543
|
+
}
|
|
10544
|
+
return depth > 0;
|
|
10545
|
+
}
|
|
10546
|
+
function hasOpaqueLineBreak(text) {
|
|
10547
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
10548
|
+
}
|
|
10549
|
+
function textOf(bytes) {
|
|
10550
|
+
if (bytes.includes(0))
|
|
10551
|
+
return null;
|
|
10552
|
+
try {
|
|
10553
|
+
return decoder.decode(bytes);
|
|
10554
|
+
} catch {
|
|
10555
|
+
return null;
|
|
10556
|
+
}
|
|
10557
|
+
}
|
|
10558
|
+
function cosmeticChange(file, before, after) {
|
|
10559
|
+
const left = textOf(before);
|
|
10560
|
+
const right = textOf(after);
|
|
10561
|
+
if (left === null || right === null)
|
|
10562
|
+
return false;
|
|
10563
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
10564
|
+
return false;
|
|
10565
|
+
const a = substanceOf(file, left);
|
|
10566
|
+
return a !== null && a === substanceOf(file, right);
|
|
10567
|
+
}
|
|
10568
|
+
var SLASH_BLOCK, C_LIKE, SYNTAXES, DIRECTIVE, extensionOf = (file) => {
|
|
10569
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
10570
|
+
const dot = name.lastIndexOf(".");
|
|
10571
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
10572
|
+
}, 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;
|
|
10573
|
+
var init_substance = __esm(() => {
|
|
10574
|
+
SLASH_BLOCK = [["/*", "*/"]];
|
|
10575
|
+
C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
10576
|
+
SYNTAXES = Object.fromEntries([
|
|
10577
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
10578
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
10579
|
+
ext,
|
|
10580
|
+
{ ...C_LIKE, triple: true }
|
|
10581
|
+
]),
|
|
10582
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
10583
|
+
...["css", "scss", "less"].map((ext) => [
|
|
10584
|
+
ext,
|
|
10585
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
10586
|
+
]),
|
|
10587
|
+
...["py", "pyi"].map((ext) => [
|
|
10588
|
+
ext,
|
|
10589
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
10590
|
+
]),
|
|
10591
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
10592
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
10593
|
+
ext,
|
|
10594
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
10595
|
+
])
|
|
10596
|
+
]);
|
|
10597
|
+
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;
|
|
10598
|
+
CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
10599
|
+
SPACE = /[ \t\r\f\v]/u;
|
|
10600
|
+
WORD = /[\w$]/u;
|
|
10601
|
+
REGEX_KEYWORDS = new Set([
|
|
10602
|
+
"return",
|
|
10603
|
+
"typeof",
|
|
10604
|
+
"instanceof",
|
|
10605
|
+
"case",
|
|
10606
|
+
"do",
|
|
10607
|
+
"else",
|
|
10608
|
+
"in",
|
|
10609
|
+
"of",
|
|
10610
|
+
"new",
|
|
10611
|
+
"delete",
|
|
10612
|
+
"void",
|
|
10613
|
+
"throw",
|
|
10614
|
+
"yield",
|
|
10615
|
+
"await"
|
|
10616
|
+
]);
|
|
10617
|
+
decoder = new TextDecoder("utf-8", { fatal: true });
|
|
10618
|
+
});
|
|
10619
|
+
|
|
10620
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
10621
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
10073
10622
|
import path10 from "node:path";
|
|
10623
|
+
function kindOf(file) {
|
|
10624
|
+
if (file.binary)
|
|
10625
|
+
return "behavior";
|
|
10626
|
+
if (file.generated)
|
|
10627
|
+
return "generated";
|
|
10628
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
10629
|
+
return "lockfile";
|
|
10630
|
+
if (isDoc(file.path))
|
|
10631
|
+
return "doc";
|
|
10632
|
+
if (file.cosmetic)
|
|
10633
|
+
return "cosmetic";
|
|
10634
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
10635
|
+
}
|
|
10636
|
+
function globMatch(pattern, file) {
|
|
10637
|
+
let regex = globCache.get(pattern);
|
|
10638
|
+
if (!regex) {
|
|
10639
|
+
let source = "";
|
|
10640
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
10641
|
+
const char = pattern[index];
|
|
10642
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
10643
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
10644
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
10645
|
+
} else if (char === "*")
|
|
10646
|
+
source += "[^/]*";
|
|
10647
|
+
else if (char === "?")
|
|
10648
|
+
source += "[^/]";
|
|
10649
|
+
else
|
|
10650
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
10651
|
+
}
|
|
10652
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
10653
|
+
globCache.set(pattern, regex);
|
|
10654
|
+
}
|
|
10655
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
10656
|
+
}
|
|
10657
|
+
function isRiskPath(file, extra = []) {
|
|
10658
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
10659
|
+
return true;
|
|
10660
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
10661
|
+
return true;
|
|
10662
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
10663
|
+
return false;
|
|
10664
|
+
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)));
|
|
10665
|
+
}
|
|
10666
|
+
function computeLane(input) {
|
|
10667
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
10668
|
+
const risky = new Set(riskFiles);
|
|
10669
|
+
const kinds = input.files.map((file) => ({
|
|
10670
|
+
file,
|
|
10671
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
10672
|
+
}));
|
|
10673
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
10674
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
10675
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
10676
|
+
const result = (lane, reasons) => ({
|
|
10677
|
+
lane,
|
|
10678
|
+
reasons,
|
|
10679
|
+
lines,
|
|
10680
|
+
files: behavior.length,
|
|
10681
|
+
riskFiles
|
|
10682
|
+
});
|
|
10683
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
10684
|
+
if (input.riskTier === "high")
|
|
10685
|
+
return result("full", ["the task judgment records high risk"]);
|
|
10686
|
+
if (riskFiles.length)
|
|
10687
|
+
return result("full", [
|
|
10688
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
10689
|
+
]);
|
|
10690
|
+
if (!behavior.length)
|
|
10691
|
+
return result("none", [
|
|
10692
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
10693
|
+
]);
|
|
10694
|
+
if (lines >= fullMinLines)
|
|
10695
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
10696
|
+
if (lines > selfMaxLines)
|
|
10697
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
10698
|
+
if (behavior.length > selfMaxFiles)
|
|
10699
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
10700
|
+
if (opaque.length)
|
|
10701
|
+
return result("focused", [
|
|
10702
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
10703
|
+
]);
|
|
10704
|
+
return result("self", [
|
|
10705
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
10706
|
+
]);
|
|
10707
|
+
}
|
|
10708
|
+
function untrackedStat(cwd, file) {
|
|
10709
|
+
try {
|
|
10710
|
+
const bytes = readFileSync3(path10.join(cwd, file));
|
|
10711
|
+
if (bytes.includes(0))
|
|
10712
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
10713
|
+
const text = bytes.toString("utf8");
|
|
10714
|
+
if (hasOpaqueLineBreak(text))
|
|
10715
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
10716
|
+
const lines = text.split(`
|
|
10717
|
+
`).length - (text.endsWith(`
|
|
10718
|
+
`) ? 1 : 0);
|
|
10719
|
+
return { path: file, added: lines, deleted: 0 };
|
|
10720
|
+
} catch {
|
|
10721
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
10722
|
+
}
|
|
10723
|
+
}
|
|
10724
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
10725
|
+
const raw = diffRaw(cwd, fork, head);
|
|
10726
|
+
if (!raw)
|
|
10727
|
+
return null;
|
|
10728
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
10729
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
10730
|
+
if (!blobs)
|
|
10731
|
+
return null;
|
|
10732
|
+
const out = new Set;
|
|
10733
|
+
const opaque = new Set;
|
|
10734
|
+
for (const entry of candidates) {
|
|
10735
|
+
const before = blobs.get(entry.oldBlob);
|
|
10736
|
+
let after = blobs.get(entry.newBlob);
|
|
10737
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
10738
|
+
try {
|
|
10739
|
+
after = readFileSync3(path10.join(cwd, entry.path));
|
|
10740
|
+
} catch {
|
|
10741
|
+
after = undefined;
|
|
10742
|
+
}
|
|
10743
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
10744
|
+
out.add(entry.path);
|
|
10745
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
10746
|
+
opaque.add(entry.path);
|
|
10747
|
+
}
|
|
10748
|
+
return { cosmetic: out, opaque };
|
|
10749
|
+
}
|
|
10750
|
+
function generatedAt(cwd, fork, paths) {
|
|
10751
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
10752
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
10753
|
+
if (!files || !blobs)
|
|
10754
|
+
return new Set;
|
|
10755
|
+
const texts = new Map;
|
|
10756
|
+
for (const [file, id] of files) {
|
|
10757
|
+
const bytes = blobs.get(id);
|
|
10758
|
+
if (bytes)
|
|
10759
|
+
texts.set(file, bytes.toString("utf8"));
|
|
10760
|
+
}
|
|
10761
|
+
return generatedByAttributes(texts, paths);
|
|
10762
|
+
}
|
|
10763
|
+
function laneFiles(cwd, fork, head) {
|
|
10764
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
10765
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
10766
|
+
if (!stats || !cosmetic)
|
|
10767
|
+
return null;
|
|
10768
|
+
const files = [
|
|
10769
|
+
...stats.map((entry) => ({
|
|
10770
|
+
...entry,
|
|
10771
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
10772
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
10773
|
+
})),
|
|
10774
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
10775
|
+
];
|
|
10776
|
+
const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
|
|
10777
|
+
return files.map((entry) => ({
|
|
10778
|
+
...entry,
|
|
10779
|
+
generated: generated.has(entry.path)
|
|
10780
|
+
}));
|
|
10781
|
+
}
|
|
10782
|
+
function branchLane(cwd, options) {
|
|
10783
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
10784
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
10785
|
+
const head = options.workingTree ? null : options.head;
|
|
10786
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
10787
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
10788
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
10789
|
+
if (!fork || !files) {
|
|
10790
|
+
const high = riskTier === "high";
|
|
10791
|
+
return {
|
|
10792
|
+
lane: high ? "full" : "focused",
|
|
10793
|
+
reasons: [
|
|
10794
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
10795
|
+
...high ? ["the task judgment records high risk"] : []
|
|
10796
|
+
],
|
|
10797
|
+
lines: 0,
|
|
10798
|
+
files: 0,
|
|
10799
|
+
riskFiles: [],
|
|
10800
|
+
base,
|
|
10801
|
+
head: options.head
|
|
10802
|
+
};
|
|
10803
|
+
}
|
|
10804
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
10805
|
+
return {
|
|
10806
|
+
...computeLane({
|
|
10807
|
+
files,
|
|
10808
|
+
riskTier,
|
|
10809
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
10810
|
+
}),
|
|
10811
|
+
base,
|
|
10812
|
+
head: options.head
|
|
10813
|
+
};
|
|
10814
|
+
}
|
|
10815
|
+
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(`
|
|
10816
|
+
`).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";
|
|
10817
|
+
var init_lane = __esm(() => {
|
|
10818
|
+
init_rev();
|
|
10819
|
+
init_ops();
|
|
10820
|
+
init_ledger();
|
|
10821
|
+
init_substance();
|
|
10822
|
+
LANE_THRESHOLDS = {
|
|
10823
|
+
selfMaxLines: 100,
|
|
10824
|
+
selfMaxFiles: 5,
|
|
10825
|
+
fullMinLines: 500
|
|
10826
|
+
};
|
|
10827
|
+
RISK_TOKENS = new Set([
|
|
10828
|
+
"auth",
|
|
10829
|
+
"authn",
|
|
10830
|
+
"authz",
|
|
10831
|
+
"oauth",
|
|
10832
|
+
"security",
|
|
10833
|
+
"permission",
|
|
10834
|
+
"permissions",
|
|
10835
|
+
"secret",
|
|
10836
|
+
"secrets",
|
|
10837
|
+
"crypto",
|
|
10838
|
+
"gate",
|
|
10839
|
+
"gates",
|
|
10840
|
+
"policy",
|
|
10841
|
+
"policies",
|
|
10842
|
+
"hook",
|
|
10843
|
+
"hooks",
|
|
10844
|
+
"husky",
|
|
10845
|
+
"lefthook",
|
|
10846
|
+
"release",
|
|
10847
|
+
"releases",
|
|
10848
|
+
"migration",
|
|
10849
|
+
"migrations",
|
|
10850
|
+
"migrate",
|
|
10851
|
+
"workflows"
|
|
10852
|
+
]);
|
|
10853
|
+
RISK_FILES = new Set([
|
|
10854
|
+
".gitlab-ci.yml",
|
|
10855
|
+
"Jenkinsfile",
|
|
10856
|
+
"azure-pipelines.yml",
|
|
10857
|
+
".pre-commit-config.yaml",
|
|
10858
|
+
".workit/risk-paths"
|
|
10859
|
+
]);
|
|
10860
|
+
RISK_NAMES = new Set([".gitattributes"]);
|
|
10861
|
+
LOCKFILES = new Set([
|
|
10862
|
+
"bun.lock",
|
|
10863
|
+
"bun.lockb",
|
|
10864
|
+
"package-lock.json",
|
|
10865
|
+
"npm-shrinkwrap.json",
|
|
10866
|
+
"yarn.lock",
|
|
10867
|
+
"pnpm-lock.yaml",
|
|
10868
|
+
"Cargo.lock",
|
|
10869
|
+
"go.sum",
|
|
10870
|
+
"poetry.lock",
|
|
10871
|
+
"uv.lock",
|
|
10872
|
+
"Pipfile.lock",
|
|
10873
|
+
"Gemfile.lock",
|
|
10874
|
+
"composer.lock"
|
|
10875
|
+
]);
|
|
10876
|
+
DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
|
|
10877
|
+
DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
10878
|
+
INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
10879
|
+
TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
|
|
10880
|
+
globCache = new Map;
|
|
10881
|
+
REGULAR = new Set(["100644", "100755"]);
|
|
10882
|
+
ABSENT = /^0+$/u;
|
|
10883
|
+
});
|
|
10884
|
+
|
|
10885
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
10886
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
10887
|
+
import path11 from "node:path";
|
|
10074
10888
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
10075
10889
|
const out = [];
|
|
10076
10890
|
const base = { scope, dependentAction: null };
|
|
10891
|
+
const proportional = verification === "proportional";
|
|
10077
10892
|
if (judgment.behaviorChange)
|
|
10078
10893
|
out.push(derived2({
|
|
10079
10894
|
...base,
|
|
@@ -10084,7 +10899,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
|
|
|
10084
10899
|
before: "close",
|
|
10085
10900
|
acceptanceAllowed: false
|
|
10086
10901
|
}));
|
|
10087
|
-
if (
|
|
10902
|
+
if (proportional && judgment.riskTier === "normal")
|
|
10903
|
+
out.push(derived2({
|
|
10904
|
+
...base,
|
|
10905
|
+
ruleId: RULES.lane,
|
|
10906
|
+
dimension: "review",
|
|
10907
|
+
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.",
|
|
10908
|
+
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.",
|
|
10909
|
+
before: "close",
|
|
10910
|
+
acceptanceAllowed: false
|
|
10911
|
+
}));
|
|
10912
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
10088
10913
|
out.push(verification === "independent" ? derived2({
|
|
10089
10914
|
...base,
|
|
10090
10915
|
ruleId: RULES.nonAuthor,
|
|
@@ -10163,7 +10988,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
10163
10988
|
case RULES.self:
|
|
10164
10989
|
case RULES.nonAuthor:
|
|
10165
10990
|
case RULES.verified:
|
|
10166
|
-
return verdict(requirement, root, ledger);
|
|
10991
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
10992
|
+
case RULES.lane:
|
|
10993
|
+
return laneVerdict(task, root, ledger);
|
|
10167
10994
|
default:
|
|
10168
10995
|
return null;
|
|
10169
10996
|
}
|
|
@@ -10222,7 +11049,7 @@ var RULES, derived2 = (spec) => ({
|
|
|
10222
11049
|
decisionIds: []
|
|
10223
11050
|
};
|
|
10224
11051
|
}, planRecorded = (task, root) => {
|
|
10225
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" &&
|
|
11052
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path11.resolve(root, ref.path)));
|
|
10226
11053
|
if (cited)
|
|
10227
11054
|
return {
|
|
10228
11055
|
met: true,
|
|
@@ -10235,14 +11062,28 @@ var RULES, derived2 = (spec) => ({
|
|
|
10235
11062
|
reason: "no plan recorded: write it (docs/ and Markdown writes stay allowed), then `workit policy assess --ref <path>`",
|
|
10236
11063
|
decisionIds: []
|
|
10237
11064
|
};
|
|
10238
|
-
},
|
|
11065
|
+
}, workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) }), laneVerdict = (task, root, ledger) => {
|
|
11066
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
11067
|
+
return {
|
|
11068
|
+
met: true,
|
|
11069
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
11070
|
+
decisionIds: []
|
|
11071
|
+
};
|
|
11072
|
+
const lane = workingLane(task, root);
|
|
11073
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
11074
|
+
const need = laneNeed(lane.lane);
|
|
11075
|
+
if (need === "none")
|
|
11076
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
11077
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
11078
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
11079
|
+
}, verdict = (ruleId, root, ledger) => {
|
|
10239
11080
|
const branch = ledger.branch();
|
|
10240
11081
|
if (!branch)
|
|
10241
11082
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
10242
11083
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
10243
11084
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
10244
11085
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
10245
|
-
const met = !failing && (
|
|
11086
|
+
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
11087
|
if (met)
|
|
10247
11088
|
return {
|
|
10248
11089
|
met: true,
|
|
@@ -10250,18 +11091,20 @@ var RULES, derived2 = (spec) => ({
|
|
|
10250
11091
|
decisionIds: []
|
|
10251
11092
|
};
|
|
10252
11093
|
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 =
|
|
11094
|
+
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
11095
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
10255
11096
|
};
|
|
10256
11097
|
var init_derive = __esm(() => {
|
|
10257
11098
|
init_rev();
|
|
10258
11099
|
init_ledger();
|
|
11100
|
+
init_lane();
|
|
10259
11101
|
init_task_contract();
|
|
10260
11102
|
RULES = {
|
|
10261
11103
|
test: "check:test",
|
|
10262
11104
|
self: "verdict:self",
|
|
10263
11105
|
nonAuthor: "verdict:non-author",
|
|
10264
11106
|
verified: "verdict:verified",
|
|
11107
|
+
lane: "verdict:lane",
|
|
10265
11108
|
product: "decision:product",
|
|
10266
11109
|
plan: "plan"
|
|
10267
11110
|
};
|
|
@@ -10819,7 +11662,7 @@ var init_safe_write = () => {};
|
|
|
10819
11662
|
|
|
10820
11663
|
// packages/workit-core/src/autonomy.ts
|
|
10821
11664
|
import os4 from "node:os";
|
|
10822
|
-
import
|
|
11665
|
+
import path12 from "node:path";
|
|
10823
11666
|
function endpointOf(entry) {
|
|
10824
11667
|
const raw = entry?.defaultEndpoint;
|
|
10825
11668
|
if (raw === undefined)
|
|
@@ -10916,10 +11759,13 @@ var accountHome = () => {
|
|
|
10916
11759
|
} catch {
|
|
10917
11760
|
return os4.homedir();
|
|
10918
11761
|
}
|
|
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 = "
|
|
11762
|
+
}, grantsHome, grantsDir = () => path12.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
|
|
11763
|
+
const configured = path12.resolve(resolveConfigDir());
|
|
11764
|
+
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`;
|
|
11765
|
+
}, 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) => {
|
|
11766
|
+
const value = entry?.verification;
|
|
11767
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
11768
|
+
}, isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
|
|
10923
11769
|
if (value === true)
|
|
10924
11770
|
return { push: true, pr: true, merge: "verified" };
|
|
10925
11771
|
if (!Array.isArray(value))
|
|
@@ -10946,6 +11792,11 @@ var init_autonomy = __esm(() => {
|
|
|
10946
11792
|
rerun: true
|
|
10947
11793
|
});
|
|
10948
11794
|
DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
|
|
11795
|
+
VERIFICATION_MODES = [
|
|
11796
|
+
"proportional",
|
|
11797
|
+
"self",
|
|
11798
|
+
"independent"
|
|
11799
|
+
];
|
|
10949
11800
|
});
|
|
10950
11801
|
|
|
10951
11802
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
@@ -11058,7 +11909,7 @@ var init_read_open_flags = () => {};
|
|
|
11058
11909
|
// node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
11059
11910
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
11060
11911
|
import fsSync from "node:fs";
|
|
11061
|
-
import
|
|
11912
|
+
import path13 from "node:path";
|
|
11062
11913
|
function parseSidecarLockSnapshot(snapshot, parser) {
|
|
11063
11914
|
return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
|
|
11064
11915
|
}
|
|
@@ -11086,13 +11937,13 @@ ${ownershipToken}
|
|
|
11086
11937
|
return { raw, ownershipToken };
|
|
11087
11938
|
}
|
|
11088
11939
|
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
11089
|
-
const resolved =
|
|
11090
|
-
const lexicalRelative =
|
|
11091
|
-
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${
|
|
11092
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
11940
|
+
const resolved = path13.resolve(lockPath);
|
|
11941
|
+
const lexicalRelative = path13.relative(lockRoot.rootDir, resolved);
|
|
11942
|
+
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path13.sep}`) && !path13.isAbsolute(lexicalRelative) ? lexicalRelative : path13.relative(lockRoot.rootReal, resolved);
|
|
11943
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
|
|
11093
11944
|
throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
11094
11945
|
}
|
|
11095
|
-
return relative.split(
|
|
11946
|
+
return relative.split(path13.sep).join(path13.posix.sep);
|
|
11096
11947
|
}
|
|
11097
11948
|
function parseSidecarLockPayload(raw, parser) {
|
|
11098
11949
|
if (parser) {
|
|
@@ -11262,7 +12113,7 @@ function sleepSync(ms) {
|
|
|
11262
12113
|
|
|
11263
12114
|
// node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
|
|
11264
12115
|
import fs10 from "node:fs";
|
|
11265
|
-
import
|
|
12116
|
+
import path14 from "node:path";
|
|
11266
12117
|
function getSyncHeldLocks() {
|
|
11267
12118
|
const globalWithState = globalThis;
|
|
11268
12119
|
if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
|
|
@@ -11340,26 +12191,26 @@ function canonicalLockParentSync(parent) {
|
|
|
11340
12191
|
return process.platform === "win32" ? fs10.realpathSync.native(parent) : fs10.realpathSync(parent);
|
|
11341
12192
|
}
|
|
11342
12193
|
function normalizeTargetPath(targetPath) {
|
|
11343
|
-
const resolved =
|
|
11344
|
-
fs10.mkdirSync(
|
|
12194
|
+
const resolved = path14.resolve(targetPath);
|
|
12195
|
+
fs10.mkdirSync(path14.dirname(resolved), { recursive: true });
|
|
11345
12196
|
try {
|
|
11346
|
-
return
|
|
12197
|
+
return path14.join(canonicalLockParentSync(path14.dirname(resolved)), path14.basename(resolved));
|
|
11347
12198
|
} catch {
|
|
11348
12199
|
return resolved;
|
|
11349
12200
|
}
|
|
11350
12201
|
}
|
|
11351
12202
|
function boundedLockPath(lockPath, lockRoot) {
|
|
11352
|
-
const resolved =
|
|
12203
|
+
const resolved = path14.resolve(lockPath);
|
|
11353
12204
|
if (!lockRoot)
|
|
11354
12205
|
return resolved;
|
|
11355
12206
|
relativeSidecarLockPath(lockRoot, resolved);
|
|
11356
|
-
const parent =
|
|
12207
|
+
const parent = path14.dirname(resolved);
|
|
11357
12208
|
const parentReal = canonicalLockParentSync(parent);
|
|
11358
|
-
const parentRelative =
|
|
11359
|
-
if (parentRelative === ".." || parentRelative.startsWith(`..${
|
|
12209
|
+
const parentRelative = path14.relative(lockRoot.rootReal, parentReal);
|
|
12210
|
+
if (parentRelative === ".." || parentRelative.startsWith(`..${path14.sep}`) || path14.isAbsolute(parentRelative)) {
|
|
11360
12211
|
throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
|
|
11361
12212
|
}
|
|
11362
|
-
return
|
|
12213
|
+
return path14.join(parentReal, path14.basename(resolved));
|
|
11363
12214
|
}
|
|
11364
12215
|
function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
11365
12216
|
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
@@ -11575,14 +12426,14 @@ var init_file_lock = __esm(() => {
|
|
|
11575
12426
|
});
|
|
11576
12427
|
|
|
11577
12428
|
// packages/workit-core/src/core/package-root.ts
|
|
11578
|
-
import { existsSync as
|
|
11579
|
-
import
|
|
12429
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
12430
|
+
import path15 from "node:path";
|
|
11580
12431
|
import { fileURLToPath } from "node:url";
|
|
11581
12432
|
var packageRoot = () => {
|
|
11582
|
-
let dir =
|
|
12433
|
+
let dir = path15.dirname(fileURLToPath(import.meta.url));
|
|
11583
12434
|
while (true) {
|
|
11584
|
-
const parent =
|
|
11585
|
-
if (
|
|
12435
|
+
const parent = path15.dirname(dir);
|
|
12436
|
+
if (existsSync4(path15.join(dir, "package.json")) || parent === dir)
|
|
11586
12437
|
return dir;
|
|
11587
12438
|
dir = parent;
|
|
11588
12439
|
}
|
|
@@ -11592,7 +12443,7 @@ var init_package_root = () => {};
|
|
|
11592
12443
|
// packages/workit-core/src/store/event-log.ts
|
|
11593
12444
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11594
12445
|
import fs11 from "node:fs";
|
|
11595
|
-
import
|
|
12446
|
+
import path16 from "node:path";
|
|
11596
12447
|
function readLog(file, from = 0, expectSeq = null) {
|
|
11597
12448
|
let fd;
|
|
11598
12449
|
try {
|
|
@@ -11665,7 +12516,7 @@ function appendEvent(file, event, validEnd) {
|
|
|
11665
12516
|
fs11.closeSync(fd);
|
|
11666
12517
|
}
|
|
11667
12518
|
if (created)
|
|
11668
|
-
fsyncDirectory(
|
|
12519
|
+
fsyncDirectory(path16.dirname(file));
|
|
11669
12520
|
return validEnd;
|
|
11670
12521
|
}
|
|
11671
12522
|
function replaceFile(file, bytes, durable) {
|
|
@@ -11681,7 +12532,7 @@ function replaceFile(file, bytes, durable) {
|
|
|
11681
12532
|
}
|
|
11682
12533
|
renameWithRetry(temporary, file);
|
|
11683
12534
|
if (durable)
|
|
11684
|
-
fsyncDirectory(
|
|
12535
|
+
fsyncDirectory(path16.dirname(file));
|
|
11685
12536
|
} catch (error) {
|
|
11686
12537
|
try {
|
|
11687
12538
|
fs11.unlinkSync(temporary);
|
|
@@ -11991,7 +12842,7 @@ var init_notes = __esm(() => {
|
|
|
11991
12842
|
// packages/workit-core/src/core/task-store.ts
|
|
11992
12843
|
import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
|
|
11993
12844
|
import * as fs12 from "node:fs";
|
|
11994
|
-
import
|
|
12845
|
+
import path17 from "node:path";
|
|
11995
12846
|
|
|
11996
12847
|
class TaskStore {
|
|
11997
12848
|
root;
|
|
@@ -12003,7 +12854,7 @@ class TaskStore {
|
|
|
12003
12854
|
migrationChecked = false;
|
|
12004
12855
|
compactDue = new Set;
|
|
12005
12856
|
constructor(root, options = {}) {
|
|
12006
|
-
const resolved =
|
|
12857
|
+
const resolved = path17.resolve(root);
|
|
12007
12858
|
this.origin = fs12.existsSync(resolved) ? fs12.realpathSync(resolved) : resolved;
|
|
12008
12859
|
this.located = resolveStore(this.origin);
|
|
12009
12860
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
@@ -12012,7 +12863,7 @@ class TaskStore {
|
|
|
12012
12863
|
this.migrateOnRead = options.migrateOnRead;
|
|
12013
12864
|
}
|
|
12014
12865
|
location() {
|
|
12015
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(
|
|
12866
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path17.join(this.root, ".git"))) {
|
|
12016
12867
|
this.located = undefined;
|
|
12017
12868
|
this.migrationChecked = false;
|
|
12018
12869
|
}
|
|
@@ -12168,7 +13019,7 @@ class TaskStore {
|
|
|
12168
13019
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
12169
13020
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
12170
13021
|
}
|
|
12171
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
13022
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path17.basename(this.root) || this.root;
|
|
12172
13023
|
const intent = {
|
|
12173
13024
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
12174
13025
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -12234,14 +13085,14 @@ class TaskStore {
|
|
|
12234
13085
|
const branch = key.ok ? key.data.branch : null;
|
|
12235
13086
|
if (!branch || !location.ok)
|
|
12236
13087
|
return success(null, null, null);
|
|
12237
|
-
const common =
|
|
12238
|
-
const heads =
|
|
12239
|
-
const headLog = location.data.git ?
|
|
13088
|
+
const common = path17.dirname(location.data.dir);
|
|
13089
|
+
const heads = path17.join(common, "logs", "refs", "heads");
|
|
13090
|
+
const headLog = location.data.git ? path17.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
12240
13091
|
const signatures = [
|
|
12241
|
-
|
|
13092
|
+
path17.join(heads, branch),
|
|
12242
13093
|
headLog,
|
|
12243
13094
|
heads,
|
|
12244
|
-
|
|
13095
|
+
path17.join(common, "packed-refs")
|
|
12245
13096
|
].map((file) => fileSignature(file) ?? "-");
|
|
12246
13097
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
12247
13098
|
let resolved = origins.get(slot);
|
|
@@ -12253,7 +13104,7 @@ class TaskStore {
|
|
|
12253
13104
|
aliases.push(rename.from);
|
|
12254
13105
|
const start = branchStart(location.data, branch, aliases);
|
|
12255
13106
|
const slash = start?.indexOf("/") ?? -1;
|
|
12256
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
13107
|
+
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
13108
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
12258
13109
|
resolved = {
|
|
12259
13110
|
signature: signatures.join("|"),
|
|
@@ -12491,13 +13342,13 @@ class TaskStore {
|
|
|
12491
13342
|
if (!ready.ok)
|
|
12492
13343
|
return ready;
|
|
12493
13344
|
const dirs = this.paths();
|
|
12494
|
-
const recovery =
|
|
13345
|
+
const recovery = path17.join(this.root, ".workit", "recovery");
|
|
12495
13346
|
if (fs12.existsSync(recovery)) {
|
|
12496
13347
|
let files = 0;
|
|
12497
13348
|
let bytes = 0;
|
|
12498
13349
|
try {
|
|
12499
13350
|
for (const name of fs12.readdirSync(recovery)) {
|
|
12500
|
-
const stat = fs12.lstatSync(
|
|
13351
|
+
const stat = fs12.lstatSync(path17.join(recovery, name));
|
|
12501
13352
|
if (!stat.isFile())
|
|
12502
13353
|
continue;
|
|
12503
13354
|
files += 1;
|
|
@@ -12550,7 +13401,7 @@ class TaskStore {
|
|
|
12550
13401
|
if (fs12.existsSync(dirs.blobs))
|
|
12551
13402
|
for (const name of fs12.readdirSync(dirs.blobs)) {
|
|
12552
13403
|
const digest = name.replace(/\.json$/, "");
|
|
12553
|
-
const file =
|
|
13404
|
+
const file = path17.join(dirs.blobs, name);
|
|
12554
13405
|
let stat;
|
|
12555
13406
|
try {
|
|
12556
13407
|
stat = fs12.lstatSync(file);
|
|
@@ -12568,7 +13419,7 @@ class TaskStore {
|
|
|
12568
13419
|
}
|
|
12569
13420
|
return success(null, null, null);
|
|
12570
13421
|
};
|
|
12571
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
13422
|
+
const swept = dryRun ? sweep() : this.withLock(path17.join(dirs.dir, "gc.lock"), sweep);
|
|
12572
13423
|
if (!swept.ok)
|
|
12573
13424
|
return swept;
|
|
12574
13425
|
const temporaryDirs = [
|
|
@@ -12576,7 +13427,7 @@ class TaskStore {
|
|
|
12576
13427
|
dirs.tasks,
|
|
12577
13428
|
dirs.blobs,
|
|
12578
13429
|
dirs.checkout,
|
|
12579
|
-
...names.map((name) =>
|
|
13430
|
+
...names.map((name) => path17.join(dirs.tasks, name))
|
|
12580
13431
|
];
|
|
12581
13432
|
for (const directory of temporaryDirs) {
|
|
12582
13433
|
if (!fs12.existsSync(directory))
|
|
@@ -12584,7 +13435,7 @@ class TaskStore {
|
|
|
12584
13435
|
for (const name of fs12.readdirSync(directory)) {
|
|
12585
13436
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
12586
13437
|
continue;
|
|
12587
|
-
const file =
|
|
13438
|
+
const file = path17.join(directory, name);
|
|
12588
13439
|
try {
|
|
12589
13440
|
const stat = fs12.lstatSync(file);
|
|
12590
13441
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
@@ -12601,30 +13452,30 @@ class TaskStore {
|
|
|
12601
13452
|
}
|
|
12602
13453
|
paths() {
|
|
12603
13454
|
const location = this.location();
|
|
12604
|
-
const dir = location.ok ? location.data.dir :
|
|
12605
|
-
const checkout =
|
|
13455
|
+
const dir = location.ok ? location.data.dir : path17.join(this.root, ".workit");
|
|
13456
|
+
const checkout = path17.join(dir, "checkouts", checkoutSlug(this.root));
|
|
12606
13457
|
return {
|
|
12607
13458
|
dir,
|
|
12608
13459
|
shared: location.ok ? location.data.shared : false,
|
|
12609
|
-
marker:
|
|
13460
|
+
marker: path17.join(dir, "store.json"),
|
|
12610
13461
|
checkout,
|
|
12611
|
-
workspace:
|
|
12612
|
-
checkoutLock:
|
|
12613
|
-
tasks:
|
|
12614
|
-
index:
|
|
12615
|
-
blobs:
|
|
12616
|
-
legacy:
|
|
12617
|
-
v2:
|
|
13462
|
+
workspace: path17.join(checkout, "workspace.json"),
|
|
13463
|
+
checkoutLock: path17.join(checkout, "metadata.lock"),
|
|
13464
|
+
tasks: path17.join(dir, "tasks"),
|
|
13465
|
+
index: path17.join(dir, "task-index.json"),
|
|
13466
|
+
blobs: path17.join(dir, "blobs", "candidates"),
|
|
13467
|
+
legacy: path17.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
13468
|
+
v2: path17.join(this.root, ".workit")
|
|
12618
13469
|
};
|
|
12619
13470
|
}
|
|
12620
13471
|
eventsPath(taskId) {
|
|
12621
|
-
return
|
|
13472
|
+
return path17.join(this.paths().tasks, taskId, "events.jsonl");
|
|
12622
13473
|
}
|
|
12623
13474
|
snapshotPath(taskId) {
|
|
12624
|
-
return
|
|
13475
|
+
return path17.join(this.paths().tasks, taskId, "snapshot.json");
|
|
12625
13476
|
}
|
|
12626
13477
|
taskLockPath(taskId) {
|
|
12627
|
-
return
|
|
13478
|
+
return path17.join(this.paths().tasks, taskId, "lock");
|
|
12628
13479
|
}
|
|
12629
13480
|
ready(write = false) {
|
|
12630
13481
|
const location = this.location();
|
|
@@ -12663,13 +13514,13 @@ class TaskStore {
|
|
|
12663
13514
|
return {
|
|
12664
13515
|
read: (digest) => {
|
|
12665
13516
|
try {
|
|
12666
|
-
return JSON.parse(fs12.readFileSync(
|
|
13517
|
+
return JSON.parse(fs12.readFileSync(path17.join(dir, `${digest}.json`), "utf8"));
|
|
12667
13518
|
} catch (error) {
|
|
12668
13519
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
12669
13520
|
}
|
|
12670
13521
|
},
|
|
12671
13522
|
write: (digest, value) => {
|
|
12672
|
-
const file =
|
|
13523
|
+
const file = path17.join(dir, `${digest}.json`);
|
|
12673
13524
|
if (fs12.existsSync(file)) {
|
|
12674
13525
|
const stamp = new Date;
|
|
12675
13526
|
try {
|
|
@@ -12861,7 +13712,7 @@ class TaskStore {
|
|
|
12861
13712
|
}
|
|
12862
13713
|
}
|
|
12863
13714
|
openTask(record, type, key, legacy = null) {
|
|
12864
|
-
const dir =
|
|
13715
|
+
const dir = path17.join(this.paths().tasks, record.id);
|
|
12865
13716
|
fs12.mkdirSync(dir, { recursive: true });
|
|
12866
13717
|
const blobs = this.blobStore();
|
|
12867
13718
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
@@ -12954,7 +13805,7 @@ class TaskStore {
|
|
|
12954
13805
|
}
|
|
12955
13806
|
withKeyLock(key, operation) {
|
|
12956
13807
|
const digest = createHash11("sha256").update(key).digest("hex").slice(0, 32);
|
|
12957
|
-
return this.withLock(
|
|
13808
|
+
return this.withLock(path17.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
12958
13809
|
}
|
|
12959
13810
|
bindLocked(taskId, key, timestamp) {
|
|
12960
13811
|
const current = this.readWorkspace();
|
|
@@ -13095,7 +13946,7 @@ class TaskStore {
|
|
|
13095
13946
|
writeWorkspace(record) {
|
|
13096
13947
|
const file = this.paths().workspace;
|
|
13097
13948
|
try {
|
|
13098
|
-
fs12.mkdirSync(
|
|
13949
|
+
fs12.mkdirSync(path17.dirname(file), { recursive: true });
|
|
13099
13950
|
replaceFile(file, `${canonicalJson(record)}
|
|
13100
13951
|
`, true);
|
|
13101
13952
|
return success(null, null, null);
|
|
@@ -13173,23 +14024,23 @@ class TaskStore {
|
|
|
13173
14024
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
13174
14025
|
}
|
|
13175
14026
|
v2Dirs() {
|
|
13176
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
14027
|
+
return [...new Set([this.root, this.origin])].map((dir) => path17.join(dir, ".workit"));
|
|
13177
14028
|
}
|
|
13178
14029
|
pendingMigrations() {
|
|
13179
14030
|
return this.v2Dirs().filter((dir) => {
|
|
13180
|
-
const raw = this.readV2Workspace(
|
|
14031
|
+
const raw = this.readV2Workspace(path17.join(dir, "workspace.json"));
|
|
13181
14032
|
return raw !== null && raw !== TOMBSTONE;
|
|
13182
14033
|
});
|
|
13183
14034
|
}
|
|
13184
14035
|
migrateV2(v2) {
|
|
13185
14036
|
const dirs = this.paths();
|
|
13186
|
-
const v2Workspace =
|
|
13187
|
-
const v2Tasks =
|
|
13188
|
-
const legacy =
|
|
14037
|
+
const v2Workspace = path17.join(v2, "workspace.json");
|
|
14038
|
+
const v2Tasks = path17.join(v2, "tasks");
|
|
14039
|
+
const legacy = path17.join(dirs.dir, "legacy", checkoutSlug(path17.dirname(v2)), "v2");
|
|
13189
14040
|
const raw = this.readV2Workspace(v2Workspace);
|
|
13190
14041
|
if (raw === null || raw === TOMBSTONE)
|
|
13191
14042
|
return success(null, null, null);
|
|
13192
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
14043
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path17.join(v2, "metadata.lock"), () => {
|
|
13193
14044
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
13194
14045
|
if (workspace === null || workspace === TOMBSTONE)
|
|
13195
14046
|
return success(null, null, null);
|
|
@@ -13198,7 +14049,7 @@ class TaskStore {
|
|
|
13198
14049
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
13199
14050
|
let files = [];
|
|
13200
14051
|
try {
|
|
13201
|
-
files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(
|
|
14052
|
+
files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path17.join(v2Tasks, name)));
|
|
13202
14053
|
} catch {}
|
|
13203
14054
|
const current = this.readWorkspace();
|
|
13204
14055
|
if (!current.ok)
|
|
@@ -13216,21 +14067,21 @@ class TaskStore {
|
|
|
13216
14067
|
let skipped = 0;
|
|
13217
14068
|
const done = new Map;
|
|
13218
14069
|
try {
|
|
13219
|
-
fs12.mkdirSync(
|
|
14070
|
+
fs12.mkdirSync(path17.join(legacy, "tasks"), { recursive: true });
|
|
13220
14071
|
for (const name of ["workspace.json", "index.json"])
|
|
13221
|
-
if (fs12.existsSync(
|
|
13222
|
-
fs12.copyFileSync(
|
|
14072
|
+
if (fs12.existsSync(path17.join(v2, name)))
|
|
14073
|
+
fs12.copyFileSync(path17.join(v2, name), path17.join(legacy, name));
|
|
13223
14074
|
for (const name of files) {
|
|
13224
14075
|
const id = name.slice(0, -5);
|
|
13225
|
-
const file =
|
|
14076
|
+
const file = path17.join(v2Tasks, name);
|
|
13226
14077
|
const bytes = fs12.readFileSync(file, "utf8");
|
|
13227
14078
|
const digest = sha256(bytes);
|
|
13228
|
-
fs12.writeFileSync(
|
|
14079
|
+
fs12.writeFileSync(path17.join(legacy, "tasks", name), bytes);
|
|
13229
14080
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
13230
14081
|
if (!parsed.ok || parsed.data.id !== id)
|
|
13231
14082
|
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
14083
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
13233
|
-
const origin = { path:
|
|
14084
|
+
const origin = { path: path17.join(legacy, "tasks", name), digest };
|
|
13234
14085
|
if (!fs12.existsSync(this.eventsPath(id))) {
|
|
13235
14086
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
13236
14087
|
migrated += 1;
|
|
@@ -13266,7 +14117,7 @@ class TaskStore {
|
|
|
13266
14117
|
try {
|
|
13267
14118
|
let complete = true;
|
|
13268
14119
|
for (const [name, digest] of done) {
|
|
13269
|
-
const file =
|
|
14120
|
+
const file = path17.join(v2Tasks, name);
|
|
13270
14121
|
if (sha256(fs12.readFileSync(file, "utf8")) !== digest) {
|
|
13271
14122
|
complete = false;
|
|
13272
14123
|
continue;
|
|
@@ -13277,7 +14128,7 @@ class TaskStore {
|
|
|
13277
14128
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
13278
14129
|
path: v2
|
|
13279
14130
|
});
|
|
13280
|
-
fs12.rmSync(
|
|
14131
|
+
fs12.rmSync(path17.join(v2, "index.json"), { force: true });
|
|
13281
14132
|
this.writeTombstone(v2, target.id);
|
|
13282
14133
|
} catch (error) {
|
|
13283
14134
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -13297,7 +14148,7 @@ class TaskStore {
|
|
|
13297
14148
|
}
|
|
13298
14149
|
adoptLocalStore() {
|
|
13299
14150
|
const dirs = this.paths();
|
|
13300
|
-
const marker =
|
|
14151
|
+
const marker = path17.join(dirs.v2, "store.json");
|
|
13301
14152
|
if (!dirs.shared || !fs12.existsSync(marker))
|
|
13302
14153
|
return success(null, null, null);
|
|
13303
14154
|
return this.withLock(dirs.checkoutLock, () => {
|
|
@@ -13307,7 +14158,7 @@ class TaskStore {
|
|
|
13307
14158
|
try {
|
|
13308
14159
|
const merge = (from, to) => {
|
|
13309
14160
|
if (!fs12.existsSync(to)) {
|
|
13310
|
-
fs12.mkdirSync(
|
|
14161
|
+
fs12.mkdirSync(path17.dirname(to), { recursive: true });
|
|
13311
14162
|
try {
|
|
13312
14163
|
fs12.renameSync(from, to);
|
|
13313
14164
|
} catch {
|
|
@@ -13318,24 +14169,24 @@ class TaskStore {
|
|
|
13318
14169
|
}
|
|
13319
14170
|
if (fs12.lstatSync(from).isDirectory() && fs12.lstatSync(to).isDirectory())
|
|
13320
14171
|
for (const name of fs12.readdirSync(from))
|
|
13321
|
-
merge(
|
|
14172
|
+
merge(path17.join(from, name), path17.join(to, name));
|
|
13322
14173
|
};
|
|
13323
14174
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
13324
|
-
const from =
|
|
14175
|
+
const from = path17.join(dirs.v2, sub);
|
|
13325
14176
|
if (!fs12.existsSync(from))
|
|
13326
14177
|
continue;
|
|
13327
14178
|
if (sub === "tasks") {
|
|
13328
14179
|
for (const name of fs12.readdirSync(from))
|
|
13329
|
-
if (validId(name) && !fs12.existsSync(
|
|
14180
|
+
if (validId(name) && !fs12.existsSync(path17.join(dirs.dir, sub, name)))
|
|
13330
14181
|
tasks += 1;
|
|
13331
14182
|
}
|
|
13332
|
-
merge(from,
|
|
14183
|
+
merge(from, path17.join(dirs.dir, sub));
|
|
13333
14184
|
}
|
|
13334
|
-
const ledger =
|
|
13335
|
-
if (fs12.existsSync(ledger) && !fs12.existsSync(
|
|
13336
|
-
merge(ledger,
|
|
13337
|
-
fs12.rmSync(
|
|
13338
|
-
fs12.renameSync(marker,
|
|
14185
|
+
const ledger = path17.join(dirs.v2, "ledger");
|
|
14186
|
+
if (fs12.existsSync(ledger) && !fs12.existsSync(path17.join(dirs.dir, "ledger")))
|
|
14187
|
+
merge(ledger, path17.join(dirs.dir, "ledger"));
|
|
14188
|
+
fs12.rmSync(path17.join(dirs.v2, "task-index.json"), { force: true });
|
|
14189
|
+
fs12.renameSync(marker, path17.join(dirs.v2, "store.moved.json"));
|
|
13339
14190
|
} catch (error) {
|
|
13340
14191
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
13341
14192
|
path: dirs.v2
|
|
@@ -13373,7 +14224,7 @@ class TaskStore {
|
|
|
13373
14224
|
schemaVersion: SCHEMA_VERSION,
|
|
13374
14225
|
id: workspaceId ?? newId(),
|
|
13375
14226
|
revision: newRevision(),
|
|
13376
|
-
root:
|
|
14227
|
+
root: path17.dirname(v2),
|
|
13377
14228
|
runtime: { createdWith: version, updatedWith: version },
|
|
13378
14229
|
writer: null,
|
|
13379
14230
|
store: {
|
|
@@ -13385,11 +14236,11 @@ class TaskStore {
|
|
|
13385
14236
|
critical: ["store"]
|
|
13386
14237
|
};
|
|
13387
14238
|
fs12.mkdirSync(v2, { recursive: true });
|
|
13388
|
-
const ignore =
|
|
14239
|
+
const ignore = path17.join(v2, ".gitignore");
|
|
13389
14240
|
if (!fs12.existsSync(ignore))
|
|
13390
14241
|
fs12.writeFileSync(ignore, `*
|
|
13391
14242
|
`);
|
|
13392
|
-
replaceFile(
|
|
14243
|
+
replaceFile(path17.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
13393
14244
|
`, true);
|
|
13394
14245
|
}
|
|
13395
14246
|
stubFor(taskId) {
|
|
@@ -13413,8 +14264,8 @@ class TaskStore {
|
|
|
13413
14264
|
}
|
|
13414
14265
|
writeTombstoneIfMissing() {
|
|
13415
14266
|
try {
|
|
13416
|
-
const v2 =
|
|
13417
|
-
if (fs12.existsSync(
|
|
14267
|
+
const v2 = path17.join(this.root, ".workit");
|
|
14268
|
+
if (fs12.existsSync(path17.join(v2, "workspace.json")))
|
|
13418
14269
|
return;
|
|
13419
14270
|
let id = null;
|
|
13420
14271
|
try {
|
|
@@ -13431,7 +14282,7 @@ class TaskStore {
|
|
|
13431
14282
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
13432
14283
|
`, false);
|
|
13433
14284
|
if (!dirs.shared) {
|
|
13434
|
-
const ignore =
|
|
14285
|
+
const ignore = path17.join(dirs.dir, ".gitignore");
|
|
13435
14286
|
let current = null;
|
|
13436
14287
|
try {
|
|
13437
14288
|
current = fs12.readFileSync(ignore, "utf8");
|
|
@@ -13491,7 +14342,7 @@ class TaskStore {
|
|
|
13491
14342
|
withLock(lockPath, operation) {
|
|
13492
14343
|
try {
|
|
13493
14344
|
this.initializeStorage();
|
|
13494
|
-
fs12.mkdirSync(
|
|
14345
|
+
fs12.mkdirSync(path17.dirname(lockPath), { recursive: true });
|
|
13495
14346
|
} catch (error) {
|
|
13496
14347
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
13497
14348
|
path: this.paths().dir
|
|
@@ -13554,7 +14405,7 @@ class TaskStore {
|
|
|
13554
14405
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
13555
14406
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
13556
14407
|
path: lockPath,
|
|
13557
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
14408
|
+
guidance: `Check permissions and the read-only attribute on ${path17.dirname(lockPath)}.`
|
|
13558
14409
|
});
|
|
13559
14410
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
13560
14411
|
let lock = null;
|
|
@@ -13612,7 +14463,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13612
14463
|
}, 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
14464
|
if (cachedRuntimeVersion === null) {
|
|
13614
14465
|
try {
|
|
13615
|
-
const pkg = JSON.parse(fs12.readFileSync(
|
|
14466
|
+
const pkg = JSON.parse(fs12.readFileSync(path17.join(packageRoot(), "package.json"), "utf8"));
|
|
13616
14467
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
13617
14468
|
} catch {
|
|
13618
14469
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -13639,7 +14490,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13639
14490
|
return "";
|
|
13640
14491
|
}
|
|
13641
14492
|
}, REFLOG_LINE, branchStart = (location, branch, aliases) => {
|
|
13642
|
-
const first = readText(
|
|
14493
|
+
const first = readText(path17.join(path17.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
13643
14494
|
`, 1)[0];
|
|
13644
14495
|
const created = REFLOG_LINE.exec(first);
|
|
13645
14496
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -13649,7 +14500,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13649
14500
|
return start;
|
|
13650
14501
|
if (!location.git)
|
|
13651
14502
|
return null;
|
|
13652
|
-
for (const line of readText(
|
|
14503
|
+
for (const line of readText(path17.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
13653
14504
|
`)) {
|
|
13654
14505
|
const entry = REFLOG_LINE.exec(line);
|
|
13655
14506
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -13658,12 +14509,12 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13658
14509
|
return moved[1];
|
|
13659
14510
|
}
|
|
13660
14511
|
return null;
|
|
13661
|
-
}, refExists = (common, ref) => fs12.existsSync(
|
|
14512
|
+
}, refExists = (common, ref) => fs12.existsSync(path17.join(common, ref)) || readText(path17.join(common, "packed-refs")).includes(` ${ref}
|
|
13662
14513
|
`), origins, sameDirectoryIdentity = (left, right) => {
|
|
13663
|
-
if (!
|
|
14514
|
+
if (!path17.isAbsolute(left) || !path17.isAbsolute(right))
|
|
13664
14515
|
return false;
|
|
13665
|
-
const normalizedLeft =
|
|
13666
|
-
const normalizedRight =
|
|
14516
|
+
const normalizedLeft = path17.resolve(left);
|
|
14517
|
+
const normalizedRight = path17.resolve(right);
|
|
13667
14518
|
try {
|
|
13668
14519
|
const a = fs12.statSync(normalizedLeft, { bigint: true });
|
|
13669
14520
|
const b = fs12.statSync(normalizedRight, { bigint: true });
|
|
@@ -13721,10 +14572,10 @@ var init_workers = __esm(() => {
|
|
|
13721
14572
|
// packages/workit-core/src/check-config.ts
|
|
13722
14573
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13723
14574
|
import fs13 from "node:fs";
|
|
13724
|
-
import
|
|
14575
|
+
import path18 from "node:path";
|
|
13725
14576
|
function checksConfigFile(root) {
|
|
13726
|
-
const current =
|
|
13727
|
-
const legacy =
|
|
14577
|
+
const current = path18.join(root, CHECKS_FILE);
|
|
14578
|
+
const legacy = path18.join(root, LEGACY_CHECKS_FILE);
|
|
13728
14579
|
return !fs13.existsSync(current) && fs13.existsSync(legacy) ? legacy : current;
|
|
13729
14580
|
}
|
|
13730
14581
|
function repoHeavyCommands(root) {
|
|
@@ -13774,12 +14625,12 @@ function splitCommand(command) {
|
|
|
13774
14625
|
return argv.length ? argv : new Error("empty command");
|
|
13775
14626
|
}
|
|
13776
14627
|
function checkRoot(cwd) {
|
|
13777
|
-
return
|
|
14628
|
+
return path18.resolve(git5(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
13778
14629
|
}
|
|
13779
14630
|
function detectedChecks(root) {
|
|
13780
14631
|
const read = (file) => {
|
|
13781
14632
|
try {
|
|
13782
|
-
return fs13.readFileSync(
|
|
14633
|
+
return fs13.readFileSync(path18.join(root, file), "utf8");
|
|
13783
14634
|
} catch {
|
|
13784
14635
|
return null;
|
|
13785
14636
|
}
|
|
@@ -13818,7 +14669,7 @@ function detectedChecks(root) {
|
|
|
13818
14669
|
function loadCheckConfig(cwd) {
|
|
13819
14670
|
const root = checkRoot(cwd);
|
|
13820
14671
|
const file = checksConfigFile(root);
|
|
13821
|
-
const legacy =
|
|
14672
|
+
const legacy = path18.basename(file) === LEGACY_CHECKS_FILE;
|
|
13822
14673
|
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
13823
14674
|
const note = legacy ? {
|
|
13824
14675
|
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
@@ -13894,10 +14745,10 @@ function workitAvailable(env = process.env) {
|
|
|
13894
14745
|
return workitOnPath;
|
|
13895
14746
|
const windows = process.platform === "win32";
|
|
13896
14747
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
13897
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
14748
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path18.delimiter).filter(Boolean);
|
|
13898
14749
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
13899
14750
|
try {
|
|
13900
|
-
return fs13.statSync(
|
|
14751
|
+
return fs13.statSync(path18.join(dir, `workit${ext}`)).isFile();
|
|
13901
14752
|
} catch {
|
|
13902
14753
|
return false;
|
|
13903
14754
|
}
|
|
@@ -13907,7 +14758,7 @@ function workitAvailable(env = process.env) {
|
|
|
13907
14758
|
function checkCommand(args, version, onPath = workitAvailable()) {
|
|
13908
14759
|
return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
|
|
13909
14760
|
}
|
|
13910
|
-
var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META,
|
|
14761
|
+
var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git5 = (cwd, args) => {
|
|
13911
14762
|
const run = spawnSync9("git", args, {
|
|
13912
14763
|
cwd,
|
|
13913
14764
|
encoding: "utf8",
|
|
@@ -13918,7 +14769,7 @@ var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "wo
|
|
|
13918
14769
|
const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
|
|
13919
14770
|
return value || null;
|
|
13920
14771
|
}, packageManager = (root) => {
|
|
13921
|
-
const has = (file) => fs13.existsSync(
|
|
14772
|
+
const has = (file) => fs13.existsSync(path18.join(root, file));
|
|
13922
14773
|
if (has("bun.lock") || has("bun.lockb"))
|
|
13923
14774
|
return "bun";
|
|
13924
14775
|
if (has("pnpm-lock.yaml"))
|
|
@@ -13944,7 +14795,7 @@ var init_check_config = __esm(() => {
|
|
|
13944
14795
|
import { createHash as createHash12 } from "node:crypto";
|
|
13945
14796
|
import * as fs14 from "node:fs";
|
|
13946
14797
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
13947
|
-
import
|
|
14798
|
+
import path19 from "node:path";
|
|
13948
14799
|
function captureCandidate(root, scope, environment = []) {
|
|
13949
14800
|
let checkout;
|
|
13950
14801
|
try {
|
|
@@ -13986,7 +14837,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13986
14837
|
for (const item of [...names].toSorted()) {
|
|
13987
14838
|
if (!scopeMatches(item, normalizedScope))
|
|
13988
14839
|
continue;
|
|
13989
|
-
const target =
|
|
14840
|
+
const target = path19.join(checkout, item);
|
|
13990
14841
|
try {
|
|
13991
14842
|
const stat = fs14.lstatSync(target);
|
|
13992
14843
|
if (stat.isSymbolicLink()) {
|
|
@@ -14395,12 +15246,12 @@ function boundTaskSummary(summary) {
|
|
|
14395
15246
|
brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
|
|
14396
15247
|
return brief;
|
|
14397
15248
|
}
|
|
14398
|
-
var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
15249
|
+
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
15250
|
if (typeof value !== "string" || !value)
|
|
14400
15251
|
return null;
|
|
14401
15252
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14402
15253
|
return null;
|
|
14403
|
-
const normalized =
|
|
15254
|
+
const normalized = path19.posix.normalize(value);
|
|
14404
15255
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14405
15256
|
return null;
|
|
14406
15257
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14442,14 +15293,14 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14442
15293
|
git: !/not a git repository/i.test(stderr)
|
|
14443
15294
|
};
|
|
14444
15295
|
}
|
|
14445
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15296
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")) : [];
|
|
14446
15297
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14447
15298
|
if (stagedDeleted.status !== 0) {
|
|
14448
15299
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14449
15300
|
if (!/not a git repository/i.test(stderr))
|
|
14450
15301
|
return { paths, uncertain: true, git: true };
|
|
14451
15302
|
} else if (stagedDeleted.stdout) {
|
|
14452
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15303
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")));
|
|
14453
15304
|
}
|
|
14454
15305
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14455
15306
|
}, walk = (root, directory, output) => {
|
|
@@ -14463,7 +15314,7 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14463
15314
|
for (const name of names) {
|
|
14464
15315
|
if (name === ".git" || name === ".workit")
|
|
14465
15316
|
continue;
|
|
14466
|
-
const target =
|
|
15317
|
+
const target = path19.join(directory, name);
|
|
14467
15318
|
let stat;
|
|
14468
15319
|
try {
|
|
14469
15320
|
stat = fs14.lstatSync(target);
|
|
@@ -14480,12 +15331,12 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14480
15331
|
}, scopeRoots = (root, scope) => {
|
|
14481
15332
|
const roots = [];
|
|
14482
15333
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14483
|
-
const target =
|
|
15334
|
+
const target = path19.resolve(root, item);
|
|
14484
15335
|
if (!inside(root, target))
|
|
14485
15336
|
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
15337
|
let ancestor = target;
|
|
14487
15338
|
while (!fs14.existsSync(ancestor) && ancestor !== root)
|
|
14488
|
-
ancestor =
|
|
15339
|
+
ancestor = path19.dirname(ancestor);
|
|
14489
15340
|
try {
|
|
14490
15341
|
if (!inside(root, fs14.realpathSync(ancestor)))
|
|
14491
15342
|
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 +15400,23 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
|
|
|
14549
15400
|
continue;
|
|
14550
15401
|
if (!reference.digest)
|
|
14551
15402
|
return failure2("invalid_input", "document references require a byte digest");
|
|
14552
|
-
const target =
|
|
14553
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15403
|
+
const target = path19.resolve(root, reference.path);
|
|
15404
|
+
if (target !== root && !target.startsWith(`${root}${path19.sep}`))
|
|
14554
15405
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14555
15406
|
fields: [
|
|
14556
15407
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
14557
15408
|
]
|
|
14558
15409
|
});
|
|
14559
15410
|
try {
|
|
14560
|
-
const below =
|
|
15411
|
+
const below = path19.relative(root, target);
|
|
14561
15412
|
let current = root;
|
|
14562
|
-
for (const segment of below.split(
|
|
15413
|
+
for (const segment of below.split(path19.sep)) {
|
|
14563
15414
|
if (!segment)
|
|
14564
15415
|
continue;
|
|
14565
|
-
current =
|
|
15416
|
+
current = path19.join(current, segment);
|
|
14566
15417
|
const stat = fs14.lstatSync(current);
|
|
14567
15418
|
const real = fs14.realpathSync(current);
|
|
14568
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15419
|
+
if (real !== root && !real.startsWith(`${root}${path19.sep}`))
|
|
14569
15420
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14570
15421
|
fields: [
|
|
14571
15422
|
{
|
|
@@ -14693,9 +15544,9 @@ Autonomy contract:
|
|
|
14693
15544
|
Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
|
|
14694
15545
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
14695
15546
|
- 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\`).
|
|
15547
|
+
self-reviewed, never verified. High risk, a focused or full lane and
|
|
15548
|
+
merge under \`verification: self|independent\` need a verifier with its
|
|
15549
|
+
own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
14699
15550
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
14700
15551
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
14701
15552
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -16566,7 +17417,7 @@ var support = (descriptor, axis) => {
|
|
|
16566
17417
|
// packages/workit-core/src/hooks/context.ts
|
|
16567
17418
|
import { createHash as createHash13 } from "node:crypto";
|
|
16568
17419
|
import fs15 from "node:fs";
|
|
16569
|
-
import
|
|
17420
|
+
import path20 from "node:path";
|
|
16570
17421
|
function sessionCompactContext(store, session, context, selection = "session-bound") {
|
|
16571
17422
|
const listed = store.listTaskIndex();
|
|
16572
17423
|
if (!listed.ok)
|
|
@@ -16601,7 +17452,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
16601
17452
|
return null;
|
|
16602
17453
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
16603
17454
|
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" ? [
|
|
17455
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
|
|
16605
17456
|
const observedFiles = filesKey(files);
|
|
16606
17457
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
16607
17458
|
if (!compact.ok || !compact.data)
|
|
@@ -16707,13 +17558,13 @@ ${options.addendum}` : ""}
|
|
|
16707
17558
|
if (location instanceof Error)
|
|
16708
17559
|
return null;
|
|
16709
17560
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
16710
|
-
return
|
|
17561
|
+
return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
16711
17562
|
} catch {
|
|
16712
17563
|
return null;
|
|
16713
17564
|
}
|
|
16714
17565
|
}, writeTurnDigest = (file, digest) => {
|
|
16715
17566
|
try {
|
|
16716
|
-
fs15.mkdirSync(
|
|
17567
|
+
fs15.mkdirSync(path20.dirname(file), { recursive: true });
|
|
16717
17568
|
fs15.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
16718
17569
|
`);
|
|
16719
17570
|
} catch {}
|
|
@@ -16726,7 +17577,7 @@ ${options.addendum}` : ""}
|
|
|
16726
17577
|
for (const name of fs15.readdirSync(dir)) {
|
|
16727
17578
|
if (!name.startsWith("turn-"))
|
|
16728
17579
|
continue;
|
|
16729
|
-
const file =
|
|
17580
|
+
const file = path20.join(dir, name);
|
|
16730
17581
|
try {
|
|
16731
17582
|
if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
16732
17583
|
fs15.rmSync(file, { force: true });
|
|
@@ -16754,7 +17605,7 @@ ${options.addendum}` : ""}
|
|
|
16754
17605
|
const file = turnMarker(input);
|
|
16755
17606
|
if (!file)
|
|
16756
17607
|
return;
|
|
16757
|
-
pruneTurnDigests(
|
|
17608
|
+
pruneTurnDigests(path20.dirname(file), Date.now());
|
|
16758
17609
|
if (text)
|
|
16759
17610
|
writeTurnDigest(file, digestOf(text));
|
|
16760
17611
|
else
|
|
@@ -16776,7 +17627,7 @@ var init_context = __esm(() => {
|
|
|
16776
17627
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
16777
17628
|
import { createHash as createHash14 } from "node:crypto";
|
|
16778
17629
|
import fs16 from "node:fs";
|
|
16779
|
-
import
|
|
17630
|
+
import path21 from "node:path";
|
|
16780
17631
|
var sessionMarkerFile = (input, session, store) => {
|
|
16781
17632
|
let dir = store;
|
|
16782
17633
|
if (dir === undefined) {
|
|
@@ -16786,7 +17637,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16786
17637
|
dir = location.dir;
|
|
16787
17638
|
}
|
|
16788
17639
|
const name = createHash14("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
16789
|
-
return
|
|
17640
|
+
return path21.join(dir, "hooks", `skills-${name}.json`);
|
|
16790
17641
|
}, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], EMPTY, readRaw = (file) => {
|
|
16791
17642
|
try {
|
|
16792
17643
|
return fs16.readFileSync(file, "utf8");
|
|
@@ -16807,7 +17658,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16807
17658
|
return { ...EMPTY };
|
|
16808
17659
|
}
|
|
16809
17660
|
}, readSessionMarker = (file) => parseMarker(readRaw(file)), writeAtomic = (file, marker) => {
|
|
16810
|
-
fs16.mkdirSync(
|
|
17661
|
+
fs16.mkdirSync(path21.dirname(file), { recursive: true });
|
|
16811
17662
|
const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
16812
17663
|
try {
|
|
16813
17664
|
fs16.writeFileSync(temp, JSON.stringify(marker));
|
|
@@ -17277,7 +18128,7 @@ import { spawnSync as spawnSync11 } from "node:child_process";
|
|
|
17277
18128
|
import { createHash as createHash15 } from "node:crypto";
|
|
17278
18129
|
import fs17 from "node:fs";
|
|
17279
18130
|
import os5 from "node:os";
|
|
17280
|
-
import
|
|
18131
|
+
import path22 from "node:path";
|
|
17281
18132
|
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
17282
18133
|
if (depth > 0)
|
|
17283
18134
|
return walk2(command, dialect, depth);
|
|
@@ -17576,7 +18427,7 @@ function noteRawCommit(input, command, key) {
|
|
|
17576
18427
|
if (action.kind !== "commit")
|
|
17577
18428
|
continue;
|
|
17578
18429
|
const file = markerFile(repo, input.session.id, key);
|
|
17579
|
-
fs17.mkdirSync(
|
|
18430
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
17580
18431
|
fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17581
18432
|
`);
|
|
17582
18433
|
}
|
|
@@ -17603,12 +18454,12 @@ function settlePendingCommit(input) {
|
|
|
17603
18454
|
settle2(input, repo, file);
|
|
17604
18455
|
} catch {}
|
|
17605
18456
|
}
|
|
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,
|
|
18457
|
+
var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLARATIONS, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, GIT_CONFIG_OPTIONS, GIT_CONFIG_ENV, ASSIGNMENT2, MAX_DEPTH = 4, MAX_EXPANSIONS = 16, isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), HERE, joinDir = (base, raw) => {
|
|
17607
18458
|
const next = raw.replace(HERE, ".");
|
|
17608
18459
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17609
18460
|
return;
|
|
17610
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
17611
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18461
|
+
const expanded = next === "~" || next.startsWith("~/") ? path22.join(os5.homedir(), next.slice(1)) : next;
|
|
18462
|
+
return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
|
|
17612
18463
|
}, rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner), cdTarget = (words) => {
|
|
17613
18464
|
const args = words.slice(1);
|
|
17614
18465
|
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
@@ -17622,7 +18473,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
17622
18473
|
const first = words[0];
|
|
17623
18474
|
if (first === undefined)
|
|
17624
18475
|
return { words, env };
|
|
17625
|
-
const assignment =
|
|
18476
|
+
const assignment = ASSIGNMENT2.exec(first);
|
|
17626
18477
|
if (assignment) {
|
|
17627
18478
|
if (GIT_CONFIG_ENV.test(assignment[1]))
|
|
17628
18479
|
env[assignment[1]] = assignment[2];
|
|
@@ -17676,10 +18527,10 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
17676
18527
|
while (rest[0]?.startsWith("-"))
|
|
17677
18528
|
rest = rest.slice(1);
|
|
17678
18529
|
}
|
|
17679
|
-
if (rest.length === 0 || !rest.every((word) =>
|
|
18530
|
+
if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
|
|
17680
18531
|
return null;
|
|
17681
18532
|
return rest.map((word) => {
|
|
17682
|
-
const [, name, value] =
|
|
18533
|
+
const [, name, value] = ASSIGNMENT2.exec(word);
|
|
17683
18534
|
return [name, expandWord(value, vars)];
|
|
17684
18535
|
});
|
|
17685
18536
|
}, literalOutput = (segment) => {
|
|
@@ -17844,7 +18695,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
17844
18695
|
return line.slice(index).join(" ").slice(15);
|
|
17845
18696
|
if (/^-S./.test(word))
|
|
17846
18697
|
return [word.slice(2), ...line.slice(index + 1)].join(" ");
|
|
17847
|
-
if (!word.startsWith("-") && !
|
|
18698
|
+
if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
|
|
17848
18699
|
return null;
|
|
17849
18700
|
}
|
|
17850
18701
|
return null;
|
|
@@ -18096,17 +18947,17 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18096
18947
|
if (!git || git.reftable)
|
|
18097
18948
|
return null;
|
|
18098
18949
|
try {
|
|
18099
|
-
const head = fs17.readFileSync(
|
|
18950
|
+
const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18100
18951
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18101
18952
|
return head;
|
|
18102
18953
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18103
18954
|
if (!ref)
|
|
18104
18955
|
return null;
|
|
18105
|
-
const common =
|
|
18956
|
+
const common = path22.dirname(repo.location.dir);
|
|
18106
18957
|
try {
|
|
18107
|
-
return fs17.readFileSync(
|
|
18958
|
+
return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
|
|
18108
18959
|
} catch {}
|
|
18109
|
-
const packed = fs17.readFileSync(
|
|
18960
|
+
const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
|
|
18110
18961
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18111
18962
|
} catch {
|
|
18112
18963
|
return null;
|
|
@@ -18172,7 +19023,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18172
19023
|
const sub = invocation.args[0];
|
|
18173
19024
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
18174
19025
|
return [invocation];
|
|
18175
|
-
const dir =
|
|
19026
|
+
const dir = path22.resolve(root, invocation.dir ?? ".");
|
|
18176
19027
|
if (!fs17.existsSync(dir))
|
|
18177
19028
|
return [invocation];
|
|
18178
19029
|
const config = gitConfig(dir, invocation, cache);
|
|
@@ -18194,11 +19045,11 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18194
19045
|
}, normalizedUrl = (url, dir) => {
|
|
18195
19046
|
const trimmed = url.trim();
|
|
18196
19047
|
if (/^file:\/\//i.test(trimmed))
|
|
18197
|
-
return
|
|
19048
|
+
return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18198
19049
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
18199
19050
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
18200
19051
|
if (!scheme && !scp)
|
|
18201
|
-
return
|
|
19052
|
+
return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18202
19053
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
18203
19054
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18204
19055
|
}, rewrittenUrl = (url, config) => {
|
|
@@ -18216,7 +19067,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18216
19067
|
return best.base + url.slice(best.prefix.length);
|
|
18217
19068
|
}
|
|
18218
19069
|
return url;
|
|
18219
|
-
},
|
|
19070
|
+
}, pushUrl2 = (config, remote, dir) => {
|
|
18220
19071
|
const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
|
|
18221
19072
|
return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
|
|
18222
19073
|
}, pushRemoteOf = (config, branch) => {
|
|
@@ -18242,7 +19093,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18242
19093
|
const pushRemote = pushRemoteOf(config, repo.branch);
|
|
18243
19094
|
if (pushRemote === null || pushRemote === remote)
|
|
18244
19095
|
return true;
|
|
18245
|
-
return
|
|
19096
|
+
return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
|
|
18246
19097
|
}, quote = (text) => `\`${text}\``, refusal = (action, branch, dir) => {
|
|
18247
19098
|
if (action.kind === "merge")
|
|
18248
19099
|
return `workit: raw ${quote(action.via === "api" ? `${action.forge} api` : action.forge === "gh" ? "gh pr merge" : "glab mr merge")}${action.via === "api" ? " merge" : ""} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
|
|
@@ -18404,7 +19255,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18404
19255
|
const out = [];
|
|
18405
19256
|
const repos = new Map;
|
|
18406
19257
|
const repoFor = (relative) => {
|
|
18407
|
-
const dir =
|
|
19258
|
+
const dir = path22.resolve(cwd, relative ?? ".");
|
|
18408
19259
|
if (!repos.has(dir)) {
|
|
18409
19260
|
const repo = repoAt(dir);
|
|
18410
19261
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18448,7 +19299,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
18448
19299
|
kind: "deny",
|
|
18449
19300
|
reason: `workit: the shell hook could not analyze this command (${error.message}), so it does not run. ${SPLIT_IT}`,
|
|
18450
19301
|
unblock: SPLIT_IT
|
|
18451
|
-
}), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) =>
|
|
19302
|
+
}), 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
19303
|
const run = spawnSync11("git", [
|
|
18453
19304
|
"log",
|
|
18454
19305
|
"-1",
|
|
@@ -18554,7 +19405,7 @@ var init_raw_git = __esm(() => {
|
|
|
18554
19405
|
]);
|
|
18555
19406
|
GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
|
|
18556
19407
|
GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
18557
|
-
|
|
19408
|
+
ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
18558
19409
|
HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
18559
19410
|
VARIABLE = /\$(?:\{(\w+)\}|(\w+))/g;
|
|
18560
19411
|
XARGS_VALUES = new Set(["-n", "-L", "-l", "-P", "-d", "-E", "-e", "-s", "-a", "--arg-file", "--delimiter"].concat([
|
|
@@ -18647,8 +19498,8 @@ var init_raw_git = __esm(() => {
|
|
|
18647
19498
|
|
|
18648
19499
|
// packages/workit-core/src/core/route-intent.ts
|
|
18649
19500
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18650
|
-
import { existsSync as
|
|
18651
|
-
import
|
|
19501
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
19502
|
+
import path23 from "node:path";
|
|
18652
19503
|
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
19504
|
const shorts = shortsOf(flags);
|
|
18654
19505
|
for (let index = 1;index < args.length; index++) {
|
|
@@ -18774,8 +19625,8 @@ var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, SUBTREE_CREATE,
|
|
|
18774
19625
|
return null;
|
|
18775
19626
|
if (dir === null)
|
|
18776
19627
|
return root;
|
|
18777
|
-
const resolved =
|
|
18778
|
-
return
|
|
19628
|
+
const resolved = path23.resolve(root, dir);
|
|
19629
|
+
return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18779
19630
|
}, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18780
19631
|
let judged = false;
|
|
18781
19632
|
const cache = new Map;
|
|
@@ -18829,7 +19680,7 @@ var init_policy = __esm(() => {
|
|
|
18829
19680
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
18830
19681
|
import fs18 from "node:fs";
|
|
18831
19682
|
import os6 from "node:os";
|
|
18832
|
-
import
|
|
19683
|
+
import path24 from "node:path";
|
|
18833
19684
|
function parseJsonc(text) {
|
|
18834
19685
|
let out = "";
|
|
18835
19686
|
for (let index = 0;index < text.length; index += 1) {
|
|
@@ -18859,21 +19710,21 @@ function parseJsonc(text) {
|
|
|
18859
19710
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
18860
19711
|
}
|
|
18861
19712
|
function gitTop(cwd) {
|
|
18862
|
-
for (let dir =
|
|
18863
|
-
if (fs18.existsSync(
|
|
19713
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
19714
|
+
if (fs18.existsSync(path24.join(dir, ".git")))
|
|
18864
19715
|
return dir;
|
|
18865
|
-
if (
|
|
19716
|
+
if (path24.dirname(dir) === dir)
|
|
18866
19717
|
return null;
|
|
18867
19718
|
}
|
|
18868
19719
|
}
|
|
18869
19720
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
18870
19721
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
18871
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
19722
|
+
const user = env.CLAUDE_CONFIG_DIR || path24.join(homeOf(env), ".claude");
|
|
18872
19723
|
return [
|
|
18873
19724
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
18874
|
-
|
|
18875
|
-
|
|
18876
|
-
|
|
19725
|
+
path24.join(user, "settings.json"),
|
|
19726
|
+
path24.join(project, ".claude", "settings.json"),
|
|
19727
|
+
path24.join(project, ".claude", "settings.local.json")
|
|
18877
19728
|
];
|
|
18878
19729
|
}
|
|
18879
19730
|
function claudeRules(cwd, env = process.env) {
|
|
@@ -18918,10 +19769,10 @@ function claudeRules(cwd, env = process.env) {
|
|
|
18918
19769
|
function opencodeRules(cwd, env = process.env) {
|
|
18919
19770
|
const rules = EMPTY2();
|
|
18920
19771
|
try {
|
|
18921
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
19772
|
+
const config = env.XDG_CONFIG_HOME || path24.join(homeOf(env), ".config");
|
|
18922
19773
|
const files = [
|
|
18923
|
-
...OPENCODE_NAMES.map((name) =>
|
|
18924
|
-
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [
|
|
19774
|
+
...OPENCODE_NAMES.map((name) => path24.join(config, "opencode", name)),
|
|
19775
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path24.join(dir, name), path24.join(dir, ".opencode", name)])),
|
|
18925
19776
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
18926
19777
|
];
|
|
18927
19778
|
for (const file of files) {
|
|
@@ -18939,9 +19790,9 @@ function opencodeRules(cwd, env = process.env) {
|
|
|
18939
19790
|
function codexRules(cwd, env = process.env) {
|
|
18940
19791
|
const rules = EMPTY2();
|
|
18941
19792
|
try {
|
|
18942
|
-
const home = env.CODEX_HOME ||
|
|
19793
|
+
const home = env.CODEX_HOME || path24.join(homeOf(env), ".codex");
|
|
18943
19794
|
const top = gitTop(cwd) ?? cwd;
|
|
18944
|
-
for (const dir of [
|
|
19795
|
+
for (const dir of [path24.join(home, "rules"), path24.join(top, ".codex", "rules")]) {
|
|
18945
19796
|
let files;
|
|
18946
19797
|
try {
|
|
18947
19798
|
files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -18951,7 +19802,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
18951
19802
|
return "unreadable";
|
|
18952
19803
|
}
|
|
18953
19804
|
for (const name of files) {
|
|
18954
|
-
const text = readText2(
|
|
19805
|
+
const text = readText2(path24.join(dir, name)) ?? "";
|
|
18955
19806
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
18956
19807
|
const rule = codexRule(part);
|
|
18957
19808
|
if (!rule)
|
|
@@ -18970,8 +19821,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
18970
19821
|
try {
|
|
18971
19822
|
const top = gitTop(cwd) ?? cwd;
|
|
18972
19823
|
for (const file of [
|
|
18973
|
-
|
|
18974
|
-
|
|
19824
|
+
path24.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
19825
|
+
path24.join(top, ".cursor", "cli.json")
|
|
18975
19826
|
]) {
|
|
18976
19827
|
const text = readText2(file);
|
|
18977
19828
|
if (text === null)
|
|
@@ -19033,9 +19884,9 @@ var EMPTY2 = () => ({ deny: [], allow: [] }), readText2 = (file) => {
|
|
|
19033
19884
|
}, isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), projectDirs = (cwd) => {
|
|
19034
19885
|
const top = gitTop(cwd);
|
|
19035
19886
|
const dirs = [];
|
|
19036
|
-
for (let dir =
|
|
19887
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
19037
19888
|
dirs.push(dir);
|
|
19038
|
-
if (!top || dir === top ||
|
|
19889
|
+
if (!top || dir === top || path24.dirname(dir) === dir)
|
|
19039
19890
|
return dirs;
|
|
19040
19891
|
}
|
|
19041
19892
|
}, homeOf = (env) => env.HOME || os6.homedir(), CLAUDE_MANAGED, bashPattern = (entry) => {
|
|
@@ -19133,18 +19984,18 @@ var init_heavy_rules = __esm(() => {
|
|
|
19133
19984
|
// packages/workit-core/src/core/heavy.ts
|
|
19134
19985
|
import fs19 from "node:fs";
|
|
19135
19986
|
import os7 from "node:os";
|
|
19136
|
-
import
|
|
19987
|
+
import path25 from "node:path";
|
|
19137
19988
|
function stateDir(env, platform = process.platform) {
|
|
19138
19989
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19139
19990
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19140
19991
|
if (env.XDG_STATE_HOME)
|
|
19141
|
-
return
|
|
19992
|
+
return path25.join(env.XDG_STATE_HOME, "workit");
|
|
19142
19993
|
const home = env.HOME || os7.homedir();
|
|
19143
19994
|
if (platform === "darwin")
|
|
19144
|
-
return
|
|
19995
|
+
return path25.join(home, "Library", "Application Support", "workit");
|
|
19145
19996
|
if (platform === "win32")
|
|
19146
|
-
return
|
|
19147
|
-
return
|
|
19997
|
+
return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
|
|
19998
|
+
return path25.join(home, ".local", "state", "workit");
|
|
19148
19999
|
}
|
|
19149
20000
|
function positional2(words, from, values = PM_VALUES) {
|
|
19150
20001
|
let index = from;
|
|
@@ -19284,7 +20135,7 @@ function machinePatterns(env) {
|
|
|
19284
20135
|
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
19285
20136
|
}
|
|
19286
20137
|
function repoPatterns(cwd) {
|
|
19287
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
20138
|
+
return repoHeavyCommands(gitTop(cwd) ?? path25.resolve(cwd));
|
|
19288
20139
|
}
|
|
19289
20140
|
function heavyLines(lines, options) {
|
|
19290
20141
|
const found = [];
|
|
@@ -19353,12 +20204,12 @@ function heavyPlan(command, options) {
|
|
|
19353
20204
|
}
|
|
19354
20205
|
function workitOnPath2(env = process.env) {
|
|
19355
20206
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
19356
|
-
for (const dir of (env.PATH ?? "").split(
|
|
20207
|
+
for (const dir of (env.PATH ?? "").split(path25.delimiter))
|
|
19357
20208
|
for (const name of names) {
|
|
19358
20209
|
if (!dir)
|
|
19359
20210
|
continue;
|
|
19360
20211
|
try {
|
|
19361
|
-
fs19.accessSync(
|
|
20212
|
+
fs19.accessSync(path25.join(dir, name), fs19.constants.X_OK);
|
|
19362
20213
|
return true;
|
|
19363
20214
|
} catch {}
|
|
19364
20215
|
}
|
|
@@ -19372,7 +20223,7 @@ function leaseRewrite(command) {
|
|
|
19372
20223
|
return null;
|
|
19373
20224
|
return `${prefix}workit check --heavy -- ${rest}`;
|
|
19374
20225
|
}
|
|
19375
|
-
var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) =>
|
|
20226
|
+
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
20227
|
try {
|
|
19377
20228
|
return JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
19378
20229
|
} catch {
|
|
@@ -19518,7 +20369,7 @@ var init_heavy = __esm(() => {
|
|
|
19518
20369
|
// packages/workit-core/src/core/heavy-words.ts
|
|
19519
20370
|
import fs20 from "node:fs";
|
|
19520
20371
|
import os8 from "node:os";
|
|
19521
|
-
import
|
|
20372
|
+
import path26 from "node:path";
|
|
19522
20373
|
function maybeHeavy(command, cwd, env = process.env) {
|
|
19523
20374
|
try {
|
|
19524
20375
|
const words = wordsOf(command);
|
|
@@ -19534,13 +20385,13 @@ var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((w
|
|
|
19534
20385
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19535
20386
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19536
20387
|
if (env.XDG_STATE_HOME)
|
|
19537
|
-
return
|
|
20388
|
+
return path26.join(env.XDG_STATE_HOME, "workit");
|
|
19538
20389
|
const home = env.HOME || os8.homedir();
|
|
19539
20390
|
if (process.platform === "darwin")
|
|
19540
|
-
return
|
|
20391
|
+
return path26.join(home, "Library", "Application Support", "workit");
|
|
19541
20392
|
if (process.platform === "win32")
|
|
19542
|
-
return
|
|
19543
|
-
return
|
|
20393
|
+
return path26.join(env.LOCALAPPDATA ?? path26.join(home, "AppData", "Local"), "workit");
|
|
20394
|
+
return path26.join(home, ".local", "state", "workit");
|
|
19544
20395
|
}, readJson2 = (file) => {
|
|
19545
20396
|
try {
|
|
19546
20397
|
return JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
@@ -19549,15 +20400,15 @@ var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((w
|
|
|
19549
20400
|
}
|
|
19550
20401
|
}, patternWords = (cwd, env) => {
|
|
19551
20402
|
const patterns = [];
|
|
19552
|
-
const machine = readJson2(
|
|
20403
|
+
const machine = readJson2(path26.join(stateDir2(env), "heavy-commands.json"));
|
|
19553
20404
|
if (Array.isArray(machine?.patterns))
|
|
19554
20405
|
patterns.push(...machine.patterns);
|
|
19555
|
-
for (let dir =
|
|
19556
|
-
if (fs20.existsSync(
|
|
20406
|
+
for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
|
|
20407
|
+
if (fs20.existsSync(path26.join(dir, ".git"))) {
|
|
19557
20408
|
patterns.push(...repoHeavyCommands(dir));
|
|
19558
20409
|
break;
|
|
19559
20410
|
}
|
|
19560
|
-
if (
|
|
20411
|
+
if (path26.dirname(dir) === dir)
|
|
19561
20412
|
break;
|
|
19562
20413
|
}
|
|
19563
20414
|
const words = new Set;
|
|
@@ -19627,7 +20478,7 @@ import { spawnSync as spawnSync13 } from "node:child_process";
|
|
|
19627
20478
|
import { createHash as createHash16 } from "node:crypto";
|
|
19628
20479
|
import fs21 from "node:fs";
|
|
19629
20480
|
import os9 from "node:os";
|
|
19630
|
-
import
|
|
20481
|
+
import path27 from "node:path";
|
|
19631
20482
|
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
20483
|
try {
|
|
19633
20484
|
process.kill(pid, 0);
|
|
@@ -19685,15 +20536,15 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
19685
20536
|
pid: () => hostPid(transport)
|
|
19686
20537
|
};
|
|
19687
20538
|
}, checkoutOf = (target) => {
|
|
19688
|
-
let dir =
|
|
19689
|
-
while (!fs21.existsSync(dir) &&
|
|
19690
|
-
dir =
|
|
20539
|
+
let dir = path27.resolve(target);
|
|
20540
|
+
while (!fs21.existsSync(dir) && path27.dirname(dir) !== dir)
|
|
20541
|
+
dir = path27.dirname(dir);
|
|
19691
20542
|
if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
|
|
19692
|
-
dir =
|
|
19693
|
-
const parts = dir.split(
|
|
20543
|
+
dir = path27.dirname(dir);
|
|
20544
|
+
const parts = dir.split(path27.sep);
|
|
19694
20545
|
const dotGit = parts.lastIndexOf(".git");
|
|
19695
20546
|
if (dotGit > 0)
|
|
19696
|
-
dir = parts.slice(0, dotGit).join(
|
|
20547
|
+
dir = parts.slice(0, dotGit).join(path27.sep) || path27.sep;
|
|
19697
20548
|
const location = resolveStore(dir);
|
|
19698
20549
|
if (location instanceof Error || !location.shared || !location.top)
|
|
19699
20550
|
return null;
|
|
@@ -19702,8 +20553,8 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
19702
20553
|
top = fs21.realpathSync.native(top);
|
|
19703
20554
|
} catch {}
|
|
19704
20555
|
return top;
|
|
19705
|
-
}, markerFile2 = (top) =>
|
|
19706
|
-
let key =
|
|
20556
|
+
}, markerFile2 = (top) => path27.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
|
|
20557
|
+
let key = path27.resolve(top);
|
|
19707
20558
|
try {
|
|
19708
20559
|
key = fs21.realpathSync.native(key);
|
|
19709
20560
|
} catch {}
|
|
@@ -19791,7 +20642,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
19791
20642
|
checkout,
|
|
19792
20643
|
heartbeatAt: now.toISOString()
|
|
19793
20644
|
};
|
|
19794
|
-
fs21.mkdirSync(
|
|
20645
|
+
fs21.mkdirSync(path27.dirname(file), { recursive: true });
|
|
19795
20646
|
const temp = `${file}.${process.pid}.tmp`;
|
|
19796
20647
|
fs21.writeFileSync(temp, JSON.stringify(marker));
|
|
19797
20648
|
fs21.renameSync(temp, file);
|
|
@@ -19812,9 +20663,9 @@ var init_live_session = __esm(() => {
|
|
|
19812
20663
|
});
|
|
19813
20664
|
|
|
19814
20665
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19815
|
-
import { existsSync as
|
|
20666
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
19816
20667
|
import os10 from "node:os";
|
|
19817
|
-
import
|
|
20668
|
+
import path28 from "node:path";
|
|
19818
20669
|
function writeGate(cwd, paths, owner, deps) {
|
|
19819
20670
|
const forged = paths?.length ? issuedTarget(cwd, paths) : null;
|
|
19820
20671
|
if (forged)
|
|
@@ -19872,21 +20723,21 @@ function beforeWriteGate(cwd, paths) {
|
|
|
19872
20723
|
}
|
|
19873
20724
|
var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
19874
20725
|
let rest = "";
|
|
19875
|
-
for (let dir =
|
|
20726
|
+
for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
|
|
19876
20727
|
try {
|
|
19877
|
-
return
|
|
20728
|
+
return path28.join(realpathSync5.native(dir), rest);
|
|
19878
20729
|
} catch {
|
|
19879
|
-
if (
|
|
19880
|
-
return
|
|
19881
|
-
rest = rest ?
|
|
20730
|
+
if (path28.dirname(dir) === dir)
|
|
20731
|
+
return path28.resolve(file);
|
|
20732
|
+
rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
|
|
19882
20733
|
}
|
|
19883
20734
|
}
|
|
19884
20735
|
}, exempt = (cwd, root, file, plans) => {
|
|
19885
20736
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
19886
|
-
const relative =
|
|
19887
|
-
if (relative.startsWith("..") ||
|
|
20737
|
+
const relative = path28.relative(fold(canonical2(root)), fold(canonical2(path28.resolve(cwd, file))));
|
|
20738
|
+
if (relative.startsWith("..") || path28.isAbsolute(relative))
|
|
19888
20739
|
return true;
|
|
19889
|
-
const posix = relative.split(
|
|
20740
|
+
const posix = relative.split(path28.sep).join("/");
|
|
19890
20741
|
const segments = posix.split("/");
|
|
19891
20742
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
19892
20743
|
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), SCRIPT_SHELLS, wrappedScript = (input) => {
|
|
@@ -20053,10 +20904,10 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20053
20904
|
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
20054
20905
|
const target = dest === undefined ? null : at(dest);
|
|
20055
20906
|
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
20056
|
-
const base = target === null ? null : into >= 0 || sources.length > 1 ||
|
|
20907
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path28.dirname(target);
|
|
20057
20908
|
return sources.flatMap((source) => {
|
|
20058
|
-
if (symbolic && base !== null && !
|
|
20059
|
-
return [
|
|
20909
|
+
if (symbolic && base !== null && !path28.isAbsolute(homeExpanded(source)))
|
|
20910
|
+
return [path28.resolve(base, source)];
|
|
20060
20911
|
const resolved = at(source);
|
|
20061
20912
|
return resolved ? [resolved] : [];
|
|
20062
20913
|
});
|
|
@@ -20082,7 +20933,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20082
20933
|
if (!unknown && targets.length === 0)
|
|
20083
20934
|
return { writes: false, targets: null };
|
|
20084
20935
|
return { writes: true, targets: unknown ? null : targets };
|
|
20085
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20936
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path28.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
20086
20937
|
const store = new TaskStore(cwd);
|
|
20087
20938
|
const listed = store.listTaskIndex();
|
|
20088
20939
|
if (!listed.ok)
|
|
@@ -20135,7 +20986,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20135
20986
|
return [];
|
|
20136
20987
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20137
20988
|
return [];
|
|
20138
|
-
return [
|
|
20989
|
+
return [path28.resolve(cwd, invocation.dir ?? ".")];
|
|
20139
20990
|
});
|
|
20140
20991
|
}, CD2, PUSHD2, POPD2, UNKNOWN_DIR, cdInto = (dir, words, strict = false) => {
|
|
20141
20992
|
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
@@ -20145,7 +20996,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20145
20996
|
return lost;
|
|
20146
20997
|
if (dir === undefined)
|
|
20147
20998
|
return lost;
|
|
20148
|
-
return target === undefined ? os10.homedir() :
|
|
20999
|
+
return target === undefined ? os10.homedir() : path28.resolve(dir, target);
|
|
20149
21000
|
}, quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, homeExpanded = (file) => {
|
|
20150
21001
|
const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
|
|
20151
21002
|
return match ? (process.env.HOME || os10.homedir()) + file.slice(match[0].length) : file;
|
|
@@ -20156,7 +21007,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20156
21007
|
const groups = [];
|
|
20157
21008
|
const at = (file) => {
|
|
20158
21009
|
const home = homeExpanded(file);
|
|
20159
|
-
return
|
|
21010
|
+
return path28.isAbsolute(home) ? home : dir === undefined ? null : path28.resolve(dir, home);
|
|
20160
21011
|
};
|
|
20161
21012
|
for (const segment of segmentsOf(command, dialect)) {
|
|
20162
21013
|
if (segment.group === "open") {
|
|
@@ -20196,7 +21047,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20196
21047
|
for (const file of opaque ?? []) {
|
|
20197
21048
|
const resolved = at(file);
|
|
20198
21049
|
if (resolved)
|
|
20199
|
-
targets.push(file === "." ?
|
|
21050
|
+
targets.push(file === "." ? path28.join(resolved, DIR_TARGET) : resolved);
|
|
20200
21051
|
}
|
|
20201
21052
|
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
20202
21053
|
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
@@ -20211,7 +21062,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20211
21062
|
const edits = editsOf(segment.words);
|
|
20212
21063
|
if (edits === null) {
|
|
20213
21064
|
if (dir !== undefined)
|
|
20214
|
-
targets.push(
|
|
21065
|
+
targets.push(path28.join(dir, DIR_TARGET));
|
|
20215
21066
|
} else
|
|
20216
21067
|
for (const file of edits) {
|
|
20217
21068
|
const resolved = at(file);
|
|
@@ -20222,18 +21073,18 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20222
21073
|
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
20223
21074
|
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
20224
21075
|
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
20225
|
-
targets.push(
|
|
21076
|
+
targets.push(path28.join(runsIn, DIR_TARGET));
|
|
20226
21077
|
}
|
|
20227
21078
|
}
|
|
20228
21079
|
return targets;
|
|
20229
21080
|
}, workitCwd = (dir, args) => {
|
|
20230
21081
|
const { rest, cwd } = skipWorkitGlobals(args);
|
|
20231
|
-
return { at: cwd === null ? dir :
|
|
21082
|
+
return { at: cwd === null ? dir : path28.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
20232
21083
|
}, shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
20233
21084
|
try {
|
|
20234
21085
|
return [
|
|
20235
21086
|
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
20236
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
21087
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path28.join(dir, DIR_TARGET))
|
|
20237
21088
|
];
|
|
20238
21089
|
} catch {
|
|
20239
21090
|
return [];
|
|
@@ -20258,7 +21109,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20258
21109
|
}, guarded = (targets) => {
|
|
20259
21110
|
const seen = new Set;
|
|
20260
21111
|
return targets.filter((target) => {
|
|
20261
|
-
const dir =
|
|
21112
|
+
const dir = path28.dirname(target);
|
|
20262
21113
|
if (seen.has(dir))
|
|
20263
21114
|
return false;
|
|
20264
21115
|
seen.add(dir);
|
|
@@ -20276,13 +21127,13 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20276
21127
|
}, claimAll = (owner, targets, deps) => {
|
|
20277
21128
|
for (const target of guarded(targets))
|
|
20278
21129
|
claimLiveSession(target, owner, deps);
|
|
20279
|
-
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
21130
|
+
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path28.resolve(cwd, file)) : [path28.join(cwd, DIR_TARGET)], issuedTarget = (cwd, targets) => {
|
|
20280
21131
|
const state = resolveStateDir();
|
|
20281
21132
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
20282
21133
|
return found ?? null;
|
|
20283
21134
|
}, underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
20284
21135
|
const win = platform === "win32";
|
|
20285
|
-
const p = win ?
|
|
21136
|
+
const p = win ? path28.win32 : path28.posix;
|
|
20286
21137
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
20287
21138
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
20288
21139
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -20353,7 +21204,7 @@ var init_write_gate = __esm(() => {
|
|
|
20353
21204
|
CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20354
21205
|
PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20355
21206
|
POPD2 = new Set(["popd", "pop-location"]);
|
|
20356
|
-
UNKNOWN_DIR =
|
|
21207
|
+
UNKNOWN_DIR = path28.resolve(path28.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
20357
21208
|
HOST_LABELS = {
|
|
20358
21209
|
claude_code: "Claude Code",
|
|
20359
21210
|
opencode: "OpenCode",
|
|
@@ -20364,6 +21215,26 @@ var init_write_gate = __esm(() => {
|
|
|
20364
21215
|
};
|
|
20365
21216
|
});
|
|
20366
21217
|
|
|
21218
|
+
// packages/workit-core/src/core/policy/current-judgment.ts
|
|
21219
|
+
function currentJudgment(cwd) {
|
|
21220
|
+
try {
|
|
21221
|
+
const store = new TaskStore(cwd);
|
|
21222
|
+
const listed = store.listTaskIndex();
|
|
21223
|
+
const key = store.currentKey();
|
|
21224
|
+
if (!listed.ok || !key.ok)
|
|
21225
|
+
return null;
|
|
21226
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
21227
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
21228
|
+
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
21229
|
+
} catch {
|
|
21230
|
+
return null;
|
|
21231
|
+
}
|
|
21232
|
+
}
|
|
21233
|
+
var init_current_judgment = __esm(() => {
|
|
21234
|
+
init_task_store();
|
|
21235
|
+
init_derive();
|
|
21236
|
+
});
|
|
21237
|
+
|
|
20367
21238
|
// packages/workit-core/src/hooks/stop.ts
|
|
20368
21239
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20369
21240
|
function stopObligation(input, descriptor) {
|
|
@@ -20402,7 +21273,7 @@ function stopObligation(input, descriptor) {
|
|
|
20402
21273
|
}
|
|
20403
21274
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
20404
21275
|
return null;
|
|
20405
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
21276
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
20406
21277
|
return left ? { ...left, head } : null;
|
|
20407
21278
|
} catch {
|
|
20408
21279
|
return null;
|
|
@@ -20505,28 +21376,31 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
20505
21376
|
kind: "checks",
|
|
20506
21377
|
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
21378
|
};
|
|
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) => {
|
|
21379
|
+
}, prBase = (rows) => {
|
|
21380
|
+
const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
|
|
21381
|
+
return typeof seen?.base === "string" ? seen.base : null;
|
|
21382
|
+
}, unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
20522
21383
|
const check = checkVerdicts(cwd, branch, all);
|
|
20523
21384
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
20524
21385
|
return null;
|
|
20525
|
-
const judgment =
|
|
21386
|
+
const judgment = currentJudgment(cwd);
|
|
20526
21387
|
const high = judgment?.riskTier === "high";
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
21388
|
+
let independent;
|
|
21389
|
+
if (verification === "proportional") {
|
|
21390
|
+
const lane = branchLane(cwd, {
|
|
21391
|
+
base: prBase(all.filter((row) => row.branch === branch)),
|
|
21392
|
+
head,
|
|
21393
|
+
judgment
|
|
21394
|
+
});
|
|
21395
|
+
const need = laneNeed(lane.lane);
|
|
21396
|
+
if (need === "none")
|
|
21397
|
+
return null;
|
|
21398
|
+
independent = need === "non-author";
|
|
21399
|
+
} else {
|
|
21400
|
+
if (judgment && !judgment.behaviorChange && !high)
|
|
21401
|
+
return null;
|
|
21402
|
+
independent = verification === "independent" || high;
|
|
21403
|
+
}
|
|
20530
21404
|
if (!independent && check.review === "self-reviewed")
|
|
20531
21405
|
return null;
|
|
20532
21406
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -20539,8 +21413,8 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
20539
21413
|
};
|
|
20540
21414
|
var init_stop = __esm(() => {
|
|
20541
21415
|
init_autonomy();
|
|
20542
|
-
|
|
20543
|
-
|
|
21416
|
+
init_current_judgment();
|
|
21417
|
+
init_lane();
|
|
20544
21418
|
init_rev();
|
|
20545
21419
|
init_ledger();
|
|
20546
21420
|
init_raw_git();
|
|
@@ -20781,10 +21655,10 @@ var init_skill_nudge = __esm(() => {
|
|
|
20781
21655
|
});
|
|
20782
21656
|
|
|
20783
21657
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20784
|
-
import { existsSync as
|
|
20785
|
-
import
|
|
21658
|
+
import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21659
|
+
import path29 from "node:path";
|
|
20786
21660
|
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) || !
|
|
21661
|
+
if (!nonEmpty2(value) || !path29.isAbsolute(value) || !existsSync9(value))
|
|
20788
21662
|
return null;
|
|
20789
21663
|
try {
|
|
20790
21664
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20850,9 +21724,9 @@ var init_fields = __esm(() => {
|
|
|
20850
21724
|
|
|
20851
21725
|
// packages/workit-core/src/hooks/standing.ts
|
|
20852
21726
|
import fs22 from "node:fs";
|
|
20853
|
-
import
|
|
21727
|
+
import path30 from "node:path";
|
|
20854
21728
|
var readPlans = (store) => {
|
|
20855
|
-
const dir =
|
|
21729
|
+
const dir = path30.join(store, "fanouts");
|
|
20856
21730
|
let names;
|
|
20857
21731
|
try {
|
|
20858
21732
|
names = fs22.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -20862,7 +21736,7 @@ var readPlans = (store) => {
|
|
|
20862
21736
|
const plans = [];
|
|
20863
21737
|
for (const name of names) {
|
|
20864
21738
|
try {
|
|
20865
|
-
const raw = JSON.parse(fs22.readFileSync(
|
|
21739
|
+
const raw = JSON.parse(fs22.readFileSync(path30.join(dir, name), "utf8"));
|
|
20866
21740
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
20867
21741
|
continue;
|
|
20868
21742
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -20988,12 +21862,12 @@ var init_cwd_normalize = __esm(() => {
|
|
|
20988
21862
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
20989
21863
|
import { createHash as createHash17 } from "node:crypto";
|
|
20990
21864
|
import fs23 from "node:fs";
|
|
20991
|
-
import
|
|
21865
|
+
import path31 from "node:path";
|
|
20992
21866
|
function confineImplementer(input, targets) {
|
|
20993
21867
|
try {
|
|
20994
21868
|
if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
|
|
20995
21869
|
return NONE5;
|
|
20996
|
-
if (targets.some((target) => within(
|
|
21870
|
+
if (targets.some((target) => within(path31.resolve(target), UNKNOWN_DIR)))
|
|
20997
21871
|
return unknownDirDeny(input, boundWorktree(input));
|
|
20998
21872
|
const bindings = canonical3(bindingDir());
|
|
20999
21873
|
const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
|
|
@@ -21028,32 +21902,32 @@ function confineImplementerShell(input, command, dialect = "posix") {
|
|
|
21028
21902
|
}
|
|
21029
21903
|
var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonical3 = (file) => {
|
|
21030
21904
|
let rest = "";
|
|
21031
|
-
for (let dir =
|
|
21905
|
+
for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
|
|
21032
21906
|
try {
|
|
21033
|
-
return
|
|
21907
|
+
return path31.join(fs23.realpathSync.native(dir), rest);
|
|
21034
21908
|
} catch {
|
|
21035
|
-
if (
|
|
21036
|
-
return
|
|
21037
|
-
rest = rest ?
|
|
21909
|
+
if (path31.dirname(dir) === dir)
|
|
21910
|
+
return path31.resolve(file);
|
|
21911
|
+
rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
|
|
21038
21912
|
}
|
|
21039
21913
|
}
|
|
21040
21914
|
}, within = (child, parent) => {
|
|
21041
21915
|
const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
|
|
21042
|
-
const relative =
|
|
21043
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
21916
|
+
const relative = path31.relative(fold(parent), fold(child));
|
|
21917
|
+
return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
|
|
21044
21918
|
}, checkoutOf2 = (file) => {
|
|
21045
|
-
for (let dir = canonical3(file);; dir =
|
|
21046
|
-
const dotGit =
|
|
21919
|
+
for (let dir = canonical3(file);; dir = path31.dirname(dir)) {
|
|
21920
|
+
const dotGit = path31.join(dir, ".git");
|
|
21047
21921
|
if (fs23.existsSync(dotGit)) {
|
|
21048
21922
|
if (!fs23.statSync(dotGit).isFile())
|
|
21049
21923
|
return { root: dir, workit: false };
|
|
21050
21924
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21051
21925
|
return {
|
|
21052
21926
|
root: dir,
|
|
21053
|
-
workit: Boolean(admin) && fs23.existsSync(
|
|
21927
|
+
workit: Boolean(admin) && fs23.existsSync(path31.join(path31.resolve(dir, admin), FANOUT_TOKEN_FILE))
|
|
21054
21928
|
};
|
|
21055
21929
|
}
|
|
21056
|
-
if (
|
|
21930
|
+
if (path31.dirname(dir) === dir)
|
|
21057
21931
|
return null;
|
|
21058
21932
|
}
|
|
21059
21933
|
}, workitWorktreeOf = (file) => {
|
|
@@ -21063,7 +21937,7 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21063
21937
|
} catch {
|
|
21064
21938
|
return null;
|
|
21065
21939
|
}
|
|
21066
|
-
}, bindingDir = () =>
|
|
21940
|
+
}, 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
21941
|
try {
|
|
21068
21942
|
const value = JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
21069
21943
|
return typeof value.worktree === "string" ? value : null;
|
|
@@ -21085,7 +21959,7 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21085
21959
|
agentId,
|
|
21086
21960
|
at: new Date().toISOString()
|
|
21087
21961
|
};
|
|
21088
|
-
fs23.mkdirSync(
|
|
21962
|
+
fs23.mkdirSync(path31.dirname(file), { recursive: true });
|
|
21089
21963
|
fs23.writeFileSync(file, JSON.stringify(binding));
|
|
21090
21964
|
try {
|
|
21091
21965
|
if (inWorkitWorkspace(input.cwd))
|
|
@@ -21103,24 +21977,24 @@ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonic
|
|
|
21103
21977
|
const checkout = checkoutOf2(cwd);
|
|
21104
21978
|
if (!checkout)
|
|
21105
21979
|
return false;
|
|
21106
|
-
const dotGit =
|
|
21980
|
+
const dotGit = path31.join(checkout.root, ".git");
|
|
21107
21981
|
if (!fs23.statSync(dotGit).isFile())
|
|
21108
21982
|
return false;
|
|
21109
21983
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21110
21984
|
if (!admin)
|
|
21111
21985
|
return false;
|
|
21112
|
-
const adminDir =
|
|
21113
|
-
const back = fs23.readFileSync(
|
|
21114
|
-
if (
|
|
21986
|
+
const adminDir = path31.resolve(checkout.root, admin);
|
|
21987
|
+
const back = fs23.readFileSync(path31.join(adminDir, "gitdir"), "utf8").trim();
|
|
21988
|
+
if (path31.relative(canonical3(path31.resolve(adminDir, back)), canonical3(dotGit)) !== "")
|
|
21115
21989
|
return false;
|
|
21116
|
-
const common =
|
|
21117
|
-
const gitDir = fs23.existsSync(common) ?
|
|
21118
|
-
const expected =
|
|
21119
|
-
return
|
|
21990
|
+
const common = path31.join(adminDir, "commondir");
|
|
21991
|
+
const gitDir = fs23.existsSync(common) ? path31.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
|
|
21992
|
+
const expected = path31.join(path31.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
|
|
21993
|
+
return path31.relative(canonical3(expected), checkout.root) === "";
|
|
21120
21994
|
} catch {
|
|
21121
21995
|
return false;
|
|
21122
21996
|
}
|
|
21123
|
-
}, shown = (target) =>
|
|
21997
|
+
}, shown = (target) => path31.basename(target) === ".workit-live-target" ? path31.dirname(target) : target, unboundDeny = (input, target) => {
|
|
21124
21998
|
const unblock = "Work only in the worktree your brief names, which the lead made with `workit fanout worktree create <slice>`: edit files under it and run one verb per call against it (`workit --cwd <worktree> git commit -m … -- <paths>`, `workit --cwd <worktree> check <name>`). If your brief names no worktree, stop and report that to the lead.";
|
|
21125
21999
|
return {
|
|
21126
22000
|
kind: "deny",
|
|
@@ -21445,14 +22319,14 @@ var init_handle = __esm(() => {
|
|
|
21445
22319
|
|
|
21446
22320
|
// packages/workit-core/src/hooks/deadline.ts
|
|
21447
22321
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
|
|
21448
|
-
import
|
|
22322
|
+
import path32 from "node:path";
|
|
21449
22323
|
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
22324
|
const value = Number(env.WORKIT_HOOK_DEADLINE_MS);
|
|
21451
22325
|
return Number.isFinite(value) && value > 0 ? value : null;
|
|
21452
22326
|
}, hookDeadlineMs = (env, host = "", native) => {
|
|
21453
22327
|
const row = HOST_DEADLINES[host];
|
|
21454
22328
|
return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
|
|
21455
|
-
}, skipsFile = (stateDir) =>
|
|
22329
|
+
}, skipsFile = (stateDir) => path32.join(stateDir, HOOK_SKIPS_FILE), recordHookSkip = (skip, stateDir = resolveStateDir()) => {
|
|
21456
22330
|
try {
|
|
21457
22331
|
mkdirSync3(stateDir, { recursive: true });
|
|
21458
22332
|
appendFileSync(skipsFile(stateDir), `${JSON.stringify(skip)}
|
|
@@ -22097,7 +22971,7 @@ var init_codex = __esm(() => {
|
|
|
22097
22971
|
|
|
22098
22972
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
22099
22973
|
import fs24 from "node:fs";
|
|
22100
|
-
import
|
|
22974
|
+
import path33 from "node:path";
|
|
22101
22975
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
22102
22976
|
if (!isRecord6(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
22103
22977
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -22178,7 +23052,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
22178
23052
|
};
|
|
22179
23053
|
}
|
|
22180
23054
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
22181
|
-
if (!file || !
|
|
23055
|
+
if (!file || !path33.isAbsolute(file))
|
|
22182
23056
|
return null;
|
|
22183
23057
|
try {
|
|
22184
23058
|
const size = fs24.statSync(file).size;
|
|
@@ -22214,7 +23088,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
22214
23088
|
} catch {
|
|
22215
23089
|
return null;
|
|
22216
23090
|
}
|
|
22217
|
-
}, within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
23091
|
+
}, within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path33.sep) ? root : `${root}${path33.sep}`), hookCwd = (input) => {
|
|
22218
23092
|
const cwd = existingDirectory(input.cwd);
|
|
22219
23093
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
22220
23094
|
return cwd;
|