@brainervirus/workit-cursor 8.23.0 → 8.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +1521 -400
- package/dist/mcp-server.js +1122 -140
- package/dist/workit-hook.js +1516 -400
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +1 -1
- package/skills/workit-fanout/references/brief.md +4 -4
- package/skills/workit-implement/SKILL.md +5 -5
- package/skills/workit-review/SKILL.md +5 -0
- package/skills/workit-ship/SKILL.md +1 -2
package/dist/mcp-server.js
CHANGED
|
@@ -6622,15 +6622,15 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-cursor/mcp/run-server.ts
|
|
6624
6624
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6625
|
-
import
|
|
6625
|
+
import path26 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-mcp/src/index.ts
|
|
6628
6628
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6629
|
-
import
|
|
6629
|
+
import path24 from "node:path";
|
|
6630
6630
|
|
|
6631
6631
|
// packages/workit-mcp/src/server.ts
|
|
6632
|
-
import { existsSync as
|
|
6633
|
-
import
|
|
6632
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
6633
|
+
import path23 from "node:path";
|
|
6634
6634
|
|
|
6635
6635
|
// node_modules/zod/v4/core/util.js
|
|
6636
6636
|
function getEnumValues(entries) {
|
|
@@ -14943,8 +14943,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
14943
14943
|
|
|
14944
14944
|
// packages/workit-core/src/core.ts
|
|
14945
14945
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
14946
|
-
import { existsSync as
|
|
14947
|
-
import
|
|
14946
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
14947
|
+
import path22 from "node:path";
|
|
14948
14948
|
|
|
14949
14949
|
// packages/workit-core/src/core/task-contract.ts
|
|
14950
14950
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17240,7 +17240,7 @@ var workspaceConfigSchema = object2({
|
|
|
17240
17240
|
}).passthrough().optional(),
|
|
17241
17241
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
17242
17242
|
defaultEndpoint: unknown().optional(),
|
|
17243
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
17243
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
17244
17244
|
delegation: unknown().optional(),
|
|
17245
17245
|
trailers: boolean2().optional(),
|
|
17246
17246
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -17781,6 +17781,144 @@ function resolveRef(cwd, ref) {
|
|
|
17781
17781
|
return null;
|
|
17782
17782
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
17783
17783
|
}
|
|
17784
|
+
function originHeadRef(cwd) {
|
|
17785
|
+
return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
|
|
17786
|
+
}
|
|
17787
|
+
function ownChangeDrift(cwd, range) {
|
|
17788
|
+
const before = diffRaw(cwd, range.fromFork, range.from);
|
|
17789
|
+
const after = diffRaw(cwd, range.toFork, range.to);
|
|
17790
|
+
if (!before || !after)
|
|
17791
|
+
return null;
|
|
17792
|
+
const judged = new Map(before.map((entry) => [entry.path, entry]));
|
|
17793
|
+
const current = new Map(after.map((entry) => [entry.path, entry]));
|
|
17794
|
+
const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
|
|
17795
|
+
return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
|
|
17796
|
+
}
|
|
17797
|
+
function sameOwnChange(cwd, range) {
|
|
17798
|
+
const fromFork = mergeBase(cwd, range.fromBase, range.from);
|
|
17799
|
+
const toFork = mergeBase(cwd, range.toBase, range.to);
|
|
17800
|
+
if (!fromFork || !toFork)
|
|
17801
|
+
return false;
|
|
17802
|
+
return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
|
|
17803
|
+
}
|
|
17804
|
+
var LANE_DIFF = [
|
|
17805
|
+
"-c",
|
|
17806
|
+
"core.quotePath=false",
|
|
17807
|
+
"diff",
|
|
17808
|
+
"--no-color",
|
|
17809
|
+
"--no-ext-diff",
|
|
17810
|
+
"--no-textconv",
|
|
17811
|
+
"--no-renames"
|
|
17812
|
+
];
|
|
17813
|
+
var laneRange = (fork, head) => {
|
|
17814
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
17815
|
+
return null;
|
|
17816
|
+
return head === null ? [fork] : [fork, head];
|
|
17817
|
+
};
|
|
17818
|
+
function diffNumstat(cwd, fork, head) {
|
|
17819
|
+
const range = laneRange(fork, head);
|
|
17820
|
+
if (!range)
|
|
17821
|
+
return null;
|
|
17822
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
17823
|
+
if (!run.ok)
|
|
17824
|
+
return null;
|
|
17825
|
+
const out = [];
|
|
17826
|
+
for (const record of run.stdout.split("\x00")) {
|
|
17827
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
17828
|
+
if (!match)
|
|
17829
|
+
continue;
|
|
17830
|
+
const binary = match[1] === "-";
|
|
17831
|
+
out.push({
|
|
17832
|
+
path: match[3],
|
|
17833
|
+
added: binary ? 0 : Number(match[1]),
|
|
17834
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
17835
|
+
binary
|
|
17836
|
+
});
|
|
17837
|
+
}
|
|
17838
|
+
return out;
|
|
17839
|
+
}
|
|
17840
|
+
function diffRaw(cwd, fork, head) {
|
|
17841
|
+
const range = laneRange(fork, head);
|
|
17842
|
+
if (!range)
|
|
17843
|
+
return null;
|
|
17844
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
17845
|
+
if (!run.ok)
|
|
17846
|
+
return null;
|
|
17847
|
+
const fields = run.stdout.split("\x00");
|
|
17848
|
+
const out = [];
|
|
17849
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
17850
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
17851
|
+
if (!meta)
|
|
17852
|
+
return null;
|
|
17853
|
+
out.push({
|
|
17854
|
+
path: fields[index + 1],
|
|
17855
|
+
oldMode: meta[1],
|
|
17856
|
+
newMode: meta[2],
|
|
17857
|
+
oldBlob: meta[3],
|
|
17858
|
+
newBlob: meta[4]
|
|
17859
|
+
});
|
|
17860
|
+
}
|
|
17861
|
+
return out;
|
|
17862
|
+
}
|
|
17863
|
+
function readBlobs(cwd, ids) {
|
|
17864
|
+
const out = new Map;
|
|
17865
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
17866
|
+
if (!wanted.length)
|
|
17867
|
+
return out;
|
|
17868
|
+
const run = spawnSync3("git", ["cat-file", "--batch"], {
|
|
17869
|
+
cwd,
|
|
17870
|
+
input: `${wanted.join(`
|
|
17871
|
+
`)}
|
|
17872
|
+
`,
|
|
17873
|
+
maxBuffer: MAX_BUFFER,
|
|
17874
|
+
timeout: GIT_TIMEOUTS.local,
|
|
17875
|
+
killSignal: "SIGKILL",
|
|
17876
|
+
windowsHide: true
|
|
17877
|
+
});
|
|
17878
|
+
if (run.status !== 0 || !run.stdout)
|
|
17879
|
+
return null;
|
|
17880
|
+
const bytes = run.stdout;
|
|
17881
|
+
let at = 0;
|
|
17882
|
+
for (const id of wanted) {
|
|
17883
|
+
const end = bytes.indexOf(10, at);
|
|
17884
|
+
if (end < 0)
|
|
17885
|
+
return null;
|
|
17886
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
17887
|
+
at = end + 1;
|
|
17888
|
+
if (header[1] === "missing")
|
|
17889
|
+
continue;
|
|
17890
|
+
const size = Number(header[2]);
|
|
17891
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
17892
|
+
return null;
|
|
17893
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
17894
|
+
at += size + 1;
|
|
17895
|
+
}
|
|
17896
|
+
return out;
|
|
17897
|
+
}
|
|
17898
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
17899
|
+
if (!safeArg(rev))
|
|
17900
|
+
return null;
|
|
17901
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
17902
|
+
if (!run.ok)
|
|
17903
|
+
return null;
|
|
17904
|
+
const out = new Map;
|
|
17905
|
+
for (const record of run.stdout.split("\x00")) {
|
|
17906
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
17907
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
17908
|
+
out.set(match[2], match[1]);
|
|
17909
|
+
}
|
|
17910
|
+
return out;
|
|
17911
|
+
}
|
|
17912
|
+
function fileAt(cwd, rev, file) {
|
|
17913
|
+
if (!safeArg(rev))
|
|
17914
|
+
return null;
|
|
17915
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
17916
|
+
return run.ok ? run.stdout : null;
|
|
17917
|
+
}
|
|
17918
|
+
function untrackedFiles(cwd) {
|
|
17919
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
17920
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
17921
|
+
}
|
|
17784
17922
|
var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
17785
17923
|
function currentBranch(cwd) {
|
|
17786
17924
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
@@ -21291,7 +21429,7 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
|
|
|
21291
21429
|
import { createHash as createHash10 } from "node:crypto";
|
|
21292
21430
|
import * as fs13 from "node:fs";
|
|
21293
21431
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
21294
|
-
import
|
|
21432
|
+
import path17 from "node:path";
|
|
21295
21433
|
|
|
21296
21434
|
// packages/workit-core/src/check-config.ts
|
|
21297
21435
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -21511,14 +21649,757 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
21511
21649
|
}
|
|
21512
21650
|
|
|
21513
21651
|
// packages/workit-core/src/core/policy/derive.ts
|
|
21514
|
-
import { existsSync as
|
|
21515
|
-
import
|
|
21652
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
21653
|
+
import path16 from "node:path";
|
|
21516
21654
|
|
|
21517
21655
|
// packages/workit-core/src/ledger.ts
|
|
21518
21656
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
21519
21657
|
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
21520
21658
|
import fs12 from "node:fs";
|
|
21659
|
+
import path15 from "node:path";
|
|
21660
|
+
|
|
21661
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
21662
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
21521
21663
|
import path14 from "node:path";
|
|
21664
|
+
|
|
21665
|
+
// packages/workit-core/src/git/default-base.ts
|
|
21666
|
+
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
21667
|
+
var trackTrunk = (cwd) => {
|
|
21668
|
+
try {
|
|
21669
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
21670
|
+
if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
|
|
21671
|
+
return null;
|
|
21672
|
+
const target = String(resolved.defaultTargetBranch ?? "").trim();
|
|
21673
|
+
return target && safeRef(target) ? target : null;
|
|
21674
|
+
} catch {
|
|
21675
|
+
return null;
|
|
21676
|
+
}
|
|
21677
|
+
};
|
|
21678
|
+
function defaultBase(cwd) {
|
|
21679
|
+
const trunk = trackTrunk(cwd);
|
|
21680
|
+
if (trunk) {
|
|
21681
|
+
for (const candidate of [`origin/${trunk}`, trunk])
|
|
21682
|
+
if (resolveRef(cwd, candidate))
|
|
21683
|
+
return candidate;
|
|
21684
|
+
}
|
|
21685
|
+
const originHead = originHeadRef(cwd);
|
|
21686
|
+
if (originHead && resolveRef(cwd, originHead))
|
|
21687
|
+
return originHead;
|
|
21688
|
+
for (const candidate of [
|
|
21689
|
+
"origin/main",
|
|
21690
|
+
"origin/master",
|
|
21691
|
+
"main",
|
|
21692
|
+
"master",
|
|
21693
|
+
"origin/develop",
|
|
21694
|
+
"develop"
|
|
21695
|
+
])
|
|
21696
|
+
if (resolveRef(cwd, candidate))
|
|
21697
|
+
return candidate;
|
|
21698
|
+
return null;
|
|
21699
|
+
}
|
|
21700
|
+
|
|
21701
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
21702
|
+
function patternRegex(dir, pattern) {
|
|
21703
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
21704
|
+
return null;
|
|
21705
|
+
const anchored = pattern.includes("/");
|
|
21706
|
+
const body = pattern.replace(/^\//u, "");
|
|
21707
|
+
let source = "";
|
|
21708
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
21709
|
+
const char = body[index];
|
|
21710
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
21711
|
+
const slash = body[index + 2] === "/";
|
|
21712
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
21713
|
+
index += slash ? 2 : 1;
|
|
21714
|
+
} else if (char === "*")
|
|
21715
|
+
source += "[^/]*";
|
|
21716
|
+
else if (char === "?")
|
|
21717
|
+
source += "[^/]";
|
|
21718
|
+
else if (char === "[") {
|
|
21719
|
+
const end = body.indexOf("]", index + 2);
|
|
21720
|
+
if (end < 0)
|
|
21721
|
+
return null;
|
|
21722
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
21723
|
+
source += `[${set}]`;
|
|
21724
|
+
index = end;
|
|
21725
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
21726
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
21727
|
+
index += 1;
|
|
21728
|
+
} else
|
|
21729
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
21730
|
+
}
|
|
21731
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
21732
|
+
try {
|
|
21733
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
21734
|
+
} catch {
|
|
21735
|
+
return null;
|
|
21736
|
+
}
|
|
21737
|
+
}
|
|
21738
|
+
function rulesOf(dir, text) {
|
|
21739
|
+
const out = [];
|
|
21740
|
+
for (const raw of text.split(`
|
|
21741
|
+
`)) {
|
|
21742
|
+
const line = raw.trim();
|
|
21743
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
21744
|
+
continue;
|
|
21745
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
21746
|
+
let generated = null;
|
|
21747
|
+
for (const attribute of attributes) {
|
|
21748
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
21749
|
+
generated = true;
|
|
21750
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
21751
|
+
generated = false;
|
|
21752
|
+
}
|
|
21753
|
+
if (generated === null)
|
|
21754
|
+
continue;
|
|
21755
|
+
const regex = patternRegex(dir, pattern);
|
|
21756
|
+
if (regex)
|
|
21757
|
+
out.push({ regex, generated });
|
|
21758
|
+
}
|
|
21759
|
+
return out;
|
|
21760
|
+
}
|
|
21761
|
+
function generatedByAttributes(files, paths) {
|
|
21762
|
+
const rules = [...files].map(([file, text]) => {
|
|
21763
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
21764
|
+
return {
|
|
21765
|
+
depth: dir ? dir.split("/").length : 0,
|
|
21766
|
+
rules: rulesOf(dir, text)
|
|
21767
|
+
};
|
|
21768
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
21769
|
+
const out = new Set;
|
|
21770
|
+
for (const file of paths) {
|
|
21771
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
21772
|
+
if (last?.generated)
|
|
21773
|
+
out.add(file);
|
|
21774
|
+
}
|
|
21775
|
+
return out;
|
|
21776
|
+
}
|
|
21777
|
+
|
|
21778
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
21779
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
21780
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
21781
|
+
var SYNTAXES = Object.fromEntries([
|
|
21782
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
21783
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
21784
|
+
ext,
|
|
21785
|
+
{ ...C_LIKE, triple: true }
|
|
21786
|
+
]),
|
|
21787
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
21788
|
+
...["css", "scss", "less"].map((ext) => [
|
|
21789
|
+
ext,
|
|
21790
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
21791
|
+
]),
|
|
21792
|
+
...["py", "pyi"].map((ext) => [
|
|
21793
|
+
ext,
|
|
21794
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
21795
|
+
]),
|
|
21796
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
21797
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
21798
|
+
ext,
|
|
21799
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
21800
|
+
])
|
|
21801
|
+
]);
|
|
21802
|
+
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;
|
|
21803
|
+
var extensionOf = (file) => {
|
|
21804
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
21805
|
+
const dot = name.lastIndexOf(".");
|
|
21806
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
21807
|
+
};
|
|
21808
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
21809
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
21810
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
21811
|
+
var WORD = /[\w$]/u;
|
|
21812
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
21813
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
21814
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
21815
|
+
var REGEX_KEYWORDS = new Set([
|
|
21816
|
+
"return",
|
|
21817
|
+
"typeof",
|
|
21818
|
+
"instanceof",
|
|
21819
|
+
"case",
|
|
21820
|
+
"do",
|
|
21821
|
+
"else",
|
|
21822
|
+
"in",
|
|
21823
|
+
"of",
|
|
21824
|
+
"new",
|
|
21825
|
+
"delete",
|
|
21826
|
+
"void",
|
|
21827
|
+
"throw",
|
|
21828
|
+
"yield",
|
|
21829
|
+
"await"
|
|
21830
|
+
]);
|
|
21831
|
+
function substanceOf(file, text) {
|
|
21832
|
+
const syntax = syntaxOf(file);
|
|
21833
|
+
if (!syntax)
|
|
21834
|
+
return plainSubstance(text);
|
|
21835
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
21836
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
21837
|
+
return plainSubstance(text);
|
|
21838
|
+
return lex(syntax, text);
|
|
21839
|
+
}
|
|
21840
|
+
function plainSubstance(text) {
|
|
21841
|
+
return text.split(`
|
|
21842
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
21843
|
+
`);
|
|
21844
|
+
}
|
|
21845
|
+
function lex(syntax, text) {
|
|
21846
|
+
const collapse = !syntax.indented;
|
|
21847
|
+
let out = "";
|
|
21848
|
+
let lineStart = 0;
|
|
21849
|
+
let kept = 0;
|
|
21850
|
+
let pendingSpace = false;
|
|
21851
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
21852
|
+
let index = 0;
|
|
21853
|
+
if (text.startsWith("#!")) {
|
|
21854
|
+
const end = text.indexOf(`
|
|
21855
|
+
`);
|
|
21856
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
21857
|
+
kept = out.length;
|
|
21858
|
+
index = end < 0 ? text.length : end;
|
|
21859
|
+
}
|
|
21860
|
+
const emitCode = (chunk) => {
|
|
21861
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
21862
|
+
const before = out[out.length - 1];
|
|
21863
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
21864
|
+
out += " ";
|
|
21865
|
+
}
|
|
21866
|
+
pendingSpace = false;
|
|
21867
|
+
out += chunk;
|
|
21868
|
+
};
|
|
21869
|
+
const emitVerbatim = (chunk) => {
|
|
21870
|
+
emitCode(chunk);
|
|
21871
|
+
kept = out.length;
|
|
21872
|
+
};
|
|
21873
|
+
const newline = () => {
|
|
21874
|
+
if (!collapse) {
|
|
21875
|
+
let end = out.length;
|
|
21876
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
21877
|
+
end -= 1;
|
|
21878
|
+
out = out.slice(0, end);
|
|
21879
|
+
}
|
|
21880
|
+
pendingSpace = false;
|
|
21881
|
+
if (out.length > lineStart) {
|
|
21882
|
+
out += `
|
|
21883
|
+
`;
|
|
21884
|
+
lineStart = out.length;
|
|
21885
|
+
}
|
|
21886
|
+
};
|
|
21887
|
+
const regexAllowed = () => {
|
|
21888
|
+
let end = out.length;
|
|
21889
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
21890
|
+
end -= 1;
|
|
21891
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
21892
|
+
return true;
|
|
21893
|
+
let start = end;
|
|
21894
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
21895
|
+
start -= 1;
|
|
21896
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
21897
|
+
};
|
|
21898
|
+
const scanString = (quote, escapes, multiline) => {
|
|
21899
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
21900
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
21901
|
+
let end = index + quote.length;
|
|
21902
|
+
while (end < text.length) {
|
|
21903
|
+
const char = text[end];
|
|
21904
|
+
if (escapes && char === "\\") {
|
|
21905
|
+
end += 2;
|
|
21906
|
+
continue;
|
|
21907
|
+
}
|
|
21908
|
+
if (text.startsWith(quote, end)) {
|
|
21909
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
21910
|
+
return false;
|
|
21911
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
21912
|
+
index = end + quote.length;
|
|
21913
|
+
return true;
|
|
21914
|
+
}
|
|
21915
|
+
if (char === `
|
|
21916
|
+
` && !multiline)
|
|
21917
|
+
return false;
|
|
21918
|
+
end += 1;
|
|
21919
|
+
}
|
|
21920
|
+
return false;
|
|
21921
|
+
};
|
|
21922
|
+
while (index < text.length) {
|
|
21923
|
+
const frame = frames[frames.length - 1];
|
|
21924
|
+
const char = text[index];
|
|
21925
|
+
if (frame.kind === "template") {
|
|
21926
|
+
if (char === "\\") {
|
|
21927
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
21928
|
+
index += 2;
|
|
21929
|
+
} else if (char === "`") {
|
|
21930
|
+
emitVerbatim(char);
|
|
21931
|
+
frames.pop();
|
|
21932
|
+
index += 1;
|
|
21933
|
+
} else if (text.startsWith("${", index)) {
|
|
21934
|
+
emitVerbatim("${");
|
|
21935
|
+
frames.push({ kind: "code", braces: 0 });
|
|
21936
|
+
index += 2;
|
|
21937
|
+
} else {
|
|
21938
|
+
out += char;
|
|
21939
|
+
kept = out.length;
|
|
21940
|
+
index += 1;
|
|
21941
|
+
}
|
|
21942
|
+
continue;
|
|
21943
|
+
}
|
|
21944
|
+
if (char === `
|
|
21945
|
+
`) {
|
|
21946
|
+
newline();
|
|
21947
|
+
index += 1;
|
|
21948
|
+
continue;
|
|
21949
|
+
}
|
|
21950
|
+
if (SPACE.test(char)) {
|
|
21951
|
+
if (collapse)
|
|
21952
|
+
pendingSpace = true;
|
|
21953
|
+
else
|
|
21954
|
+
out += char;
|
|
21955
|
+
index += 1;
|
|
21956
|
+
continue;
|
|
21957
|
+
}
|
|
21958
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
21959
|
+
if (lineOpener) {
|
|
21960
|
+
const end = text.indexOf(`
|
|
21961
|
+
`, index);
|
|
21962
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
21963
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
21964
|
+
emitVerbatim(comment);
|
|
21965
|
+
else
|
|
21966
|
+
pendingSpace = true;
|
|
21967
|
+
index = end < 0 ? text.length : end;
|
|
21968
|
+
continue;
|
|
21969
|
+
}
|
|
21970
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
21971
|
+
if (block) {
|
|
21972
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
21973
|
+
if (end < 0)
|
|
21974
|
+
return null;
|
|
21975
|
+
const comment = text.slice(index, end + block[1].length);
|
|
21976
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
21977
|
+
emitVerbatim(comment);
|
|
21978
|
+
else
|
|
21979
|
+
pendingSpace = true;
|
|
21980
|
+
if (comment.includes(`
|
|
21981
|
+
`))
|
|
21982
|
+
newline();
|
|
21983
|
+
index = end + block[1].length;
|
|
21984
|
+
continue;
|
|
21985
|
+
}
|
|
21986
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
21987
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
21988
|
+
return null;
|
|
21989
|
+
continue;
|
|
21990
|
+
}
|
|
21991
|
+
if (syntax.quotes.includes(char)) {
|
|
21992
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
21993
|
+
return null;
|
|
21994
|
+
continue;
|
|
21995
|
+
}
|
|
21996
|
+
if (syntax.literal?.includes(char)) {
|
|
21997
|
+
if (!scanString(char, false, false))
|
|
21998
|
+
return null;
|
|
21999
|
+
continue;
|
|
22000
|
+
}
|
|
22001
|
+
if (syntax.rawBacktick && char === "`") {
|
|
22002
|
+
if (!scanString("`", false, true))
|
|
22003
|
+
return null;
|
|
22004
|
+
continue;
|
|
22005
|
+
}
|
|
22006
|
+
if (syntax.js && char === "`") {
|
|
22007
|
+
emitVerbatim(char);
|
|
22008
|
+
frames.push({ kind: "template" });
|
|
22009
|
+
index += 1;
|
|
22010
|
+
continue;
|
|
22011
|
+
}
|
|
22012
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
22013
|
+
return null;
|
|
22014
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
22015
|
+
const lineEnd = text.indexOf(`
|
|
22016
|
+
`, index);
|
|
22017
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
22018
|
+
return null;
|
|
22019
|
+
}
|
|
22020
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
22021
|
+
let end = index + 1;
|
|
22022
|
+
let inClass = false;
|
|
22023
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
22024
|
+
if (text[end] === `
|
|
22025
|
+
`)
|
|
22026
|
+
return null;
|
|
22027
|
+
if (text[end] === "\\")
|
|
22028
|
+
end += 1;
|
|
22029
|
+
else if (text[end] === "[")
|
|
22030
|
+
inClass = true;
|
|
22031
|
+
else if (text[end] === "]")
|
|
22032
|
+
inClass = false;
|
|
22033
|
+
end += 1;
|
|
22034
|
+
}
|
|
22035
|
+
if (end >= text.length)
|
|
22036
|
+
return null;
|
|
22037
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
22038
|
+
index = end + 1;
|
|
22039
|
+
continue;
|
|
22040
|
+
}
|
|
22041
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
22042
|
+
if (char === "{")
|
|
22043
|
+
frame.braces += 1;
|
|
22044
|
+
else if (char === "}") {
|
|
22045
|
+
if (frame.braces === 0) {
|
|
22046
|
+
emitVerbatim(char);
|
|
22047
|
+
frames.pop();
|
|
22048
|
+
index += 1;
|
|
22049
|
+
continue;
|
|
22050
|
+
}
|
|
22051
|
+
frame.braces -= 1;
|
|
22052
|
+
}
|
|
22053
|
+
}
|
|
22054
|
+
emitCode(char);
|
|
22055
|
+
index += 1;
|
|
22056
|
+
}
|
|
22057
|
+
if (frames.length !== 1)
|
|
22058
|
+
return null;
|
|
22059
|
+
newline();
|
|
22060
|
+
return out;
|
|
22061
|
+
}
|
|
22062
|
+
function unbalanced(body) {
|
|
22063
|
+
let depth = 0;
|
|
22064
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
22065
|
+
if (part === "{")
|
|
22066
|
+
depth += 1;
|
|
22067
|
+
else if (part === "}")
|
|
22068
|
+
depth = Math.max(0, depth - 1);
|
|
22069
|
+
}
|
|
22070
|
+
return depth > 0;
|
|
22071
|
+
}
|
|
22072
|
+
function hasOpaqueLineBreak(text) {
|
|
22073
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
22074
|
+
}
|
|
22075
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
22076
|
+
function textOf(bytes) {
|
|
22077
|
+
if (bytes.includes(0))
|
|
22078
|
+
return null;
|
|
22079
|
+
try {
|
|
22080
|
+
return decoder.decode(bytes);
|
|
22081
|
+
} catch {
|
|
22082
|
+
return null;
|
|
22083
|
+
}
|
|
22084
|
+
}
|
|
22085
|
+
function cosmeticChange(file, before, after) {
|
|
22086
|
+
const left = textOf(before);
|
|
22087
|
+
const right = textOf(after);
|
|
22088
|
+
if (left === null || right === null)
|
|
22089
|
+
return false;
|
|
22090
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
22091
|
+
return false;
|
|
22092
|
+
const a = substanceOf(file, left);
|
|
22093
|
+
return a !== null && a === substanceOf(file, right);
|
|
22094
|
+
}
|
|
22095
|
+
|
|
22096
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
22097
|
+
var LANE_THRESHOLDS = {
|
|
22098
|
+
selfMaxLines: 100,
|
|
22099
|
+
selfMaxFiles: 5,
|
|
22100
|
+
fullMinLines: 500
|
|
22101
|
+
};
|
|
22102
|
+
var RISK_TOKENS = new Set([
|
|
22103
|
+
"auth",
|
|
22104
|
+
"authn",
|
|
22105
|
+
"authz",
|
|
22106
|
+
"oauth",
|
|
22107
|
+
"security",
|
|
22108
|
+
"permission",
|
|
22109
|
+
"permissions",
|
|
22110
|
+
"secret",
|
|
22111
|
+
"secrets",
|
|
22112
|
+
"crypto",
|
|
22113
|
+
"gate",
|
|
22114
|
+
"gates",
|
|
22115
|
+
"policy",
|
|
22116
|
+
"policies",
|
|
22117
|
+
"hook",
|
|
22118
|
+
"hooks",
|
|
22119
|
+
"husky",
|
|
22120
|
+
"lefthook",
|
|
22121
|
+
"release",
|
|
22122
|
+
"releases",
|
|
22123
|
+
"migration",
|
|
22124
|
+
"migrations",
|
|
22125
|
+
"migrate",
|
|
22126
|
+
"workflows"
|
|
22127
|
+
]);
|
|
22128
|
+
var RISK_FILES = new Set([
|
|
22129
|
+
".gitlab-ci.yml",
|
|
22130
|
+
"Jenkinsfile",
|
|
22131
|
+
"azure-pipelines.yml",
|
|
22132
|
+
".pre-commit-config.yaml",
|
|
22133
|
+
".workit/risk-paths"
|
|
22134
|
+
]);
|
|
22135
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
22136
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
22137
|
+
var LOCKFILES = new Set([
|
|
22138
|
+
"bun.lock",
|
|
22139
|
+
"bun.lockb",
|
|
22140
|
+
"package-lock.json",
|
|
22141
|
+
"npm-shrinkwrap.json",
|
|
22142
|
+
"yarn.lock",
|
|
22143
|
+
"pnpm-lock.yaml",
|
|
22144
|
+
"Cargo.lock",
|
|
22145
|
+
"go.sum",
|
|
22146
|
+
"poetry.lock",
|
|
22147
|
+
"uv.lock",
|
|
22148
|
+
"Pipfile.lock",
|
|
22149
|
+
"Gemfile.lock",
|
|
22150
|
+
"composer.lock"
|
|
22151
|
+
]);
|
|
22152
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
|
|
22153
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
22154
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
22155
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
|
|
22156
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
22157
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
22158
|
+
function kindOf(file) {
|
|
22159
|
+
if (file.binary)
|
|
22160
|
+
return "behavior";
|
|
22161
|
+
if (file.generated)
|
|
22162
|
+
return "generated";
|
|
22163
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
22164
|
+
return "lockfile";
|
|
22165
|
+
if (isDoc(file.path))
|
|
22166
|
+
return "doc";
|
|
22167
|
+
if (file.cosmetic)
|
|
22168
|
+
return "cosmetic";
|
|
22169
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
22170
|
+
}
|
|
22171
|
+
var globCache = new Map;
|
|
22172
|
+
function globMatch(pattern, file) {
|
|
22173
|
+
let regex = globCache.get(pattern);
|
|
22174
|
+
if (!regex) {
|
|
22175
|
+
let source = "";
|
|
22176
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
22177
|
+
const char = pattern[index];
|
|
22178
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
22179
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
22180
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
22181
|
+
} else if (char === "*")
|
|
22182
|
+
source += "[^/]*";
|
|
22183
|
+
else if (char === "?")
|
|
22184
|
+
source += "[^/]";
|
|
22185
|
+
else
|
|
22186
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
22187
|
+
}
|
|
22188
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
22189
|
+
globCache.set(pattern, regex);
|
|
22190
|
+
}
|
|
22191
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
22192
|
+
}
|
|
22193
|
+
function isRiskPath(file, extra = []) {
|
|
22194
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
22195
|
+
return true;
|
|
22196
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
22197
|
+
return true;
|
|
22198
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
22199
|
+
return false;
|
|
22200
|
+
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)));
|
|
22201
|
+
}
|
|
22202
|
+
var parseRiskPaths = (text) => text.split(`
|
|
22203
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
22204
|
+
function computeLane(input) {
|
|
22205
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
22206
|
+
const risky = new Set(riskFiles);
|
|
22207
|
+
const kinds = input.files.map((file) => ({
|
|
22208
|
+
file,
|
|
22209
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
22210
|
+
}));
|
|
22211
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
22212
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
22213
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
22214
|
+
const result = (lane, reasons) => ({
|
|
22215
|
+
lane,
|
|
22216
|
+
reasons,
|
|
22217
|
+
lines,
|
|
22218
|
+
files: behavior.length,
|
|
22219
|
+
riskFiles
|
|
22220
|
+
});
|
|
22221
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
22222
|
+
if (input.riskTier === "high")
|
|
22223
|
+
return result("full", ["the task judgment records high risk"]);
|
|
22224
|
+
if (riskFiles.length)
|
|
22225
|
+
return result("full", [
|
|
22226
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
22227
|
+
]);
|
|
22228
|
+
if (!behavior.length)
|
|
22229
|
+
return result("none", [
|
|
22230
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
22231
|
+
]);
|
|
22232
|
+
if (lines >= fullMinLines)
|
|
22233
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
22234
|
+
if (lines > selfMaxLines)
|
|
22235
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
22236
|
+
if (behavior.length > selfMaxFiles)
|
|
22237
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
22238
|
+
if (opaque.length)
|
|
22239
|
+
return result("focused", [
|
|
22240
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
22241
|
+
]);
|
|
22242
|
+
return result("self", [
|
|
22243
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
22244
|
+
]);
|
|
22245
|
+
}
|
|
22246
|
+
function untrackedStat(cwd, file) {
|
|
22247
|
+
try {
|
|
22248
|
+
const bytes = readFileSync4(path14.join(cwd, file));
|
|
22249
|
+
if (bytes.includes(0))
|
|
22250
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
22251
|
+
const text = bytes.toString("utf8");
|
|
22252
|
+
if (hasOpaqueLineBreak(text))
|
|
22253
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
22254
|
+
const lines = text.split(`
|
|
22255
|
+
`).length - (text.endsWith(`
|
|
22256
|
+
`) ? 1 : 0);
|
|
22257
|
+
return { path: file, added: lines, deleted: 0 };
|
|
22258
|
+
} catch {
|
|
22259
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
22260
|
+
}
|
|
22261
|
+
}
|
|
22262
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
22263
|
+
var ABSENT = /^0+$/u;
|
|
22264
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
22265
|
+
const raw = diffRaw(cwd, fork, head);
|
|
22266
|
+
if (!raw)
|
|
22267
|
+
return null;
|
|
22268
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
22269
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
22270
|
+
if (!blobs)
|
|
22271
|
+
return null;
|
|
22272
|
+
const out = new Set;
|
|
22273
|
+
const opaque = new Set;
|
|
22274
|
+
for (const entry of candidates) {
|
|
22275
|
+
const before = blobs.get(entry.oldBlob);
|
|
22276
|
+
let after = blobs.get(entry.newBlob);
|
|
22277
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
22278
|
+
try {
|
|
22279
|
+
after = readFileSync4(path14.join(cwd, entry.path));
|
|
22280
|
+
} catch {
|
|
22281
|
+
after = undefined;
|
|
22282
|
+
}
|
|
22283
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
22284
|
+
out.add(entry.path);
|
|
22285
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
22286
|
+
opaque.add(entry.path);
|
|
22287
|
+
}
|
|
22288
|
+
return { cosmetic: out, opaque };
|
|
22289
|
+
}
|
|
22290
|
+
function generatedAt(cwd, fork, paths) {
|
|
22291
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
22292
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
22293
|
+
if (!files || !blobs)
|
|
22294
|
+
return new Set;
|
|
22295
|
+
const texts = new Map;
|
|
22296
|
+
for (const [file, id] of files) {
|
|
22297
|
+
const bytes = blobs.get(id);
|
|
22298
|
+
if (bytes)
|
|
22299
|
+
texts.set(file, bytes.toString("utf8"));
|
|
22300
|
+
}
|
|
22301
|
+
return generatedByAttributes(texts, paths);
|
|
22302
|
+
}
|
|
22303
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
22304
|
+
function laneFiles(cwd, fork, head, attributesAt = fork) {
|
|
22305
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
22306
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
22307
|
+
if (!stats || !cosmetic)
|
|
22308
|
+
return null;
|
|
22309
|
+
const files = [
|
|
22310
|
+
...stats.map((entry) => ({
|
|
22311
|
+
...entry,
|
|
22312
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
22313
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
22314
|
+
})),
|
|
22315
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
22316
|
+
];
|
|
22317
|
+
const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
|
|
22318
|
+
return files.map((entry) => ({
|
|
22319
|
+
...entry,
|
|
22320
|
+
generated: generated.has(entry.path)
|
|
22321
|
+
}));
|
|
22322
|
+
}
|
|
22323
|
+
function driftFiles(cwd, range) {
|
|
22324
|
+
if (range.fromFork === range.toFork)
|
|
22325
|
+
return laneFiles(cwd, range.from, range.to, range.toFork);
|
|
22326
|
+
const drifted = ownChangeDrift(cwd, range);
|
|
22327
|
+
const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
|
|
22328
|
+
if (!drifted || !stats)
|
|
22329
|
+
return null;
|
|
22330
|
+
const byPath = new Map(stats.map((entry) => [entry.path, entry]));
|
|
22331
|
+
const generated = generatedAt(cwd, range.toFork, drifted);
|
|
22332
|
+
return drifted.map((file) => ({
|
|
22333
|
+
path: file,
|
|
22334
|
+
added: byPath.get(file)?.added ?? 0,
|
|
22335
|
+
deleted: byPath.get(file)?.deleted ?? 0,
|
|
22336
|
+
...byPath.get(file)?.binary ? { binary: true } : {},
|
|
22337
|
+
cosmetic: false,
|
|
22338
|
+
generated: generated.has(file)
|
|
22339
|
+
}));
|
|
22340
|
+
}
|
|
22341
|
+
function branchLane(cwd, options) {
|
|
22342
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
22343
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
22344
|
+
const head = options.workingTree ? null : options.head;
|
|
22345
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
22346
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
22347
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
22348
|
+
if (!fork || !files) {
|
|
22349
|
+
const high = riskTier === "high";
|
|
22350
|
+
return {
|
|
22351
|
+
lane: high ? "full" : "focused",
|
|
22352
|
+
reasons: [
|
|
22353
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
22354
|
+
...high ? ["the task judgment records high risk"] : []
|
|
22355
|
+
],
|
|
22356
|
+
lines: 0,
|
|
22357
|
+
files: 0,
|
|
22358
|
+
riskFiles: [],
|
|
22359
|
+
base,
|
|
22360
|
+
head: options.head
|
|
22361
|
+
};
|
|
22362
|
+
}
|
|
22363
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
22364
|
+
return {
|
|
22365
|
+
...computeLane({
|
|
22366
|
+
files,
|
|
22367
|
+
riskTier,
|
|
22368
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
22369
|
+
}),
|
|
22370
|
+
base,
|
|
22371
|
+
head: options.head
|
|
22372
|
+
};
|
|
22373
|
+
}
|
|
22374
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
22375
|
+
|
|
22376
|
+
// packages/workit-core/src/core/policy/carry.ts
|
|
22377
|
+
var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
|
|
22378
|
+
function verdictDrift(cwd, input) {
|
|
22379
|
+
const since = input.from;
|
|
22380
|
+
const stale = (reason, files = null, focused = false) => ({
|
|
22381
|
+
carries: false,
|
|
22382
|
+
since,
|
|
22383
|
+
files,
|
|
22384
|
+
focused,
|
|
22385
|
+
reason
|
|
22386
|
+
});
|
|
22387
|
+
const fromFork = mergeBase(cwd, input.fromBase, input.from);
|
|
22388
|
+
const toFork = mergeBase(cwd, input.toBase, input.to);
|
|
22389
|
+
const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
|
|
22390
|
+
if (!fromFork || !toFork || !files)
|
|
22391
|
+
return stale(`the change since ${since.slice(0, 12)} cannot be read`);
|
|
22392
|
+
const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
|
|
22393
|
+
const lane = computeLane({ files, riskPaths });
|
|
22394
|
+
const paths = files.map((file) => file.path);
|
|
22395
|
+
if (lane.lane === "none")
|
|
22396
|
+
return { carries: true, rule: "lane-none", since, files: paths };
|
|
22397
|
+
if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
|
|
22398
|
+
return { carries: true, rule: "tests-only", since, files: paths };
|
|
22399
|
+
return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
|
|
22400
|
+
}
|
|
22401
|
+
|
|
22402
|
+
// packages/workit-core/src/ledger.ts
|
|
21522
22403
|
var LEDGER_VERSION = 1;
|
|
21523
22404
|
var MAX_LINE_BYTES = 4096;
|
|
21524
22405
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
@@ -21563,52 +22444,19 @@ var git4 = (cwd, args) => {
|
|
|
21563
22444
|
const value = run.ok ? run.stdout.trim() : "";
|
|
21564
22445
|
return value ? value : null;
|
|
21565
22446
|
};
|
|
21566
|
-
var
|
|
22447
|
+
var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
|
|
21567
22448
|
function resolveCommit(cwd, ref) {
|
|
21568
|
-
if (!
|
|
22449
|
+
if (!safeRef2(ref))
|
|
21569
22450
|
return null;
|
|
21570
22451
|
return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
21571
22452
|
}
|
|
21572
22453
|
function branchHead(cwd, branch) {
|
|
21573
|
-
if (!
|
|
22454
|
+
if (!safeRef2(branch))
|
|
21574
22455
|
return null;
|
|
21575
22456
|
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
21576
22457
|
}
|
|
21577
|
-
var trackTrunk = (cwd) => {
|
|
21578
|
-
try {
|
|
21579
|
-
const resolved = vcsConfig("resolve", cwd);
|
|
21580
|
-
if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
|
|
21581
|
-
return null;
|
|
21582
|
-
const target = String(resolved.defaultTargetBranch ?? "").trim();
|
|
21583
|
-
return target && safeRef(target) ? target : null;
|
|
21584
|
-
} catch {
|
|
21585
|
-
return null;
|
|
21586
|
-
}
|
|
21587
|
-
};
|
|
21588
|
-
function defaultBase(cwd) {
|
|
21589
|
-
const trunk = trackTrunk(cwd);
|
|
21590
|
-
if (trunk) {
|
|
21591
|
-
for (const candidate of [`origin/${trunk}`, trunk])
|
|
21592
|
-
if (resolveCommit(cwd, candidate))
|
|
21593
|
-
return candidate;
|
|
21594
|
-
}
|
|
21595
|
-
const originHead = git4(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
21596
|
-
if (originHead && resolveCommit(cwd, originHead))
|
|
21597
|
-
return originHead;
|
|
21598
|
-
for (const candidate of [
|
|
21599
|
-
"origin/main",
|
|
21600
|
-
"origin/master",
|
|
21601
|
-
"main",
|
|
21602
|
-
"master",
|
|
21603
|
-
"origin/develop",
|
|
21604
|
-
"develop"
|
|
21605
|
-
])
|
|
21606
|
-
if (resolveCommit(cwd, candidate))
|
|
21607
|
-
return candidate;
|
|
21608
|
-
return null;
|
|
21609
|
-
}
|
|
21610
22458
|
function diffHash(cwd, base, head) {
|
|
21611
|
-
if (!
|
|
22459
|
+
if (!safeRef2(base) || !safeRef2(head))
|
|
21612
22460
|
return null;
|
|
21613
22461
|
const diff = gitRun2(cwd, [
|
|
21614
22462
|
"diff",
|
|
@@ -21640,14 +22488,14 @@ function diffHash(cwd, base, head) {
|
|
|
21640
22488
|
return `sha256:${hash.digest("hex")}`;
|
|
21641
22489
|
}
|
|
21642
22490
|
function storeRoot(cwd) {
|
|
21643
|
-
const location = resolveStore(
|
|
22491
|
+
const location = resolveStore(path15.resolve(cwd));
|
|
21644
22492
|
if (location instanceof Error)
|
|
21645
22493
|
return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
|
|
21646
22494
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
21647
22495
|
}
|
|
21648
22496
|
function ledgerPath(cwd) {
|
|
21649
22497
|
const root = storeRoot(cwd);
|
|
21650
|
-
return root.ok ? { ok: true, value:
|
|
22498
|
+
return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
21651
22499
|
}
|
|
21652
22500
|
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
21653
22501
|
var NETWORK_FS_TYPES = new Set([
|
|
@@ -21677,13 +22525,13 @@ var LOCK_STALE_MS = 30000;
|
|
|
21677
22525
|
var LOCK_OWNER = "owner";
|
|
21678
22526
|
var lockToken = (lock) => {
|
|
21679
22527
|
try {
|
|
21680
|
-
return fs12.readFileSync(
|
|
22528
|
+
return fs12.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
|
|
21681
22529
|
} catch {
|
|
21682
22530
|
return null;
|
|
21683
22531
|
}
|
|
21684
22532
|
};
|
|
21685
22533
|
var lockAgeMs = (lock) => {
|
|
21686
|
-
for (const target of [
|
|
22534
|
+
for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
|
|
21687
22535
|
try {
|
|
21688
22536
|
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
21689
22537
|
} catch {}
|
|
@@ -21700,7 +22548,7 @@ var ledgerLock = {
|
|
|
21700
22548
|
throw error;
|
|
21701
22549
|
}
|
|
21702
22550
|
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
21703
|
-
fs12.writeFileSync(
|
|
22551
|
+
fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
|
|
21704
22552
|
return token;
|
|
21705
22553
|
},
|
|
21706
22554
|
release(lock, token) {
|
|
@@ -21833,8 +22681,8 @@ function appendLine(file, build, options) {
|
|
|
21833
22681
|
}
|
|
21834
22682
|
};
|
|
21835
22683
|
try {
|
|
21836
|
-
fs12.mkdirSync(
|
|
21837
|
-
if (!options.forceLock && !onNetworkFs(
|
|
22684
|
+
fs12.mkdirSync(path15.dirname(file), { recursive: true });
|
|
22685
|
+
if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
|
|
21838
22686
|
return write();
|
|
21839
22687
|
const locked = withLedgerLock(file, write);
|
|
21840
22688
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -22065,7 +22913,7 @@ function verdictBasis(verdict, current) {
|
|
|
22065
22913
|
}
|
|
22066
22914
|
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
22067
22915
|
function trailerSessions(cwd, base, head) {
|
|
22068
|
-
if (!base || !head || !
|
|
22916
|
+
if (!base || !head || !safeRef2(base) || !safeRef2(head))
|
|
22069
22917
|
return [];
|
|
22070
22918
|
const out = git4(cwd, [
|
|
22071
22919
|
"log",
|
|
@@ -22078,7 +22926,7 @@ var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
|
|
|
22078
22926
|
function branchScope(cwd, branch) {
|
|
22079
22927
|
const renamedAt = new Map;
|
|
22080
22928
|
let since = Number.NEGATIVE_INFINITY;
|
|
22081
|
-
if (
|
|
22929
|
+
if (safeRef2(branch)) {
|
|
22082
22930
|
const log = git4(cwd, [
|
|
22083
22931
|
"log",
|
|
22084
22932
|
"-g",
|
|
@@ -22223,12 +23071,29 @@ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
|
22223
23071
|
}
|
|
22224
23072
|
return effective;
|
|
22225
23073
|
}
|
|
22226
|
-
|
|
23074
|
+
var restackExact = new WeakMap;
|
|
23075
|
+
function restackCarries(exactIn, rows, branch, from, to) {
|
|
22227
23076
|
if (!from || !to)
|
|
22228
23077
|
return false;
|
|
23078
|
+
const exact = (row, fromHead, head) => {
|
|
23079
|
+
if (exactIn === null)
|
|
23080
|
+
return true;
|
|
23081
|
+
const cwd = exactIn;
|
|
23082
|
+
let known = restackExact.get(row);
|
|
23083
|
+
if (known === undefined) {
|
|
23084
|
+
known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
|
|
23085
|
+
from: fromHead,
|
|
23086
|
+
to: head,
|
|
23087
|
+
fromBase: row.fromBase,
|
|
23088
|
+
toBase: row.toBase
|
|
23089
|
+
});
|
|
23090
|
+
restackExact.set(row, known);
|
|
23091
|
+
}
|
|
23092
|
+
return known;
|
|
23093
|
+
};
|
|
22229
23094
|
const edges = new Map;
|
|
22230
23095
|
for (const row of rows)
|
|
22231
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
23096
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head && exact(row, row.fromHead, row.head))
|
|
22232
23097
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
22233
23098
|
const seen = new Set([from]);
|
|
22234
23099
|
const queue = [from];
|
|
@@ -22262,9 +23127,57 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
22262
23127
|
return key;
|
|
22263
23128
|
};
|
|
22264
23129
|
const keyFor = (base) => keysAt(base, head);
|
|
22265
|
-
const
|
|
23130
|
+
const driftCache = new Map;
|
|
23131
|
+
const driftOf = (row) => {
|
|
23132
|
+
if (driftCache.has(row))
|
|
23133
|
+
return driftCache.get(row) ?? null;
|
|
23134
|
+
let drift = null;
|
|
23135
|
+
const toBase = row.base ?? fallbackBase;
|
|
23136
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
23137
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
23138
|
+
if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
|
|
23139
|
+
drift = verdictDrift(cwd, {
|
|
23140
|
+
from: row.head,
|
|
23141
|
+
to: head,
|
|
23142
|
+
fromBase,
|
|
23143
|
+
toBase: toBaseSha,
|
|
23144
|
+
kind: typeof row.kind === "string" ? row.kind : "review"
|
|
23145
|
+
});
|
|
23146
|
+
driftCache.set(row, drift);
|
|
23147
|
+
return drift;
|
|
23148
|
+
};
|
|
23149
|
+
const exactCache = new Map;
|
|
23150
|
+
const exactCarry = (row) => {
|
|
23151
|
+
if (FAILING.has(String(row.result)))
|
|
23152
|
+
return true;
|
|
23153
|
+
let exact = exactCache.get(row);
|
|
23154
|
+
if (exact === undefined) {
|
|
23155
|
+
const toBase = row.base ?? fallbackBase;
|
|
23156
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
23157
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
23158
|
+
exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
|
|
23159
|
+
exactCache.set(row, exact);
|
|
23160
|
+
}
|
|
23161
|
+
return exact;
|
|
23162
|
+
};
|
|
23163
|
+
const keyBasis = (row) => {
|
|
22266
23164
|
const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
22267
|
-
return basis === "
|
|
23165
|
+
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
23166
|
+
};
|
|
23167
|
+
const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
|
|
23168
|
+
const carryOf = (row) => {
|
|
23169
|
+
if (keyBasis(row) !== "stale")
|
|
23170
|
+
return null;
|
|
23171
|
+
if (restacked(row))
|
|
23172
|
+
return null;
|
|
23173
|
+
const drift = driftOf(row);
|
|
23174
|
+
return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
|
|
23175
|
+
};
|
|
23176
|
+
const basisOf = (row) => {
|
|
23177
|
+
const basis = keyBasis(row);
|
|
23178
|
+
if (basis !== "stale")
|
|
23179
|
+
return basis;
|
|
23180
|
+
return restacked(row) || carryOf(row) ? "carried" : basis;
|
|
22268
23181
|
};
|
|
22269
23182
|
const sameCode = (failed, row) => {
|
|
22270
23183
|
if (failed.head && failed.head === row.head)
|
|
@@ -22272,7 +23185,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
22272
23185
|
const base = failed.base ?? fallbackBase;
|
|
22273
23186
|
const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
|
|
22274
23187
|
const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
|
|
22275
|
-
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
23188
|
+
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
|
|
22276
23189
|
};
|
|
22277
23190
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
22278
23191
|
const failureAuthorCache = new Map;
|
|
@@ -22328,6 +23241,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
22328
23241
|
independent: independence.length === 0,
|
|
22329
23242
|
accepted: reasons.length === 0,
|
|
22330
23243
|
reasons,
|
|
23244
|
+
carry: basis === "carried" ? carryOf(row) : null,
|
|
22331
23245
|
verdict: row
|
|
22332
23246
|
});
|
|
22333
23247
|
if (reasons.length === 0) {
|
|
@@ -22356,7 +23270,12 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
22356
23270
|
verdict: failing?.verdict ?? null,
|
|
22357
23271
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
22358
23272
|
};
|
|
22359
|
-
const
|
|
23273
|
+
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))));
|
|
23274
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
23275
|
+
const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
|
|
23276
|
+
const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23277
|
+
const drift = lastPass ? driftOf(lastPass.verdict) : null;
|
|
23278
|
+
const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
|
|
22360
23279
|
return {
|
|
22361
23280
|
branch,
|
|
22362
23281
|
head,
|
|
@@ -22365,6 +23284,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
22365
23284
|
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
22366
23285
|
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
22367
23286
|
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
23287
|
+
carry: carryEntry?.carry ?? null,
|
|
23288
|
+
reverify,
|
|
22368
23289
|
accepted,
|
|
22369
23290
|
verdicts,
|
|
22370
23291
|
authors: [...authors].toSorted(),
|
|
@@ -22384,6 +23305,7 @@ var RULES = {
|
|
|
22384
23305
|
self: "verdict:self",
|
|
22385
23306
|
nonAuthor: "verdict:non-author",
|
|
22386
23307
|
verified: "verdict:verified",
|
|
23308
|
+
lane: "verdict:lane",
|
|
22387
23309
|
product: "decision:product",
|
|
22388
23310
|
plan: "plan"
|
|
22389
23311
|
};
|
|
@@ -22406,6 +23328,7 @@ var normalizedScope = (scope) => ({
|
|
|
22406
23328
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
22407
23329
|
const out = [];
|
|
22408
23330
|
const base = { scope, dependentAction: null };
|
|
23331
|
+
const proportional = verification === "proportional";
|
|
22409
23332
|
if (judgment.behaviorChange)
|
|
22410
23333
|
out.push(derived2({
|
|
22411
23334
|
...base,
|
|
@@ -22416,7 +23339,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
|
|
|
22416
23339
|
before: "close",
|
|
22417
23340
|
acceptanceAllowed: false
|
|
22418
23341
|
}));
|
|
22419
|
-
if (
|
|
23342
|
+
if (proportional && judgment.riskTier === "normal")
|
|
23343
|
+
out.push(derived2({
|
|
23344
|
+
...base,
|
|
23345
|
+
ruleId: RULES.lane,
|
|
23346
|
+
dimension: "review",
|
|
23347
|
+
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.",
|
|
23348
|
+
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.",
|
|
23349
|
+
before: "close",
|
|
23350
|
+
acceptanceAllowed: false
|
|
23351
|
+
}));
|
|
23352
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
22420
23353
|
out.push(verification === "independent" ? derived2({
|
|
22421
23354
|
...base,
|
|
22422
23355
|
ruleId: RULES.nonAuthor,
|
|
@@ -22519,7 +23452,7 @@ var productDecision = (task, ledger) => {
|
|
|
22519
23452
|
};
|
|
22520
23453
|
};
|
|
22521
23454
|
var planRecorded = (task, root) => {
|
|
22522
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" &&
|
|
23455
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path16.resolve(root, ref.path)));
|
|
22523
23456
|
if (cited)
|
|
22524
23457
|
return {
|
|
22525
23458
|
met: true,
|
|
@@ -22533,14 +23466,30 @@ var planRecorded = (task, root) => {
|
|
|
22533
23466
|
decisionIds: []
|
|
22534
23467
|
};
|
|
22535
23468
|
};
|
|
22536
|
-
var
|
|
23469
|
+
var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
|
|
23470
|
+
var laneVerdict = (task, root, ledger) => {
|
|
23471
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
23472
|
+
return {
|
|
23473
|
+
met: true,
|
|
23474
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
23475
|
+
decisionIds: []
|
|
23476
|
+
};
|
|
23477
|
+
const lane = workingLane(task, root);
|
|
23478
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
23479
|
+
const need = laneNeed(lane.lane);
|
|
23480
|
+
if (need === "none")
|
|
23481
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
23482
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
23483
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
23484
|
+
};
|
|
23485
|
+
var verdict = (ruleId, root, ledger) => {
|
|
22537
23486
|
const branch = ledger.branch();
|
|
22538
23487
|
if (!branch)
|
|
22539
23488
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
22540
23489
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
22541
23490
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
22542
23491
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
22543
|
-
const met = !failing && (
|
|
23492
|
+
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)));
|
|
22544
23493
|
if (met)
|
|
22545
23494
|
return {
|
|
22546
23495
|
met: true,
|
|
@@ -22548,7 +23497,7 @@ var verdict = (requirement, root, ledger) => {
|
|
|
22548
23497
|
decisionIds: []
|
|
22549
23498
|
};
|
|
22550
23499
|
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, " ");
|
|
22551
|
-
const unblock =
|
|
23500
|
+
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>\``;
|
|
22552
23501
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
22553
23502
|
};
|
|
22554
23503
|
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
@@ -22560,7 +23509,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
22560
23509
|
case RULES.self:
|
|
22561
23510
|
case RULES.nonAuthor:
|
|
22562
23511
|
case RULES.verified:
|
|
22563
|
-
return verdict(requirement, root, ledger);
|
|
23512
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
23513
|
+
case RULES.lane:
|
|
23514
|
+
return laneVerdict(task, root, ledger);
|
|
22564
23515
|
default:
|
|
22565
23516
|
return null;
|
|
22566
23517
|
}
|
|
@@ -22568,14 +23519,14 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
22568
23519
|
|
|
22569
23520
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
22570
23521
|
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
22571
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
22572
|
-
var relative = (root, candidate) =>
|
|
23522
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
|
|
23523
|
+
var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
|
|
22573
23524
|
var canonicalPath = (value) => {
|
|
22574
23525
|
if (typeof value !== "string" || !value)
|
|
22575
23526
|
return null;
|
|
22576
23527
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
22577
23528
|
return null;
|
|
22578
|
-
const normalized =
|
|
23529
|
+
const normalized = path17.posix.normalize(value);
|
|
22579
23530
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
22580
23531
|
return null;
|
|
22581
23532
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -22623,14 +23574,14 @@ var gitPaths = (root) => {
|
|
|
22623
23574
|
git: !/not a git repository/i.test(stderr)
|
|
22624
23575
|
};
|
|
22625
23576
|
}
|
|
22626
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
23577
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
|
|
22627
23578
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
22628
23579
|
if (stagedDeleted.status !== 0) {
|
|
22629
23580
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
22630
23581
|
if (!/not a git repository/i.test(stderr))
|
|
22631
23582
|
return { paths, uncertain: true, git: true };
|
|
22632
23583
|
} else if (stagedDeleted.stdout) {
|
|
22633
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
23584
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
|
|
22634
23585
|
}
|
|
22635
23586
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
22636
23587
|
};
|
|
@@ -22645,7 +23596,7 @@ var walk = (root, directory, output) => {
|
|
|
22645
23596
|
for (const name of names) {
|
|
22646
23597
|
if (name === ".git" || name === ".workit")
|
|
22647
23598
|
continue;
|
|
22648
|
-
const target =
|
|
23599
|
+
const target = path17.join(directory, name);
|
|
22649
23600
|
let stat;
|
|
22650
23601
|
try {
|
|
22651
23602
|
stat = fs13.lstatSync(target);
|
|
@@ -22663,12 +23614,12 @@ var walk = (root, directory, output) => {
|
|
|
22663
23614
|
var scopeRoots = (root, scope) => {
|
|
22664
23615
|
const roots = [];
|
|
22665
23616
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
22666
|
-
const target =
|
|
23617
|
+
const target = path17.resolve(root, item);
|
|
22667
23618
|
if (!inside(root, target))
|
|
22668
23619
|
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` }] });
|
|
22669
23620
|
let ancestor = target;
|
|
22670
23621
|
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
22671
|
-
ancestor =
|
|
23622
|
+
ancestor = path17.dirname(ancestor);
|
|
22672
23623
|
try {
|
|
22673
23624
|
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
22674
23625
|
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` }] });
|
|
@@ -22720,7 +23671,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
22720
23671
|
for (const item of [...names].toSorted()) {
|
|
22721
23672
|
if (!scopeMatches(item, normalizedScope))
|
|
22722
23673
|
continue;
|
|
22723
|
-
const target =
|
|
23674
|
+
const target = path17.join(checkout, item);
|
|
22724
23675
|
try {
|
|
22725
23676
|
const stat = fs13.lstatSync(target);
|
|
22726
23677
|
if (stat.isSymbolicLink()) {
|
|
@@ -22904,23 +23855,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
22904
23855
|
continue;
|
|
22905
23856
|
if (!reference.digest)
|
|
22906
23857
|
return failure2("invalid_input", "document references require a byte digest");
|
|
22907
|
-
const target =
|
|
22908
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
23858
|
+
const target = path17.resolve(root, reference.path);
|
|
23859
|
+
if (target !== root && !target.startsWith(`${root}${path17.sep}`))
|
|
22909
23860
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
22910
23861
|
fields: [
|
|
22911
23862
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
22912
23863
|
]
|
|
22913
23864
|
});
|
|
22914
23865
|
try {
|
|
22915
|
-
const below =
|
|
23866
|
+
const below = path17.relative(root, target);
|
|
22916
23867
|
let current = root;
|
|
22917
|
-
for (const segment of below.split(
|
|
23868
|
+
for (const segment of below.split(path17.sep)) {
|
|
22918
23869
|
if (!segment)
|
|
22919
23870
|
continue;
|
|
22920
|
-
current =
|
|
23871
|
+
current = path17.join(current, segment);
|
|
22921
23872
|
const stat = fs13.lstatSync(current);
|
|
22922
23873
|
const real = fs13.realpathSync(current);
|
|
22923
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
23874
|
+
if (real !== root && !real.startsWith(`${root}${path17.sep}`))
|
|
22924
23875
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
22925
23876
|
fields: [
|
|
22926
23877
|
{
|
|
@@ -23779,7 +24730,7 @@ var judgeInputSummary = (raw) => {
|
|
|
23779
24730
|
|
|
23780
24731
|
// packages/workit-core/src/autonomy.ts
|
|
23781
24732
|
import os4 from "node:os";
|
|
23782
|
-
import
|
|
24733
|
+
import path18 from "node:path";
|
|
23783
24734
|
var accountHome = () => {
|
|
23784
24735
|
try {
|
|
23785
24736
|
return os4.userInfo().homedir;
|
|
@@ -23788,10 +24739,10 @@ var accountHome = () => {
|
|
|
23788
24739
|
}
|
|
23789
24740
|
};
|
|
23790
24741
|
var grantsHome = { resolve: accountHome };
|
|
23791
|
-
var grantsDir = () =>
|
|
24742
|
+
var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
|
|
23792
24743
|
var grantsOverride = () => {
|
|
23793
|
-
const configured =
|
|
23794
|
-
return configured ===
|
|
24744
|
+
const configured = path18.resolve(resolveConfigDir());
|
|
24745
|
+
return configured === path18.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 ${path18.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
23795
24746
|
};
|
|
23796
24747
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
23797
24748
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -23844,8 +24795,16 @@ function endpointView(entry, context) {
|
|
|
23844
24795
|
...issue ? { endpointIssue: issue } : {}
|
|
23845
24796
|
};
|
|
23846
24797
|
}
|
|
23847
|
-
var
|
|
23848
|
-
|
|
24798
|
+
var VERIFICATION_MODES = [
|
|
24799
|
+
"proportional",
|
|
24800
|
+
"self",
|
|
24801
|
+
"independent"
|
|
24802
|
+
];
|
|
24803
|
+
var DEFAULT_VERIFICATION = "proportional";
|
|
24804
|
+
var verificationOf = (entry) => {
|
|
24805
|
+
const value = entry?.verification;
|
|
24806
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
24807
|
+
};
|
|
23849
24808
|
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
23850
24809
|
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
23851
24810
|
var fromAutoApprove = (value) => {
|
|
@@ -25593,8 +26552,8 @@ class WorkitCore {
|
|
|
25593
26552
|
}
|
|
25594
26553
|
// packages/workit-core/src/core/repo-context.ts
|
|
25595
26554
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
25596
|
-
import { existsSync as
|
|
25597
|
-
import
|
|
26555
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
26556
|
+
import path19 from "node:path";
|
|
25598
26557
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
25599
26558
|
try {
|
|
25600
26559
|
gitRevisionParts(value);
|
|
@@ -25728,7 +26687,7 @@ var findPrTemplate = (cwd) => {
|
|
|
25728
26687
|
]) {
|
|
25729
26688
|
const parts = rel.split("/");
|
|
25730
26689
|
const base = parts[parts.length - 1];
|
|
25731
|
-
const dir = parts.length > 1 ?
|
|
26690
|
+
const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
25732
26691
|
const actual = probeCaseInsensitive(dir, base);
|
|
25733
26692
|
if (actual)
|
|
25734
26693
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -25759,11 +26718,11 @@ var diffStatForRange = (cwd, range) => {
|
|
|
25759
26718
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
25760
26719
|
};
|
|
25761
26720
|
var packageScripts = (cwd, keys) => {
|
|
25762
|
-
const pkgPath =
|
|
25763
|
-
if (!
|
|
26721
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
26722
|
+
if (!existsSync7(pkgPath))
|
|
25764
26723
|
return [];
|
|
25765
26724
|
try {
|
|
25766
|
-
const pkg = JSON.parse(
|
|
26725
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
25767
26726
|
return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
|
|
25768
26727
|
} catch {
|
|
25769
26728
|
return [];
|
|
@@ -25785,9 +26744,9 @@ var documentationFiles = (cwd) => {
|
|
|
25785
26744
|
if (entry.isDirectory()) {
|
|
25786
26745
|
if (excluded.has(entry.name))
|
|
25787
26746
|
continue;
|
|
25788
|
-
walk(
|
|
26747
|
+
walk(path19.join(dir, entry.name), depth + 1);
|
|
25789
26748
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
25790
|
-
matches.push("./" +
|
|
26749
|
+
matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
25791
26750
|
}
|
|
25792
26751
|
}
|
|
25793
26752
|
};
|
|
@@ -25795,15 +26754,15 @@ var documentationFiles = (cwd) => {
|
|
|
25795
26754
|
return matches.toSorted().slice(0, 200);
|
|
25796
26755
|
};
|
|
25797
26756
|
var readTrimmed2 = (file, maxLines) => {
|
|
25798
|
-
if (!
|
|
26757
|
+
if (!existsSync7(file))
|
|
25799
26758
|
return "";
|
|
25800
|
-
const lines =
|
|
26759
|
+
const lines = readFileSync6(file, "utf8").split(`
|
|
25801
26760
|
`);
|
|
25802
26761
|
return lines.slice(0, maxLines).join(`
|
|
25803
26762
|
`);
|
|
25804
26763
|
};
|
|
25805
26764
|
function prReadyContext(root, range) {
|
|
25806
|
-
const cwd =
|
|
26765
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
25807
26766
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25808
26767
|
return invalidRange(cwd);
|
|
25809
26768
|
let stdout = "";
|
|
@@ -25861,7 +26820,7 @@ ${status.stderr}` : "");
|
|
|
25861
26820
|
stdout += `template_path: ${template}
|
|
25862
26821
|
|
|
25863
26822
|
`;
|
|
25864
|
-
stdout += readTrimmed2(
|
|
26823
|
+
stdout += readTrimmed2(path19.join(cwd, template), 220);
|
|
25865
26824
|
} else {
|
|
25866
26825
|
stdout += `template_path: none
|
|
25867
26826
|
|
|
@@ -25871,7 +26830,7 @@ ${status.stderr}` : "");
|
|
|
25871
26830
|
stdout += `
|
|
25872
26831
|
`;
|
|
25873
26832
|
stdout += printSection("Recent Validation Signals");
|
|
25874
|
-
if (
|
|
26833
|
+
if (existsSync7(path19.join(cwd, "package.json"))) {
|
|
25875
26834
|
stdout += `package.json detected. Common scripts:
|
|
25876
26835
|
`;
|
|
25877
26836
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -25880,7 +26839,7 @@ ${status.stderr}` : "");
|
|
|
25880
26839
|
`) + `
|
|
25881
26840
|
`;
|
|
25882
26841
|
}
|
|
25883
|
-
if (
|
|
26842
|
+
if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
25884
26843
|
stdout += `Rust project detected.
|
|
25885
26844
|
`;
|
|
25886
26845
|
}
|
|
@@ -25913,7 +26872,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
25913
26872
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
25914
26873
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
25915
26874
|
function changelogContext(root, range) {
|
|
25916
|
-
const cwd =
|
|
26875
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
25917
26876
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25918
26877
|
return invalidRange(cwd);
|
|
25919
26878
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25931,8 +26890,8 @@ function changelogContext(root, range) {
|
|
|
25931
26890
|
stdout += CHANGELOG_RULES + `
|
|
25932
26891
|
`;
|
|
25933
26892
|
stdout += printSection("Existing CHANGELOG.md");
|
|
25934
|
-
const changelogPath =
|
|
25935
|
-
if (
|
|
26893
|
+
const changelogPath = path19.join(cwd, "CHANGELOG.md");
|
|
26894
|
+
if (existsSync7(changelogPath)) {
|
|
25936
26895
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
25937
26896
|
`;
|
|
25938
26897
|
} else {
|
|
@@ -25951,7 +26910,7 @@ function changelogContext(root, range) {
|
|
|
25951
26910
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25952
26911
|
}
|
|
25953
26912
|
function docsRefreshContext(root, range) {
|
|
25954
|
-
const cwd =
|
|
26913
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
25955
26914
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25956
26915
|
return invalidRange(cwd);
|
|
25957
26916
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25973,8 +26932,8 @@ function docsRefreshContext(root, range) {
|
|
|
25973
26932
|
`) + `
|
|
25974
26933
|
`;
|
|
25975
26934
|
stdout += printSection("README Preview");
|
|
25976
|
-
const readme =
|
|
25977
|
-
if (
|
|
26935
|
+
const readme = path19.join(cwd, "README.md");
|
|
26936
|
+
if (existsSync7(readme)) {
|
|
25978
26937
|
stdout += readTrimmed2(readme, 220) + `
|
|
25979
26938
|
`;
|
|
25980
26939
|
} else {
|
|
@@ -25982,10 +26941,10 @@ function docsRefreshContext(root, range) {
|
|
|
25982
26941
|
`;
|
|
25983
26942
|
}
|
|
25984
26943
|
stdout += printSection("Package Scripts");
|
|
25985
|
-
const pkgPath =
|
|
25986
|
-
if (
|
|
26944
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
26945
|
+
if (existsSync7(pkgPath)) {
|
|
25987
26946
|
try {
|
|
25988
|
-
const pkg = JSON.parse(
|
|
26947
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
25989
26948
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
25990
26949
|
`;
|
|
25991
26950
|
} catch {}
|
|
@@ -25996,7 +26955,7 @@ function docsRefreshContext(root, range) {
|
|
|
25996
26955
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25997
26956
|
}
|
|
25998
26957
|
function releaseNotesContext(root, rangeOrTag) {
|
|
25999
|
-
const cwd =
|
|
26958
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
26000
26959
|
if (!rangeOrTag) {
|
|
26001
26960
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
26002
26961
|
`, exitCode: 1, cwd };
|
|
@@ -26034,7 +26993,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
26034
26993
|
`;
|
|
26035
26994
|
stdout += printSection("Existing Release Files");
|
|
26036
26995
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
26037
|
-
if (
|
|
26996
|
+
if (existsSync7(path19.join(cwd, rel)))
|
|
26038
26997
|
stdout += rel + `
|
|
26039
26998
|
`;
|
|
26040
26999
|
}
|
|
@@ -26066,7 +27025,7 @@ import { spawnSync as spawnSync13 } from "node:child_process";
|
|
|
26066
27025
|
|
|
26067
27026
|
// packages/workit-core/src/core/pr-create.ts
|
|
26068
27027
|
import fs14 from "node:fs";
|
|
26069
|
-
import
|
|
27028
|
+
import path20 from "node:path";
|
|
26070
27029
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
26071
27030
|
// packages/workit-core/src/forge/pr-body.ts
|
|
26072
27031
|
function parseGhIssue(value) {
|
|
@@ -26077,11 +27036,11 @@ function parseGhIssue(value) {
|
|
|
26077
27036
|
// packages/workit-core/src/core/pr-create.ts
|
|
26078
27037
|
function whichOnPath(tool) {
|
|
26079
27038
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
26080
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27039
|
+
for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
|
|
26081
27040
|
if (!dir)
|
|
26082
27041
|
continue;
|
|
26083
27042
|
for (const name of names) {
|
|
26084
|
-
const candidate =
|
|
27043
|
+
const candidate = path20.join(dir, name);
|
|
26085
27044
|
try {
|
|
26086
27045
|
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
26087
27046
|
return candidate;
|
|
@@ -26202,7 +27161,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
26202
27161
|
|
|
26203
27162
|
// packages/workit-core/src/core/youtrack.ts
|
|
26204
27163
|
import fs15 from "node:fs";
|
|
26205
|
-
import
|
|
27164
|
+
import path21 from "node:path";
|
|
26206
27165
|
|
|
26207
27166
|
// packages/workit-core/src/core/templates.ts
|
|
26208
27167
|
var repoRoot2 = assetRoot();
|
|
@@ -26215,8 +27174,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
26215
27174
|
// packages/workit-core/src/core/youtrack.ts
|
|
26216
27175
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
26217
27176
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
26218
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
26219
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27177
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(configDir(), "youtrack.json");
|
|
27178
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(resolveConfigDir(), "youtrack.json");
|
|
26220
27179
|
var youTrackTokenModeOk = (p) => {
|
|
26221
27180
|
if (process.platform === "win32")
|
|
26222
27181
|
return true;
|
|
@@ -26284,7 +27243,7 @@ var youTrackToken = () => {
|
|
|
26284
27243
|
if ("error" in loaded)
|
|
26285
27244
|
return loaded;
|
|
26286
27245
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
26287
|
-
const tokenPath = tokenFile ?
|
|
27246
|
+
const tokenPath = tokenFile ? path21.isAbsolute(tokenFile) ? path21.resolve(tokenFile) : path21.resolve(process.cwd(), tokenFile) : "";
|
|
26288
27247
|
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
26289
27248
|
return { error: "missing youtrack.token" };
|
|
26290
27249
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -26626,15 +27585,15 @@ function resolveGitRevision(root, value) {
|
|
|
26626
27585
|
}
|
|
26627
27586
|
function resolveInside(root, candidate) {
|
|
26628
27587
|
const base = realpathSync5(root);
|
|
26629
|
-
const target =
|
|
26630
|
-
if (target !== base && !target.startsWith(base +
|
|
27588
|
+
const target = path22.resolve(base, candidate);
|
|
27589
|
+
if (target !== base && !target.startsWith(base + path22.sep)) {
|
|
26631
27590
|
throw new Error("path must stay inside repository root");
|
|
26632
27591
|
}
|
|
26633
27592
|
let ancestor = target;
|
|
26634
|
-
while (!
|
|
26635
|
-
ancestor =
|
|
27593
|
+
while (!existsSync8(ancestor))
|
|
27594
|
+
ancestor = path22.dirname(ancestor);
|
|
26636
27595
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
26637
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
27596
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path22.sep)) {
|
|
26638
27597
|
throw new Error("path must stay inside repository root");
|
|
26639
27598
|
}
|
|
26640
27599
|
return target;
|
|
@@ -26682,7 +27641,7 @@ var safeCapability = (value, allowed) => allowed.includes(value) ? value : "cont
|
|
|
26682
27641
|
var TOOL_CAPABILITIES = [...CONTEXT_KINDS, "workspace", "native_caller_identity"];
|
|
26683
27642
|
var VERSION = (() => {
|
|
26684
27643
|
try {
|
|
26685
|
-
const packageJson = JSON.parse(
|
|
27644
|
+
const packageJson = JSON.parse(readFileSync7(new URL("../package.json", import.meta.url), "utf8"));
|
|
26686
27645
|
return typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
|
|
26687
27646
|
} catch {
|
|
26688
27647
|
return "0.0.0";
|
|
@@ -26692,13 +27651,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
26692
27651
|
if (!workspaceRoot)
|
|
26693
27652
|
return [];
|
|
26694
27653
|
const roots = new Set;
|
|
26695
|
-
for (const candidate of [workspaceRoot,
|
|
26696
|
-
if (
|
|
27654
|
+
for (const candidate of [workspaceRoot, path23.resolve(workspaceRoot)]) {
|
|
27655
|
+
if (path23.isAbsolute(candidate) && candidate !== path23.parse(candidate).root)
|
|
26697
27656
|
roots.add(candidate);
|
|
26698
27657
|
}
|
|
26699
27658
|
try {
|
|
26700
27659
|
const canonical = realpathSync6(workspaceRoot);
|
|
26701
|
-
if (canonical !==
|
|
27660
|
+
if (canonical !== path23.parse(canonical).root)
|
|
26702
27661
|
roots.add(canonical);
|
|
26703
27662
|
} catch {}
|
|
26704
27663
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -26780,12 +27739,12 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
26780
27739
|
};
|
|
26781
27740
|
function createMcpServer(host, contextProvider) {
|
|
26782
27741
|
assertMcpHost(host);
|
|
26783
|
-
const here =
|
|
27742
|
+
const here = path23.dirname(fileURLToPath2(import.meta.url));
|
|
26784
27743
|
const sourceMarker = markSourcesLoaded([
|
|
26785
|
-
|
|
26786
|
-
|
|
26787
|
-
|
|
26788
|
-
].filter((file) =>
|
|
27744
|
+
path23.join(here, "server.ts"),
|
|
27745
|
+
path23.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
27746
|
+
path23.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
27747
|
+
].filter((file) => existsSync9(file)));
|
|
26789
27748
|
let staleWarned = false;
|
|
26790
27749
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
26791
27750
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -26965,8 +27924,8 @@ var main = async () => {
|
|
|
26965
27924
|
process.exitCode = 2;
|
|
26966
27925
|
}
|
|
26967
27926
|
};
|
|
26968
|
-
var entryPath = process.argv[1] ?
|
|
26969
|
-
if (entryPath ===
|
|
27927
|
+
var entryPath = process.argv[1] ? path24.resolve(process.argv[1]) : "";
|
|
27928
|
+
if (entryPath === path24.resolve(fileURLToPath3(import.meta.url)))
|
|
26970
27929
|
await main();
|
|
26971
27930
|
// packages/workit-core/src/hooks/descriptor.ts
|
|
26972
27931
|
var support = (descriptor, axis) => {
|
|
@@ -26996,6 +27955,25 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
26996
27955
|
var signals = new Map;
|
|
26997
27956
|
var cache = new Map;
|
|
26998
27957
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
27958
|
+
// packages/workit-core/src/hooks/delivery-verbs.ts
|
|
27959
|
+
var WORKIT_ALLOWED_VERBS = [
|
|
27960
|
+
"git",
|
|
27961
|
+
"pr",
|
|
27962
|
+
"ci",
|
|
27963
|
+
"stack",
|
|
27964
|
+
"release",
|
|
27965
|
+
"task",
|
|
27966
|
+
"ledger",
|
|
27967
|
+
"evidence",
|
|
27968
|
+
"finding",
|
|
27969
|
+
"decision",
|
|
27970
|
+
"worker",
|
|
27971
|
+
"verify-delivery",
|
|
27972
|
+
"policy",
|
|
27973
|
+
"doctor",
|
|
27974
|
+
"help"
|
|
27975
|
+
];
|
|
27976
|
+
var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
|
|
26999
27977
|
// packages/workit-core/src/core/heavy.ts
|
|
27000
27978
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
27001
27979
|
var sub = (...names) => new Set(names);
|
|
@@ -27120,7 +28098,7 @@ var EXEC = new Set(["exec", "x", "dlx"]);
|
|
|
27120
28098
|
// packages/workit-core/src/core/heavy-words.ts
|
|
27121
28099
|
var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
|
|
27122
28100
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
27123
|
-
import
|
|
28101
|
+
import path25 from "node:path";
|
|
27124
28102
|
|
|
27125
28103
|
// packages/workit-core/src/core/live-session.ts
|
|
27126
28104
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
@@ -27161,7 +28139,7 @@ var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
|
27161
28139
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
27162
28140
|
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
27163
28141
|
var POPD2 = new Set(["popd", "pop-location"]);
|
|
27164
|
-
var UNKNOWN_DIR =
|
|
28142
|
+
var UNKNOWN_DIR = path25.resolve(path25.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
27165
28143
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
27166
28144
|
if (false) {}
|
|
27167
28145
|
|
|
@@ -27204,6 +28182,10 @@ var WRITE_TOOLS = new Set([
|
|
|
27204
28182
|
// packages/workit-core/src/hooks/standing.ts
|
|
27205
28183
|
var sinceStart = new Map;
|
|
27206
28184
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
28185
|
+
var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
28186
|
+
var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
|
28187
|
+
var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
|
|
28188
|
+
var VERBS = new Set(WORKIT_ALLOWED_VERBS);
|
|
27207
28189
|
var FILTERS = new Set(["tail", "head", "grep"]);
|
|
27208
28190
|
|
|
27209
28191
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
@@ -27387,7 +28369,7 @@ var cursorContextProvider = (workspaceRoot = process.env.WORKFLOW_WORKSPACE_ROOT
|
|
|
27387
28369
|
var stampCursorSessionStart = (stateDir = resolveStateDir()) => {
|
|
27388
28370
|
try {
|
|
27389
28371
|
mkdirSync3(stateDir, { recursive: true });
|
|
27390
|
-
writeFileSync3(
|
|
28372
|
+
writeFileSync3(path26.join(stateDir, "cursor-session-last-start"), `${JSON.stringify({ at: new Date().toISOString() })}
|
|
27391
28373
|
`);
|
|
27392
28374
|
} catch {}
|
|
27393
28375
|
};
|