@brainervirus/workit-codex 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/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +986 -161
- package/dist/{workit-hook-hegafjtc.js → workit-hook-q465naq9.js} +1035 -188
- package/dist/workit-hook.js +2 -2
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +1 -1
- package/skills/workit-implement/SKILL.md +5 -5
- package/skills/workit-review/SKILL.md +3 -0
- package/skills/workit-ship/SKILL.md +1 -2
- /package/dist/{workit-hook-thet0x8m.js → workit-hook-vhz9yrcw.js} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
readStandingRev,
|
|
13
13
|
readStandingSeen,
|
|
14
14
|
writeStandingSeen
|
|
15
|
-
} from "./workit-hook-
|
|
15
|
+
} from "./workit-hook-vhz9yrcw.js";
|
|
16
16
|
// packages/workit-core/src/hooks/descriptor.ts
|
|
17
17
|
var support = (descriptor, axis) => {
|
|
18
18
|
if (axis.startsWith("event:"))
|
|
@@ -40,7 +40,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
40
40
|
// packages/workit-core/src/hooks/context.ts
|
|
41
41
|
import { createHash as createHash12 } from "node:crypto";
|
|
42
42
|
import fs13 from "node:fs";
|
|
43
|
-
import
|
|
43
|
+
import path18 from "node:path";
|
|
44
44
|
|
|
45
45
|
// packages/workit-core/src/core/methods.ts
|
|
46
46
|
var METHODS = {
|
|
@@ -115,9 +115,9 @@ Autonomy contract:
|
|
|
115
115
|
Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
|
|
116
116
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
117
117
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
118
|
-
self-reviewed, never verified. High risk,
|
|
119
|
-
merge need a verifier with its
|
|
120
|
-
\`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
118
|
+
self-reviewed, never verified. High risk, a focused or full lane and
|
|
119
|
+
merge under \`verification: self|independent\` need a verifier with its
|
|
120
|
+
own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
121
121
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
122
122
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
123
123
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -7174,7 +7174,7 @@ function requirementId(input) {
|
|
|
7174
7174
|
import { createHash as createHash11 } from "node:crypto";
|
|
7175
7175
|
import * as fs12 from "node:fs";
|
|
7176
7176
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
7177
|
-
import
|
|
7177
|
+
import path16 from "node:path";
|
|
7178
7178
|
|
|
7179
7179
|
// packages/workit-core/src/git/rev.ts
|
|
7180
7180
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -7337,6 +7337,124 @@ function resolveRef(cwd, ref) {
|
|
|
7337
7337
|
return null;
|
|
7338
7338
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
7339
7339
|
}
|
|
7340
|
+
var LANE_DIFF = [
|
|
7341
|
+
"-c",
|
|
7342
|
+
"core.quotePath=false",
|
|
7343
|
+
"diff",
|
|
7344
|
+
"--no-color",
|
|
7345
|
+
"--no-ext-diff",
|
|
7346
|
+
"--no-textconv",
|
|
7347
|
+
"--no-renames"
|
|
7348
|
+
];
|
|
7349
|
+
var laneRange = (fork, head) => {
|
|
7350
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
7351
|
+
return null;
|
|
7352
|
+
return head === null ? [fork] : [fork, head];
|
|
7353
|
+
};
|
|
7354
|
+
function diffNumstat(cwd, fork, head) {
|
|
7355
|
+
const range = laneRange(fork, head);
|
|
7356
|
+
if (!range)
|
|
7357
|
+
return null;
|
|
7358
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
7359
|
+
if (!run.ok)
|
|
7360
|
+
return null;
|
|
7361
|
+
const out = [];
|
|
7362
|
+
for (const record of run.stdout.split("\x00")) {
|
|
7363
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
7364
|
+
if (!match)
|
|
7365
|
+
continue;
|
|
7366
|
+
const binary = match[1] === "-";
|
|
7367
|
+
out.push({
|
|
7368
|
+
path: match[3],
|
|
7369
|
+
added: binary ? 0 : Number(match[1]),
|
|
7370
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
7371
|
+
binary
|
|
7372
|
+
});
|
|
7373
|
+
}
|
|
7374
|
+
return out;
|
|
7375
|
+
}
|
|
7376
|
+
function diffRaw(cwd, fork, head) {
|
|
7377
|
+
const range = laneRange(fork, head);
|
|
7378
|
+
if (!range)
|
|
7379
|
+
return null;
|
|
7380
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
7381
|
+
if (!run.ok)
|
|
7382
|
+
return null;
|
|
7383
|
+
const fields = run.stdout.split("\x00");
|
|
7384
|
+
const out = [];
|
|
7385
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
7386
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
7387
|
+
if (!meta)
|
|
7388
|
+
return null;
|
|
7389
|
+
out.push({
|
|
7390
|
+
path: fields[index + 1],
|
|
7391
|
+
oldMode: meta[1],
|
|
7392
|
+
newMode: meta[2],
|
|
7393
|
+
oldBlob: meta[3],
|
|
7394
|
+
newBlob: meta[4]
|
|
7395
|
+
});
|
|
7396
|
+
}
|
|
7397
|
+
return out;
|
|
7398
|
+
}
|
|
7399
|
+
function readBlobs(cwd, ids) {
|
|
7400
|
+
const out = new Map;
|
|
7401
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
7402
|
+
if (!wanted.length)
|
|
7403
|
+
return out;
|
|
7404
|
+
const run = spawnSync2("git", ["cat-file", "--batch"], {
|
|
7405
|
+
cwd,
|
|
7406
|
+
input: `${wanted.join(`
|
|
7407
|
+
`)}
|
|
7408
|
+
`,
|
|
7409
|
+
maxBuffer: MAX_BUFFER,
|
|
7410
|
+
timeout: GIT_TIMEOUTS.local,
|
|
7411
|
+
killSignal: "SIGKILL",
|
|
7412
|
+
windowsHide: true
|
|
7413
|
+
});
|
|
7414
|
+
if (run.status !== 0 || !run.stdout)
|
|
7415
|
+
return null;
|
|
7416
|
+
const bytes = run.stdout;
|
|
7417
|
+
let at = 0;
|
|
7418
|
+
for (const id of wanted) {
|
|
7419
|
+
const end = bytes.indexOf(10, at);
|
|
7420
|
+
if (end < 0)
|
|
7421
|
+
return null;
|
|
7422
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
7423
|
+
at = end + 1;
|
|
7424
|
+
if (header[1] === "missing")
|
|
7425
|
+
continue;
|
|
7426
|
+
const size = Number(header[2]);
|
|
7427
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
7428
|
+
return null;
|
|
7429
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
7430
|
+
at += size + 1;
|
|
7431
|
+
}
|
|
7432
|
+
return out;
|
|
7433
|
+
}
|
|
7434
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
7435
|
+
if (!safeArg(rev))
|
|
7436
|
+
return null;
|
|
7437
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
7438
|
+
if (!run.ok)
|
|
7439
|
+
return null;
|
|
7440
|
+
const out = new Map;
|
|
7441
|
+
for (const record of run.stdout.split("\x00")) {
|
|
7442
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
7443
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
7444
|
+
out.set(match[2], match[1]);
|
|
7445
|
+
}
|
|
7446
|
+
return out;
|
|
7447
|
+
}
|
|
7448
|
+
function fileAt(cwd, rev, file) {
|
|
7449
|
+
if (!safeArg(rev))
|
|
7450
|
+
return null;
|
|
7451
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
7452
|
+
return run.ok ? run.stdout : null;
|
|
7453
|
+
}
|
|
7454
|
+
function untrackedFiles(cwd) {
|
|
7455
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
7456
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
7457
|
+
}
|
|
7340
7458
|
var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
7341
7459
|
function currentBranch(cwd) {
|
|
7342
7460
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
@@ -8329,7 +8447,7 @@ var workspaceConfigSchema = object({
|
|
|
8329
8447
|
}).passthrough().optional(),
|
|
8330
8448
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
8331
8449
|
defaultEndpoint: unknown().optional(),
|
|
8332
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
8450
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
8333
8451
|
delegation: unknown().optional(),
|
|
8334
8452
|
trailers: boolean2().optional(),
|
|
8335
8453
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -12032,8 +12150,8 @@ class TaskStore {
|
|
|
12032
12150
|
}
|
|
12033
12151
|
|
|
12034
12152
|
// packages/workit-core/src/core/policy/derive.ts
|
|
12035
|
-
import { existsSync as
|
|
12036
|
-
import
|
|
12153
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
12154
|
+
import path15 from "node:path";
|
|
12037
12155
|
|
|
12038
12156
|
// packages/workit-core/src/ledger.ts
|
|
12039
12157
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -12880,7 +12998,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
12880
12998
|
verdict: failing?.verdict ?? null,
|
|
12881
12999
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
12882
13000
|
};
|
|
12883
|
-
const
|
|
13001
|
+
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))));
|
|
13002
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
12884
13003
|
return {
|
|
12885
13004
|
branch,
|
|
12886
13005
|
head,
|
|
@@ -12924,12 +13043,682 @@ var OBSERVED_ONLY = new Set([
|
|
|
12924
13043
|
...PR_ROW_TYPES
|
|
12925
13044
|
]);
|
|
12926
13045
|
|
|
13046
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
13047
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
13048
|
+
import path14 from "node:path";
|
|
13049
|
+
// packages/workit-core/src/forge/redact.ts
|
|
13050
|
+
var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
|
|
13051
|
+
var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
|
|
13052
|
+
var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
13053
|
+
var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
13054
|
+
|
|
13055
|
+
// packages/workit-core/src/git/ops.ts
|
|
13056
|
+
var COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
13057
|
+
|
|
13058
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
13059
|
+
function patternRegex(dir, pattern) {
|
|
13060
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
13061
|
+
return null;
|
|
13062
|
+
const anchored = pattern.includes("/");
|
|
13063
|
+
const body = pattern.replace(/^\//u, "");
|
|
13064
|
+
let source = "";
|
|
13065
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
13066
|
+
const char = body[index];
|
|
13067
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
13068
|
+
const slash = body[index + 2] === "/";
|
|
13069
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
13070
|
+
index += slash ? 2 : 1;
|
|
13071
|
+
} else if (char === "*")
|
|
13072
|
+
source += "[^/]*";
|
|
13073
|
+
else if (char === "?")
|
|
13074
|
+
source += "[^/]";
|
|
13075
|
+
else if (char === "[") {
|
|
13076
|
+
const end = body.indexOf("]", index + 2);
|
|
13077
|
+
if (end < 0)
|
|
13078
|
+
return null;
|
|
13079
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
13080
|
+
source += `[${set}]`;
|
|
13081
|
+
index = end;
|
|
13082
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
13083
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
13084
|
+
index += 1;
|
|
13085
|
+
} else
|
|
13086
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
13087
|
+
}
|
|
13088
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
13089
|
+
try {
|
|
13090
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
13091
|
+
} catch {
|
|
13092
|
+
return null;
|
|
13093
|
+
}
|
|
13094
|
+
}
|
|
13095
|
+
function rulesOf(dir, text) {
|
|
13096
|
+
const out = [];
|
|
13097
|
+
for (const raw of text.split(`
|
|
13098
|
+
`)) {
|
|
13099
|
+
const line = raw.trim();
|
|
13100
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
13101
|
+
continue;
|
|
13102
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
13103
|
+
let generated = null;
|
|
13104
|
+
for (const attribute of attributes) {
|
|
13105
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
13106
|
+
generated = true;
|
|
13107
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
13108
|
+
generated = false;
|
|
13109
|
+
}
|
|
13110
|
+
if (generated === null)
|
|
13111
|
+
continue;
|
|
13112
|
+
const regex = patternRegex(dir, pattern);
|
|
13113
|
+
if (regex)
|
|
13114
|
+
out.push({ regex, generated });
|
|
13115
|
+
}
|
|
13116
|
+
return out;
|
|
13117
|
+
}
|
|
13118
|
+
function generatedByAttributes(files, paths) {
|
|
13119
|
+
const rules = [...files].map(([file, text]) => {
|
|
13120
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
13121
|
+
return {
|
|
13122
|
+
depth: dir ? dir.split("/").length : 0,
|
|
13123
|
+
rules: rulesOf(dir, text)
|
|
13124
|
+
};
|
|
13125
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
13126
|
+
const out = new Set;
|
|
13127
|
+
for (const file of paths) {
|
|
13128
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
13129
|
+
if (last?.generated)
|
|
13130
|
+
out.add(file);
|
|
13131
|
+
}
|
|
13132
|
+
return out;
|
|
13133
|
+
}
|
|
13134
|
+
|
|
13135
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
13136
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
13137
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
13138
|
+
var SYNTAXES = Object.fromEntries([
|
|
13139
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
13140
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
13141
|
+
ext,
|
|
13142
|
+
{ ...C_LIKE, triple: true }
|
|
13143
|
+
]),
|
|
13144
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
13145
|
+
...["css", "scss", "less"].map((ext) => [
|
|
13146
|
+
ext,
|
|
13147
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
13148
|
+
]),
|
|
13149
|
+
...["py", "pyi"].map((ext) => [
|
|
13150
|
+
ext,
|
|
13151
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
13152
|
+
]),
|
|
13153
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
13154
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
13155
|
+
ext,
|
|
13156
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
13157
|
+
])
|
|
13158
|
+
]);
|
|
13159
|
+
var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
|
|
13160
|
+
var extensionOf = (file) => {
|
|
13161
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
13162
|
+
const dot = name.lastIndexOf(".");
|
|
13163
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
13164
|
+
};
|
|
13165
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
13166
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
13167
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
13168
|
+
var WORD = /[\w$]/u;
|
|
13169
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
13170
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
13171
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
13172
|
+
var REGEX_KEYWORDS = new Set([
|
|
13173
|
+
"return",
|
|
13174
|
+
"typeof",
|
|
13175
|
+
"instanceof",
|
|
13176
|
+
"case",
|
|
13177
|
+
"do",
|
|
13178
|
+
"else",
|
|
13179
|
+
"in",
|
|
13180
|
+
"of",
|
|
13181
|
+
"new",
|
|
13182
|
+
"delete",
|
|
13183
|
+
"void",
|
|
13184
|
+
"throw",
|
|
13185
|
+
"yield",
|
|
13186
|
+
"await"
|
|
13187
|
+
]);
|
|
13188
|
+
function substanceOf(file, text) {
|
|
13189
|
+
const syntax = syntaxOf(file);
|
|
13190
|
+
if (!syntax)
|
|
13191
|
+
return plainSubstance(text);
|
|
13192
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
13193
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
13194
|
+
return plainSubstance(text);
|
|
13195
|
+
return lex(syntax, text);
|
|
13196
|
+
}
|
|
13197
|
+
function plainSubstance(text) {
|
|
13198
|
+
return text.split(`
|
|
13199
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
13200
|
+
`);
|
|
13201
|
+
}
|
|
13202
|
+
function lex(syntax, text) {
|
|
13203
|
+
const collapse = !syntax.indented;
|
|
13204
|
+
let out = "";
|
|
13205
|
+
let lineStart = 0;
|
|
13206
|
+
let kept = 0;
|
|
13207
|
+
let pendingSpace = false;
|
|
13208
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
13209
|
+
let index = 0;
|
|
13210
|
+
if (text.startsWith("#!")) {
|
|
13211
|
+
const end = text.indexOf(`
|
|
13212
|
+
`);
|
|
13213
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
13214
|
+
kept = out.length;
|
|
13215
|
+
index = end < 0 ? text.length : end;
|
|
13216
|
+
}
|
|
13217
|
+
const emitCode = (chunk) => {
|
|
13218
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
13219
|
+
const before = out[out.length - 1];
|
|
13220
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
13221
|
+
out += " ";
|
|
13222
|
+
}
|
|
13223
|
+
pendingSpace = false;
|
|
13224
|
+
out += chunk;
|
|
13225
|
+
};
|
|
13226
|
+
const emitVerbatim = (chunk) => {
|
|
13227
|
+
emitCode(chunk);
|
|
13228
|
+
kept = out.length;
|
|
13229
|
+
};
|
|
13230
|
+
const newline = () => {
|
|
13231
|
+
if (!collapse) {
|
|
13232
|
+
let end = out.length;
|
|
13233
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
13234
|
+
end -= 1;
|
|
13235
|
+
out = out.slice(0, end);
|
|
13236
|
+
}
|
|
13237
|
+
pendingSpace = false;
|
|
13238
|
+
if (out.length > lineStart) {
|
|
13239
|
+
out += `
|
|
13240
|
+
`;
|
|
13241
|
+
lineStart = out.length;
|
|
13242
|
+
}
|
|
13243
|
+
};
|
|
13244
|
+
const regexAllowed = () => {
|
|
13245
|
+
let end = out.length;
|
|
13246
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
13247
|
+
end -= 1;
|
|
13248
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
13249
|
+
return true;
|
|
13250
|
+
let start = end;
|
|
13251
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
13252
|
+
start -= 1;
|
|
13253
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
13254
|
+
};
|
|
13255
|
+
const scanString = (quote, escapes, multiline) => {
|
|
13256
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
13257
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
13258
|
+
let end = index + quote.length;
|
|
13259
|
+
while (end < text.length) {
|
|
13260
|
+
const char = text[end];
|
|
13261
|
+
if (escapes && char === "\\") {
|
|
13262
|
+
end += 2;
|
|
13263
|
+
continue;
|
|
13264
|
+
}
|
|
13265
|
+
if (text.startsWith(quote, end)) {
|
|
13266
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
13267
|
+
return false;
|
|
13268
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
13269
|
+
index = end + quote.length;
|
|
13270
|
+
return true;
|
|
13271
|
+
}
|
|
13272
|
+
if (char === `
|
|
13273
|
+
` && !multiline)
|
|
13274
|
+
return false;
|
|
13275
|
+
end += 1;
|
|
13276
|
+
}
|
|
13277
|
+
return false;
|
|
13278
|
+
};
|
|
13279
|
+
while (index < text.length) {
|
|
13280
|
+
const frame = frames[frames.length - 1];
|
|
13281
|
+
const char = text[index];
|
|
13282
|
+
if (frame.kind === "template") {
|
|
13283
|
+
if (char === "\\") {
|
|
13284
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
13285
|
+
index += 2;
|
|
13286
|
+
} else if (char === "`") {
|
|
13287
|
+
emitVerbatim(char);
|
|
13288
|
+
frames.pop();
|
|
13289
|
+
index += 1;
|
|
13290
|
+
} else if (text.startsWith("${", index)) {
|
|
13291
|
+
emitVerbatim("${");
|
|
13292
|
+
frames.push({ kind: "code", braces: 0 });
|
|
13293
|
+
index += 2;
|
|
13294
|
+
} else {
|
|
13295
|
+
out += char;
|
|
13296
|
+
kept = out.length;
|
|
13297
|
+
index += 1;
|
|
13298
|
+
}
|
|
13299
|
+
continue;
|
|
13300
|
+
}
|
|
13301
|
+
if (char === `
|
|
13302
|
+
`) {
|
|
13303
|
+
newline();
|
|
13304
|
+
index += 1;
|
|
13305
|
+
continue;
|
|
13306
|
+
}
|
|
13307
|
+
if (SPACE.test(char)) {
|
|
13308
|
+
if (collapse)
|
|
13309
|
+
pendingSpace = true;
|
|
13310
|
+
else
|
|
13311
|
+
out += char;
|
|
13312
|
+
index += 1;
|
|
13313
|
+
continue;
|
|
13314
|
+
}
|
|
13315
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
13316
|
+
if (lineOpener) {
|
|
13317
|
+
const end = text.indexOf(`
|
|
13318
|
+
`, index);
|
|
13319
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
13320
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
13321
|
+
emitVerbatim(comment);
|
|
13322
|
+
else
|
|
13323
|
+
pendingSpace = true;
|
|
13324
|
+
index = end < 0 ? text.length : end;
|
|
13325
|
+
continue;
|
|
13326
|
+
}
|
|
13327
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
13328
|
+
if (block) {
|
|
13329
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
13330
|
+
if (end < 0)
|
|
13331
|
+
return null;
|
|
13332
|
+
const comment = text.slice(index, end + block[1].length);
|
|
13333
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
13334
|
+
emitVerbatim(comment);
|
|
13335
|
+
else
|
|
13336
|
+
pendingSpace = true;
|
|
13337
|
+
if (comment.includes(`
|
|
13338
|
+
`))
|
|
13339
|
+
newline();
|
|
13340
|
+
index = end + block[1].length;
|
|
13341
|
+
continue;
|
|
13342
|
+
}
|
|
13343
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
13344
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
13345
|
+
return null;
|
|
13346
|
+
continue;
|
|
13347
|
+
}
|
|
13348
|
+
if (syntax.quotes.includes(char)) {
|
|
13349
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
13350
|
+
return null;
|
|
13351
|
+
continue;
|
|
13352
|
+
}
|
|
13353
|
+
if (syntax.literal?.includes(char)) {
|
|
13354
|
+
if (!scanString(char, false, false))
|
|
13355
|
+
return null;
|
|
13356
|
+
continue;
|
|
13357
|
+
}
|
|
13358
|
+
if (syntax.rawBacktick && char === "`") {
|
|
13359
|
+
if (!scanString("`", false, true))
|
|
13360
|
+
return null;
|
|
13361
|
+
continue;
|
|
13362
|
+
}
|
|
13363
|
+
if (syntax.js && char === "`") {
|
|
13364
|
+
emitVerbatim(char);
|
|
13365
|
+
frames.push({ kind: "template" });
|
|
13366
|
+
index += 1;
|
|
13367
|
+
continue;
|
|
13368
|
+
}
|
|
13369
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
13370
|
+
return null;
|
|
13371
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
13372
|
+
const lineEnd = text.indexOf(`
|
|
13373
|
+
`, index);
|
|
13374
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
13375
|
+
return null;
|
|
13376
|
+
}
|
|
13377
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
13378
|
+
let end = index + 1;
|
|
13379
|
+
let inClass = false;
|
|
13380
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
13381
|
+
if (text[end] === `
|
|
13382
|
+
`)
|
|
13383
|
+
return null;
|
|
13384
|
+
if (text[end] === "\\")
|
|
13385
|
+
end += 1;
|
|
13386
|
+
else if (text[end] === "[")
|
|
13387
|
+
inClass = true;
|
|
13388
|
+
else if (text[end] === "]")
|
|
13389
|
+
inClass = false;
|
|
13390
|
+
end += 1;
|
|
13391
|
+
}
|
|
13392
|
+
if (end >= text.length)
|
|
13393
|
+
return null;
|
|
13394
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
13395
|
+
index = end + 1;
|
|
13396
|
+
continue;
|
|
13397
|
+
}
|
|
13398
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
13399
|
+
if (char === "{")
|
|
13400
|
+
frame.braces += 1;
|
|
13401
|
+
else if (char === "}") {
|
|
13402
|
+
if (frame.braces === 0) {
|
|
13403
|
+
emitVerbatim(char);
|
|
13404
|
+
frames.pop();
|
|
13405
|
+
index += 1;
|
|
13406
|
+
continue;
|
|
13407
|
+
}
|
|
13408
|
+
frame.braces -= 1;
|
|
13409
|
+
}
|
|
13410
|
+
}
|
|
13411
|
+
emitCode(char);
|
|
13412
|
+
index += 1;
|
|
13413
|
+
}
|
|
13414
|
+
if (frames.length !== 1)
|
|
13415
|
+
return null;
|
|
13416
|
+
newline();
|
|
13417
|
+
return out;
|
|
13418
|
+
}
|
|
13419
|
+
function unbalanced(body) {
|
|
13420
|
+
let depth = 0;
|
|
13421
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
13422
|
+
if (part === "{")
|
|
13423
|
+
depth += 1;
|
|
13424
|
+
else if (part === "}")
|
|
13425
|
+
depth = Math.max(0, depth - 1);
|
|
13426
|
+
}
|
|
13427
|
+
return depth > 0;
|
|
13428
|
+
}
|
|
13429
|
+
function hasOpaqueLineBreak(text) {
|
|
13430
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
13431
|
+
}
|
|
13432
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
13433
|
+
function textOf(bytes) {
|
|
13434
|
+
if (bytes.includes(0))
|
|
13435
|
+
return null;
|
|
13436
|
+
try {
|
|
13437
|
+
return decoder.decode(bytes);
|
|
13438
|
+
} catch {
|
|
13439
|
+
return null;
|
|
13440
|
+
}
|
|
13441
|
+
}
|
|
13442
|
+
function cosmeticChange(file, before, after) {
|
|
13443
|
+
const left = textOf(before);
|
|
13444
|
+
const right = textOf(after);
|
|
13445
|
+
if (left === null || right === null)
|
|
13446
|
+
return false;
|
|
13447
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
13448
|
+
return false;
|
|
13449
|
+
const a = substanceOf(file, left);
|
|
13450
|
+
return a !== null && a === substanceOf(file, right);
|
|
13451
|
+
}
|
|
13452
|
+
|
|
13453
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
13454
|
+
var LANE_THRESHOLDS = {
|
|
13455
|
+
selfMaxLines: 100,
|
|
13456
|
+
selfMaxFiles: 5,
|
|
13457
|
+
fullMinLines: 500
|
|
13458
|
+
};
|
|
13459
|
+
var RISK_TOKENS = new Set([
|
|
13460
|
+
"auth",
|
|
13461
|
+
"authn",
|
|
13462
|
+
"authz",
|
|
13463
|
+
"oauth",
|
|
13464
|
+
"security",
|
|
13465
|
+
"permission",
|
|
13466
|
+
"permissions",
|
|
13467
|
+
"secret",
|
|
13468
|
+
"secrets",
|
|
13469
|
+
"crypto",
|
|
13470
|
+
"gate",
|
|
13471
|
+
"gates",
|
|
13472
|
+
"policy",
|
|
13473
|
+
"policies",
|
|
13474
|
+
"hook",
|
|
13475
|
+
"hooks",
|
|
13476
|
+
"husky",
|
|
13477
|
+
"lefthook",
|
|
13478
|
+
"release",
|
|
13479
|
+
"releases",
|
|
13480
|
+
"migration",
|
|
13481
|
+
"migrations",
|
|
13482
|
+
"migrate",
|
|
13483
|
+
"workflows"
|
|
13484
|
+
]);
|
|
13485
|
+
var RISK_FILES = new Set([
|
|
13486
|
+
".gitlab-ci.yml",
|
|
13487
|
+
"Jenkinsfile",
|
|
13488
|
+
"azure-pipelines.yml",
|
|
13489
|
+
".pre-commit-config.yaml",
|
|
13490
|
+
".workit/risk-paths"
|
|
13491
|
+
]);
|
|
13492
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
13493
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
13494
|
+
var LOCKFILES = new Set([
|
|
13495
|
+
"bun.lock",
|
|
13496
|
+
"bun.lockb",
|
|
13497
|
+
"package-lock.json",
|
|
13498
|
+
"npm-shrinkwrap.json",
|
|
13499
|
+
"yarn.lock",
|
|
13500
|
+
"pnpm-lock.yaml",
|
|
13501
|
+
"Cargo.lock",
|
|
13502
|
+
"go.sum",
|
|
13503
|
+
"poetry.lock",
|
|
13504
|
+
"uv.lock",
|
|
13505
|
+
"Pipfile.lock",
|
|
13506
|
+
"Gemfile.lock",
|
|
13507
|
+
"composer.lock"
|
|
13508
|
+
]);
|
|
13509
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
|
|
13510
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
13511
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
13512
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
|
|
13513
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
13514
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
13515
|
+
function kindOf(file) {
|
|
13516
|
+
if (file.binary)
|
|
13517
|
+
return "behavior";
|
|
13518
|
+
if (file.generated)
|
|
13519
|
+
return "generated";
|
|
13520
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
13521
|
+
return "lockfile";
|
|
13522
|
+
if (isDoc(file.path))
|
|
13523
|
+
return "doc";
|
|
13524
|
+
if (file.cosmetic)
|
|
13525
|
+
return "cosmetic";
|
|
13526
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
13527
|
+
}
|
|
13528
|
+
var globCache = new Map;
|
|
13529
|
+
function globMatch(pattern, file) {
|
|
13530
|
+
let regex = globCache.get(pattern);
|
|
13531
|
+
if (!regex) {
|
|
13532
|
+
let source = "";
|
|
13533
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
13534
|
+
const char = pattern[index];
|
|
13535
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
13536
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
13537
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
13538
|
+
} else if (char === "*")
|
|
13539
|
+
source += "[^/]*";
|
|
13540
|
+
else if (char === "?")
|
|
13541
|
+
source += "[^/]";
|
|
13542
|
+
else
|
|
13543
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
13544
|
+
}
|
|
13545
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
13546
|
+
globCache.set(pattern, regex);
|
|
13547
|
+
}
|
|
13548
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
13549
|
+
}
|
|
13550
|
+
function isRiskPath(file, extra = []) {
|
|
13551
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
13552
|
+
return true;
|
|
13553
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
13554
|
+
return true;
|
|
13555
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
13556
|
+
return false;
|
|
13557
|
+
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)));
|
|
13558
|
+
}
|
|
13559
|
+
var parseRiskPaths = (text) => text.split(`
|
|
13560
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
13561
|
+
function computeLane(input) {
|
|
13562
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
13563
|
+
const risky = new Set(riskFiles);
|
|
13564
|
+
const kinds = input.files.map((file) => ({
|
|
13565
|
+
file,
|
|
13566
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
13567
|
+
}));
|
|
13568
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
13569
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
13570
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
13571
|
+
const result = (lane, reasons) => ({
|
|
13572
|
+
lane,
|
|
13573
|
+
reasons,
|
|
13574
|
+
lines,
|
|
13575
|
+
files: behavior.length,
|
|
13576
|
+
riskFiles
|
|
13577
|
+
});
|
|
13578
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
13579
|
+
if (input.riskTier === "high")
|
|
13580
|
+
return result("full", ["the task judgment records high risk"]);
|
|
13581
|
+
if (riskFiles.length)
|
|
13582
|
+
return result("full", [
|
|
13583
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
13584
|
+
]);
|
|
13585
|
+
if (!behavior.length)
|
|
13586
|
+
return result("none", [
|
|
13587
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
13588
|
+
]);
|
|
13589
|
+
if (lines >= fullMinLines)
|
|
13590
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
13591
|
+
if (lines > selfMaxLines)
|
|
13592
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
13593
|
+
if (behavior.length > selfMaxFiles)
|
|
13594
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
13595
|
+
if (opaque.length)
|
|
13596
|
+
return result("focused", [
|
|
13597
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
13598
|
+
]);
|
|
13599
|
+
return result("self", [
|
|
13600
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
13601
|
+
]);
|
|
13602
|
+
}
|
|
13603
|
+
function untrackedStat(cwd, file) {
|
|
13604
|
+
try {
|
|
13605
|
+
const bytes = readFileSync4(path14.join(cwd, file));
|
|
13606
|
+
if (bytes.includes(0))
|
|
13607
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
13608
|
+
const text = bytes.toString("utf8");
|
|
13609
|
+
if (hasOpaqueLineBreak(text))
|
|
13610
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
13611
|
+
const lines = text.split(`
|
|
13612
|
+
`).length - (text.endsWith(`
|
|
13613
|
+
`) ? 1 : 0);
|
|
13614
|
+
return { path: file, added: lines, deleted: 0 };
|
|
13615
|
+
} catch {
|
|
13616
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
13617
|
+
}
|
|
13618
|
+
}
|
|
13619
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
13620
|
+
var ABSENT = /^0+$/u;
|
|
13621
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
13622
|
+
const raw = diffRaw(cwd, fork, head);
|
|
13623
|
+
if (!raw)
|
|
13624
|
+
return null;
|
|
13625
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
13626
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
13627
|
+
if (!blobs)
|
|
13628
|
+
return null;
|
|
13629
|
+
const out = new Set;
|
|
13630
|
+
const opaque = new Set;
|
|
13631
|
+
for (const entry of candidates) {
|
|
13632
|
+
const before = blobs.get(entry.oldBlob);
|
|
13633
|
+
let after = blobs.get(entry.newBlob);
|
|
13634
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
13635
|
+
try {
|
|
13636
|
+
after = readFileSync4(path14.join(cwd, entry.path));
|
|
13637
|
+
} catch {
|
|
13638
|
+
after = undefined;
|
|
13639
|
+
}
|
|
13640
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
13641
|
+
out.add(entry.path);
|
|
13642
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
13643
|
+
opaque.add(entry.path);
|
|
13644
|
+
}
|
|
13645
|
+
return { cosmetic: out, opaque };
|
|
13646
|
+
}
|
|
13647
|
+
function generatedAt(cwd, fork, paths) {
|
|
13648
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
13649
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
13650
|
+
if (!files || !blobs)
|
|
13651
|
+
return new Set;
|
|
13652
|
+
const texts = new Map;
|
|
13653
|
+
for (const [file, id] of files) {
|
|
13654
|
+
const bytes = blobs.get(id);
|
|
13655
|
+
if (bytes)
|
|
13656
|
+
texts.set(file, bytes.toString("utf8"));
|
|
13657
|
+
}
|
|
13658
|
+
return generatedByAttributes(texts, paths);
|
|
13659
|
+
}
|
|
13660
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
13661
|
+
function laneFiles(cwd, fork, head) {
|
|
13662
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
13663
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
13664
|
+
if (!stats || !cosmetic)
|
|
13665
|
+
return null;
|
|
13666
|
+
const files = [
|
|
13667
|
+
...stats.map((entry) => ({
|
|
13668
|
+
...entry,
|
|
13669
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
13670
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
13671
|
+
})),
|
|
13672
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
13673
|
+
];
|
|
13674
|
+
const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
|
|
13675
|
+
return files.map((entry) => ({
|
|
13676
|
+
...entry,
|
|
13677
|
+
generated: generated.has(entry.path)
|
|
13678
|
+
}));
|
|
13679
|
+
}
|
|
13680
|
+
function branchLane(cwd, options) {
|
|
13681
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
13682
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
13683
|
+
const head = options.workingTree ? null : options.head;
|
|
13684
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
13685
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
13686
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
13687
|
+
if (!fork || !files) {
|
|
13688
|
+
const high = riskTier === "high";
|
|
13689
|
+
return {
|
|
13690
|
+
lane: high ? "full" : "focused",
|
|
13691
|
+
reasons: [
|
|
13692
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
13693
|
+
...high ? ["the task judgment records high risk"] : []
|
|
13694
|
+
],
|
|
13695
|
+
lines: 0,
|
|
13696
|
+
files: 0,
|
|
13697
|
+
riskFiles: [],
|
|
13698
|
+
base,
|
|
13699
|
+
head: options.head
|
|
13700
|
+
};
|
|
13701
|
+
}
|
|
13702
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
13703
|
+
return {
|
|
13704
|
+
...computeLane({
|
|
13705
|
+
files,
|
|
13706
|
+
riskTier,
|
|
13707
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
13708
|
+
}),
|
|
13709
|
+
base,
|
|
13710
|
+
head: options.head
|
|
13711
|
+
};
|
|
13712
|
+
}
|
|
13713
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
13714
|
+
|
|
12927
13715
|
// packages/workit-core/src/core/policy/derive.ts
|
|
12928
13716
|
var RULES = {
|
|
12929
13717
|
test: "check:test",
|
|
12930
13718
|
self: "verdict:self",
|
|
12931
13719
|
nonAuthor: "verdict:non-author",
|
|
12932
13720
|
verified: "verdict:verified",
|
|
13721
|
+
lane: "verdict:lane",
|
|
12933
13722
|
product: "decision:product",
|
|
12934
13723
|
plan: "plan"
|
|
12935
13724
|
};
|
|
@@ -12952,6 +13741,7 @@ var normalizedScope = (scope) => ({
|
|
|
12952
13741
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
12953
13742
|
const out = [];
|
|
12954
13743
|
const base = { scope, dependentAction: null };
|
|
13744
|
+
const proportional = verification === "proportional";
|
|
12955
13745
|
if (judgment.behaviorChange)
|
|
12956
13746
|
out.push(derived2({
|
|
12957
13747
|
...base,
|
|
@@ -12962,7 +13752,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
|
|
|
12962
13752
|
before: "close",
|
|
12963
13753
|
acceptanceAllowed: false
|
|
12964
13754
|
}));
|
|
12965
|
-
if (
|
|
13755
|
+
if (proportional && judgment.riskTier === "normal")
|
|
13756
|
+
out.push(derived2({
|
|
13757
|
+
...base,
|
|
13758
|
+
ruleId: RULES.lane,
|
|
13759
|
+
dimension: "review",
|
|
13760
|
+
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.",
|
|
13761
|
+
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.",
|
|
13762
|
+
before: "close",
|
|
13763
|
+
acceptanceAllowed: false
|
|
13764
|
+
}));
|
|
13765
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
12966
13766
|
out.push(verification === "independent" ? derived2({
|
|
12967
13767
|
...base,
|
|
12968
13768
|
ruleId: RULES.nonAuthor,
|
|
@@ -13065,7 +13865,7 @@ var productDecision = (task, ledger) => {
|
|
|
13065
13865
|
};
|
|
13066
13866
|
};
|
|
13067
13867
|
var planRecorded = (task, root) => {
|
|
13068
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" &&
|
|
13868
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path15.resolve(root, ref.path)));
|
|
13069
13869
|
if (cited)
|
|
13070
13870
|
return {
|
|
13071
13871
|
met: true,
|
|
@@ -13079,14 +13879,30 @@ var planRecorded = (task, root) => {
|
|
|
13079
13879
|
decisionIds: []
|
|
13080
13880
|
};
|
|
13081
13881
|
};
|
|
13082
|
-
var
|
|
13882
|
+
var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
|
|
13883
|
+
var laneVerdict = (task, root, ledger) => {
|
|
13884
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
13885
|
+
return {
|
|
13886
|
+
met: true,
|
|
13887
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
13888
|
+
decisionIds: []
|
|
13889
|
+
};
|
|
13890
|
+
const lane = workingLane(task, root);
|
|
13891
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
13892
|
+
const need = laneNeed(lane.lane);
|
|
13893
|
+
if (need === "none")
|
|
13894
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
13895
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
13896
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
13897
|
+
};
|
|
13898
|
+
var verdict = (ruleId, root, ledger) => {
|
|
13083
13899
|
const branch = ledger.branch();
|
|
13084
13900
|
if (!branch)
|
|
13085
13901
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
13086
13902
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
13087
13903
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
13088
13904
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
13089
|
-
const met = !failing && (
|
|
13905
|
+
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)));
|
|
13090
13906
|
if (met)
|
|
13091
13907
|
return {
|
|
13092
13908
|
met: true,
|
|
@@ -13094,7 +13910,7 @@ var verdict = (requirement, root, ledger) => {
|
|
|
13094
13910
|
decisionIds: []
|
|
13095
13911
|
};
|
|
13096
13912
|
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, " ");
|
|
13097
|
-
const unblock =
|
|
13913
|
+
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>\``;
|
|
13098
13914
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
13099
13915
|
};
|
|
13100
13916
|
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
@@ -13106,7 +13922,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
13106
13922
|
case RULES.self:
|
|
13107
13923
|
case RULES.nonAuthor:
|
|
13108
13924
|
case RULES.verified:
|
|
13109
|
-
return verdict(requirement, root, ledger);
|
|
13925
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
13926
|
+
case RULES.lane:
|
|
13927
|
+
return laneVerdict(task, root, ledger);
|
|
13110
13928
|
default:
|
|
13111
13929
|
return null;
|
|
13112
13930
|
}
|
|
@@ -13125,14 +13943,14 @@ function writeBlockers(task, root) {
|
|
|
13125
13943
|
|
|
13126
13944
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
13127
13945
|
var digestBytes = (value) => createHash11("sha256").update(value).digest("hex");
|
|
13128
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
13129
|
-
var relative = (root, candidate) =>
|
|
13946
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
13947
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
13130
13948
|
var canonicalPath = (value) => {
|
|
13131
13949
|
if (typeof value !== "string" || !value)
|
|
13132
13950
|
return null;
|
|
13133
13951
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
13134
13952
|
return null;
|
|
13135
|
-
const normalized =
|
|
13953
|
+
const normalized = path16.posix.normalize(value);
|
|
13136
13954
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
13137
13955
|
return null;
|
|
13138
13956
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -13180,14 +13998,14 @@ var gitPaths = (root) => {
|
|
|
13180
13998
|
git: !/not a git repository/i.test(stderr)
|
|
13181
13999
|
};
|
|
13182
14000
|
}
|
|
13183
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14001
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
13184
14002
|
const stagedDeleted = spawnSync9("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
13185
14003
|
if (stagedDeleted.status !== 0) {
|
|
13186
14004
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
13187
14005
|
if (!/not a git repository/i.test(stderr))
|
|
13188
14006
|
return { paths, uncertain: true, git: true };
|
|
13189
14007
|
} else if (stagedDeleted.stdout) {
|
|
13190
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14008
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
13191
14009
|
}
|
|
13192
14010
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
13193
14011
|
};
|
|
@@ -13202,7 +14020,7 @@ var walk = (root, directory, output) => {
|
|
|
13202
14020
|
for (const name of names) {
|
|
13203
14021
|
if (name === ".git" || name === ".workit")
|
|
13204
14022
|
continue;
|
|
13205
|
-
const target =
|
|
14023
|
+
const target = path16.join(directory, name);
|
|
13206
14024
|
let stat;
|
|
13207
14025
|
try {
|
|
13208
14026
|
stat = fs12.lstatSync(target);
|
|
@@ -13220,12 +14038,12 @@ var walk = (root, directory, output) => {
|
|
|
13220
14038
|
var scopeRoots = (root, scope) => {
|
|
13221
14039
|
const roots = [];
|
|
13222
14040
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
13223
|
-
const target =
|
|
14041
|
+
const target = path16.resolve(root, item);
|
|
13224
14042
|
if (!inside(root, target))
|
|
13225
14043
|
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` }] });
|
|
13226
14044
|
let ancestor = target;
|
|
13227
14045
|
while (!fs12.existsSync(ancestor) && ancestor !== root)
|
|
13228
|
-
ancestor =
|
|
14046
|
+
ancestor = path16.dirname(ancestor);
|
|
13229
14047
|
try {
|
|
13230
14048
|
if (!inside(root, fs12.realpathSync(ancestor)))
|
|
13231
14049
|
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` }] });
|
|
@@ -13277,7 +14095,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13277
14095
|
for (const item of [...names].toSorted()) {
|
|
13278
14096
|
if (!scopeMatches(item, normalizedScope))
|
|
13279
14097
|
continue;
|
|
13280
|
-
const target =
|
|
14098
|
+
const target = path16.join(checkout, item);
|
|
13281
14099
|
try {
|
|
13282
14100
|
const stat = fs12.lstatSync(target);
|
|
13283
14101
|
if (stat.isSymbolicLink()) {
|
|
@@ -13461,23 +14279,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
13461
14279
|
continue;
|
|
13462
14280
|
if (!reference.digest)
|
|
13463
14281
|
return failure2("invalid_input", "document references require a byte digest");
|
|
13464
|
-
const target =
|
|
13465
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
14282
|
+
const target = path16.resolve(root, reference.path);
|
|
14283
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
13466
14284
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
13467
14285
|
fields: [
|
|
13468
14286
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
13469
14287
|
]
|
|
13470
14288
|
});
|
|
13471
14289
|
try {
|
|
13472
|
-
const below =
|
|
14290
|
+
const below = path16.relative(root, target);
|
|
13473
14291
|
let current = root;
|
|
13474
|
-
for (const segment of below.split(
|
|
14292
|
+
for (const segment of below.split(path16.sep)) {
|
|
13475
14293
|
if (!segment)
|
|
13476
14294
|
continue;
|
|
13477
|
-
current =
|
|
14295
|
+
current = path16.join(current, segment);
|
|
13478
14296
|
const stat = fs12.lstatSync(current);
|
|
13479
14297
|
const real = fs12.realpathSync(current);
|
|
13480
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
14298
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
13481
14299
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
13482
14300
|
fields: [
|
|
13483
14301
|
{
|
|
@@ -14398,7 +15216,7 @@ var judgeInputSummary = (raw) => {
|
|
|
14398
15216
|
|
|
14399
15217
|
// packages/workit-core/src/autonomy.ts
|
|
14400
15218
|
import os4 from "node:os";
|
|
14401
|
-
import
|
|
15219
|
+
import path17 from "node:path";
|
|
14402
15220
|
var accountHome = () => {
|
|
14403
15221
|
try {
|
|
14404
15222
|
return os4.userInfo().homedir;
|
|
@@ -14407,10 +15225,10 @@ var accountHome = () => {
|
|
|
14407
15225
|
}
|
|
14408
15226
|
};
|
|
14409
15227
|
var grantsHome = { resolve: accountHome };
|
|
14410
|
-
var grantsDir = () =>
|
|
15228
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
14411
15229
|
var grantsOverride = () => {
|
|
14412
|
-
const configured =
|
|
14413
|
-
return configured ===
|
|
15230
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
15231
|
+
return configured === path17.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 ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
14414
15232
|
};
|
|
14415
15233
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
14416
15234
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -14463,8 +15281,16 @@ function endpointView(entry, context) {
|
|
|
14463
15281
|
...issue ? { endpointIssue: issue } : {}
|
|
14464
15282
|
};
|
|
14465
15283
|
}
|
|
14466
|
-
var
|
|
14467
|
-
|
|
15284
|
+
var VERIFICATION_MODES = [
|
|
15285
|
+
"proportional",
|
|
15286
|
+
"self",
|
|
15287
|
+
"independent"
|
|
15288
|
+
];
|
|
15289
|
+
var DEFAULT_VERIFICATION = "proportional";
|
|
15290
|
+
var verificationOf = (entry) => {
|
|
15291
|
+
const value = entry?.verification;
|
|
15292
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
15293
|
+
};
|
|
14468
15294
|
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
14469
15295
|
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
14470
15296
|
var fromAutoApprove = (value) => {
|
|
@@ -16423,7 +17249,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
16423
17249
|
return null;
|
|
16424
17250
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
16425
17251
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
16426
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
17252
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path18.resolve(store.root, ref.path)] : []));
|
|
16427
17253
|
const observedFiles = filesKey(files);
|
|
16428
17254
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
16429
17255
|
if (!compact.ok || !compact.data)
|
|
@@ -16501,14 +17327,14 @@ var turnMarker = (input) => {
|
|
|
16501
17327
|
if (location instanceof Error)
|
|
16502
17328
|
return null;
|
|
16503
17329
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
16504
|
-
return
|
|
17330
|
+
return path18.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
16505
17331
|
} catch {
|
|
16506
17332
|
return null;
|
|
16507
17333
|
}
|
|
16508
17334
|
};
|
|
16509
17335
|
var writeTurnDigest = (file, digest) => {
|
|
16510
17336
|
try {
|
|
16511
|
-
fs13.mkdirSync(
|
|
17337
|
+
fs13.mkdirSync(path18.dirname(file), { recursive: true });
|
|
16512
17338
|
fs13.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
16513
17339
|
`);
|
|
16514
17340
|
} catch {}
|
|
@@ -16523,7 +17349,7 @@ var pruneTurnDigests = (dir, now) => {
|
|
|
16523
17349
|
for (const name of fs13.readdirSync(dir)) {
|
|
16524
17350
|
if (!name.startsWith("turn-"))
|
|
16525
17351
|
continue;
|
|
16526
|
-
const file =
|
|
17352
|
+
const file = path18.join(dir, name);
|
|
16527
17353
|
try {
|
|
16528
17354
|
if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
16529
17355
|
fs13.rmSync(file, { force: true });
|
|
@@ -16553,7 +17379,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
16553
17379
|
const file = turnMarker(input);
|
|
16554
17380
|
if (!file)
|
|
16555
17381
|
return;
|
|
16556
|
-
pruneTurnDigests(
|
|
17382
|
+
pruneTurnDigests(path18.dirname(file), Date.now());
|
|
16557
17383
|
if (text)
|
|
16558
17384
|
writeTurnDigest(file, digestOf(text));
|
|
16559
17385
|
else
|
|
@@ -16561,20 +17387,20 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
16561
17387
|
};
|
|
16562
17388
|
// packages/workit-core/src/core/route-intent.ts
|
|
16563
17389
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
16564
|
-
import { existsSync as
|
|
16565
|
-
import
|
|
17390
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
17391
|
+
import path21 from "node:path";
|
|
16566
17392
|
|
|
16567
17393
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
16568
17394
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
16569
17395
|
import { createHash as createHash14 } from "node:crypto";
|
|
16570
17396
|
import fs15 from "node:fs";
|
|
16571
17397
|
import os5 from "node:os";
|
|
16572
|
-
import
|
|
17398
|
+
import path20 from "node:path";
|
|
16573
17399
|
|
|
16574
17400
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
16575
17401
|
import { createHash as createHash13 } from "node:crypto";
|
|
16576
17402
|
import fs14 from "node:fs";
|
|
16577
|
-
import
|
|
17403
|
+
import path19 from "node:path";
|
|
16578
17404
|
var sessionMarkerFile = (input, session, store) => {
|
|
16579
17405
|
let dir = store;
|
|
16580
17406
|
if (dir === undefined) {
|
|
@@ -16584,7 +17410,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16584
17410
|
dir = location.dir;
|
|
16585
17411
|
}
|
|
16586
17412
|
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
16587
|
-
return
|
|
17413
|
+
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
16588
17414
|
};
|
|
16589
17415
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16590
17416
|
var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
|
|
@@ -16611,7 +17437,7 @@ var parseMarker = (text) => {
|
|
|
16611
17437
|
};
|
|
16612
17438
|
var readSessionMarker = (file) => parseMarker(readRaw(file));
|
|
16613
17439
|
var writeAtomic = (file, marker) => {
|
|
16614
|
-
fs14.mkdirSync(
|
|
17440
|
+
fs14.mkdirSync(path19.dirname(file), { recursive: true });
|
|
16615
17441
|
const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
16616
17442
|
try {
|
|
16617
17443
|
fs14.writeFileSync(temp, JSON.stringify(marker));
|
|
@@ -17139,17 +17965,17 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
17139
17965
|
]);
|
|
17140
17966
|
var GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
|
|
17141
17967
|
var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
17142
|
-
var
|
|
17968
|
+
var ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
17143
17969
|
var MAX_DEPTH = 4;
|
|
17144
17970
|
var MAX_EXPANSIONS = 16;
|
|
17145
|
-
var isAbsolute = (value) =>
|
|
17971
|
+
var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
17146
17972
|
var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
17147
17973
|
var joinDir = (base, raw) => {
|
|
17148
17974
|
const next = raw.replace(HERE, ".");
|
|
17149
17975
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17150
17976
|
return;
|
|
17151
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
17152
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
17977
|
+
const expanded = next === "~" || next.startsWith("~/") ? path20.join(os5.homedir(), next.slice(1)) : next;
|
|
17978
|
+
return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
|
|
17153
17979
|
};
|
|
17154
17980
|
var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
|
|
17155
17981
|
var cdTarget = (words) => {
|
|
@@ -17166,7 +17992,7 @@ var stripPrefixes = (input) => {
|
|
|
17166
17992
|
const first = words[0];
|
|
17167
17993
|
if (first === undefined)
|
|
17168
17994
|
return { words, env };
|
|
17169
|
-
const assignment =
|
|
17995
|
+
const assignment = ASSIGNMENT2.exec(first);
|
|
17170
17996
|
if (assignment) {
|
|
17171
17997
|
if (GIT_CONFIG_ENV.test(assignment[1]))
|
|
17172
17998
|
env[assignment[1]] = assignment[2];
|
|
@@ -17225,10 +18051,10 @@ var assignments = (words, vars) => {
|
|
|
17225
18051
|
while (rest[0]?.startsWith("-"))
|
|
17226
18052
|
rest = rest.slice(1);
|
|
17227
18053
|
}
|
|
17228
|
-
if (rest.length === 0 || !rest.every((word) =>
|
|
18054
|
+
if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
|
|
17229
18055
|
return null;
|
|
17230
18056
|
return rest.map((word) => {
|
|
17231
|
-
const [, name, value] =
|
|
18057
|
+
const [, name, value] = ASSIGNMENT2.exec(word);
|
|
17232
18058
|
return [name, expandWord(value, vars)];
|
|
17233
18059
|
});
|
|
17234
18060
|
};
|
|
@@ -17416,7 +18242,7 @@ var splitString = (line) => {
|
|
|
17416
18242
|
return line.slice(index).join(" ").slice(15);
|
|
17417
18243
|
if (/^-S./.test(word))
|
|
17418
18244
|
return [word.slice(2), ...line.slice(index + 1)].join(" ");
|
|
17419
|
-
if (!word.startsWith("-") && !
|
|
18245
|
+
if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
|
|
17420
18246
|
return null;
|
|
17421
18247
|
}
|
|
17422
18248
|
return null;
|
|
@@ -18000,17 +18826,17 @@ var headSha2 = (repo) => {
|
|
|
18000
18826
|
if (!git || git.reftable)
|
|
18001
18827
|
return null;
|
|
18002
18828
|
try {
|
|
18003
|
-
const head = fs15.readFileSync(
|
|
18829
|
+
const head = fs15.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18004
18830
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18005
18831
|
return head;
|
|
18006
18832
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18007
18833
|
if (!ref)
|
|
18008
18834
|
return null;
|
|
18009
|
-
const common =
|
|
18835
|
+
const common = path20.dirname(repo.location.dir);
|
|
18010
18836
|
try {
|
|
18011
|
-
return fs15.readFileSync(
|
|
18837
|
+
return fs15.readFileSync(path20.join(common, ref), "utf8").trim() || null;
|
|
18012
18838
|
} catch {}
|
|
18013
|
-
const packed = fs15.readFileSync(
|
|
18839
|
+
const packed = fs15.readFileSync(path20.join(common, "packed-refs"), "utf8");
|
|
18014
18840
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18015
18841
|
} catch {
|
|
18016
18842
|
return null;
|
|
@@ -18098,7 +18924,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
18098
18924
|
const sub = invocation.args[0];
|
|
18099
18925
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
18100
18926
|
return [invocation];
|
|
18101
|
-
const dir =
|
|
18927
|
+
const dir = path20.resolve(root, invocation.dir ?? ".");
|
|
18102
18928
|
if (!fs15.existsSync(dir))
|
|
18103
18929
|
return [invocation];
|
|
18104
18930
|
const config = gitConfig(dir, invocation, cache);
|
|
@@ -18121,11 +18947,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
18121
18947
|
var normalizedUrl = (url, dir) => {
|
|
18122
18948
|
const trimmed = url.trim();
|
|
18123
18949
|
if (/^file:\/\//i.test(trimmed))
|
|
18124
|
-
return
|
|
18950
|
+
return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18125
18951
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
18126
18952
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
18127
18953
|
if (!scheme && !scp)
|
|
18128
|
-
return
|
|
18954
|
+
return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18129
18955
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
18130
18956
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18131
18957
|
};
|
|
@@ -18145,7 +18971,7 @@ var rewrittenUrl = (url, config) => {
|
|
|
18145
18971
|
}
|
|
18146
18972
|
return url;
|
|
18147
18973
|
};
|
|
18148
|
-
var
|
|
18974
|
+
var pushUrl2 = (config, remote, dir) => {
|
|
18149
18975
|
const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
|
|
18150
18976
|
return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
|
|
18151
18977
|
};
|
|
@@ -18173,7 +18999,7 @@ var toPushRemote = (repo, remote, config) => {
|
|
|
18173
18999
|
const pushRemote = pushRemoteOf(config, repo.branch);
|
|
18174
19000
|
if (pushRemote === null || pushRemote === remote)
|
|
18175
19001
|
return true;
|
|
18176
|
-
return
|
|
19002
|
+
return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
|
|
18177
19003
|
};
|
|
18178
19004
|
var quote = (text) => `\`${text}\``;
|
|
18179
19005
|
var refusal = (action, branch, dir) => {
|
|
@@ -18349,7 +19175,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
|
18349
19175
|
const out = [];
|
|
18350
19176
|
const repos = new Map;
|
|
18351
19177
|
const repoFor = (relative) => {
|
|
18352
|
-
const dir =
|
|
19178
|
+
const dir = path20.resolve(cwd, relative ?? ".");
|
|
18353
19179
|
if (!repos.has(dir)) {
|
|
18354
19180
|
const repo = repoAt(dir);
|
|
18355
19181
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18435,7 +19261,7 @@ var analysisDenial = (error) => ({
|
|
|
18435
19261
|
});
|
|
18436
19262
|
var NEXT_COMMAND = "next";
|
|
18437
19263
|
var MARKER_TTL_SECONDS = 3600;
|
|
18438
|
-
var markerFile = (repo, session, key) =>
|
|
19264
|
+
var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18439
19265
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
18440
19266
|
function noteRawCommit(input, command, key) {
|
|
18441
19267
|
try {
|
|
@@ -18446,7 +19272,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18446
19272
|
if (action.kind !== "commit")
|
|
18447
19273
|
continue;
|
|
18448
19274
|
const file = markerFile(repo, input.session.id, key);
|
|
18449
|
-
fs15.mkdirSync(
|
|
19275
|
+
fs15.mkdirSync(path20.dirname(file), { recursive: true });
|
|
18450
19276
|
fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18451
19277
|
`);
|
|
18452
19278
|
}
|
|
@@ -18677,8 +19503,8 @@ var judgedDir = (root, dir) => {
|
|
|
18677
19503
|
return null;
|
|
18678
19504
|
if (dir === null)
|
|
18679
19505
|
return root;
|
|
18680
|
-
const resolved =
|
|
18681
|
-
return
|
|
19506
|
+
const resolved = path21.resolve(root, dir);
|
|
19507
|
+
return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18682
19508
|
};
|
|
18683
19509
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18684
19510
|
let judged = false;
|
|
@@ -18714,7 +19540,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
18714
19540
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
18715
19541
|
import fs16 from "node:fs";
|
|
18716
19542
|
import os6 from "node:os";
|
|
18717
|
-
import
|
|
19543
|
+
import path22 from "node:path";
|
|
18718
19544
|
var EMPTY2 = () => ({ deny: [], allow: [] });
|
|
18719
19545
|
var readText2 = (file) => {
|
|
18720
19546
|
try {
|
|
@@ -18755,19 +19581,19 @@ function parseJsonc(text) {
|
|
|
18755
19581
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
18756
19582
|
}
|
|
18757
19583
|
function gitTop(cwd) {
|
|
18758
|
-
for (let dir =
|
|
18759
|
-
if (fs16.existsSync(
|
|
19584
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
19585
|
+
if (fs16.existsSync(path22.join(dir, ".git")))
|
|
18760
19586
|
return dir;
|
|
18761
|
-
if (
|
|
19587
|
+
if (path22.dirname(dir) === dir)
|
|
18762
19588
|
return null;
|
|
18763
19589
|
}
|
|
18764
19590
|
}
|
|
18765
19591
|
var projectDirs = (cwd) => {
|
|
18766
19592
|
const top = gitTop(cwd);
|
|
18767
19593
|
const dirs = [];
|
|
18768
|
-
for (let dir =
|
|
19594
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
18769
19595
|
dirs.push(dir);
|
|
18770
|
-
if (!top || dir === top ||
|
|
19596
|
+
if (!top || dir === top || path22.dirname(dir) === dir)
|
|
18771
19597
|
return dirs;
|
|
18772
19598
|
}
|
|
18773
19599
|
};
|
|
@@ -18779,12 +19605,12 @@ var CLAUDE_MANAGED = {
|
|
|
18779
19605
|
};
|
|
18780
19606
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
18781
19607
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
18782
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
19608
|
+
const user = env.CLAUDE_CONFIG_DIR || path22.join(homeOf(env), ".claude");
|
|
18783
19609
|
return [
|
|
18784
19610
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
19611
|
+
path22.join(user, "settings.json"),
|
|
19612
|
+
path22.join(project, ".claude", "settings.json"),
|
|
19613
|
+
path22.join(project, ".claude", "settings.local.json")
|
|
18788
19614
|
];
|
|
18789
19615
|
}
|
|
18790
19616
|
var bashPattern = (entry) => {
|
|
@@ -18874,10 +19700,10 @@ var opencodeConfig = (value, rules) => {
|
|
|
18874
19700
|
function opencodeRules(cwd, env = process.env) {
|
|
18875
19701
|
const rules = EMPTY2();
|
|
18876
19702
|
try {
|
|
18877
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
19703
|
+
const config = env.XDG_CONFIG_HOME || path22.join(homeOf(env), ".config");
|
|
18878
19704
|
const files = [
|
|
18879
|
-
...OPENCODE_NAMES.map((name) =>
|
|
18880
|
-
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [
|
|
19705
|
+
...OPENCODE_NAMES.map((name) => path22.join(config, "opencode", name)),
|
|
19706
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path22.join(dir, name), path22.join(dir, ".opencode", name)])),
|
|
18881
19707
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
18882
19708
|
];
|
|
18883
19709
|
for (const file of files) {
|
|
@@ -18921,9 +19747,9 @@ var codexRule = (body) => {
|
|
|
18921
19747
|
function codexRules(cwd, env = process.env) {
|
|
18922
19748
|
const rules = EMPTY2();
|
|
18923
19749
|
try {
|
|
18924
|
-
const home = env.CODEX_HOME ||
|
|
19750
|
+
const home = env.CODEX_HOME || path22.join(homeOf(env), ".codex");
|
|
18925
19751
|
const top = gitTop(cwd) ?? cwd;
|
|
18926
|
-
for (const dir of [
|
|
19752
|
+
for (const dir of [path22.join(home, "rules"), path22.join(top, ".codex", "rules")]) {
|
|
18927
19753
|
let files;
|
|
18928
19754
|
try {
|
|
18929
19755
|
files = fs16.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -18933,7 +19759,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
18933
19759
|
return "unreadable";
|
|
18934
19760
|
}
|
|
18935
19761
|
for (const name of files) {
|
|
18936
|
-
const text = readText2(
|
|
19762
|
+
const text = readText2(path22.join(dir, name)) ?? "";
|
|
18937
19763
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
18938
19764
|
const rule = codexRule(part);
|
|
18939
19765
|
if (!rule)
|
|
@@ -18952,8 +19778,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
18952
19778
|
try {
|
|
18953
19779
|
const top = gitTop(cwd) ?? cwd;
|
|
18954
19780
|
for (const file of [
|
|
18955
|
-
|
|
18956
|
-
|
|
19781
|
+
path22.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
19782
|
+
path22.join(top, ".cursor", "cli.json")
|
|
18957
19783
|
]) {
|
|
18958
19784
|
const text = readText2(file);
|
|
18959
19785
|
if (text === null)
|
|
@@ -19029,21 +19855,21 @@ function wrapAllowed(read, words, command, options = {}) {
|
|
|
19029
19855
|
// packages/workit-core/src/core/heavy.ts
|
|
19030
19856
|
import fs17 from "node:fs";
|
|
19031
19857
|
import os7 from "node:os";
|
|
19032
|
-
import
|
|
19858
|
+
import path23 from "node:path";
|
|
19033
19859
|
function stateDir(env, platform = process.platform) {
|
|
19034
19860
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19035
19861
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19036
19862
|
if (env.XDG_STATE_HOME)
|
|
19037
|
-
return
|
|
19863
|
+
return path23.join(env.XDG_STATE_HOME, "workit");
|
|
19038
19864
|
const home = env.HOME || os7.homedir();
|
|
19039
19865
|
if (platform === "darwin")
|
|
19040
|
-
return
|
|
19866
|
+
return path23.join(home, "Library", "Application Support", "workit");
|
|
19041
19867
|
if (platform === "win32")
|
|
19042
|
-
return
|
|
19043
|
-
return
|
|
19868
|
+
return path23.join(env.LOCALAPPDATA ?? path23.join(home, "AppData", "Local"), "workit");
|
|
19869
|
+
return path23.join(home, ".local", "state", "workit");
|
|
19044
19870
|
}
|
|
19045
19871
|
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
19046
|
-
var heavyCommandsFile = (env) =>
|
|
19872
|
+
var heavyCommandsFile = (env) => path23.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
19047
19873
|
var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
|
|
19048
19874
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
19049
19875
|
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
@@ -19318,7 +20144,7 @@ function machinePatterns(env) {
|
|
|
19318
20144
|
return isRecord4(value) ? stringList(value.patterns) : [];
|
|
19319
20145
|
}
|
|
19320
20146
|
function repoPatterns(cwd) {
|
|
19321
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
20147
|
+
return repoHeavyCommands(gitTop(cwd) ?? path23.resolve(cwd));
|
|
19322
20148
|
}
|
|
19323
20149
|
function heavyLines(lines, options) {
|
|
19324
20150
|
const found = [];
|
|
@@ -19396,12 +20222,12 @@ function heavyPlan(command, options) {
|
|
|
19396
20222
|
}
|
|
19397
20223
|
function workitOnPath(env = process.env) {
|
|
19398
20224
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
19399
|
-
for (const dir of (env.PATH ?? "").split(
|
|
20225
|
+
for (const dir of (env.PATH ?? "").split(path23.delimiter))
|
|
19400
20226
|
for (const name of names) {
|
|
19401
20227
|
if (!dir)
|
|
19402
20228
|
continue;
|
|
19403
20229
|
try {
|
|
19404
|
-
fs17.accessSync(
|
|
20230
|
+
fs17.accessSync(path23.join(dir, name), fs17.constants.X_OK);
|
|
19405
20231
|
return true;
|
|
19406
20232
|
} catch {}
|
|
19407
20233
|
}
|
|
@@ -19461,16 +20287,16 @@ var splitDenial = (plan) => ({
|
|
|
19461
20287
|
unblock: plan.unblock
|
|
19462
20288
|
});
|
|
19463
20289
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19464
|
-
import { existsSync as
|
|
20290
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
19465
20291
|
import os9 from "node:os";
|
|
19466
|
-
import
|
|
20292
|
+
import path25 from "node:path";
|
|
19467
20293
|
|
|
19468
20294
|
// packages/workit-core/src/core/live-session.ts
|
|
19469
20295
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
19470
20296
|
import { createHash as createHash15 } from "node:crypto";
|
|
19471
20297
|
import fs18 from "node:fs";
|
|
19472
20298
|
import os8 from "node:os";
|
|
19473
|
-
import
|
|
20299
|
+
import path24 from "node:path";
|
|
19474
20300
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
19475
20301
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
19476
20302
|
var liveProbe = {
|
|
@@ -19544,15 +20370,15 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
19544
20370
|
};
|
|
19545
20371
|
};
|
|
19546
20372
|
var checkoutOf = (target) => {
|
|
19547
|
-
let dir =
|
|
19548
|
-
while (!fs18.existsSync(dir) &&
|
|
19549
|
-
dir =
|
|
20373
|
+
let dir = path24.resolve(target);
|
|
20374
|
+
while (!fs18.existsSync(dir) && path24.dirname(dir) !== dir)
|
|
20375
|
+
dir = path24.dirname(dir);
|
|
19550
20376
|
if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
|
|
19551
|
-
dir =
|
|
19552
|
-
const parts = dir.split(
|
|
20377
|
+
dir = path24.dirname(dir);
|
|
20378
|
+
const parts = dir.split(path24.sep);
|
|
19553
20379
|
const dotGit = parts.lastIndexOf(".git");
|
|
19554
20380
|
if (dotGit > 0)
|
|
19555
|
-
dir = parts.slice(0, dotGit).join(
|
|
20381
|
+
dir = parts.slice(0, dotGit).join(path24.sep) || path24.sep;
|
|
19556
20382
|
const location = resolveStore(dir);
|
|
19557
20383
|
if (location instanceof Error || !location.shared || !location.top)
|
|
19558
20384
|
return null;
|
|
@@ -19562,9 +20388,9 @@ var checkoutOf = (target) => {
|
|
|
19562
20388
|
} catch {}
|
|
19563
20389
|
return top;
|
|
19564
20390
|
};
|
|
19565
|
-
var markerFile2 = (top) =>
|
|
20391
|
+
var markerFile2 = (top) => path24.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
19566
20392
|
var checkoutKey = (top, platform = process.platform) => {
|
|
19567
|
-
let key =
|
|
20393
|
+
let key = path24.resolve(top);
|
|
19568
20394
|
try {
|
|
19569
20395
|
key = fs18.realpathSync.native(key);
|
|
19570
20396
|
} catch {}
|
|
@@ -19659,7 +20485,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
19659
20485
|
checkout,
|
|
19660
20486
|
heartbeatAt: now.toISOString()
|
|
19661
20487
|
};
|
|
19662
|
-
fs18.mkdirSync(
|
|
20488
|
+
fs18.mkdirSync(path24.dirname(file), { recursive: true });
|
|
19663
20489
|
const temp = `${file}.${process.pid}.tmp`;
|
|
19664
20490
|
fs18.writeFileSync(temp, JSON.stringify(marker));
|
|
19665
20491
|
fs18.renameSync(temp, file);
|
|
@@ -19671,22 +20497,22 @@ var NONE3 = { kind: "none" };
|
|
|
19671
20497
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
19672
20498
|
var canonical2 = (file) => {
|
|
19673
20499
|
let rest = "";
|
|
19674
|
-
for (let dir =
|
|
20500
|
+
for (let dir = path25.resolve(file);; dir = path25.dirname(dir)) {
|
|
19675
20501
|
try {
|
|
19676
|
-
return
|
|
20502
|
+
return path25.join(realpathSync5.native(dir), rest);
|
|
19677
20503
|
} catch {
|
|
19678
|
-
if (
|
|
19679
|
-
return
|
|
19680
|
-
rest = rest ?
|
|
20504
|
+
if (path25.dirname(dir) === dir)
|
|
20505
|
+
return path25.resolve(file);
|
|
20506
|
+
rest = rest ? path25.join(path25.basename(dir), rest) : path25.basename(dir);
|
|
19681
20507
|
}
|
|
19682
20508
|
}
|
|
19683
20509
|
};
|
|
19684
20510
|
var exempt = (cwd, root, file, plans) => {
|
|
19685
20511
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
19686
|
-
const relative =
|
|
19687
|
-
if (relative.startsWith("..") ||
|
|
20512
|
+
const relative = path25.relative(fold(canonical2(root)), fold(canonical2(path25.resolve(cwd, file))));
|
|
20513
|
+
if (relative.startsWith("..") || path25.isAbsolute(relative))
|
|
19688
20514
|
return true;
|
|
19689
|
-
const posix = relative.split(
|
|
20515
|
+
const posix = relative.split(path25.sep).join("/");
|
|
19690
20516
|
const segments = posix.split("/");
|
|
19691
20517
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
19692
20518
|
};
|
|
@@ -19863,10 +20689,10 @@ var linkSources = (args, at) => {
|
|
|
19863
20689
|
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
19864
20690
|
const target = dest === undefined ? null : at(dest);
|
|
19865
20691
|
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
19866
|
-
const base = target === null ? null : into >= 0 || sources.length > 1 ||
|
|
20692
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path25.dirname(target);
|
|
19867
20693
|
return sources.flatMap((source) => {
|
|
19868
|
-
if (symbolic && base !== null && !
|
|
19869
|
-
return [
|
|
20694
|
+
if (symbolic && base !== null && !path25.isAbsolute(homeExpanded(source)))
|
|
20695
|
+
return [path25.resolve(base, source)];
|
|
19870
20696
|
const resolved = at(source);
|
|
19871
20697
|
return resolved ? [resolved] : [];
|
|
19872
20698
|
});
|
|
@@ -19895,7 +20721,7 @@ var shellWrites = (command, depth = 0) => {
|
|
|
19895
20721
|
return { writes: true, targets: unknown ? null : targets };
|
|
19896
20722
|
};
|
|
19897
20723
|
var cache2 = new Map;
|
|
19898
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20724
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path25.resolve(root, file)) ?? "-").join("\x00");
|
|
19899
20725
|
var gateFor = (cwd) => {
|
|
19900
20726
|
const store = new TaskStore(cwd);
|
|
19901
20727
|
const listed = store.listTaskIndex();
|
|
@@ -19974,13 +20800,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
19974
20800
|
return [];
|
|
19975
20801
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
19976
20802
|
return [];
|
|
19977
|
-
return [
|
|
20803
|
+
return [path25.resolve(cwd, invocation.dir ?? ".")];
|
|
19978
20804
|
});
|
|
19979
20805
|
};
|
|
19980
20806
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
19981
20807
|
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
19982
20808
|
var POPD2 = new Set(["popd", "pop-location"]);
|
|
19983
|
-
var UNKNOWN_DIR =
|
|
20809
|
+
var UNKNOWN_DIR = path25.resolve(path25.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
19984
20810
|
var cdInto = (dir, words, strict = false) => {
|
|
19985
20811
|
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
19986
20812
|
const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
@@ -19989,7 +20815,7 @@ var cdInto = (dir, words, strict = false) => {
|
|
|
19989
20815
|
return lost;
|
|
19990
20816
|
if (dir === undefined)
|
|
19991
20817
|
return lost;
|
|
19992
|
-
return target === undefined ? os9.homedir() :
|
|
20818
|
+
return target === undefined ? os9.homedir() : path25.resolve(dir, target);
|
|
19993
20819
|
};
|
|
19994
20820
|
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
19995
20821
|
var homeExpanded = (file) => {
|
|
@@ -20003,7 +20829,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20003
20829
|
const groups = [];
|
|
20004
20830
|
const at = (file) => {
|
|
20005
20831
|
const home = homeExpanded(file);
|
|
20006
|
-
return
|
|
20832
|
+
return path25.isAbsolute(home) ? home : dir === undefined ? null : path25.resolve(dir, home);
|
|
20007
20833
|
};
|
|
20008
20834
|
for (const segment of segmentsOf(command, dialect)) {
|
|
20009
20835
|
if (segment.group === "open") {
|
|
@@ -20043,7 +20869,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20043
20869
|
for (const file of opaque ?? []) {
|
|
20044
20870
|
const resolved = at(file);
|
|
20045
20871
|
if (resolved)
|
|
20046
|
-
targets.push(file === "." ?
|
|
20872
|
+
targets.push(file === "." ? path25.join(resolved, DIR_TARGET) : resolved);
|
|
20047
20873
|
}
|
|
20048
20874
|
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
20049
20875
|
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
@@ -20058,7 +20884,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20058
20884
|
const edits = editsOf(segment.words);
|
|
20059
20885
|
if (edits === null) {
|
|
20060
20886
|
if (dir !== undefined)
|
|
20061
|
-
targets.push(
|
|
20887
|
+
targets.push(path25.join(dir, DIR_TARGET));
|
|
20062
20888
|
} else
|
|
20063
20889
|
for (const file of edits) {
|
|
20064
20890
|
const resolved = at(file);
|
|
@@ -20069,20 +20895,20 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
20069
20895
|
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
20070
20896
|
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
20071
20897
|
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
20072
|
-
targets.push(
|
|
20898
|
+
targets.push(path25.join(runsIn, DIR_TARGET));
|
|
20073
20899
|
}
|
|
20074
20900
|
}
|
|
20075
20901
|
return targets;
|
|
20076
20902
|
};
|
|
20077
20903
|
var workitCwd = (dir, args) => {
|
|
20078
20904
|
const { rest, cwd } = skipWorkitGlobals(args);
|
|
20079
|
-
return { at: cwd === null ? dir :
|
|
20905
|
+
return { at: cwd === null ? dir : path25.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
20080
20906
|
};
|
|
20081
20907
|
var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
20082
20908
|
try {
|
|
20083
20909
|
return [
|
|
20084
20910
|
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
20085
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
20911
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path25.join(dir, DIR_TARGET))
|
|
20086
20912
|
];
|
|
20087
20913
|
} catch {
|
|
20088
20914
|
return [];
|
|
@@ -20119,7 +20945,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
20119
20945
|
var guarded = (targets) => {
|
|
20120
20946
|
const seen = new Set;
|
|
20121
20947
|
return targets.filter((target) => {
|
|
20122
|
-
const dir =
|
|
20948
|
+
const dir = path25.dirname(target);
|
|
20123
20949
|
if (seen.has(dir))
|
|
20124
20950
|
return false;
|
|
20125
20951
|
seen.add(dir);
|
|
@@ -20140,7 +20966,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
20140
20966
|
for (const target of guarded(targets))
|
|
20141
20967
|
claimLiveSession(target, owner, deps);
|
|
20142
20968
|
};
|
|
20143
|
-
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
20969
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path25.resolve(cwd, file)) : [path25.join(cwd, DIR_TARGET)];
|
|
20144
20970
|
var issuedTarget = (cwd, targets) => {
|
|
20145
20971
|
const state = resolveStateDir();
|
|
20146
20972
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
@@ -20148,7 +20974,7 @@ var issuedTarget = (cwd, targets) => {
|
|
|
20148
20974
|
};
|
|
20149
20975
|
var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
20150
20976
|
const win = platform === "win32";
|
|
20151
|
-
const p = win ?
|
|
20977
|
+
const p = win ? path25.win32 : path25.posix;
|
|
20152
20978
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
20153
20979
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
20154
20980
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -20239,6 +21065,24 @@ function beforeWriteGate(cwd, paths) {
|
|
|
20239
21065
|
}
|
|
20240
21066
|
// packages/workit-core/src/hooks/stop.ts
|
|
20241
21067
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
21068
|
+
|
|
21069
|
+
// packages/workit-core/src/core/policy/current-judgment.ts
|
|
21070
|
+
function currentJudgment(cwd) {
|
|
21071
|
+
try {
|
|
21072
|
+
const store = new TaskStore(cwd);
|
|
21073
|
+
const listed = store.listTaskIndex();
|
|
21074
|
+
const key = store.currentKey();
|
|
21075
|
+
if (!listed.ok || !key.ok)
|
|
21076
|
+
return null;
|
|
21077
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
21078
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
21079
|
+
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
21080
|
+
} catch {
|
|
21081
|
+
return null;
|
|
21082
|
+
}
|
|
21083
|
+
}
|
|
21084
|
+
|
|
21085
|
+
// packages/workit-core/src/hooks/stop.ts
|
|
20242
21086
|
var NONE4 = { kind: "none" };
|
|
20243
21087
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
20244
21088
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -20331,29 +21175,32 @@ var redChecks = (rows, pr, head, endpoint) => {
|
|
|
20331
21175
|
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)" : ""}.`
|
|
20332
21176
|
};
|
|
20333
21177
|
};
|
|
20334
|
-
var
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
const listed = store.listTaskIndex();
|
|
20338
|
-
const key = store.currentKey();
|
|
20339
|
-
if (!listed.ok || !key.ok)
|
|
20340
|
-
return null;
|
|
20341
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
20342
|
-
const task = entry ? store.readTask(entry.id) : null;
|
|
20343
|
-
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
20344
|
-
} catch {
|
|
20345
|
-
return null;
|
|
20346
|
-
}
|
|
21178
|
+
var prBase = (rows) => {
|
|
21179
|
+
const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
|
|
21180
|
+
return typeof seen?.base === "string" ? seen.base : null;
|
|
20347
21181
|
};
|
|
20348
|
-
var unverified = (cwd, branch, all, descriptor, verification) => {
|
|
21182
|
+
var unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
20349
21183
|
const check = checkVerdicts(cwd, branch, all);
|
|
20350
21184
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
20351
21185
|
return null;
|
|
20352
|
-
const judgment =
|
|
21186
|
+
const judgment = currentJudgment(cwd);
|
|
20353
21187
|
const high = judgment?.riskTier === "high";
|
|
20354
|
-
|
|
20355
|
-
|
|
20356
|
-
|
|
21188
|
+
let independent;
|
|
21189
|
+
if (verification === "proportional") {
|
|
21190
|
+
const lane = branchLane(cwd, {
|
|
21191
|
+
base: prBase(all.filter((row) => row.branch === branch)),
|
|
21192
|
+
head,
|
|
21193
|
+
judgment
|
|
21194
|
+
});
|
|
21195
|
+
const need = laneNeed(lane.lane);
|
|
21196
|
+
if (need === "none")
|
|
21197
|
+
return null;
|
|
21198
|
+
independent = need === "non-author";
|
|
21199
|
+
} else {
|
|
21200
|
+
if (judgment && !judgment.behaviorChange && !high)
|
|
21201
|
+
return null;
|
|
21202
|
+
independent = verification === "independent" || high;
|
|
21203
|
+
}
|
|
20357
21204
|
if (!independent && check.review === "self-reviewed")
|
|
20358
21205
|
return null;
|
|
20359
21206
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -20400,7 +21247,7 @@ function stopObligation(input, descriptor) {
|
|
|
20400
21247
|
}
|
|
20401
21248
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
20402
21249
|
return null;
|
|
20403
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
21250
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
20404
21251
|
return left ? { ...left, head } : null;
|
|
20405
21252
|
} catch {
|
|
20406
21253
|
return null;
|
|
@@ -20661,13 +21508,13 @@ ${line}` };
|
|
|
20661
21508
|
return decision;
|
|
20662
21509
|
};
|
|
20663
21510
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20664
|
-
import { existsSync as
|
|
20665
|
-
import
|
|
21511
|
+
import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21512
|
+
import path26 from "node:path";
|
|
20666
21513
|
var isRecord5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20667
21514
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
20668
21515
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
20669
21516
|
var existingDirectory = (value) => {
|
|
20670
|
-
if (!nonEmpty2(value) || !
|
|
21517
|
+
if (!nonEmpty2(value) || !path26.isAbsolute(value) || !existsSync9(value))
|
|
20671
21518
|
return null;
|
|
20672
21519
|
try {
|
|
20673
21520
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20735,9 +21582,9 @@ var writeTargets = (input) => {
|
|
|
20735
21582
|
};
|
|
20736
21583
|
// packages/workit-core/src/hooks/standing.ts
|
|
20737
21584
|
import fs19 from "node:fs";
|
|
20738
|
-
import
|
|
21585
|
+
import path27 from "node:path";
|
|
20739
21586
|
var readPlans = (store) => {
|
|
20740
|
-
const dir =
|
|
21587
|
+
const dir = path27.join(store, "fanouts");
|
|
20741
21588
|
let names;
|
|
20742
21589
|
try {
|
|
20743
21590
|
names = fs19.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -20747,7 +21594,7 @@ var readPlans = (store) => {
|
|
|
20747
21594
|
const plans = [];
|
|
20748
21595
|
for (const name of names) {
|
|
20749
21596
|
try {
|
|
20750
|
-
const raw = JSON.parse(fs19.readFileSync(
|
|
21597
|
+
const raw = JSON.parse(fs19.readFileSync(path27.join(dir, name), "utf8"));
|
|
20751
21598
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
20752
21599
|
continue;
|
|
20753
21600
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -20873,40 +21720,40 @@ var cwdRewrite = (command) => {
|
|
|
20873
21720
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
20874
21721
|
import { createHash as createHash16 } from "node:crypto";
|
|
20875
21722
|
import fs20 from "node:fs";
|
|
20876
|
-
import
|
|
21723
|
+
import path28 from "node:path";
|
|
20877
21724
|
var FANOUT_TOKEN_FILE = "workit-fanout-token";
|
|
20878
21725
|
var NONE5 = { kind: "none" };
|
|
20879
21726
|
var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
|
|
20880
21727
|
var canonical3 = (file) => {
|
|
20881
21728
|
let rest = "";
|
|
20882
|
-
for (let dir =
|
|
21729
|
+
for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
|
|
20883
21730
|
try {
|
|
20884
|
-
return
|
|
21731
|
+
return path28.join(fs20.realpathSync.native(dir), rest);
|
|
20885
21732
|
} catch {
|
|
20886
|
-
if (
|
|
20887
|
-
return
|
|
20888
|
-
rest = rest ?
|
|
21733
|
+
if (path28.dirname(dir) === dir)
|
|
21734
|
+
return path28.resolve(file);
|
|
21735
|
+
rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
|
|
20889
21736
|
}
|
|
20890
21737
|
}
|
|
20891
21738
|
};
|
|
20892
21739
|
var within = (child, parent) => {
|
|
20893
21740
|
const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
|
|
20894
|
-
const relative =
|
|
20895
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
21741
|
+
const relative = path28.relative(fold(parent), fold(child));
|
|
21742
|
+
return relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative);
|
|
20896
21743
|
};
|
|
20897
21744
|
var checkoutOf2 = (file) => {
|
|
20898
|
-
for (let dir = canonical3(file);; dir =
|
|
20899
|
-
const dotGit =
|
|
21745
|
+
for (let dir = canonical3(file);; dir = path28.dirname(dir)) {
|
|
21746
|
+
const dotGit = path28.join(dir, ".git");
|
|
20900
21747
|
if (fs20.existsSync(dotGit)) {
|
|
20901
21748
|
if (!fs20.statSync(dotGit).isFile())
|
|
20902
21749
|
return { root: dir, workit: false };
|
|
20903
21750
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs20.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
20904
21751
|
return {
|
|
20905
21752
|
root: dir,
|
|
20906
|
-
workit: Boolean(admin) && fs20.existsSync(
|
|
21753
|
+
workit: Boolean(admin) && fs20.existsSync(path28.join(path28.resolve(dir, admin), FANOUT_TOKEN_FILE))
|
|
20907
21754
|
};
|
|
20908
21755
|
}
|
|
20909
|
-
if (
|
|
21756
|
+
if (path28.dirname(dir) === dir)
|
|
20910
21757
|
return null;
|
|
20911
21758
|
}
|
|
20912
21759
|
};
|
|
@@ -20918,8 +21765,8 @@ var workitWorktreeOf = (file) => {
|
|
|
20918
21765
|
return null;
|
|
20919
21766
|
}
|
|
20920
21767
|
};
|
|
20921
|
-
var bindingDir = () =>
|
|
20922
|
-
var bindingFile = (session, agentId) =>
|
|
21768
|
+
var bindingDir = () => path28.join(resolveStateDir(), "worktree-bind");
|
|
21769
|
+
var bindingFile = (session, agentId) => path28.join(bindingDir(), `${createHash16("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
|
|
20923
21770
|
var readBinding = (file) => {
|
|
20924
21771
|
try {
|
|
20925
21772
|
const value = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
@@ -20944,7 +21791,7 @@ var bind = (input, worktree) => {
|
|
|
20944
21791
|
agentId,
|
|
20945
21792
|
at: new Date().toISOString()
|
|
20946
21793
|
};
|
|
20947
|
-
fs20.mkdirSync(
|
|
21794
|
+
fs20.mkdirSync(path28.dirname(file), { recursive: true });
|
|
20948
21795
|
fs20.writeFileSync(file, JSON.stringify(binding));
|
|
20949
21796
|
try {
|
|
20950
21797
|
if (inWorkitWorkspace(input.cwd))
|
|
@@ -20963,25 +21810,25 @@ var hostIsolated = (cwd, agentId) => {
|
|
|
20963
21810
|
const checkout = checkoutOf2(cwd);
|
|
20964
21811
|
if (!checkout)
|
|
20965
21812
|
return false;
|
|
20966
|
-
const dotGit =
|
|
21813
|
+
const dotGit = path28.join(checkout.root, ".git");
|
|
20967
21814
|
if (!fs20.statSync(dotGit).isFile())
|
|
20968
21815
|
return false;
|
|
20969
21816
|
const admin = /^gitdir:\s*(.+)$/mu.exec(fs20.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
20970
21817
|
if (!admin)
|
|
20971
21818
|
return false;
|
|
20972
|
-
const adminDir =
|
|
20973
|
-
const back = fs20.readFileSync(
|
|
20974
|
-
if (
|
|
21819
|
+
const adminDir = path28.resolve(checkout.root, admin);
|
|
21820
|
+
const back = fs20.readFileSync(path28.join(adminDir, "gitdir"), "utf8").trim();
|
|
21821
|
+
if (path28.relative(canonical3(path28.resolve(adminDir, back)), canonical3(dotGit)) !== "")
|
|
20975
21822
|
return false;
|
|
20976
|
-
const common =
|
|
20977
|
-
const gitDir = fs20.existsSync(common) ?
|
|
20978
|
-
const expected =
|
|
20979
|
-
return
|
|
21823
|
+
const common = path28.join(adminDir, "commondir");
|
|
21824
|
+
const gitDir = fs20.existsSync(common) ? path28.resolve(adminDir, fs20.readFileSync(common, "utf8").trim()) : adminDir;
|
|
21825
|
+
const expected = path28.join(path28.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
|
|
21826
|
+
return path28.relative(canonical3(expected), checkout.root) === "";
|
|
20980
21827
|
} catch {
|
|
20981
21828
|
return false;
|
|
20982
21829
|
}
|
|
20983
21830
|
};
|
|
20984
|
-
var shown = (target) =>
|
|
21831
|
+
var shown = (target) => path28.basename(target) === ".workit-live-target" ? path28.dirname(target) : target;
|
|
20985
21832
|
var unboundDeny = (input, target) => {
|
|
20986
21833
|
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.";
|
|
20987
21834
|
return {
|
|
@@ -21011,7 +21858,7 @@ function confineImplementer(input, targets) {
|
|
|
21011
21858
|
try {
|
|
21012
21859
|
if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
|
|
21013
21860
|
return NONE5;
|
|
21014
|
-
if (targets.some((target) => within(
|
|
21861
|
+
if (targets.some((target) => within(path28.resolve(target), UNKNOWN_DIR)))
|
|
21015
21862
|
return unknownDirDeny(input, boundWorktree(input));
|
|
21016
21863
|
const bindings = canonical3(bindingDir());
|
|
21017
21864
|
const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
|
|
@@ -21331,7 +22178,7 @@ var dispatchHook = (adapter, raw, env = process.env) => {
|
|
|
21331
22178
|
};
|
|
21332
22179
|
// packages/workit-core/src/hooks/deadline.ts
|
|
21333
22180
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
|
|
21334
|
-
import
|
|
22181
|
+
import path29 from "node:path";
|
|
21335
22182
|
var HOOK_SKIP_TEXT = "workit context skipped: host under load";
|
|
21336
22183
|
var HOOK_DEADLINE_MS = 8000;
|
|
21337
22184
|
var HOST_DEADLINES = {
|
|
@@ -21349,7 +22196,7 @@ var hookDeadlineMs = (env, host = "", native) => {
|
|
|
21349
22196
|
const row = HOST_DEADLINES[host];
|
|
21350
22197
|
return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
|
|
21351
22198
|
};
|
|
21352
|
-
var skipsFile = (stateDir) =>
|
|
22199
|
+
var skipsFile = (stateDir) => path29.join(stateDir, HOOK_SKIPS_FILE);
|
|
21353
22200
|
var recordHookSkip = (skip, stateDir = resolveStateDir()) => {
|
|
21354
22201
|
try {
|
|
21355
22202
|
mkdirSync3(stateDir, { recursive: true });
|
|
@@ -21994,7 +22841,7 @@ var codexAdapter = {
|
|
|
21994
22841
|
|
|
21995
22842
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21996
22843
|
import fs21 from "node:fs";
|
|
21997
|
-
import
|
|
22844
|
+
import path30 from "node:path";
|
|
21998
22845
|
var undocumented = { support: "undocumented", native: null };
|
|
21999
22846
|
var none = { support: "none", native: null };
|
|
22000
22847
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -22191,7 +23038,7 @@ var protocolEvent2 = (input) => {
|
|
|
22191
23038
|
};
|
|
22192
23039
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
22193
23040
|
var lastAssistantText = (file) => {
|
|
22194
|
-
if (!file || !
|
|
23041
|
+
if (!file || !path30.isAbsolute(file))
|
|
22195
23042
|
return null;
|
|
22196
23043
|
try {
|
|
22197
23044
|
const size = fs21.statSync(file).size;
|
|
@@ -22228,7 +23075,7 @@ var lastAssistantText = (file) => {
|
|
|
22228
23075
|
return null;
|
|
22229
23076
|
}
|
|
22230
23077
|
};
|
|
22231
|
-
var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
23078
|
+
var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path30.sep) ? root : `${root}${path30.sep}`);
|
|
22232
23079
|
var hookCwd = (input) => {
|
|
22233
23080
|
const cwd = existingDirectory(input.cwd);
|
|
22234
23081
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|