@brainervirus/workit-codex 8.22.3 → 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/agents/workit-implementer.toml +27 -19
- package/dist/launch-mcp.js +1002 -160
- package/dist/{workit-hook-73m7khyw.js → workit-hook-q465naq9.js} +1461 -209
- package/dist/workit-hook.js +4 -2
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +5 -5
- package/skills/workit-fanout/references/brief.md +17 -11
- package/skills/workit-fanout/references/orchestration.md +6 -6
- package/skills/workit-implement/SKILL.md +7 -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;
|
|
@@ -17683,6 +18509,24 @@ function workitInvocations(command, dialect = "posix", depth = 0) {
|
|
|
17683
18509
|
}
|
|
17684
18510
|
return found;
|
|
17685
18511
|
}
|
|
18512
|
+
var WORKIT_GLOBAL = /^--(?:cwd|session|token|as)(=|$)/;
|
|
18513
|
+
function skipWorkitGlobals(args) {
|
|
18514
|
+
let cwd = null;
|
|
18515
|
+
let at = 0;
|
|
18516
|
+
for (;at < args.length; at++) {
|
|
18517
|
+
const match = WORKIT_GLOBAL.exec(args[at]);
|
|
18518
|
+
if (match?.[1] === "=") {
|
|
18519
|
+
if (args[at].startsWith("--cwd="))
|
|
18520
|
+
cwd = args[at].slice(6);
|
|
18521
|
+
} else if (match && args[at + 1] !== undefined) {
|
|
18522
|
+
if (args[at] === "--cwd")
|
|
18523
|
+
cwd = args[at + 1];
|
|
18524
|
+
at++;
|
|
18525
|
+
} else if (args[at] !== "--json")
|
|
18526
|
+
break;
|
|
18527
|
+
}
|
|
18528
|
+
return { rest: args.slice(at), cwd };
|
|
18529
|
+
}
|
|
17686
18530
|
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
17687
18531
|
var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
|
|
17688
18532
|
var pushDestination = (destination) => {
|
|
@@ -17982,17 +18826,17 @@ var headSha2 = (repo) => {
|
|
|
17982
18826
|
if (!git || git.reftable)
|
|
17983
18827
|
return null;
|
|
17984
18828
|
try {
|
|
17985
|
-
const head = fs15.readFileSync(
|
|
18829
|
+
const head = fs15.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
17986
18830
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
17987
18831
|
return head;
|
|
17988
18832
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
17989
18833
|
if (!ref)
|
|
17990
18834
|
return null;
|
|
17991
|
-
const common =
|
|
18835
|
+
const common = path20.dirname(repo.location.dir);
|
|
17992
18836
|
try {
|
|
17993
|
-
return fs15.readFileSync(
|
|
18837
|
+
return fs15.readFileSync(path20.join(common, ref), "utf8").trim() || null;
|
|
17994
18838
|
} catch {}
|
|
17995
|
-
const packed = fs15.readFileSync(
|
|
18839
|
+
const packed = fs15.readFileSync(path20.join(common, "packed-refs"), "utf8");
|
|
17996
18840
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
17997
18841
|
} catch {
|
|
17998
18842
|
return null;
|
|
@@ -18080,7 +18924,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
18080
18924
|
const sub = invocation.args[0];
|
|
18081
18925
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
18082
18926
|
return [invocation];
|
|
18083
|
-
const dir =
|
|
18927
|
+
const dir = path20.resolve(root, invocation.dir ?? ".");
|
|
18084
18928
|
if (!fs15.existsSync(dir))
|
|
18085
18929
|
return [invocation];
|
|
18086
18930
|
const config = gitConfig(dir, invocation, cache);
|
|
@@ -18103,11 +18947,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
18103
18947
|
var normalizedUrl = (url, dir) => {
|
|
18104
18948
|
const trimmed = url.trim();
|
|
18105
18949
|
if (/^file:\/\//i.test(trimmed))
|
|
18106
|
-
return
|
|
18950
|
+
return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18107
18951
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
18108
18952
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
18109
18953
|
if (!scheme && !scp)
|
|
18110
|
-
return
|
|
18954
|
+
return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18111
18955
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
18112
18956
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
18113
18957
|
};
|
|
@@ -18127,7 +18971,7 @@ var rewrittenUrl = (url, config) => {
|
|
|
18127
18971
|
}
|
|
18128
18972
|
return url;
|
|
18129
18973
|
};
|
|
18130
|
-
var
|
|
18974
|
+
var pushUrl2 = (config, remote, dir) => {
|
|
18131
18975
|
const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
|
|
18132
18976
|
return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
|
|
18133
18977
|
};
|
|
@@ -18155,7 +18999,7 @@ var toPushRemote = (repo, remote, config) => {
|
|
|
18155
18999
|
const pushRemote = pushRemoteOf(config, repo.branch);
|
|
18156
19000
|
if (pushRemote === null || pushRemote === remote)
|
|
18157
19001
|
return true;
|
|
18158
|
-
return
|
|
19002
|
+
return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
|
|
18159
19003
|
};
|
|
18160
19004
|
var quote = (text) => `\`${text}\``;
|
|
18161
19005
|
var refusal = (action, branch, dir) => {
|
|
@@ -18331,7 +19175,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
|
18331
19175
|
const out = [];
|
|
18332
19176
|
const repos = new Map;
|
|
18333
19177
|
const repoFor = (relative) => {
|
|
18334
|
-
const dir =
|
|
19178
|
+
const dir = path20.resolve(cwd, relative ?? ".");
|
|
18335
19179
|
if (!repos.has(dir)) {
|
|
18336
19180
|
const repo = repoAt(dir);
|
|
18337
19181
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18417,7 +19261,7 @@ var analysisDenial = (error) => ({
|
|
|
18417
19261
|
});
|
|
18418
19262
|
var NEXT_COMMAND = "next";
|
|
18419
19263
|
var MARKER_TTL_SECONDS = 3600;
|
|
18420
|
-
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`);
|
|
18421
19265
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
18422
19266
|
function noteRawCommit(input, command, key) {
|
|
18423
19267
|
try {
|
|
@@ -18428,7 +19272,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18428
19272
|
if (action.kind !== "commit")
|
|
18429
19273
|
continue;
|
|
18430
19274
|
const file = markerFile(repo, input.session.id, key);
|
|
18431
|
-
fs15.mkdirSync(
|
|
19275
|
+
fs15.mkdirSync(path20.dirname(file), { recursive: true });
|
|
18432
19276
|
fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18433
19277
|
`);
|
|
18434
19278
|
}
|
|
@@ -18659,8 +19503,8 @@ var judgedDir = (root, dir) => {
|
|
|
18659
19503
|
return null;
|
|
18660
19504
|
if (dir === null)
|
|
18661
19505
|
return root;
|
|
18662
|
-
const resolved =
|
|
18663
|
-
return
|
|
19506
|
+
const resolved = path21.resolve(root, dir);
|
|
19507
|
+
return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18664
19508
|
};
|
|
18665
19509
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18666
19510
|
let judged = false;
|
|
@@ -18696,7 +19540,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
18696
19540
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
18697
19541
|
import fs16 from "node:fs";
|
|
18698
19542
|
import os6 from "node:os";
|
|
18699
|
-
import
|
|
19543
|
+
import path22 from "node:path";
|
|
18700
19544
|
var EMPTY2 = () => ({ deny: [], allow: [] });
|
|
18701
19545
|
var readText2 = (file) => {
|
|
18702
19546
|
try {
|
|
@@ -18737,19 +19581,19 @@ function parseJsonc(text) {
|
|
|
18737
19581
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
18738
19582
|
}
|
|
18739
19583
|
function gitTop(cwd) {
|
|
18740
|
-
for (let dir =
|
|
18741
|
-
if (fs16.existsSync(
|
|
19584
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
19585
|
+
if (fs16.existsSync(path22.join(dir, ".git")))
|
|
18742
19586
|
return dir;
|
|
18743
|
-
if (
|
|
19587
|
+
if (path22.dirname(dir) === dir)
|
|
18744
19588
|
return null;
|
|
18745
19589
|
}
|
|
18746
19590
|
}
|
|
18747
19591
|
var projectDirs = (cwd) => {
|
|
18748
19592
|
const top = gitTop(cwd);
|
|
18749
19593
|
const dirs = [];
|
|
18750
|
-
for (let dir =
|
|
19594
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
18751
19595
|
dirs.push(dir);
|
|
18752
|
-
if (!top || dir === top ||
|
|
19596
|
+
if (!top || dir === top || path22.dirname(dir) === dir)
|
|
18753
19597
|
return dirs;
|
|
18754
19598
|
}
|
|
18755
19599
|
};
|
|
@@ -18761,12 +19605,12 @@ var CLAUDE_MANAGED = {
|
|
|
18761
19605
|
};
|
|
18762
19606
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
18763
19607
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
18764
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
19608
|
+
const user = env.CLAUDE_CONFIG_DIR || path22.join(homeOf(env), ".claude");
|
|
18765
19609
|
return [
|
|
18766
19610
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
18767
|
-
|
|
18768
|
-
|
|
18769
|
-
|
|
19611
|
+
path22.join(user, "settings.json"),
|
|
19612
|
+
path22.join(project, ".claude", "settings.json"),
|
|
19613
|
+
path22.join(project, ".claude", "settings.local.json")
|
|
18770
19614
|
];
|
|
18771
19615
|
}
|
|
18772
19616
|
var bashPattern = (entry) => {
|
|
@@ -18856,10 +19700,10 @@ var opencodeConfig = (value, rules) => {
|
|
|
18856
19700
|
function opencodeRules(cwd, env = process.env) {
|
|
18857
19701
|
const rules = EMPTY2();
|
|
18858
19702
|
try {
|
|
18859
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
19703
|
+
const config = env.XDG_CONFIG_HOME || path22.join(homeOf(env), ".config");
|
|
18860
19704
|
const files = [
|
|
18861
|
-
...OPENCODE_NAMES.map((name) =>
|
|
18862
|
-
...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)])),
|
|
18863
19707
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
18864
19708
|
];
|
|
18865
19709
|
for (const file of files) {
|
|
@@ -18903,9 +19747,9 @@ var codexRule = (body) => {
|
|
|
18903
19747
|
function codexRules(cwd, env = process.env) {
|
|
18904
19748
|
const rules = EMPTY2();
|
|
18905
19749
|
try {
|
|
18906
|
-
const home = env.CODEX_HOME ||
|
|
19750
|
+
const home = env.CODEX_HOME || path22.join(homeOf(env), ".codex");
|
|
18907
19751
|
const top = gitTop(cwd) ?? cwd;
|
|
18908
|
-
for (const dir of [
|
|
19752
|
+
for (const dir of [path22.join(home, "rules"), path22.join(top, ".codex", "rules")]) {
|
|
18909
19753
|
let files;
|
|
18910
19754
|
try {
|
|
18911
19755
|
files = fs16.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -18915,7 +19759,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
18915
19759
|
return "unreadable";
|
|
18916
19760
|
}
|
|
18917
19761
|
for (const name of files) {
|
|
18918
|
-
const text = readText2(
|
|
19762
|
+
const text = readText2(path22.join(dir, name)) ?? "";
|
|
18919
19763
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
18920
19764
|
const rule = codexRule(part);
|
|
18921
19765
|
if (!rule)
|
|
@@ -18934,8 +19778,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
18934
19778
|
try {
|
|
18935
19779
|
const top = gitTop(cwd) ?? cwd;
|
|
18936
19780
|
for (const file of [
|
|
18937
|
-
|
|
18938
|
-
|
|
19781
|
+
path22.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
19782
|
+
path22.join(top, ".cursor", "cli.json")
|
|
18939
19783
|
]) {
|
|
18940
19784
|
const text = readText2(file);
|
|
18941
19785
|
if (text === null)
|
|
@@ -19011,21 +19855,21 @@ function wrapAllowed(read, words, command, options = {}) {
|
|
|
19011
19855
|
// packages/workit-core/src/core/heavy.ts
|
|
19012
19856
|
import fs17 from "node:fs";
|
|
19013
19857
|
import os7 from "node:os";
|
|
19014
|
-
import
|
|
19858
|
+
import path23 from "node:path";
|
|
19015
19859
|
function stateDir(env, platform = process.platform) {
|
|
19016
19860
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
19017
19861
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
19018
19862
|
if (env.XDG_STATE_HOME)
|
|
19019
|
-
return
|
|
19863
|
+
return path23.join(env.XDG_STATE_HOME, "workit");
|
|
19020
19864
|
const home = env.HOME || os7.homedir();
|
|
19021
19865
|
if (platform === "darwin")
|
|
19022
|
-
return
|
|
19866
|
+
return path23.join(home, "Library", "Application Support", "workit");
|
|
19023
19867
|
if (platform === "win32")
|
|
19024
|
-
return
|
|
19025
|
-
return
|
|
19868
|
+
return path23.join(env.LOCALAPPDATA ?? path23.join(home, "AppData", "Local"), "workit");
|
|
19869
|
+
return path23.join(home, ".local", "state", "workit");
|
|
19026
19870
|
}
|
|
19027
19871
|
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
19028
|
-
var heavyCommandsFile = (env) =>
|
|
19872
|
+
var heavyCommandsFile = (env) => path23.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
19029
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;
|
|
19030
19874
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
19031
19875
|
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
@@ -19300,7 +20144,7 @@ function machinePatterns(env) {
|
|
|
19300
20144
|
return isRecord4(value) ? stringList(value.patterns) : [];
|
|
19301
20145
|
}
|
|
19302
20146
|
function repoPatterns(cwd) {
|
|
19303
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
20147
|
+
return repoHeavyCommands(gitTop(cwd) ?? path23.resolve(cwd));
|
|
19304
20148
|
}
|
|
19305
20149
|
function heavyLines(lines, options) {
|
|
19306
20150
|
const found = [];
|
|
@@ -19378,12 +20222,12 @@ function heavyPlan(command, options) {
|
|
|
19378
20222
|
}
|
|
19379
20223
|
function workitOnPath(env = process.env) {
|
|
19380
20224
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
19381
|
-
for (const dir of (env.PATH ?? "").split(
|
|
20225
|
+
for (const dir of (env.PATH ?? "").split(path23.delimiter))
|
|
19382
20226
|
for (const name of names) {
|
|
19383
20227
|
if (!dir)
|
|
19384
20228
|
continue;
|
|
19385
20229
|
try {
|
|
19386
|
-
fs17.accessSync(
|
|
20230
|
+
fs17.accessSync(path23.join(dir, name), fs17.constants.X_OK);
|
|
19387
20231
|
return true;
|
|
19388
20232
|
} catch {}
|
|
19389
20233
|
}
|
|
@@ -19443,16 +20287,16 @@ var splitDenial = (plan) => ({
|
|
|
19443
20287
|
unblock: plan.unblock
|
|
19444
20288
|
});
|
|
19445
20289
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19446
|
-
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20290
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
19447
20291
|
import os9 from "node:os";
|
|
19448
|
-
import
|
|
20292
|
+
import path25 from "node:path";
|
|
19449
20293
|
|
|
19450
20294
|
// packages/workit-core/src/core/live-session.ts
|
|
19451
20295
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
19452
20296
|
import { createHash as createHash15 } from "node:crypto";
|
|
19453
20297
|
import fs18 from "node:fs";
|
|
19454
20298
|
import os8 from "node:os";
|
|
19455
|
-
import
|
|
20299
|
+
import path24 from "node:path";
|
|
19456
20300
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
19457
20301
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
19458
20302
|
var liveProbe = {
|
|
@@ -19526,15 +20370,15 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
19526
20370
|
};
|
|
19527
20371
|
};
|
|
19528
20372
|
var checkoutOf = (target) => {
|
|
19529
|
-
let dir =
|
|
19530
|
-
while (!fs18.existsSync(dir) &&
|
|
19531
|
-
dir =
|
|
20373
|
+
let dir = path24.resolve(target);
|
|
20374
|
+
while (!fs18.existsSync(dir) && path24.dirname(dir) !== dir)
|
|
20375
|
+
dir = path24.dirname(dir);
|
|
19532
20376
|
if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
|
|
19533
|
-
dir =
|
|
19534
|
-
const parts = dir.split(
|
|
20377
|
+
dir = path24.dirname(dir);
|
|
20378
|
+
const parts = dir.split(path24.sep);
|
|
19535
20379
|
const dotGit = parts.lastIndexOf(".git");
|
|
19536
20380
|
if (dotGit > 0)
|
|
19537
|
-
dir = parts.slice(0, dotGit).join(
|
|
20381
|
+
dir = parts.slice(0, dotGit).join(path24.sep) || path24.sep;
|
|
19538
20382
|
const location = resolveStore(dir);
|
|
19539
20383
|
if (location instanceof Error || !location.shared || !location.top)
|
|
19540
20384
|
return null;
|
|
@@ -19544,9 +20388,9 @@ var checkoutOf = (target) => {
|
|
|
19544
20388
|
} catch {}
|
|
19545
20389
|
return top;
|
|
19546
20390
|
};
|
|
19547
|
-
var markerFile2 = (top) =>
|
|
20391
|
+
var markerFile2 = (top) => path24.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
19548
20392
|
var checkoutKey = (top, platform = process.platform) => {
|
|
19549
|
-
let key =
|
|
20393
|
+
let key = path24.resolve(top);
|
|
19550
20394
|
try {
|
|
19551
20395
|
key = fs18.realpathSync.native(key);
|
|
19552
20396
|
} catch {}
|
|
@@ -19641,7 +20485,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
19641
20485
|
checkout,
|
|
19642
20486
|
heartbeatAt: now.toISOString()
|
|
19643
20487
|
};
|
|
19644
|
-
fs18.mkdirSync(
|
|
20488
|
+
fs18.mkdirSync(path24.dirname(file), { recursive: true });
|
|
19645
20489
|
const temp = `${file}.${process.pid}.tmp`;
|
|
19646
20490
|
fs18.writeFileSync(temp, JSON.stringify(marker));
|
|
19647
20491
|
fs18.renameSync(temp, file);
|
|
@@ -19653,26 +20497,26 @@ var NONE3 = { kind: "none" };
|
|
|
19653
20497
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
19654
20498
|
var canonical2 = (file) => {
|
|
19655
20499
|
let rest = "";
|
|
19656
|
-
for (let dir =
|
|
20500
|
+
for (let dir = path25.resolve(file);; dir = path25.dirname(dir)) {
|
|
19657
20501
|
try {
|
|
19658
|
-
return
|
|
20502
|
+
return path25.join(realpathSync5.native(dir), rest);
|
|
19659
20503
|
} catch {
|
|
19660
|
-
if (
|
|
19661
|
-
return
|
|
19662
|
-
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);
|
|
19663
20507
|
}
|
|
19664
20508
|
}
|
|
19665
20509
|
};
|
|
19666
20510
|
var exempt = (cwd, root, file, plans) => {
|
|
19667
20511
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
19668
|
-
const relative =
|
|
19669
|
-
if (relative.startsWith("..") ||
|
|
20512
|
+
const relative = path25.relative(fold(canonical2(root)), fold(canonical2(path25.resolve(cwd, file))));
|
|
20513
|
+
if (relative.startsWith("..") || path25.isAbsolute(relative))
|
|
19670
20514
|
return true;
|
|
19671
|
-
const posix = relative.split(
|
|
20515
|
+
const posix = relative.split(path25.sep).join("/");
|
|
19672
20516
|
const segments = posix.split("/");
|
|
19673
20517
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
19674
20518
|
};
|
|
19675
|
-
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
20519
|
+
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs", "busybox"]);
|
|
19676
20520
|
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
19677
20521
|
var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
19678
20522
|
var wrappedScript = (input) => {
|
|
@@ -19684,9 +20528,9 @@ var wrappedScript = (input) => {
|
|
|
19684
20528
|
return null;
|
|
19685
20529
|
if ("script" in tail)
|
|
19686
20530
|
return tail.script;
|
|
19687
|
-
const
|
|
19688
|
-
if (SCRIPT_SHELLS.has((
|
|
19689
|
-
return script
|
|
20531
|
+
const script = shellScript2(tail.argv);
|
|
20532
|
+
if (SCRIPT_SHELLS.has((tail.argv[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && script)
|
|
20533
|
+
return script;
|
|
19690
20534
|
return tail.argv.map(quoteWord2).join(" ");
|
|
19691
20535
|
};
|
|
19692
20536
|
var editsOf = (input) => {
|
|
@@ -19765,6 +20609,94 @@ var editsOf = (input) => {
|
|
|
19765
20609
|
return [];
|
|
19766
20610
|
}
|
|
19767
20611
|
};
|
|
20612
|
+
var shellScript2 = (words) => {
|
|
20613
|
+
if (!SCRIPT_SHELLS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
|
|
20614
|
+
return null;
|
|
20615
|
+
for (let index = 1;index < words.length; index++) {
|
|
20616
|
+
const word = words[index];
|
|
20617
|
+
if (/^-[a-z]*c$/.test(word))
|
|
20618
|
+
return words[index + 1] ?? null;
|
|
20619
|
+
if (/^[-+][oO]$/.test(word))
|
|
20620
|
+
index++;
|
|
20621
|
+
else if (!/^[-+]/.test(word))
|
|
20622
|
+
return null;
|
|
20623
|
+
}
|
|
20624
|
+
return null;
|
|
20625
|
+
};
|
|
20626
|
+
var XARGS_VALUE = /^-[IinLPdsEa]$/;
|
|
20627
|
+
var runsWriter = (words) => {
|
|
20628
|
+
const filled = [...words.filter((word) => word !== "{}" && word !== ";" && word !== "+"), "x"];
|
|
20629
|
+
const edits = editsOf(filled);
|
|
20630
|
+
return edits === null || edits.length > 0 || opaqueWrites(filled) !== null;
|
|
20631
|
+
};
|
|
20632
|
+
var opaqueWrites = (input) => {
|
|
20633
|
+
let words = input;
|
|
20634
|
+
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0]))) {
|
|
20635
|
+
if (words[0] === "xargs") {
|
|
20636
|
+
let at = 1;
|
|
20637
|
+
while (words[at]?.startsWith("-"))
|
|
20638
|
+
at += XARGS_VALUE.test(words[at]) ? 2 : 1;
|
|
20639
|
+
return runsWriter(words.slice(at)) ? ["."] : null;
|
|
20640
|
+
}
|
|
20641
|
+
words = words.slice(1);
|
|
20642
|
+
}
|
|
20643
|
+
const [verb, ...args] = words;
|
|
20644
|
+
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
20645
|
+
const files = operands(args);
|
|
20646
|
+
const has = (pattern) => args.some((arg) => pattern.test(arg));
|
|
20647
|
+
const listed = (named) => named.length ? named : ["."];
|
|
20648
|
+
if (name === "unlink" || name === "shred")
|
|
20649
|
+
return listed(files);
|
|
20650
|
+
if (/^(?:chmod|chown|chgrp|chattr)$/.test(name))
|
|
20651
|
+
return listed(has(/^--reference/) ? files : files.slice(1));
|
|
20652
|
+
if (name === "rsync") {
|
|
20653
|
+
const dest = files.at(-1);
|
|
20654
|
+
const remote = /^[\w.@-]*:/.test(dest ?? "") && !/^[A-Za-z]:[\\/]/.test(dest ?? "");
|
|
20655
|
+
return dest === undefined ? ["."] : remote ? [] : [dest];
|
|
20656
|
+
}
|
|
20657
|
+
if (name === "find") {
|
|
20658
|
+
const exec = args.findIndex((arg) => /^-(?:exec|execdir|ok|okdir)$/.test(arg));
|
|
20659
|
+
const writes = has(/^-(?:delete|fprint\w*|fls)$/) || exec >= 0 && runsWriter(args.slice(exec + 1));
|
|
20660
|
+
const end = args.findIndex((arg) => /^[-(!]/.test(arg));
|
|
20661
|
+
return writes ? listed(end < 0 ? args : args.slice(0, end)) : null;
|
|
20662
|
+
}
|
|
20663
|
+
if (/^g?awk$/.test(name)) {
|
|
20664
|
+
if (!has(/^-i|^--include/))
|
|
20665
|
+
return null;
|
|
20666
|
+
const named = [];
|
|
20667
|
+
let program = !has(/^-f$|^--file/);
|
|
20668
|
+
for (let index = 0;index < args.length; index++)
|
|
20669
|
+
if (/^(?:-[ifvF]|--(?:include|file|assign|field-separator))$/.test(args[index]))
|
|
20670
|
+
index++;
|
|
20671
|
+
else if (args[index].startsWith("-"))
|
|
20672
|
+
continue;
|
|
20673
|
+
else if (program)
|
|
20674
|
+
program = false;
|
|
20675
|
+
else
|
|
20676
|
+
named.push(args[index]);
|
|
20677
|
+
return listed(named);
|
|
20678
|
+
}
|
|
20679
|
+
if (name === "perl")
|
|
20680
|
+
return has(/^-[a-zA-Z]*[eE]/) ? ["."] : null;
|
|
20681
|
+
if (/^(?:python[\d.]*|node|bun|deno|ruby|php)$/.test(name))
|
|
20682
|
+
return has(/^-[a-zA-Z]*[ce]$|^--eval$|^-p$/) ? ["."] : null;
|
|
20683
|
+
return shellScript2(words) === null ? null : ["."];
|
|
20684
|
+
};
|
|
20685
|
+
var linkSources = (args, at) => {
|
|
20686
|
+
const names = operands([...args]);
|
|
20687
|
+
const into = args.indexOf("-t");
|
|
20688
|
+
const dest = into >= 0 ? args[into + 1] : names.length > 1 ? names.at(-1) : ".";
|
|
20689
|
+
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
20690
|
+
const target = dest === undefined ? null : at(dest);
|
|
20691
|
+
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
20692
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path25.dirname(target);
|
|
20693
|
+
return sources.flatMap((source) => {
|
|
20694
|
+
if (symbolic && base !== null && !path25.isAbsolute(homeExpanded(source)))
|
|
20695
|
+
return [path25.resolve(base, source)];
|
|
20696
|
+
const resolved = at(source);
|
|
20697
|
+
return resolved ? [resolved] : [];
|
|
20698
|
+
});
|
|
20699
|
+
};
|
|
19768
20700
|
var shellWrites = (command, depth = 0) => {
|
|
19769
20701
|
const targets = [];
|
|
19770
20702
|
let unknown = false;
|
|
@@ -19789,7 +20721,7 @@ var shellWrites = (command, depth = 0) => {
|
|
|
19789
20721
|
return { writes: true, targets: unknown ? null : targets };
|
|
19790
20722
|
};
|
|
19791
20723
|
var cache2 = new Map;
|
|
19792
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20724
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path25.resolve(root, file)) ?? "-").join("\x00");
|
|
19793
20725
|
var gateFor = (cwd) => {
|
|
19794
20726
|
const store = new TaskStore(cwd);
|
|
19795
20727
|
const listed = store.listTaskIndex();
|
|
@@ -19868,32 +20800,36 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
19868
20800
|
return [];
|
|
19869
20801
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
19870
20802
|
return [];
|
|
19871
|
-
return [
|
|
20803
|
+
return [path25.resolve(cwd, invocation.dir ?? ".")];
|
|
19872
20804
|
});
|
|
19873
20805
|
};
|
|
19874
20806
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
19875
20807
|
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
19876
20808
|
var POPD2 = new Set(["popd", "pop-location"]);
|
|
19877
|
-
var
|
|
20809
|
+
var UNKNOWN_DIR = path25.resolve(path25.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
20810
|
+
var cdInto = (dir, words, strict = false) => {
|
|
20811
|
+
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
19878
20812
|
const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
19879
20813
|
const target = named === undefined ? undefined : homeExpanded(named);
|
|
19880
|
-
if (
|
|
19881
|
-
return;
|
|
19882
|
-
|
|
20814
|
+
if (target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20815
|
+
return lost;
|
|
20816
|
+
if (dir === undefined)
|
|
20817
|
+
return lost;
|
|
20818
|
+
return target === undefined ? os9.homedir() : path25.resolve(dir, target);
|
|
19883
20819
|
};
|
|
19884
20820
|
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
19885
20821
|
var homeExpanded = (file) => {
|
|
19886
20822
|
const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
|
|
19887
20823
|
return match ? (process.env.HOME || os9.homedir()) + file.slice(match[0].length) : file;
|
|
19888
20824
|
};
|
|
19889
|
-
var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
20825
|
+
var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
19890
20826
|
const targets = [];
|
|
19891
20827
|
let dir = cwd;
|
|
19892
20828
|
let stack = [];
|
|
19893
20829
|
const groups = [];
|
|
19894
20830
|
const at = (file) => {
|
|
19895
20831
|
const home = homeExpanded(file);
|
|
19896
|
-
return
|
|
20832
|
+
return path25.isAbsolute(home) ? home : dir === undefined ? null : path25.resolve(dir, home);
|
|
19897
20833
|
};
|
|
19898
20834
|
for (const segment of segmentsOf(command, dialect)) {
|
|
19899
20835
|
if (segment.group === "open") {
|
|
@@ -19910,21 +20846,35 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
|
19910
20846
|
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
19911
20847
|
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
19912
20848
|
if (CD2.has(name)) {
|
|
19913
|
-
dir = cdInto(dir, words);
|
|
20849
|
+
dir = cdInto(dir, words, strict);
|
|
19914
20850
|
continue;
|
|
19915
20851
|
}
|
|
19916
20852
|
if (PUSHD2.has(name)) {
|
|
19917
20853
|
stack.push(dir);
|
|
19918
|
-
dir = cdInto(dir, words);
|
|
20854
|
+
dir = cdInto(dir, words, strict);
|
|
19919
20855
|
continue;
|
|
19920
20856
|
}
|
|
19921
20857
|
if (POPD2.has(name)) {
|
|
19922
|
-
dir = stack.length ? stack.pop() : undefined;
|
|
20858
|
+
dir = stack.length ? stack.pop() : strict ? UNKNOWN_DIR : undefined;
|
|
19923
20859
|
continue;
|
|
19924
20860
|
}
|
|
19925
|
-
const inner = depth < 4 ? wrappedScript(words) : null;
|
|
20861
|
+
const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
|
|
19926
20862
|
if (inner !== null && dir !== undefined)
|
|
19927
|
-
targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
|
|
20863
|
+
targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
|
|
20864
|
+
if (strict && dir !== undefined) {
|
|
20865
|
+
const runIn = dir;
|
|
20866
|
+
for (const sub of depth < 4 ? segment.subs ?? [] : [])
|
|
20867
|
+
targets.push(...segmentTargets(runIn, sub, dialect, depth + 1, true));
|
|
20868
|
+
const opaque = segment.unanalyzable ? ["."] : inner === null ? opaqueWrites(words) : null;
|
|
20869
|
+
for (const file of opaque ?? []) {
|
|
20870
|
+
const resolved = at(file);
|
|
20871
|
+
if (resolved)
|
|
20872
|
+
targets.push(file === "." ? path25.join(resolved, DIR_TARGET) : resolved);
|
|
20873
|
+
}
|
|
20874
|
+
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
20875
|
+
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
20876
|
+
targets.push(...linkSources(words.slice(ln + 1), at));
|
|
20877
|
+
}
|
|
19928
20878
|
for (const file of segment.redirects)
|
|
19929
20879
|
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
19930
20880
|
const resolved = at(file);
|
|
@@ -19934,18 +20884,36 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
|
19934
20884
|
const edits = editsOf(segment.words);
|
|
19935
20885
|
if (edits === null) {
|
|
19936
20886
|
if (dir !== undefined)
|
|
19937
|
-
targets.push(
|
|
20887
|
+
targets.push(path25.join(dir, DIR_TARGET));
|
|
19938
20888
|
} else
|
|
19939
20889
|
for (const file of edits) {
|
|
19940
20890
|
const resolved = at(file);
|
|
19941
20891
|
if (resolved)
|
|
19942
20892
|
targets.push(resolved);
|
|
19943
20893
|
}
|
|
19944
|
-
if (dir !== undefined
|
|
19945
|
-
|
|
20894
|
+
if (dir !== undefined)
|
|
20895
|
+
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
20896
|
+
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
20897
|
+
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
20898
|
+
targets.push(path25.join(runsIn, DIR_TARGET));
|
|
20899
|
+
}
|
|
19946
20900
|
}
|
|
19947
20901
|
return targets;
|
|
19948
20902
|
};
|
|
20903
|
+
var workitCwd = (dir, args) => {
|
|
20904
|
+
const { rest, cwd } = skipWorkitGlobals(args);
|
|
20905
|
+
return { at: cwd === null ? dir : path25.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
20906
|
+
};
|
|
20907
|
+
var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
20908
|
+
try {
|
|
20909
|
+
return [
|
|
20910
|
+
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
20911
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path25.join(dir, DIR_TARGET))
|
|
20912
|
+
];
|
|
20913
|
+
} catch {
|
|
20914
|
+
return [];
|
|
20915
|
+
}
|
|
20916
|
+
};
|
|
19949
20917
|
var overridden = (command, dialect) => {
|
|
19950
20918
|
const segments = segmentsOf(command, dialect);
|
|
19951
20919
|
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
@@ -19977,7 +20945,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
19977
20945
|
var guarded = (targets) => {
|
|
19978
20946
|
const seen = new Set;
|
|
19979
20947
|
return targets.filter((target) => {
|
|
19980
|
-
const dir =
|
|
20948
|
+
const dir = path25.dirname(target);
|
|
19981
20949
|
if (seen.has(dir))
|
|
19982
20950
|
return false;
|
|
19983
20951
|
seen.add(dir);
|
|
@@ -19998,7 +20966,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
19998
20966
|
for (const target of guarded(targets))
|
|
19999
20967
|
claimLiveSession(target, owner, deps);
|
|
20000
20968
|
};
|
|
20001
|
-
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)];
|
|
20002
20970
|
var issuedTarget = (cwd, targets) => {
|
|
20003
20971
|
const state = resolveStateDir();
|
|
20004
20972
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
@@ -20006,7 +20974,7 @@ var issuedTarget = (cwd, targets) => {
|
|
|
20006
20974
|
};
|
|
20007
20975
|
var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
20008
20976
|
const win = platform === "win32";
|
|
20009
|
-
const p = win ?
|
|
20977
|
+
const p = win ? path25.win32 : path25.posix;
|
|
20010
20978
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
20011
20979
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
20012
20980
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -20051,16 +21019,8 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
|
20051
21019
|
} catch {}
|
|
20052
21020
|
const writes = shellWrites(command);
|
|
20053
21021
|
let targets = [];
|
|
20054
|
-
if (owner)
|
|
20055
|
-
|
|
20056
|
-
targets = [
|
|
20057
|
-
...segmentTargets(cwd, command, dialect),
|
|
20058
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) => path24.join(dir, DIR_TARGET))
|
|
20059
|
-
];
|
|
20060
|
-
} catch {
|
|
20061
|
-
targets = [];
|
|
20062
|
-
}
|
|
20063
|
-
}
|
|
21022
|
+
if (owner)
|
|
21023
|
+
targets = shellMutationTargets(cwd, command, dialect);
|
|
20064
21024
|
if (owner && targets.length) {
|
|
20065
21025
|
const live = liveGate(owner, targets, deps);
|
|
20066
21026
|
if (live.kind !== "none") {
|
|
@@ -20105,6 +21065,24 @@ function beforeWriteGate(cwd, paths) {
|
|
|
20105
21065
|
}
|
|
20106
21066
|
// packages/workit-core/src/hooks/stop.ts
|
|
20107
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
|
|
20108
21086
|
var NONE4 = { kind: "none" };
|
|
20109
21087
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
20110
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;
|
|
@@ -20197,29 +21175,32 @@ var redChecks = (rows, pr, head, endpoint) => {
|
|
|
20197
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)" : ""}.`
|
|
20198
21176
|
};
|
|
20199
21177
|
};
|
|
20200
|
-
var
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
const listed = store.listTaskIndex();
|
|
20204
|
-
const key = store.currentKey();
|
|
20205
|
-
if (!listed.ok || !key.ok)
|
|
20206
|
-
return null;
|
|
20207
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
20208
|
-
const task = entry ? store.readTask(entry.id) : null;
|
|
20209
|
-
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
20210
|
-
} catch {
|
|
20211
|
-
return null;
|
|
20212
|
-
}
|
|
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;
|
|
20213
21181
|
};
|
|
20214
|
-
var unverified = (cwd, branch, all, descriptor, verification) => {
|
|
21182
|
+
var unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
20215
21183
|
const check = checkVerdicts(cwd, branch, all);
|
|
20216
21184
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
20217
21185
|
return null;
|
|
20218
|
-
const judgment =
|
|
21186
|
+
const judgment = currentJudgment(cwd);
|
|
20219
21187
|
const high = judgment?.riskTier === "high";
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
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
|
+
}
|
|
20223
21204
|
if (!independent && check.review === "self-reviewed")
|
|
20224
21205
|
return null;
|
|
20225
21206
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -20266,7 +21247,7 @@ function stopObligation(input, descriptor) {
|
|
|
20266
21247
|
}
|
|
20267
21248
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
20268
21249
|
return null;
|
|
20269
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
21250
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
20270
21251
|
return left ? { ...left, head } : null;
|
|
20271
21252
|
} catch {
|
|
20272
21253
|
return null;
|
|
@@ -20527,13 +21508,13 @@ ${line}` };
|
|
|
20527
21508
|
return decision;
|
|
20528
21509
|
};
|
|
20529
21510
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20530
|
-
import { existsSync as
|
|
20531
|
-
import
|
|
21511
|
+
import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21512
|
+
import path26 from "node:path";
|
|
20532
21513
|
var isRecord5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20533
21514
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
20534
21515
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
20535
21516
|
var existingDirectory = (value) => {
|
|
20536
|
-
if (!nonEmpty2(value) || !
|
|
21517
|
+
if (!nonEmpty2(value) || !path26.isAbsolute(value) || !existsSync9(value))
|
|
20537
21518
|
return null;
|
|
20538
21519
|
try {
|
|
20539
21520
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20601,9 +21582,9 @@ var writeTargets = (input) => {
|
|
|
20601
21582
|
};
|
|
20602
21583
|
// packages/workit-core/src/hooks/standing.ts
|
|
20603
21584
|
import fs19 from "node:fs";
|
|
20604
|
-
import
|
|
21585
|
+
import path27 from "node:path";
|
|
20605
21586
|
var readPlans = (store) => {
|
|
20606
|
-
const dir =
|
|
21587
|
+
const dir = path27.join(store, "fanouts");
|
|
20607
21588
|
let names;
|
|
20608
21589
|
try {
|
|
20609
21590
|
names = fs19.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -20613,7 +21594,7 @@ var readPlans = (store) => {
|
|
|
20613
21594
|
const plans = [];
|
|
20614
21595
|
for (const name of names) {
|
|
20615
21596
|
try {
|
|
20616
|
-
const raw = JSON.parse(fs19.readFileSync(
|
|
21597
|
+
const raw = JSON.parse(fs19.readFileSync(path27.join(dir, name), "utf8"));
|
|
20617
21598
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
20618
21599
|
continue;
|
|
20619
21600
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -20736,9 +21717,266 @@ var cwdRewrite = (command) => {
|
|
|
20736
21717
|
return `workit --cwd ${dir} ${rest.slice("workit".length).trimStart()}`.trimEnd();
|
|
20737
21718
|
};
|
|
20738
21719
|
|
|
21720
|
+
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
21721
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
21722
|
+
import fs20 from "node:fs";
|
|
21723
|
+
import path28 from "node:path";
|
|
21724
|
+
var FANOUT_TOKEN_FILE = "workit-fanout-token";
|
|
21725
|
+
var NONE5 = { kind: "none" };
|
|
21726
|
+
var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
|
|
21727
|
+
var canonical3 = (file) => {
|
|
21728
|
+
let rest = "";
|
|
21729
|
+
for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
|
|
21730
|
+
try {
|
|
21731
|
+
return path28.join(fs20.realpathSync.native(dir), rest);
|
|
21732
|
+
} catch {
|
|
21733
|
+
if (path28.dirname(dir) === dir)
|
|
21734
|
+
return path28.resolve(file);
|
|
21735
|
+
rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
|
|
21736
|
+
}
|
|
21737
|
+
}
|
|
21738
|
+
};
|
|
21739
|
+
var within = (child, parent) => {
|
|
21740
|
+
const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
|
|
21741
|
+
const relative = path28.relative(fold(parent), fold(child));
|
|
21742
|
+
return relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative);
|
|
21743
|
+
};
|
|
21744
|
+
var checkoutOf2 = (file) => {
|
|
21745
|
+
for (let dir = canonical3(file);; dir = path28.dirname(dir)) {
|
|
21746
|
+
const dotGit = path28.join(dir, ".git");
|
|
21747
|
+
if (fs20.existsSync(dotGit)) {
|
|
21748
|
+
if (!fs20.statSync(dotGit).isFile())
|
|
21749
|
+
return { root: dir, workit: false };
|
|
21750
|
+
const admin = /^gitdir:\s*(.+)$/mu.exec(fs20.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21751
|
+
return {
|
|
21752
|
+
root: dir,
|
|
21753
|
+
workit: Boolean(admin) && fs20.existsSync(path28.join(path28.resolve(dir, admin), FANOUT_TOKEN_FILE))
|
|
21754
|
+
};
|
|
21755
|
+
}
|
|
21756
|
+
if (path28.dirname(dir) === dir)
|
|
21757
|
+
return null;
|
|
21758
|
+
}
|
|
21759
|
+
};
|
|
21760
|
+
var workitWorktreeOf = (file) => {
|
|
21761
|
+
try {
|
|
21762
|
+
const checkout = checkoutOf2(file);
|
|
21763
|
+
return checkout?.workit ? checkout.root : null;
|
|
21764
|
+
} catch {
|
|
21765
|
+
return null;
|
|
21766
|
+
}
|
|
21767
|
+
};
|
|
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`);
|
|
21770
|
+
var readBinding = (file) => {
|
|
21771
|
+
try {
|
|
21772
|
+
const value = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
21773
|
+
return typeof value.worktree === "string" ? value : null;
|
|
21774
|
+
} catch {
|
|
21775
|
+
return null;
|
|
21776
|
+
}
|
|
21777
|
+
};
|
|
21778
|
+
var boundWorktree = (input) => {
|
|
21779
|
+
const agentId = input.session.agentId;
|
|
21780
|
+
if (!agentId)
|
|
21781
|
+
return null;
|
|
21782
|
+
const binding = readBinding(bindingFile(input.session.id, agentId));
|
|
21783
|
+
return binding && workitWorktreeOf(binding.worktree) === binding.worktree ? binding.worktree : null;
|
|
21784
|
+
};
|
|
21785
|
+
var bind = (input, worktree) => {
|
|
21786
|
+
const agentId = input.session.agentId;
|
|
21787
|
+
const file = bindingFile(input.session.id, agentId);
|
|
21788
|
+
const binding = {
|
|
21789
|
+
worktree,
|
|
21790
|
+
session: input.session.id,
|
|
21791
|
+
agentId,
|
|
21792
|
+
at: new Date().toISOString()
|
|
21793
|
+
};
|
|
21794
|
+
fs20.mkdirSync(path28.dirname(file), { recursive: true });
|
|
21795
|
+
fs20.writeFileSync(file, JSON.stringify(binding));
|
|
21796
|
+
try {
|
|
21797
|
+
if (inWorkitWorkspace(input.cwd))
|
|
21798
|
+
appendHookObserved(input.cwd, {
|
|
21799
|
+
type: "worktree.bound",
|
|
21800
|
+
actor: { host: input.host, session: input.session.id, agentId },
|
|
21801
|
+
agentType: input.session.agentType,
|
|
21802
|
+
worktree
|
|
21803
|
+
});
|
|
21804
|
+
} catch {}
|
|
21805
|
+
};
|
|
21806
|
+
var hostIsolated = (cwd, agentId) => {
|
|
21807
|
+
try {
|
|
21808
|
+
if (agentId === null)
|
|
21809
|
+
return false;
|
|
21810
|
+
const checkout = checkoutOf2(cwd);
|
|
21811
|
+
if (!checkout)
|
|
21812
|
+
return false;
|
|
21813
|
+
const dotGit = path28.join(checkout.root, ".git");
|
|
21814
|
+
if (!fs20.statSync(dotGit).isFile())
|
|
21815
|
+
return false;
|
|
21816
|
+
const admin = /^gitdir:\s*(.+)$/mu.exec(fs20.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
21817
|
+
if (!admin)
|
|
21818
|
+
return false;
|
|
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)) !== "")
|
|
21822
|
+
return false;
|
|
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) === "";
|
|
21827
|
+
} catch {
|
|
21828
|
+
return false;
|
|
21829
|
+
}
|
|
21830
|
+
};
|
|
21831
|
+
var shown = (target) => path28.basename(target) === ".workit-live-target" ? path28.dirname(target) : target;
|
|
21832
|
+
var unboundDeny = (input, target) => {
|
|
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.";
|
|
21834
|
+
return {
|
|
21835
|
+
kind: "deny",
|
|
21836
|
+
reason: `workit worktree confinement: implementer ${input.session.agentId} is not working in a Workit worktree yet, and ${shown(target)} is outside every worktree \`workit fanout worktree create\` made, so it would change the lead's checkout. ${unblock}`,
|
|
21837
|
+
unblock
|
|
21838
|
+
};
|
|
21839
|
+
};
|
|
21840
|
+
var outsideDeny = (input, worktree, target) => {
|
|
21841
|
+
const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit --cwd ${worktree} git commit -m … -- <paths>\`, \`workit --cwd ${worktree} check <name>\` (or \`git -C ${worktree} …\`); a command whose writes Workit cannot see is judged by the directory it runs in. Anything outside it is a follow-up for your report.`;
|
|
21842
|
+
return {
|
|
21843
|
+
kind: "deny",
|
|
21844
|
+
reason: `workit worktree confinement: implementer ${input.session.agentId} is bound to the worktree ${worktree}, and ${shown(target)} is outside it. ${unblock}`,
|
|
21845
|
+
unblock
|
|
21846
|
+
};
|
|
21847
|
+
};
|
|
21848
|
+
var unknownDirDeny = (input, worktree) => {
|
|
21849
|
+
const wt = worktree ?? "<worktree>";
|
|
21850
|
+
const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit --cwd ${wt}\`.`;
|
|
21851
|
+
return {
|
|
21852
|
+
kind: "deny",
|
|
21853
|
+
reason: `workit worktree confinement: implementer ${input.session.agentId} writes after an unknown directory change. ${unblock}`,
|
|
21854
|
+
unblock
|
|
21855
|
+
};
|
|
21856
|
+
};
|
|
21857
|
+
function confineImplementer(input, targets) {
|
|
21858
|
+
try {
|
|
21859
|
+
if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
|
|
21860
|
+
return NONE5;
|
|
21861
|
+
if (targets.some((target) => within(path28.resolve(target), UNKNOWN_DIR)))
|
|
21862
|
+
return unknownDirDeny(input, boundWorktree(input));
|
|
21863
|
+
const bindings = canonical3(bindingDir());
|
|
21864
|
+
const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
|
|
21865
|
+
if (judged.length === 0)
|
|
21866
|
+
return NONE5;
|
|
21867
|
+
let worktree = boundWorktree(input);
|
|
21868
|
+
if (!worktree) {
|
|
21869
|
+
for (const { real } of judged) {
|
|
21870
|
+
worktree = within(real, bindings) ? null : workitWorktreeOf(real);
|
|
21871
|
+
if (worktree)
|
|
21872
|
+
break;
|
|
21873
|
+
}
|
|
21874
|
+
if (!worktree)
|
|
21875
|
+
return unboundDeny(input, judged[0].named);
|
|
21876
|
+
bind(input, worktree);
|
|
21877
|
+
}
|
|
21878
|
+
const root = canonical3(worktree);
|
|
21879
|
+
const outside = judged.find(({ real }) => !within(real, root) || within(real, bindings));
|
|
21880
|
+
return outside ? outsideDeny(input, worktree, outside.named) : NONE5;
|
|
21881
|
+
} catch {
|
|
21882
|
+
return NONE5;
|
|
21883
|
+
}
|
|
21884
|
+
}
|
|
21885
|
+
var MUTATING_REMOTE = new Set([
|
|
21886
|
+
"add",
|
|
21887
|
+
"remove",
|
|
21888
|
+
"rm",
|
|
21889
|
+
"rename",
|
|
21890
|
+
"set-url",
|
|
21891
|
+
"set-head",
|
|
21892
|
+
"set-branches",
|
|
21893
|
+
"prune"
|
|
21894
|
+
]);
|
|
21895
|
+
var CONFIG_READ = /^(?:--get(?:-all|-regexp|-urlmatch)?|--list|-l|get|list)$/;
|
|
21896
|
+
var CONFIG_WRITE = /^(?:--(?:unset(?:-all)?|add|replace-all|rename-section|remove-section|edit)|-e|set|unset|rename-section|remove-section|edit)$/;
|
|
21897
|
+
var CONFIG_VALUE = /^(?:--file|-f|--blob|--type|--default|--comment)$/;
|
|
21898
|
+
var leadOwnedGit = (args) => {
|
|
21899
|
+
const [sub = "", ...rest] = args;
|
|
21900
|
+
if (rest.some((arg) => arg === "--help" || arg === "-h"))
|
|
21901
|
+
return false;
|
|
21902
|
+
const names = rest.filter((arg) => !arg.startsWith("-"));
|
|
21903
|
+
const flag = (pattern) => rest.some((arg) => pattern.test(arg));
|
|
21904
|
+
switch (sub) {
|
|
21905
|
+
case "worktree":
|
|
21906
|
+
return names[0] !== undefined && names[0] !== "list";
|
|
21907
|
+
case "update-ref":
|
|
21908
|
+
return true;
|
|
21909
|
+
case "symbolic-ref":
|
|
21910
|
+
return names.length > 1 || flag(/^(?:-d|--delete)$/);
|
|
21911
|
+
case "branch":
|
|
21912
|
+
return flag(/^-[a-zA-Z]*[dDfmMcC]|^--(?:delete|force|move|copy)$/);
|
|
21913
|
+
case "tag":
|
|
21914
|
+
return flag(/^-[a-zA-Z]*[df]|^--(?:delete|force)$/);
|
|
21915
|
+
case "push": {
|
|
21916
|
+
if (flag(/^(?:-d|--delete|--mirror|--prune)$/))
|
|
21917
|
+
return true;
|
|
21918
|
+
const refspecs = names.slice(1);
|
|
21919
|
+
return refspecs.some((name) => name.includes(":") || name.startsWith("+")) || refspecs.length > 0 && flag(/^-[a-zA-Z]*f|^--force/);
|
|
21920
|
+
}
|
|
21921
|
+
case "replace":
|
|
21922
|
+
case "filter-branch":
|
|
21923
|
+
case "gc":
|
|
21924
|
+
case "prune":
|
|
21925
|
+
case "repack":
|
|
21926
|
+
case "sparse-checkout":
|
|
21927
|
+
return true;
|
|
21928
|
+
case "notes":
|
|
21929
|
+
case "reflog":
|
|
21930
|
+
case "submodule":
|
|
21931
|
+
return !/^(?:list|show|status|exists)$/.test(names[0] ?? "list");
|
|
21932
|
+
case "stash":
|
|
21933
|
+
return names[0] === "drop" || names[0] === "clear";
|
|
21934
|
+
case "remote":
|
|
21935
|
+
return MUTATING_REMOTE.has(names[0] ?? "");
|
|
21936
|
+
case "config": {
|
|
21937
|
+
if (rest.some((arg) => CONFIG_READ.test(arg)))
|
|
21938
|
+
return false;
|
|
21939
|
+
if (rest.some((arg) => CONFIG_WRITE.test(arg)))
|
|
21940
|
+
return true;
|
|
21941
|
+
let operands = 0;
|
|
21942
|
+
for (let index = 0;index < rest.length; index++)
|
|
21943
|
+
if (CONFIG_VALUE.test(rest[index]))
|
|
21944
|
+
index++;
|
|
21945
|
+
else if (!rest[index].startsWith("-"))
|
|
21946
|
+
operands++;
|
|
21947
|
+
return operands > 1;
|
|
21948
|
+
}
|
|
21949
|
+
default:
|
|
21950
|
+
return false;
|
|
21951
|
+
}
|
|
21952
|
+
};
|
|
21953
|
+
var leadOwnedWorkit = (args) => !args.includes("--help") && args.some((_, start) => {
|
|
21954
|
+
if (start > 0 && !/(?:^|[\\/])workit(?:\.exe|\.cmd)?$/.test(args[start - 1]))
|
|
21955
|
+
return false;
|
|
21956
|
+
const [verb, sub] = skipWorkitGlobals(args.slice(start)).rest;
|
|
21957
|
+
return verb === "fanout" && /^(?:plan|worktree)$/.test(sub ?? "");
|
|
21958
|
+
});
|
|
21959
|
+
var leadOwnedDeny = (input, command) => {
|
|
21960
|
+
const unblock = "Worktrees, branches other than your own, refs, tags, remotes, git config, the shared stash and the fan-out plan belong to the lead: report what you need changed as a follow-up instead.";
|
|
21961
|
+
return {
|
|
21962
|
+
kind: "deny",
|
|
21963
|
+
reason: `workit worktree confinement: implementer ${input.session.agentId} may not run \`${command.slice(0, 160)}\`. ${unblock}`,
|
|
21964
|
+
unblock
|
|
21965
|
+
};
|
|
21966
|
+
};
|
|
21967
|
+
function confineImplementerShell(input, command, dialect = "posix") {
|
|
21968
|
+
try {
|
|
21969
|
+
if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
|
|
21970
|
+
return NONE5;
|
|
21971
|
+
if (rawInvocations(command, dialect).some((invocation) => invocation.tool === "git" && leadOwnedGit(invocation.args)) || workitInvocations(command, dialect).some(leadOwnedWorkit))
|
|
21972
|
+
return leadOwnedDeny(input, command);
|
|
21973
|
+
} catch {}
|
|
21974
|
+
return confineImplementer(input, shellMutationTargets(input.cwd, command, dialect, true));
|
|
21975
|
+
}
|
|
21976
|
+
|
|
20739
21977
|
// packages/workit-core/src/hooks/handle.ts
|
|
20740
21978
|
var usable2 = (value) => value === "native" || value === "partial";
|
|
20741
|
-
var
|
|
21979
|
+
var NONE6 = { kind: "none" };
|
|
20742
21980
|
var resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn);
|
|
20743
21981
|
var offered = new Set;
|
|
20744
21982
|
var ROLES = {
|
|
@@ -20751,6 +21989,7 @@ var workitRole = (descriptor, agentType) => {
|
|
|
20751
21989
|
const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
|
|
20752
21990
|
return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
|
|
20753
21991
|
};
|
|
21992
|
+
var implementerAgent = (input, descriptor) => Boolean(input.session.agentId) && input.session.agentType !== null && workitRole(descriptor, input.session.agentType) === "implementer";
|
|
20754
21993
|
var subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128);
|
|
20755
21994
|
var CLAUDE_WORKTREE = /[\\/]\.claude[\\/]worktrees[\\/]/;
|
|
20756
21995
|
var isolatedGit = (cwd) => `Run git as plain, separate commands inside your worktree (\`git switch -c <branch> <base>\`, ${workspaceTrailers(cwd).enabled ? '`git commit --trailer "Workit-Session=<your session>"`, `git push`; `workit doctor --fix` can install a prepare-commit-msg hook that adds the trailer' : "`git commit` (this workspace sets trailers: false: add no Workit-Session trailer), `git push`"}; add Co-Authored-By with \`git commit --trailer\` in the same commit, never by amending), never \`workit git …\`: Claude Code's worktree isolation refuses a command that wraps git and cannot be turned off (https://code.claude.com/docs/en/worktrees#how-claude-code-enforces-isolation). Never dodge that check by renaming or re-wrapping git. Use workit for the non-git verbs (\`workit check\`, \`workit ledger\`, \`workit pr\`).`;
|
|
@@ -20758,7 +21997,7 @@ var subagentStartText = (input, descriptor, event) => {
|
|
|
20758
21997
|
const session = input.session.id ?? null;
|
|
20759
21998
|
const role = workitRole(descriptor, event.agentType);
|
|
20760
21999
|
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
20761
|
-
const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer"
|
|
22000
|
+
const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" ? hostIsolated(input.cwd, event.agentId) : CLAUDE_WORKTREE.test(input.cwd));
|
|
20762
22001
|
const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
|
|
20763
22002
|
if (role === "implementer" && isolated)
|
|
20764
22003
|
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${isolatedGit(input.cwd)} Plain git is expected here, and the branch name is still policy-checked: a raw \`git switch -c\`, \`git checkout -b\`, \`git branch\` or \`git worktree add -b\` onto a protected or non-compliant name is denied, so use a policy-compliant branch.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
@@ -20766,7 +22005,7 @@ var subagentStartText = (input, descriptor, event) => {
|
|
|
20766
22005
|
return `${isolatedGit(input.cwd)}
|
|
20767
22006
|
${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
20768
22007
|
if (role === "implementer")
|
|
20769
|
-
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree (the lead makes
|
|
22008
|
+
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree, the one its brief names (the lead makes it with \`workit fanout worktree create <slice>\` before spawning), never in the lead's checkout, within its brief's scope. Workit binds it to the first Workit worktree it changes: from then on an edit or a mutating git command outside that worktree is denied, and before then so is one outside every Workit worktree. Run Workit against the worktree one verb per call, never \`cd … &&\` chains or \`$VAR\` paths: \`workit --cwd <worktree> git commit -m "<message>" -- <paths>\`, \`workit --cwd <worktree> check <name>\`. The worktree is on the brief's branch once the lead made it; otherwise switch with \`workit --cwd <worktree> git branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
20770
22009
|
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
20771
22010
|
if (role !== "judge" || !event.agentId)
|
|
20772
22011
|
return readOnly;
|
|
@@ -20797,7 +22036,7 @@ function decide(input, deps) {
|
|
|
20797
22036
|
switch (event.kind) {
|
|
20798
22037
|
case "session.start": {
|
|
20799
22038
|
if (!usable2(descriptor.context.sessionStart))
|
|
20800
|
-
return
|
|
22039
|
+
return NONE6;
|
|
20801
22040
|
const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
|
|
20802
22041
|
const offer = event.source === "startup" && (key === null || !offered.has(key));
|
|
20803
22042
|
if (offer && key !== null)
|
|
@@ -20812,26 +22051,31 @@ function decide(input, deps) {
|
|
|
20812
22051
|
}
|
|
20813
22052
|
case "context.turn": {
|
|
20814
22053
|
if (!usable2(descriptor.context.perTurn))
|
|
20815
|
-
return
|
|
22054
|
+
return NONE6;
|
|
20816
22055
|
const current = turnContextText(input, descriptor);
|
|
20817
22056
|
const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
20818
|
-
return withContextLine(text ? { kind: "context", text } :
|
|
22057
|
+
return withContextLine(text ? { kind: "context", text } : NONE6, promptNudge(input, event.prompt));
|
|
20819
22058
|
}
|
|
20820
22059
|
case "shell.pre": {
|
|
20821
22060
|
const skillRead = skillReadIn(event.command, event.dialect);
|
|
20822
22061
|
if (skillRead)
|
|
20823
22062
|
recordSkillLoad(input, skillRead, "read");
|
|
20824
22063
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
20825
|
-
const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) :
|
|
22064
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE6;
|
|
20826
22065
|
if (policy.kind !== "none")
|
|
20827
22066
|
return policy;
|
|
20828
22067
|
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
20829
22068
|
if (!postTool)
|
|
20830
22069
|
settlePendingCommit(input);
|
|
20831
|
-
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) :
|
|
22070
|
+
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE6;
|
|
20832
22071
|
if (raw.kind === "deny")
|
|
20833
22072
|
return raw;
|
|
20834
|
-
|
|
22073
|
+
if (canDeny && implementerAgent(input, descriptor)) {
|
|
22074
|
+
const confined = confineImplementerShell(input, event.command, event.dialect);
|
|
22075
|
+
if (confined.kind === "deny")
|
|
22076
|
+
return confined;
|
|
22077
|
+
}
|
|
22078
|
+
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE6;
|
|
20835
22079
|
if (gate.kind === "deny")
|
|
20836
22080
|
return gate;
|
|
20837
22081
|
const plan = heavyGate(input.cwd, event.command, {
|
|
@@ -20854,10 +22098,10 @@ function decide(input, deps) {
|
|
|
20854
22098
|
command: rewrittenTo,
|
|
20855
22099
|
input: event.toolInput ? { ...event.toolInput, command: rewrittenTo } : null,
|
|
20856
22100
|
text
|
|
20857
|
-
} :
|
|
22101
|
+
} : NONE6;
|
|
20858
22102
|
if (descriptor.transport !== "hook-process")
|
|
20859
22103
|
return rewritten(null);
|
|
20860
|
-
const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) :
|
|
22104
|
+
const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE6, aside), shipNudge(input, event.command));
|
|
20861
22105
|
if (!lease)
|
|
20862
22106
|
return cwdCommand && nudged.kind !== "deny" ? rewritten(nudged.kind === "context" ? nudged.text : null) : nudged;
|
|
20863
22107
|
return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
|
|
@@ -20866,9 +22110,17 @@ function decide(input, deps) {
|
|
|
20866
22110
|
case "shell.post":
|
|
20867
22111
|
if (usable2(descriptor.events["shell.post"].support))
|
|
20868
22112
|
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
20869
|
-
return
|
|
20870
|
-
case "write.pre":
|
|
20871
|
-
|
|
22113
|
+
return NONE6;
|
|
22114
|
+
case "write.pre": {
|
|
22115
|
+
if (!usable2(descriptor.events["write.pre"].support))
|
|
22116
|
+
return NONE6;
|
|
22117
|
+
if (implementerAgent(input, descriptor)) {
|
|
22118
|
+
const confined = confineImplementer(input, liveTargets(input.cwd, event.paths));
|
|
22119
|
+
if (confined.kind === "deny")
|
|
22120
|
+
return confined;
|
|
22121
|
+
}
|
|
22122
|
+
return writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport));
|
|
22123
|
+
}
|
|
20872
22124
|
case "subagent.start":
|
|
20873
22125
|
if (workitRole(descriptor, event.agentType) === "judge")
|
|
20874
22126
|
trackJudge(input, event.agentId, true);
|
|
@@ -20885,22 +22137,22 @@ function decide(input, deps) {
|
|
|
20885
22137
|
case "tool.pre":
|
|
20886
22138
|
if (event.skill)
|
|
20887
22139
|
recordSkillLoad(input, event.skill, "tool");
|
|
20888
|
-
return
|
|
22140
|
+
return NONE6;
|
|
20889
22141
|
case "stop":
|
|
20890
|
-
return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) :
|
|
22142
|
+
return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
|
|
20891
22143
|
case "subagent.stop":
|
|
20892
22144
|
trackJudge(input, event.agentId, false);
|
|
20893
|
-
return
|
|
22145
|
+
return NONE6;
|
|
20894
22146
|
case "prompt.submit":
|
|
20895
|
-
return
|
|
22147
|
+
return NONE6;
|
|
20896
22148
|
}
|
|
20897
22149
|
}
|
|
20898
22150
|
var failureDecision = (descriptor, event, error) => {
|
|
20899
22151
|
if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
|
|
20900
|
-
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } :
|
|
22152
|
+
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE6;
|
|
20901
22153
|
if (event === "session.start" || event === "subagent.start")
|
|
20902
22154
|
return { kind: "context", text: `[workit diagnostic: ${error}]` };
|
|
20903
|
-
return
|
|
22155
|
+
return NONE6;
|
|
20904
22156
|
};
|
|
20905
22157
|
var dispatchHook = (adapter, raw, env = process.env) => {
|
|
20906
22158
|
const parsed = adapter.parse(raw, env);
|
|
@@ -20926,7 +22178,7 @@ var dispatchHook = (adapter, raw, env = process.env) => {
|
|
|
20926
22178
|
};
|
|
20927
22179
|
// packages/workit-core/src/hooks/deadline.ts
|
|
20928
22180
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
|
|
20929
|
-
import
|
|
22181
|
+
import path29 from "node:path";
|
|
20930
22182
|
var HOOK_SKIP_TEXT = "workit context skipped: host under load";
|
|
20931
22183
|
var HOOK_DEADLINE_MS = 8000;
|
|
20932
22184
|
var HOST_DEADLINES = {
|
|
@@ -20944,7 +22196,7 @@ var hookDeadlineMs = (env, host = "", native) => {
|
|
|
20944
22196
|
const row = HOST_DEADLINES[host];
|
|
20945
22197
|
return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
|
|
20946
22198
|
};
|
|
20947
|
-
var skipsFile = (stateDir) =>
|
|
22199
|
+
var skipsFile = (stateDir) => path29.join(stateDir, HOOK_SKIPS_FILE);
|
|
20948
22200
|
var recordHookSkip = (skip, stateDir = resolveStateDir()) => {
|
|
20949
22201
|
try {
|
|
20950
22202
|
mkdirSync3(stateDir, { recursive: true });
|
|
@@ -20962,7 +22214,7 @@ var QUIET = new Set([
|
|
|
20962
22214
|
]);
|
|
20963
22215
|
var skipDecision = (event) => QUIET.has(event) ? { kind: "none" } : { kind: "context", text: HOOK_SKIP_TEXT };
|
|
20964
22216
|
var TIMED_OUT = Symbol("timed out");
|
|
20965
|
-
var
|
|
22217
|
+
var within2 = async (work, ms) => {
|
|
20966
22218
|
let timer;
|
|
20967
22219
|
const expired = new Promise((resolve) => {
|
|
20968
22220
|
timer = setTimeout(() => resolve(TIMED_OUT), Math.max(0, ms));
|
|
@@ -20993,7 +22245,7 @@ var readWithin = async (stdin, budget) => {
|
|
|
20993
22245
|
text += String(chunk);
|
|
20994
22246
|
return text;
|
|
20995
22247
|
})();
|
|
20996
|
-
const text = await
|
|
22248
|
+
const text = await within2(read, budget.startedAt + budget.deadlineMs - budget.now());
|
|
20997
22249
|
return isTimedOut(text) ? null : text;
|
|
20998
22250
|
};
|
|
20999
22251
|
var note = (adapter, raw, env, budget, outcome, stage) => {
|
|
@@ -21022,7 +22274,7 @@ var dispatchWithin = async (adapter, raw, env, budget, dispatch = dispatchHook)
|
|
|
21022
22274
|
if (budget.host !== null && typeof native === "string")
|
|
21023
22275
|
budget.deadlineMs = hookDeadlineMs(env, budget.host, native);
|
|
21024
22276
|
const remaining = () => budget.startedAt + budget.deadlineMs - budget.now();
|
|
21025
|
-
const result = remaining() > 0 ? await
|
|
22277
|
+
const result = remaining() > 0 ? await within2(Promise.resolve().then(() => dispatch(adapter, raw, env)), remaining()) : TIMED_OUT;
|
|
21026
22278
|
if (isTimedOut(result))
|
|
21027
22279
|
return skippedHook(adapter, raw, env, budget, "dispatch");
|
|
21028
22280
|
if (remaining() < 0)
|
|
@@ -21588,8 +22840,8 @@ var codexAdapter = {
|
|
|
21588
22840
|
};
|
|
21589
22841
|
|
|
21590
22842
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21591
|
-
import
|
|
21592
|
-
import
|
|
22843
|
+
import fs21 from "node:fs";
|
|
22844
|
+
import path30 from "node:path";
|
|
21593
22845
|
var undocumented = { support: "undocumented", native: null };
|
|
21594
22846
|
var none = { support: "none", native: null };
|
|
21595
22847
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -21786,17 +23038,17 @@ var protocolEvent2 = (input) => {
|
|
|
21786
23038
|
};
|
|
21787
23039
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
21788
23040
|
var lastAssistantText = (file) => {
|
|
21789
|
-
if (!file || !
|
|
23041
|
+
if (!file || !path30.isAbsolute(file))
|
|
21790
23042
|
return null;
|
|
21791
23043
|
try {
|
|
21792
|
-
const size =
|
|
23044
|
+
const size = fs21.statSync(file).size;
|
|
21793
23045
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21794
23046
|
const buffer = Buffer.alloc(size - start);
|
|
21795
|
-
const fd =
|
|
23047
|
+
const fd = fs21.openSync(file, "r");
|
|
21796
23048
|
try {
|
|
21797
|
-
|
|
23049
|
+
fs21.readSync(fd, buffer, 0, buffer.length, start);
|
|
21798
23050
|
} finally {
|
|
21799
|
-
|
|
23051
|
+
fs21.closeSync(fd);
|
|
21800
23052
|
}
|
|
21801
23053
|
const lines = buffer.toString("utf8").split(`
|
|
21802
23054
|
`);
|
|
@@ -21823,7 +23075,7 @@ var lastAssistantText = (file) => {
|
|
|
21823
23075
|
return null;
|
|
21824
23076
|
}
|
|
21825
23077
|
};
|
|
21826
|
-
var
|
|
23078
|
+
var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path30.sep) ? root : `${root}${path30.sep}`);
|
|
21827
23079
|
var hookCwd = (input) => {
|
|
21828
23080
|
const cwd = existingDirectory(input.cwd);
|
|
21829
23081
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
@@ -21832,7 +23084,7 @@ var hookCwd = (input) => {
|
|
|
21832
23084
|
};
|
|
21833
23085
|
var cursorWorkspaceRoot = (input) => {
|
|
21834
23086
|
const cwd = existingDirectory(input.cwd);
|
|
21835
|
-
return cwd && input.workspace_roots.find((root) =>
|
|
23087
|
+
return cwd && input.workspace_roots.find((root) => within3(root, cwd)) || input.workspace_roots[0];
|
|
21836
23088
|
};
|
|
21837
23089
|
var cursorDeny = (reason) => ({
|
|
21838
23090
|
permission: "deny",
|